ARM Cortex A9交叉编译时出现奇怪的浮点行为

7

我正在尝试从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;
}

在 ARM Cortex 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

回答一些下面的评论:
- 交叉编译器配置为小端字节序,与tegra机器上的本地编译器一样。
- 我不认为这是内存对齐问题,因为我在移植到arm时遇到了这些问题,应该向应用程序发送SIGBUS或记录到syslog中,请参阅/proc/cpu/alignment的文档。

我目前的解决方法是复制交叉编译工具链并使用LD_LIBRARY_PATH来使用它...不太好,但暂时足够好。




编辑:
谢谢你们的回答。
与此同时,我发现tegra设备上的linux分发版是使用'-mfloat-abi=softfp'编译的,尽管文档指出需要使用'-mfloat-abi=hard'编译工具链。
更改工具链取得了成功。

似乎可以使用'readelf -A'在任何系统二进制文件上查看硬件浮点和软件浮点之间的区别:
如果输出包含以下行:'Tag_ABI_VFP_args: VFP registers',则是使用'-mfloat-abi=hard'进行编译。如果缺少此行,则该二进制文件很可能是使用'-mfloat-abi=softfp'编译的。
线路'Tag_ABI_HardFP_use: SP and DP'并不表示编译器标志'-mfloat-abi=hard'。


1
在开关后添加“-v”也会显示隐式标志。 - dbrank0
1
你读过这个吗?http://gcc.gnu.org/onlinedocs/gccint/Floating-Point.html - plan9assembler
你能运行并发帖 "arm-cortex_a9-linux-gnueabi-objdump -S test_*" 的结果吗? - auselen
不,Tegra使用: gcc版本4.5.2(Ubuntu/Linaro 4.5.2-8ubuntu4)交叉编译器使用: gcc版本4.5.3(crosstool-NG 1.16.0) - user982210
1
@std''OrgnlDave但是0是特殊的,它由所有零位表示。没有任何重新排列的方法不会导致0。我认为更可能是ABI不匹配。 - Mark Ransom
显示剩余7条评论
2个回答

4

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

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>

这里是将一个double传递到r2:r3中,并将std::cout传递到r0中。

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>

这里将一个 double 类型的值传递给了 d0(VFP 寄存器),并将 std::cout 传递给了 r0。需要注意的是,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的库,一切都可以正常工作。

我的建议是找出设备和编译器库不同的原因,并解决这个问题。


太酷了!我认为OP可以使用arm-cortex_a9-linux-gnueabi-g++ test.cpp -mfloat-abi=softfp -o test_cc进行验证(看起来交叉编译工具链已经默认设置为硬件) - auselen

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接