将 C++ 目标文件从 linux .o 转换为 Windows .obj
Posted
技术标签:
【中文标题】将 C++ 目标文件从 linux .o 转换为 Windows .obj【英文标题】:Converting C++ object file from linux .o to Windows .obj 【发布时间】:2011-01-22 22:02:26 【问题描述】:如果这听起来很疯狂,我很抱歉。无论如何我可以将从 g++ 编译器获得的 .o 文件转换为与 Visual Studio 兼容的 *obj。
This 是我考虑进行此转换的原因。
【问题讨论】:
我怀疑这是可能的,但我很想听听答案。 Visual Studio 自带编译器。只需使用它将源文件转换为与 Visual Studio 兼容的目标文件。 @Rafid,这是可能的。看我的回答。我已经为我的 GEMM 代码和我的 Mandelbrot 集代码做了这个。在这两种情况下,在 Visual Studio 中使用 GCC 编译的对象要快得多,因为 GCC 优化得更好。 @DavidHeffernan,GCC 比 Visual Studio 编译器优化得更好,那么在需要 Visual Studio 性能的区域使用 GCC 有什么问题? @Zboson,很有趣!你值得 +1! 【参考方案1】:有办法做到这一点,而且并不难。
您需要了解的主要内容是函数调用约定、对象格式和函数名称修改。
函数调用约定。
在 32 位模式下,Windows 和 Unix(即 Linux、BSD、Mac OS X...)使用相同的函数调用约定。
在 64 位模式下,Windows 和 Unix 使用不同的函数调用约定。为了使您使用 GCC 编译的目标文件能够在 64 位模式下与 MSVC 一起使用,您必须使用 Windows 函数调用约定。要使用 gcc 执行此操作,您可以使用 mabi=ms
例如:
g++ -c -mabi=ms -mavx -fopenmp -O3 foo.cpp
对象文件格式
Linux 的目标文件格式是 ELF,Windows 的目标文件格式是 COFF/PE。为了在 MSVC 中使用 GCC 编译的对象,需要将其从 ELF 转换为 COFF。为此,您需要一个目标文件转换器。我使用 Agner Fog 的objconv。例如要将 ELF64 转换为 64 位 COFF64 (PE32+),请执行以下操作:
objconv -fcoff64 foo.o foo.obj
函数名称修改
由于函数重载,C++ 会破坏函数名称。 GCC 和 MSVC 以不同的方式执行此操作。要解决此问题,您可以使用 external "C"
继续函数名称。
有关调用约定、对象格式和函数名称修改的更多详细信息,请参阅 Agner Fog 的手册 calling conventions。
下面是我用 GCC 编译然后在 MSVC (because GCC optimized it better) 中使用的模块。我用-mabi=ms
编译它,用objconv
将它转换为COFF64,然后将它链接到完美运行的Visual Studio。
#include <immintrin.h>
extern "C" void inner(const int n, const float *a, const float *b, float *c, const int stridea, const int strideb, const int stridec)
const int vec_size = 8;
__m256 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
tmp0 = _mm256_loadu_ps(&c[0*vec_size]);
tmp1 = _mm256_loadu_ps(&c[1*vec_size]);
tmp2 = _mm256_loadu_ps(&c[2*vec_size]);
tmp3 = _mm256_loadu_ps(&c[3*vec_size]);
tmp4 = _mm256_loadu_ps(&c[4*vec_size]);
tmp5 = _mm256_loadu_ps(&c[5*vec_size]);
tmp6 = _mm256_loadu_ps(&c[6*vec_size]);
tmp7 = _mm256_loadu_ps(&c[7*vec_size]);
for(int i=0; i<n; i++)
__m256 areg0 = _mm256_set1_ps(a[i]);
__m256 breg0 = _mm256_loadu_ps(&b[vec_size*(8*i + 0)]);
tmp0 = _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0);
__m256 breg1 = _mm256_loadu_ps(&b[vec_size*(8*i + 1)]);
tmp1 = _mm256_add_ps(_mm256_mul_ps(areg0,breg1), tmp1);
__m256 breg2 = _mm256_loadu_ps(&b[vec_size*(8*i + 2)]);
tmp2 = _mm256_add_ps(_mm256_mul_ps(areg0,breg2), tmp2);
__m256 breg3 = _mm256_loadu_ps(&b[vec_size*(8*i + 3)]);
tmp3 = _mm256_add_ps(_mm256_mul_ps(areg0,breg3), tmp3);
__m256 breg4 = _mm256_loadu_ps(&b[vec_size*(8*i + 4)]);
tmp4 = _mm256_add_ps(_mm256_mul_ps(areg0,breg4), tmp4);
__m256 breg5 = _mm256_loadu_ps(&b[vec_size*(8*i + 5)]);
tmp5 = _mm256_add_ps(_mm256_mul_ps(areg0,breg5), tmp5);
__m256 breg6 = _mm256_loadu_ps(&b[vec_size*(8*i + 6)]);
tmp6 = _mm256_add_ps(_mm256_mul_ps(areg0,breg6), tmp6);
__m256 breg7 = _mm256_loadu_ps(&b[vec_size*(8*i + 7)]);
tmp7 = _mm256_add_ps(_mm256_mul_ps(areg0,breg7), tmp7);
_mm256_storeu_ps(&c[0*vec_size], tmp0);
_mm256_storeu_ps(&c[1*vec_size], tmp1);
_mm256_storeu_ps(&c[2*vec_size], tmp2);
_mm256_storeu_ps(&c[3*vec_size], tmp3);
_mm256_storeu_ps(&c[4*vec_size], tmp4);
_mm256_storeu_ps(&c[5*vec_size], tmp5);
_mm256_storeu_ps(&c[6*vec_size], tmp6);
_mm256_storeu_ps(&c[7*vec_size], tmp7);
【讨论】:
C++ vtable 格式的差异呢?据我所知,您的解决方案仅适用于 C 或类似 C 的 C++(没有类,特别是具有多态性的类)代码。 @VestniK,我不知道。我只使用了这个“技巧”,这样我就不必学习 64 位汇编来让 MSVC 做我想做的事(GCC 已经在做)。我只测试了这个答案中的例子。【参考方案2】:嗯,从技术上讲,作为 gnu bintuils 一部分的 objcopy 命令可能能够做到这一点。 然而,这是一个巨大的然而,转换格式是不够的。您需要一个 g++ 编译器版本,该版本具有与 vc++ 一样的精确调用约定和名称修饰,对于初学者来说,以及编译结构的相同想法等等。
将文件物理转换为有效的 .obj 文件是可能的,但它对您的帮助可能不是很大。
【讨论】:
【参考方案3】:不,没有办法,尤其是 .o 文件不是在 Linux 上使用交叉编译器编译的。无论如何,这听起来是一种非常奇怪的解决单个链接错误的方法。
【讨论】:
以上是关于将 C++ 目标文件从 linux .o 转换为 Windows .obj的主要内容,如果未能解决你的问题,请参考以下文章
是否有可以将 Visual Studio 目标文件转换为 GCC 格式的工具?