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'.
My guess: Without proper switches indicating vfp hardware support, the compiler will use software libraries to do floating point math on arm. If you compile with static linking, these libraries will get included into binary -- result: it works. If you use normal (dynamic) linking mode the libraries do not get included -- result: it does not work for some reason. The libraries on your tegra system are somehow incompatibile (probably due to calling convention) with what your crosscompiler is producing.
Looking at the assembly output, we can see a discrepancy in the two files.
In
test_nativ
:This is passing a
double
inr2:r3
, andstd::cout
inr0
.In
test_cc
:This passes a
double
ind0
(a VFP register), andstd::cout
inr0
. Observe here thatr2:r3
is loaded (byldrd
) with the floating point value that is printed out second, i.e. 0.0. Because the dynamically-linkedostream::operator<<(double val)
expects its argument inr2:r3
, 0 is printed out first.I can explain the second weird-looking float too. Here's where the second float is printed:
See that
r3
is set tor0
, the address ofcout
. From above,r0 = 0x011040
. Thus, the register pairr2: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.