Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[i128] C FFI mismatch on GNU/Linux with gcc #38762

Closed
est31 opened this issue Jan 1, 2017 · 7 comments
Closed

[i128] C FFI mismatch on GNU/Linux with gcc #38762

est31 opened this issue Jan 1, 2017 · 7 comments
Labels
A-FFI Area: Foreign function interface (FFI) C-bug Category: This is a bug.

Comments

@est31
Copy link
Member

est31 commented Jan 1, 2017

Given the following setup:

test.rs:

#![feature(i128_type)]

#[repr(C)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct Foo {
    a: i128,
    b: i8,
    c: u16,
}

#[link(name = "test", kind = "static")]
extern "C" {
    fn foo(f: Foo) -> Foo;
}

fn main() {
    unsafe {
        let a = Foo { a: 1, b: 2, c: 3 };
        let b = foo(a);
        assert_eq!(a, b);
    }
}

test.c:

#include <stdint.h>

struct Foo {
    __int128 a;
    int8_t b;
    uint16_t c;
};

struct Foo foo(struct Foo foo) {
    return foo;
}

gcc version 6.2.0, rust of current master (ac5cd3b), and a 64 bit gnu/linux platform, main will panic:

thread 'main' panicked at 'assertion failed: `(left == right)` (left: `Foo { a: 139755467620616, b: 2, c: 3 }`, right: `Foo { a: 1, b: 2, c: 3 }`)'

cc #35118 (tracking issue)
cc @nagisa , me

@est31 est31 changed the title i128 C FFI mismatch on GNU/Linux with gcc [i128] C FFI mismatch on GNU/Linux with gcc Jan 1, 2017
@nagisa
Copy link
Member

nagisa commented Jan 5, 2017

139755467620616 looks suspiciously like an address. My suspicion is that there’s a level of indirection missing here?

@nagisa
Copy link
Member

nagisa commented Jan 5, 2017

The value is passed correctly to the C side, but after return memcpy value becomes wrong.

These are the signatures at the LLVM level:

; C
define void @foo(%struct.Foo* noalias nocapture sret, %struct.Foo* byval align 16) local_unnamed_addr #0;

; rust
declare void @foo(%Foo* noalias nocapture sret dereferenceable(24), %Foo* byval noalias nocapture dereferenceable(24)) unnamed_addr #2;

Concerns:

  • C asks for argument be aligned to 16 bytes, but we alloca the value with alignment of 8 (still fails even though the pointer is 16-aligned).
  • %Foo type on Rust side hasn’t the trailing padding (C type = type { i128, i8, i16, [12 x i8] }, Rust = type { i128, i8, i16 }), not sure how much it matters.

What happens here is that the memcpy on C side overwrites the padding that does not exist on the rust side.

That’s how rust instructs LLVM to allocate stack:

%sret = %Foo, aligned 8 ; sret slot (is 24 bytes)
%arg = %Foo, aligned 8 ; argument slot (is 24 bytes)
%somepointer

Then C side does memcpy(%sret, %arg, 32 bytes) and overwrites some of the space occupied by %arg with %somepointer.


The fix here is to calculate alignment correctly, which I seem to have messed up. Ugh.

#[repr(C)]
struct Foo {
    a: i128,
    b: i8,
    c: u16,
}

fn main() {
    assert_eq!(16, ::std::mem::align_of::<Foo>()); // fails currently
}

@nagisa
Copy link
Member

nagisa commented Jan 6, 2017

clang has this code snippet which ignores the data-layout of the target (probably to match sysv64 ABI. Also potentially has buggy interactions with the LLVM expectations?).

We can’t quite do that because of various assertions that the sizes and alignments match the data-layout. Any suggestions @eddyb? Is changing data-layout string an option here?

EDIT: Oh %Foo* byval noalias nocapture dereferenceable(24)) just needs an alignment attr.

@eddyb
Copy link
Member

eddyb commented Jan 6, 2017

We can add extra alignment attributes through cabi_* without a lot of work, if that's needed.

@nagisa
Copy link
Member

nagisa commented Jan 6, 2017

#38870 fixes the by-value case, but

struct Foo foo(struct Foo *a) {
    return *a;
}
let b;
let a = Foo { a: 1, b: 2, c: 3 };
b = foo(&a);
assert_eq!(a, b);

will still sigsegv(!) for the same reasons if both rustc and C sides are compiled with opts on my machine.

@nagisa
Copy link
Member

nagisa commented Jan 22, 2017

LLVM patch

@nagisa
Copy link
Member

nagisa commented Feb 10, 2017

LLVM patch has landed as llvm-mirror/llvm@d11a6cc, however only for x86_64 and PPC64, so we might eventually run into a similar issue on more obscure platforms later.

Not sure if the patch is important enough to backport into our branch as opposed to just waiting for LLVM 4.0 to get released.

EDIT: it got backed out; clang sucks.

@Mark-Simulacrum Mark-Simulacrum added the A-FFI Area: Foreign function interface (FFI) label Jun 23, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 26, 2017
@est31 est31 closed this as completed Oct 19, 2018
@rust-lang rust-lang locked and limited conversation to collaborators Oct 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-FFI Area: Foreign function interface (FFI) C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

4 participants