Skip to content

fastffi attribute is sometimes ignored or incorrectly assigned #6660

Description

@nikomatsakis

The following test, originally written for #3678, results in a segfault because insufficient stack is allocated (at least it does on the branch I plan to land that fixes #3678).

// Test what happens when we link to the same function name with
// different types. This situation can easily arise when linking two
// crates that make use of the same C library, for example.

mod A {
    pub struct TwoU32s {
        one: u32, two: u32
    }
    extern {
        pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
    }
}

mod B {
    pub struct TwoU32s {
        one: u32, two: u32
    }
    extern {
        pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
    }
}

fn wrapper() {
}

fn main() {
    unsafe {
        let a = A::rust_dbg_extern_return_TwoU32s();
        // println(fmt!("%?", a));
        // assert_eq!(a.one, 10);
        // assert_eq!(a.two, 20);
    }

    unsafe {
        let b = B::rust_dbg_extern_return_TwoU32s();
        // println(fmt!("%?", b));
        // assert_eq!(b.one, 10);
        // assert_eq!(b.two, 20);
    }
}

Paging @pcwalton, maybe you could take a look (once the PR for #3678 lands?)

Activity

  1. graydon commented on Aug 15, 2013

    @graydon
    Contributor

    I'm not entirely clear what's going on here. Both functions appear to have the same type but the testcase says it's testing them being different. Can you clarify?

  2. nikomatsakis commented on Aug 15, 2013

    @nikomatsakis
    ContributorAuthor

    Woah, I forgot about this bug. This is an interesting test case. It has nothing to do with fastffi. In fact, i'm not sure why it crashes, it kind of .. looks like an LLVM bug? But something fishy is definitely going on. Here is an updated test:

    // Test what happens when we link to the same function name with
    // different types. This situation can easily arise when linking two
    // crates that make use of the same C library, for example.
    
    mod A {
        pub struct TwoU32s {
            one: u32, two: u32
        }
        extern {
            pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
        }
    }
    
    mod B {
        pub struct TwoU32s {
            one: u32, two: u32
        }
        extern {
            pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
        }
    }
    
    fn wrapper() {
    }
    
    #[fixed_stack_segment]
    fn main() {
        unsafe {
            let a = A::rust_dbg_extern_return_TwoU32s();
        }
    
        unsafe {
            let b = B::rust_dbg_extern_return_TwoU32s();
        }
    }
    

    On my branch for #8535, this code crashes when main tries to return to an invalid address. This only happens if BOTH calls are present. The assembly code shows that between the two calls, there is a subtract of $esp which misaligns the stack frame (look for // XXX):

        subl    $36, %esp
    .Ltmp12:
        .cfi_offset %ebx, -12
        calll   .L1$pb
    .L1$pb:
        popl    %eax
    .Ltmp13:
        addl    $_GLOBAL_OFFSET_TABLE_+(.Ltmp13-.L1$pb), %eax
        leal    -16(%ebp), %ecx
        movl    %ecx, (%esp)
        movl    %eax, %ebx
        movl    %eax, -28(%ebp)
        calll   rust_dbg_extern_return_TwoU32s@PLT
        subl    $4, %esp // XXX
        leal    -24(%ebp), %eax
        movl    %eax, (%esp)
        movl    -28(%ebp), %ebx
        calll   rust_dbg_extern_return_TwoU32s@PLT
        addl    $36, %esp
        popl    %ebx
        popl    %ebp
        ret
    

    However, when I dump the LLVM output with --emit-llvm -S, I get nonsense:

    
    ; Function Attrs: fixedstacksegment noinline uwtable
    define void @"_ZN4main16_cd92990fc38403c7_0$x2e0E"({ i32, %tydesc*, i8*, i8*, i8 }*) #1 {
    "function top level":
      %a = alloca %"struct.A::TwoU32s"
      %b = alloca %"struct.B::TwoU32s"
      call void @rust_dbg_extern_return_TwoU32s(%"struct.A::TwoU32s"* %a)
      call void bitcast (void (%"struct.A::TwoU32s"*)* @rust_dbg_extern_return_TwoU32s to void (%"struct.B::TwoU32s"*)*)(%"struct.B::TwoU32s"* %b)
      ret void
    }
    

    There is only one call here at all?

    Anyway, an interesting bug. (note that I am running on 32 bit linux, btw, which may be relevant)

  3. luqmana commented on Aug 16, 2013

    @luqmana
    Contributor

    Should it be casting the whole function as opposed to just the argument in the second call?

  4. nikomatsakis commented on Aug 16, 2013

    @nikomatsakis
    ContributorAuthor

    Oh, wait, you're right, I misread that LLVM code, there are in fact two calls. I'd say it... looks about right, actually. Yes, it should be casting the function.

  5. thestinger commented on Oct 8, 2013

    @thestinger
    Contributor

    This is no longer used.

  6. added a commit that references this issue on Feb 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-FFIArea: Foreign function interface (FFI)A-codegenArea: Code generationI-crashIssue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions