10

I'm sorry if this sounds crazy. Is there anyway I can convert .o file that I get from g++ compiler to *obj that is compatible with Visual Studio.

This is the reason I am considering doing this conversion.

Community
  • 1
  • 1
softwarematter
  • 28,015
  • 64
  • 169
  • 263
  • 2
    I doubt this is possible, but I would be interested in hearing an answer. – Rafid Jan 22 '11 at 22:08
  • 5
    Visual studio comes with a compiler. Just use that to convert your source file into an object file compatible with Visual Studio. – David Heffernan Jan 22 '11 at 22:29
  • @Rafid, this is possible. See my answer. I have done this for my GEMM code and my Mandelbrot set code. In both cases using the object compiled by GCC in Visual Studio is a lot faster because GCC optimizes better. – Z boson Jan 19 '14 at 19:14
  • 1
    @DavidHeffernan, GCC optimizes better than the Visual Studio compiler so what's wrong with using GCC in the regions you need performance in Visual Studio? – Z boson Jan 19 '14 at 19:17
  • @Zboson, interesting! You deserve +1! – Rafid Jan 23 '14 at 08:58

3 Answers3

15

There is a way to do this and it's not so difficult.

The main things you need to know about are the function calling conventions, the object format, and function name mangling.

Function Calling Conventions.

In 32-bit mode Windows and Unix (i.e. Linux, BSD, Mac OS X,...) use the same function calling conventions.

In 64-bit mode Windows and Unix use different function calling conventions. In order for your object file compiled with GCC to work with MSVC in 64 bit mode you must use the Windows function calling convention. To do this with gcc you can use mabi=ms for example:

g++ -c -mabi=ms -mavx -fopenmp -O3 foo.cpp

The Object File Format

The object file format for Linux is ELF and for windows it's COFF/PE. In order to use an object compiled with GCC in MSVC it needs to be converted from ELF to COFF. To to this you need an object file converter. I use Agner Fog's objconv. For example to convert from ELF64 to 64-bit COFF64 (PE32+) do:

objconv -fcoff64 foo.o foo.obj

Function Name Mangling

Due to function overloading C++ mangles the function names. GCC and MSVC do this differently. To get around this you can proceed the function name with external "C".

More details of the calling conventions, object format, and function name mangling can be found in Agner Fog's manual calling conventions.

Below is a module I compiled with GCC and then used in MSVC (because GCC optimized it better). I compiled it with -mabi=ms, converted it to COFF64 with objconv and then linked it into Visual Studio which ran flawlessly.

#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);
}
Community
  • 1
  • 1
Z boson
  • 32,619
  • 11
  • 123
  • 226
  • 1
    What about C++ vtable format differences? As far as I can see your solution will work only with C or C-like C++ (no classes aspecially classes with polymorphism) code. – VestniK Jul 03 '14 at 03:55
  • @VestniK, I don't know. I only used this "trick" so that I did not have to learn 64-bit assembly to get MSVC to do what I wanted (which GCC was already doing). I have only tested on examples such as in this answer. – Z boson Jul 14 '14 at 11:01
3

Hmm, technically the objcopy command which is part of gnu bintuils might be able to do it. However, and this is a huge however, converting the format isn't enough. You'd need a version of the g++ compiler that has the exact calling conventions and name mangling as vc++ for starters, and the same idea of compiling structures and so on.

Physically converting the file into a valid .obj file may well be possible, but it's likely not very helpful to you.

jcoder
  • 29,554
  • 19
  • 87
  • 130
1

No, there is no way, especially as the .o file was not compiled with a cross-compiler on Linux. In any case, this sounds like a very strange approach to solve a single linking error.

Lauri Nurmi
  • 566
  • 1
  • 3
  • 14