Skip to content

FFI: wrong argument passing order on linux x86_64 C ABI #25594

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

Closed
chyh1990 opened this issue May 19, 2015 · 2 comments
Closed

FFI: wrong argument passing order on linux x86_64 C ABI #25594

chyh1990 opened this issue May 19, 2015 · 2 comments
Assignees
Labels
A-codegen Area: Code generation

Comments

@chyh1990
Copy link

On linux x86_64, FFI passes arguments in wrong order if the function has more than 6 arguments, with struct (passed by value) mixed.

Reproduce

bug.c

#include <stdio.h>

struct Rect {
        int l;
        int t;
        int r;
        int b;
};

void bug(int a, int b, int c, int d,
        int e, struct Rect f, int g, int h) {
        fprintf(stderr, "%d %d %d %d %d\n",
                a, b, c, d, e);
        fprintf(stderr, "%d %d %d %d\n",
                f.l, f.t, f.r, f.b);
        fprintf(stderr, "%d %d\n", g, h);
}

bug.rs

#[repr(C)]
struct Rect {
    l: i32,
    t: i32,
    r: i32,
    b: i32,
}

#[link(name = "bug")]
extern "C" {
    fn bug(a: i32, b: i32, c: i32, d: i32,
           e: i32, f: Rect, g: i32, h: i32);
}

fn main() {
    let r = Rect {
        l: 10,
        t: 20,
        r: 30,
        b: 40
    };
    unsafe { bug(1, 2, 3, 4, 5, r, 6, 7); }
}
gcc -fPIC -o libbug.so -shared bug.c
rustc -L. bug.rs
./bug

Rust version

rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
binary: rustc
commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
commit-date: 2015-05-13
build-date: 2015-05-14
host: x86_64-unknown-linux-gnu
release: 1.0.0

Expected output:
1 2 3 4 5
10 20 30 40
6 7

Actual output:
1 2 3 4 5
30 40 6 32740
10 7

Reason

Only 6 arguments can be passed by registers (see http://www.x86-64.org/documentation/abi.pdf).
In the above example, a, b, c, d, e, g should be passed in registers, f & h should be passed on stack.

But Rust FFI mistakenly passes a, b, c, d, e and lower 8bytes of f in registers. This is wrong
according to abi.pdf, page 20:

If there are no registers available for any eightbyte of an argument, the whole
argument is passed on the stack. If registers have already been assigned for some
eightbytes of such an argument, the assignments get reverted.

@SimonSapin
Copy link
Contributor

I’ve (probably) hit this in html5ever-python. I worked around it by adding a dummy usize padding argument to my callback so that (presumably) a struct would not be split between registers and the stack.

Debugging details
Work around commit

@SimonSapin
Copy link
Contributor

A comment about this by Armin Rigo, Python-CFFI and PyPy developer:

Argh, the x86-64 calling convention is full of obscure details. Fwiw, cffi uses libffi to do that. In PyPy the JIT does it directly but only if the arguments are only plain integers/pointers/floats; for more complicated cases involving structs it falls back to libffi too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation
Projects
None yet
Development

No branches or pull requests

4 participants