简体   繁体   中英

BadImageFormatException on interop from DLL build with gcc on 64-bit system

I'm developing a .NET library. As a part of the task to make the library cross-platform, I decided to put all platform-specific functions to C file and build DLL (for Windows) and dylib (for macOS). To start the task I've created a simple test app: console application on Windows on .NET Framework that calls a simple function from the generated DLL.

Content of the C file the DLL (test.dll) built from:

int Foo() { return 123; }

Test program code:

class Program
{
    [DllImport("test")]
    public static extern int Foo();

    static void Main(string[] args)
    {
        var result = Foo();
        Console.WriteLine($"Result = {result}");
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
}

When I run the program I get BadImageFormatException .

If I go the project settings in Visual Studio, I see:

在此处输入图像描述

If I either uncheck 'Prefer 32-bit' or select x64 in the dropdown, all is fine and I see correct output:

Result = 123
Press any key to exit...

BUT that's not a solution for me. The library is used by many users and I don't want (and mustn't) to force them to do some environment setup, like selecting target platform. Also it should work on both x86 and x64.

DLL is built with gcc:

gcc -v -c test.c
gcc -v -shared -o test.dll test.o

My question is how to handle the trouble and create DLL that can be targeted in any .NET app without preliminary actions? Maybe it's possible to build a DLL with dual-platform targeting (not only x64)?

I ended up with building two versions of DLL, 32-bit and 64-bit. So in code we have classes like this:

Api.cs

namespace DualLibClassLibrary
{
    internal abstract class Api
    {
        public abstract int Method();
    }
}

Api32.cs

using System.Runtime.InteropServices;

namespace DualLibClassLibrary
{
    internal sealed class Api32 : Api
    {
        [DllImport("test32")]
        public static extern int Foo();

        public override int Method()
        {
            return Foo();
        }
    }
}

Api64.cs

using System.Runtime.InteropServices;

namespace DualLibClassLibrary
{
    internal sealed class Api64 : Api
    {
        [DllImport("test64")]
        public static extern int Foo();

        public override int Method()
        {
            return Foo();
        }
    }
}

ApiProvider.cs

using System;

namespace DualLibClassLibrary
{
    internal static class ApiProvider
    {
        private static readonly bool Is64Bit = IntPtr.Size == 8;
        private static Api _api;

        public static Api Api
        {
            get
            {
                if (_api == null)
                    _api = Is64Bit ? (Api)new Api64() : new Api32();

                return _api;
            }
        }
    }
}

So we if we want to call Method method, we'll write:

var result = ApiProvider.Api.Method();

And of course we need to have following files in output:

  • test32.dll (and dylib, and so on)
  • test64.dll (and dylib, and so on)

With this system the program will select desired file relying on whether the current process is 64-bit or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM