4

I'm working on some Ada code which I have to call from C and I've encountered a problem which I can't solve and don't know why it's happening.

Here is a test project to illustrate the problem:

lookup.ads

with Interfaces.C; use Interfaces.C;

package lookup is
    procedure Printf(str : in Interfaces.C.char_array; i : in Positive);
    pragma Import(C, printf, "printf");

    procedure PrintLookup;
    pragma Export(C, PrintLookup, "print_lookup");
end lookup;

lookup.adb

with Interfaces.C; use Interfaces.C;

package body lookup is

    -- Month_Length : constant array (1..12) of Positive := (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

    Month_Length : constant array (1..12) of Positive := (4 | 6 | 9 | 11 => 30, 2 => 28, others => 31);

    procedure PrintLookup is
    begin

        printf("Month_Length(5): %d"&To_C(ascii.LF)&To_C(ascii.NUL), Month_Length(5));

    end PrintLookup;

end lookup;

main.adb

with lookup;

procedure main is
begin
    lookup.PrintLookup;
end main;

main.c

extern void print_lookup();

int main()
{
    print_lookup();
    return 0;
}

And I have a simple makefile to build it:

BUILD=ada

GM=gnatmake
CC=gcc
LIB=-L/usr/lib/gcc/i686-linux-gnu/4.9/adalib


ifeq ($(BUILD),ada)
main:
    $(GM) lookup.adb main.adb
else
main: lookup.o main.o
    $(CC) $(LIB) lookup.o main.o -o $@ -lgnat

lookup.o:
    $(GM) lookup.adb

main.o:
    $(CC) -c main.c
endif


.PHONY: clean
clean:
    rm -f lookup.ali lookup.o
    rm -f main.ali main.o
    rm -f main

The makefile will generate an executable, called main. If the BUILD variable in the first line of the makefile is set to ada, it will use the Ada main.adb, otherwise the C main.c

Now, here comes the problem: if in the lookup.adb I use the first variant of the Month_Length array (which is commented out right now), I get the following output for both mains, which is correct:

Month_Length(5): 31

But in case of the other array (which is called a lookup table), the C variant returns 0:

Month_Length(5): 0

Does anyone have any idea why the lookup table array returns 0 when called from C? Does anyone encountered this issue? What am I missing? I appreciate your help.

egilhh
  • 6,464
  • 1
  • 18
  • 19
Zoltán
  • 678
  • 4
  • 15
  • 6
    To cite [https://docs.adacore.com/gnat_ugn-docs/html/gnat_ugn/gnat_ugn/building_executable_programs_with_gnat.html] : " `adainit` You must call this routine to initialize the Ada part of the program by calling the necessary elaboration routines. A call to adainit is required before the first call to an Ada subprogram." – Vroomfondel Nov 23 '18 at 11:32
  • That is true, thanks for the hint, I've posted an answer on how i managed to solve it. – Zoltán Nov 23 '18 at 14:08

2 Answers2

3

As Vroomfondel mentioned in the comment, adainit must be called to initilise ADA. Here are the modifications I made to get this working:

Here is the makefile:

BUILD=c

GM=gnatmake
GB=gnatbind
CC=gcc
LIB=-L/usr/lib/gcc/i686-linux-gnu/4.9/adalib


ifeq ($(BUILD),ada)
main:
    $(GM) lookup.adb main.adb
else
main: lookup.o main.o
    $(CC) $(LIB) lookup.o b~lookup.o main.o -o $@ -lgnat

lookup.o:
    $(GM) lookup.adb
    $(GB) -n lookup.ali
    $(GM) b~lookup.adb

main.o:
    $(CC) -c main.c
endif


.PHONY: clean
clean:
    rm -f lookup.ali lookup.o
    rm -f b~lookup.*
    rm -f main.ali main.o
    rm -f main

gnatbind generates the b~lookup.ads and b~lookup.adb files which will contain the adainit() and adafinal() functions, then I've built them with gnatmake (I had to build because i'm not using gnatlink) and I've included the generated b~lookup.o file in the linking part.

The main.c had to be modified as follows (simply calling the init and final functions before and after the ADA call):

extern void print_lookup();
extern void adainit();
extern void adafinal();

int main()
{
    adainit();
    print_lookup();
    adafinal();

    return 0;
}

The rest remains the same.

Zoltán
  • 678
  • 4
  • 15
  • the ada lib elaboration is often forgotten. To my knowledge, there is a mean to force elaboration code to be automagically executed when loading library. Can't remember the details right now ... – LoneWanderer Nov 23 '18 at 23:53
  • @LoneWanderer, see [this answer](https://stackoverflow.com/a/26821216/40851) - from "A way round the second problem ..." – Simon Wright Nov 24 '18 at 12:29
2

As a complement answer, I remembered that there was an alternative that would make the call to init() automatically.

Simon Wright pointed out his detailed answer to this question

When a DLL is loaded, Windows systematically invokes a routine called DllMain. It would therefore be possible to call adainit directly from DllMain without having to provide an explicit initialization routine. Unfortunately, it is not possible to call adainit from the DllMain if your program has library level tasks because access to the DllMain entry point is serialized by the system (that is, only a single thread can execute “through” it at a time), which means that the GNAT run time will deadlock waiting for the newly created task to complete its initialization.

see this link

one can also have a look at detailed approach here

LoneWanderer
  • 3,058
  • 1
  • 23
  • 41