1

The initialization code in a DLL build with gnat is not running automatically when imported. I did a MCVE that consists on:

division.ads

with System;
with Interfaces.C;

package Division is
   --Neither of these work
   procedure DllMainCRTStartup ;
   pragma Export (StdCall, DllMainCRTStartup , "DllMainCRTStartup"); --Edited as noticed by Brian
   -- procedure DllMain
   -- pragma Export (StdCall, DllMain , "DllMain ");

   function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
   pragma Export (C, Div, "MyDivision");

   -- --If I put this, it does not compile... maybe a wrong linkage option set?
   -- procedure AdaInit; 
   -- pragma Import (C, AdaInit, "adainit");
end Division;

division.adb

with text_io;

package body Division is
   procedure DllMainCRTStartup  is begin --DllMain or DllMainCRTStartup
      text_io.put("INIT CODE YEAH!!!*************!"); --This does not execute :(
      --AdaInit;
   end DllMainCRTStartup ;

   function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
      X : INTEGER := A/B;
   begin
      return X;
   end Div;
end Division;

and the gpr:

library project Proj_Name is
  for Library_Name use "math";
  for Object_Dir use "obj";
  for Source_Dirs use ("src");
  for Library_Dir use "lib";
  for Library_Interface use ("Division");
  for Library_Kind use "dynamic";
  for Library_Options use ("-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib",
                           "-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib\libgnat");
end Proj_Name;

I am testing the dll from python, with ctypes. I import it with ctypes.CDLL and I'm able to use MyDivision. However, the init code does not run when importing the dll, as the text_io is not executed.

On the other hand, if I add the AdaInit procedure to the code I get something like this when compiling:

undefined reference to `adainit'

Thank you very much!

gccinac
  • 103
  • 2
  • 11
  • "adainit" isn't something you write or supply. It's something that the framework would normally run to initialise the Ada RTS, before any Ada code, like "crt0.c" runs (normally invisibly) before main() is ever called in a C program. However that doesn't explain why your DLL startup isn't being called. One oddity : why is there a space in the exported name? –  Nov 23 '16 at 13:20
  • Ups... typo. However, I changed it but it did not work :( – gccinac Nov 23 '16 at 15:38

1 Answers1

3

I’m not sure how you know that the initialization code isn’t being run?

I’m running on macOS, but the Ada aspects should be similar. I wrote this package spec/body as a simpler version of yours:

package Division is

   function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
   pragma Export (C, Div, "MyDivision");

end Division;

with Ada.Text_IO;
package body Division is

   function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
      X : INTEGER := A/B;
   begin
      return X;
   end Div;

   procedure Test_For_Elaboration is
   begin
      Ada.Text_IO.Put_Line ("hello world!");
   end Test_For_Elaboration;

begin
   Test_For_Elaboration;
end Division;

with this simpler GPR

library project Proj_Name is
  for Library_Name use "math";
  for Object_Dir use "obj";
  for Source_Dirs use ("src");
  for Library_Dir use "lib";
  for Library_Interface use ("Division");
  for Library_Kind use "dynamic";
end Proj_Name;

and tested with this C code:

#include <stdio.h>

extern int MyDivision(int, int);

int main()
{
  printf("42 / 2 => %d\n", MyDivision(42, 2));
  return 0;
}

and the result was

$ ./caller 
hello world!
42 / 2 => 21

so clearly, for me, library elaboration was called without my having to do anything.

The reason is that you specified Library_Interface in your project file, which means you’re building a stand-alone library, which

is a library that contains the necessary code to elaborate the Ada units that are included in the library. A stand-alone library is a convenient way to add an Ada subsystem to a more global system whose main is not in Ada since it makes the elaboration of the Ada part mostly transparent.

You can specify a stand-alone dynamic library which is not automatically initialized, using

for Library_Auto_Init use "false";

in which case you need to call the library’s initialization procedure yourself; it's called {library-name}init (in your case, mathinit). But then you need to call it from your main program; it’d need to be declared, in C

extern void mathinit();
Simon Wright
  • 25,108
  • 2
  • 35
  • 62
  • Ah you got the solution! Thanks! I thought it was enough to export the function to run when imported with the name DllMain. It seems that it is not the way to go, as in your solution you use the begin Test_For_Elaboration; end Division; – gccinac Nov 28 '16 at 08:46
  • D'oh! I should have noticed there was no package initialisation code... –  Nov 28 '16 at 11:36