Description
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.