将 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的主要内容,如果未能解决你的问题,请参考以下文章

如何将javascript代码编译为c++或java

为什么.o(目标文件)链接速度比.lib(静态库)快?

非托管 C++ 加密字符串转换为 C# byte[]

是否有可以将 Visual Studio 目标文件转换为 GCC 格式的工具?

将十六进制转换为从文件 C++ 读取的 ASCII 的正确方法

使用 emscripten 将 c++ 文件转换为 wasm 时出错