手臂皮层a9交叉编译奇怪的浮点行为

arm cortex a9 cross compiling strange floating point behaviour

本文关键字:皮层 a9 交叉编译      更新时间:2023-10-16

我正试图将一个更大的应用程序从x86移植到arm cortex a9,但在交叉编译应用程序时,我遇到了像modf这样的浮点函数的奇怪分割错误,其他libc++函数似乎只是错误地处理了浮点,但不会崩溃(见下文)。

所以我尝试了这个小测试程序,它也可以触发错误。测试程序的输出(见下文)应该证明了我的问题。

#include <iostream>
int main(int argc, char *argv[])
{
double x = 80;
double y = 0;
std::cout << x << "t" << y << std::endl;
return 0;
}

在手臂皮层a9:上编译

@tegra$ g++ -Wall test.cpp -o test_nativ
@tegra$ ./test_nativ 
80      0

交叉编译

@x86$ arm-cortex_a9-linux-gnueabi-g++ test.cpp  -o test_cc
@tegra$ ./test_cc
0       1.47895e-309

使用"-static"链接器选项交叉编译。

@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

为了回答下面的一些评论:
-交叉编译器是为little-endian设置的,tegra机器上的本机编译器也是如此
-我不认为这是内存对齐问题,在移植到arm时我也有这些问题,这些应该将SIGBUS发送到应用程序或日志发送到syslog,请参阅/proc/cpu/alignment的文档

我目前的解决方法是复制交叉编译的工具链,并将其与LD_LIBRARY_PATH一起使用。。。不太好,但目前已经足够好了



编辑:
感谢您的回答
与此同时,我发现tegra设备上的linux发行版是用"-mfloat abi=softfp"编译的,尽管文档中指出,需要用"-mfloat abi=hard"编译工具链。
改变工具链带来了成功。

似乎可以在任何系统二进制文件上使用"readelf-A"来查看hard和softfp之间的区别:
如果输出包含行:"Tag_ABI_VFP_args:VFP registers",则使用"-mfloat ABI=hard"进行编译。如果这一行缺失,二进制文件很可能是用"-mfloat abi=softfp"编译的。
行"Tag_ABI_HardFP_use:SP and DP"不表示compilerflag"-mfloat ABI=hard"。

查看汇编输出,我们可以看到这两个文件中存在差异。

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>

这在r2:r3中传递一个double,在r0中传递std::cout

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>

这使d0(VFP寄存器)中的doubler0中的std::cout通过。注意,r2:r3(由ldrd)加载了第二次打印出来的浮点值,即0.0。因为动态链接的ostream::operator<<(double val)r2:r3中期望其参数,所以0首先被打印出来。

我也能解释第二个看起来很奇怪的浮子。这里是第二个浮动的打印位置:

8708:       e1a03000        mov     r3, r0
870c:       e1a00003        mov     r0, r3
8710:       ed1b0b05        vldr    d0, [fp, #-20]  ; 0xffffffec
8714:       ebffff9c        bl      858c <_init+0x20>

请注意,r3被设置为r0,即cout的地址。从上面看,r0 = 0x011040。因此,寄存器对r2:r3变为0x0001104000000000,其解码为1.478946186471156e-309作为双。

因此,问题是桌面GCC的库使用VFP/NEON指令,而设备上的动态库不使用这些指令。如果您使用-static,您将获得VFP/NEON库,并且一切都可以正常工作。

我的建议只是弄清楚为什么设备库和编译器库不同,并解决这个问题。

My猜测:如果没有适当的开关来指示vfp硬件支持,编译器将使用软件库在arm上进行浮点运算。如果使用静态链接进行编译,这些库将被包含在二进制文件中——结果:它可以工作。如果您使用正常(动态)链接模式,则库不会被包括在内——结果:由于某种原因,它不起作用。tegra系统上的库在某种程度上与交叉编译器生成的库不兼容(可能是由于调用约定)。