I am trying to port a larger application from x86 to arm cortex a9, but I'm getting strange segmentation faults with floating point functions like modf when cross compiling the application, other libc++ functions just seem to handle floats wrong, but don't crash(see below).
So I tried this small test programm, which can trigger the error too. The output of the test programm(see below) should demonstrate my problem.
#include <iostream>
int main(int argc, char *argv[])
{
double x = 80;
double y = 0;
std::cout << x << "\t" << y << std::endl;
return 0;
}
compiled on arm cortex a9:
@tegra$ g++ -Wall test.cpp -o test_nativ
@tegra$ ./test_nativ
80 0
cross compiled
@x86$ arm-cortex_a9-linux-gnueabi-g++ test.cpp -o test_cc
@tegra$ ./test_cc
0 1.47895e-309
cross compiled with '-static' linker option.
@x86$ arm-cortex_a9-linux-gnueabi-g++ -static test.cpp -o test_cc_static
@tegra$ ./test_cc_static
80 0
.
@x86$ arm-cortex_a9-linux-gnueabi-objdump -S test_cc
see: http://pastebin.com/3kqHHLgQ
@tegra$ objdump -S test_nativ
see: http://pastebin.com/zK35KL4X
.
To answer some of the comments below:
- Cross compiler is setup for little endian, as is the native compiler on the tegra machine.
- I don't believe its a memory alignment issue, had my share of these while porting to arm and these should send SIGBUS to application or log to syslog, see documentation for /proc/cpu/alignment.
My current workaround is to copy over the crosscompiled toolchain and use it with LD_LIBRARY_PATH ... not nice, but good enough for the time being.
Edit:
Thank you for your Answers.
In the meantime I found out that the linux distribution on the tegra device was compiled with '-mfloat-abi=softfp' though the documentation stated, that a toolchain compiled with '-mfloat-abi=hard' is required.
Changing the toolchain brought the success.
It seems that the difference between hard and softfp can be seen using 'readelf -A' on any system binary:
If the Output contains the line: 'Tag_ABI_VFP_args: VFP registers' it is compiled with '-mfloat-abi=hard'. If this line is missing the binary is most likely compiled with '-mfloat-abi=softfp'.
The line 'Tag_ABI_HardFP_use: SP and DP' does not indicate the compilerflag '-mfloat-abi=hard'.
Looking at the assembly output, we can see a discrepancy in the two files.
In test_nativ
:
86ec: 4602 mov r2, r0
86ee: 460b mov r3, r1
86f0: f241 0044 movw r0, #4164 ; 0x1044
86f4: f2c0 0001 movt r0, #1
86f8: f7ff ef5c blx 85b4 <_init+0x20>
This is passing a double
in r2:r3
, and std::cout
in r0
.
In test_cc
:
86d8: e28f3068 add r3, pc, #104 ; 0x68
86dc: e1c320d0 ldrd r2, [r3]
86e0: e14b21f4 strd r2, [fp, #-20] ; 0xffffffec
86e4: e3010040 movw r0, #4160 ; 0x1040
86e8: e3400001 movt r0, #1
86ec: ed1b0b03 vldr d0, [fp, #-12]
86f0: ebffffa5 bl 858c <_init+0x20>
This passes a double
in d0
(a VFP register), and std::cout
in r0
. Observe here that r2:r3
is loaded (by ldrd
) with the floating point value that is printed out second, i.e. 0.0. Because the dynamically-linked ostream::operator<<(double val)
expects its argument in r2:r3
, 0 is printed out first.
I can explain the second weird-looking float too. Here's where the second float is printed:
8708: e1a03000 mov r3, r0
870c: e1a00003 mov r0, r3
8710: ed1b0b05 vldr d0, [fp, #-20] ; 0xffffffec
8714: ebffff9c bl 858c <_init+0x20>
See that r3
is set to r0
, the address of cout
. From above, r0 = 0x011040
. Thus, the register pair r2:r3
becomes 0x0001104000000000, which decodes to 1.478946186471156e-309 as a double.
So the problem is that your desktop GCC's libraries uses VFP/NEON instructions, which are not used by the on-device dynamic libraries. If you use -static
, you get the VFP/NEON libraries, and everything works again.
My suggestion would just be to figure out why the device and compiler libraries differ, and get that sorted out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With