Search code examples
windowsdlladagnatgnat-gps

pragma export function is not external in Ada executable


I need a Visual Studio C++ DLL be able to call a function in my Ada mainline. The Ada code has a function spec like

package offset is
    function GET_OFFSET return integer;
    pragma Export (Stdcall, GET_OFFSET, "fnAdaOffset");
end offset;

The C++ function then would call the Ada method as follows:

typedef int (*tdAdaOffset)(void);
tdAdaOffset _ptAdaOffset = NULL;

int AdaOffset()
{
   if (_ptAdaOffset == NULL)
   {
      _ptAdaOffset = (tdAdaOffset)GetProcAddress(GetModuleHandle(NULL), "fnAdaOffset@0");
      if (_ptAdaOffset == NULL)
         throw "Function not found";
   }
   return (*_ptAdaOffset)();
}

I believe this would work. The problem I have is that Ada refuses to mark the function GET_OFFSET as external in the executable, i.e. doing dumpbin /exports ada.exe shows me no exported functions.

I've read various solutions such as --version-script for the linker , but my linker seems too old to know about this switch.

Another promising option was to add -shared to the link step, but while this now exposes the functions, it also changes the output file to a DLL (with .EXE as its extension (!)), so that's not useful either.

Before I upgrade my toolchain, is there another linker switch I can try, or any other suggestions?


Solution

  • You need to tell the linker to export the symbols from the executable. ld has an option --export-dynamic, however that only works on ELF targets:

    Note that this option is specific to ELF targeted ports. PE targets support a similar function to export all symbols from a DLL or EXE ; see the description of --export-all-symbols below.

    Therefore, on Windows, you need to use --export-all-symbols.

    Depending on how you compile your Ada code, you may need to pass this option through the compiler command (gcc). To tell gcc that an option is to be consumed by the linker, you prefix it with -Wl and then give the linker options separated by commas. In this case, you would end up with -Wl,--export-all-symbols.

    If you're using GPRBuild, the relevant part of your .gpr file would look like this:

    package Linker is
      for Default_Switches ("Ada") use ("-Wl,--export-all-symbols");
    end Linker;
    

    Side notes:

    • Be aware that C++'s int is not necessarily the same as Ada's Integer and you should use Interfaces.C.int for the return type in Ada instead.
    • Calling conventions must match. Stdcall in Ada matches an explicit __stdcall in C++. If you don't have __stdcall in the C++ code, use the C calling convention in Ada instead.