Search code examples
visual-c++dllmingwdllexport

Set up a MSVC DLL to be ABI-compatible to a __stdcall MinGW DLL


I'm part of a team reviving a dormant open source project. One of the first things I did was fix the software to build with Visual C++ on Windows. The plan is to distribute the MSVC version in the upcoming release.

The current DLL is built with the __stdcall calling convention, for reasons I can't explain. No big deal, except it was also built with MinGW and MinGW and Visual C++ disagree about what __stdcall means. See here for details, but basically MinGW mangles the name like Function@n and MSVC mangles it like _Function@n. It's a good reason not to use __stdcall in your DLLs...

The switch to __stdcall was important enough to the previous maintainers that they bumped a major version number (because it broke ABI compatibility) when they switched to it and I'm reluctant to bump it again to switch back to __cdecl.

Basically, I need to convince MSVC to decorate the symbols in the DLL like MinGW does, either with some sort of alias, or just changing the name. I don't care which, as the other software in the suite can be easily rebuilt to call the DLL however is needed - it's existing software I'm worried about.

I suspect I need a .DEF file for this, but I'm reluctant to make one by hand. There's dozens of exported functions.

So here's the question - is there any automated or mostly-automated way to do this?


Solution

  • I was able to write a simple Python script to do this. I don't know if there's an easier way, but this works for me

    You'll want pexports (linked from here), and you can run it on your DLL with pexports mylib.dll > mylib-stock.def. Then run this Python script with fixdefs.py mylib-stock.def mylib.def and include that while building the library. Seems to work fine. You'll get a file like:

    _Function@n
    Function@n=_Function@n
    

    Script:

    #!/usr/bin/env python
    from sys import argv
    
    def fixline(line):
        if line.startswith('_'):
            line=line.strip()
            return "%s\n%s=%s\n" % (line, line[1:], line)
        return line
    
    if __name__=="__main__":
        if len(argv)!=3:
            print "usage %s <input.def> <output.def>" % argv[0]
            exit(1)
    
        with open(argv[1], 'r') as input:
            with open(argv[2], 'w') as output:
                for line in input:
                output.write(fixline(line))