arm cortex a9 cross compiling strange floating poi

2019-03-20 04:21发布

问题:

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

回答1:

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.



回答2:

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.