为 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 个合理的选项,按严重程度大致递减排列:
- Link 交叉编译时是静态的。
- 将交叉工具链的 hard-float 库放在设备文件系统的某个地方,您可以在其中交叉编译的程序找到它们(可能求助于 LD_LIBRARY_PATH)——显然您不能简单地替换现有的库或对于其余已安装的程序,您会得到反向 ABI 不匹配。
- 将设备上的系统库复制到您的主机上,这样您就可以将交叉 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 反对其库的软浮动版本,如果它支持的话。
我在使用 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 个合理的选项,按严重程度大致递减排列:
- Link 交叉编译时是静态的。
- 将交叉工具链的 hard-float 库放在设备文件系统的某个地方,您可以在其中交叉编译的程序找到它们(可能求助于 LD_LIBRARY_PATH)——显然您不能简单地替换现有的库或对于其余已安装的程序,您会得到反向 ABI 不匹配。
- 将设备上的系统库复制到您的主机上,这样您就可以将交叉 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 反对其库的软浮动版本,如果它支持的话。