为 ARM 交叉编译时未初始化特征向量

Eigen vectors not initialized when cross-compiled for ARM

我在使用 arm-linux-gnueabihf-g++(Linaro 的 gcc 4.8 版)的交叉编译程序上使用 Eigen3。目标平台是 gumstix 的 duovero,使用 Poky 发行版 - ARMv7。当我 运行 使用 Eigen 代码的程序时,我在 Eigen 对象上得到了非常奇怪的值(请参阅此 post 末尾的输出示例)。

我试过关闭矢量化,我试过所有这些标志

-marm 
-mcpu=cortex-a7
-mfpu=neon 
-mfloat-abi=hard 

但总是得到相同的行为。如果我在 duovero 上编译相同的代码,它可以正常工作(向量已正确初始化),但在我交叉编译时却不行。我什至从不同的主机(windows7 和 ubuntu 14.04)交叉编译。

知道为什么会这样吗?

这是我的简单程序(根据评论更新)

#include <iostream>
using namespace std;

#include <stdio.h>

#include <Eigen/Eigen>
#include <Eigen/Dense>
using namespace Eigen;

int main()
{
    cout << "Hello World!" << endl;
    int j =3;
    cout << j << endl << endl; // ok

    float k =4.2;
    cout << k << endl << endl; // not ok

    printf("%f\n\n", k ); // ok

    Vector3d test1;
    test1 << 1.2, 2.3, 3.4;
    cout << test1 << endl << endl; // not ok

    printf("%f\n\n", test1(0) ); // ok

    Vector3d test2(1,2,3);
    cout << test2 << endl; // not ok
    cout << test2(1) << endl << endl; // not ok

    printf("%f\n\n", test2(0) ); // ok

    cout << 0.5f << endl; // not ok
    printf("%f\n\n", 0.5f ); // ok

    return 0;
}

这是我得到的输出(更新)

Hello World!
3

0

4.200000

-1.24694e-06
-1.24695e-06
-1.24695e-06

1.200000

-1.24692e-06
-1.24692e-06
-1.24693e-06
3.8852e+68

1.000000

0
0.500000

编辑 当我添加标志时:-mfloat-abi=soft 我得到这个错误

arm-linux-gnueabihf-g++ -c -mfloat-abi=soft -g -Wall -W -fPIE  -IC:\tmp\testingEigen -I. -IC:\COSMOS\source\thirdparty\arm\eigen3 -IC:\Qt.4\mingw491_32\mkspecs\linux-arm-gnueabihf-g++ -o main.obj C:\tmp\testingEigen\main.cpp
arm-linux-gnueabihf-g++  -o testingEigen main.obj    
c:/program files (x86)/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2014.01/bin/../lib/gcc/arm-linux-gnueabihf/4.8.3/../../../../arm-linux-gnueabihf/bin/ld.exe: error: testingEigen uses VFP register arguments, main.obj does not
makefile:79: recipe for target 'testingEigen' failed
c:/program files (x86)/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2014.01/bin/../lib/gcc/arm-linux-gnueabihf/4.8.3/../../../../arm-linux-gnueabihf/bin/ld.exe: failed to merge target specific data of file main.obj

更新: 我尝试了 Notlikethat 的建议。测试了库(例如 readelf -h /usr/lib/libstdc++.so.6.0.18),发现当前构建的绝对是软浮点 ABI。

当我 link 静态地我的代码运行良好时(即使交叉编译器用于硬浮点,这也是有效的,因为即使图像配置为软 fp,硬件实际上有一个 FPU)。接下来我做的是找到一个能够做 softfp 的交叉编译器,当我添加标志时它也能工作。我是从 https://launchpad.net/linaro-toolchain-binaries/+milestone/2012.04 下载的。

我想我的下一步是为可以进行硬浮动的 duovero 编译一个狭小的图像。有人做过吗?

最后更新: 实际上,我刚刚使用这些说明 https://github.com/gumstix/yocto-manifest

为 duovero 从 yocto (poky 1.7) 编译了最新的 poky 图像

并意识到此构建使用硬 fp。现在我的交叉编译器 (arm-linux-gnueabihf-g++) 和目标具有相同的浮点配置,我的代码与 Eigen 和其他一切都运行完美!快乐的! :)

问题似乎是您的设备上有一个 soft-float libstdc++(和朋友)。

考虑一个看似无害的函数,如 std::ostream::operator<<(float) - 当您使用 hard-float 工具链进行交叉编译时,您生成的代码会将浮点数传递给 FPU 寄存器中的函数。 static linker 知道的足够多,可以仔细检查它是否与它 linking 所针对的库相匹配(与交叉工具链本身捆绑在一起的硬浮动库)。然后你把那个二进制文件 运行 放在设备上...

动态 linker 不太聪明,它只是要确保它找到的任何 libstdc++ 都提供程序要求的符号。它找到了一个符合该法案的足够相似的版本,所以一切看起来都很好。除了现在你遇到了这样的情况,你的代码将 float 参数传递给 FPU 寄存器中的库函数,但是库函数(是软浮点数)期望它们在通用寄存器中的 float 参数,所以它们会找到未初始化的垃圾。

最好的办法是完全避免 "link against one library, run against another" 不匹配,有 3 个合理的选项,按严重程度大致递减排列:

  1. Link 交叉编译时是静态的。
  2. 将交叉工具链的 hard-float 库放在设备文件系统的某个地方,您可以在其中交叉编译的程序找到它们(可能求助于 LD_LIBRARY_PATH)——显然您不能简单地替换现有的库或对于其余已安装的程序,您会得到反向 ABI 不匹配。
  3. 将设备上的系统库复制到您的主机上,这样您就可以将交叉 linker 指向它们而不是其捆绑的库,并与 --mfloat-abi=soft 交叉编译。

我通常会建议选项 3 作为一般情况,但是 hard-float 确实有一些性能优势,因此对于浮点密集型代码,因此可能值得付出一些努力来获得它上班。

请注意,虽然一些硬浮动(即 "arm-linux-gnueabihf-")交叉工具链还包括通过 multilib 的软浮动库,但其他工具链(如我使用的 Linaro 库)要么没有,要么只启用它们对于代表古代目标的选项的特定组合,您可能不想要。如果您 确实 有一个合适的 multilib 工具链,这就是 @TurboJ 在评论中的建议 - 使用 arm-linux-gnueabihf-gcc 作为 link 命令( not arm-linux-gnueabihf-ld) 指定 --mfloat-abi=soft 会告诉它 link 反对其库的软浮动版本,如果它支持的话。