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

4

2 回答 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>

这是传递一个doubleinr2:r3std::coutin 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>

这会传递一个doublein d0(一个 VFP 寄存器)和std::coutin r0。观察这里r2:r3加载(由ldrd)与第二个打印输出的浮点值,即 0.0。因为动态链接ostream::operator<<(double val)需要它的参数 in 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 库,并且一切都会再次运行。

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

于 2012-10-10T13:28:21.070 回答
0

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

于 2012-10-10T12:09:18.487 回答