Long time listener, first time caller. I'm aware this is a somewhat obscure question, and don't expect too much. :-)
I have the following Ada files:
greeter.ads
package Greeter is
procedure Hello;
end Greeter;
greeter.adb
with Ada.Text_IO; use Ada.Text_IO;
package body Greeter is
procedure Hello is
begin
Put_Line ("Hello, world!");
end Hello;
end Greeter;
And compile them into a shared object like this:
gnatmake -z -fPIC greeter.adb
gcc -shared -o libgreeter.so greeter.o
This compiles fine. nm
shows the following symbols:
$ nm -D libgreeter.so
w _Jv_RegisterClasses
0000000000201028 A __bss_start
w __cxa_finalize
w __gmon_start__
U __gnat_eh_personality
0000000000201028 A _edata
0000000000201038 A _end
00000000000006a8 T _fini
0000000000000520 T _init
U ada__text_io__put_line__2
0000000000201018 D greeter_E
000000000000063c T greeter__hello
Now I try to load that shared object in Perl:
#!/usr/bin/env perl
use 5.014;
use strict;
use warnings;
#BEGIN { $ENV{PERL_DL_DEBUG} = 1 };
package Greeter
{
use constant ADADIR => '/usr/lib/gcc/x86_64-linux-gnu/4.4/rts-native/adalib/';
use constant OURDIR => do { (my $f = __FILE__) =~ s{[^/]+$}//; $f || "." };
require DynaLoader;
our @ISA = 'DynaLoader';
my $runtime = DynaLoader::dl_load_file(
ADADIR.'/libgnat.so',
) or die DynaLoader::dl_error();
my $gep = DynaLoader::dl_find_symbol(
$runtime,
'__gnat_eh_personality',
) or die DynaLoader::dl_error();
my $libref = DynaLoader::dl_load_file(
OURDIR.'/libgreeter.so',
0x01,
) or die DynaLoader::dl_error();
my $func = DynaLoader::dl_find_symbol(
$libref,
'greeter__hello',
) or die DynaLoader::dl_error();
print $func, $/;
}
But this bombs out with the following message:
./libgreeter.so: undefined symbol: __gnat_eh_personality at ./greeter.pl line 26.
Does anybody have any hints? Is there something better/easier than DynaLoader that I should be using??
I have a repository with all the relevant files here:
- https://bitbucket.org/tobyink/ada-perl-mashup
I can't help with the Perl side (you require 5.14, Mac OS X has 5.12, Debian 6 has 5.10). That said, I can help with building the library for a C main and direct linking ...
The GNAT build process is sufficiently complicated that there are two tools to support it, gnatmake
and gprbuild
. It’s likely (writing at September 2015) that gnatmake
will lose the ability to build libraries, so gprbuild
is the better option.
I think you need a stand-alone library project (that is, one with the initialization and finalization operations that control Ada elaboration; if you don't initialize the Ada library, you'll get SEGVs or other bad behaviours). You'll find the lowdown on building one here.
The greeter.gpr
I wrote is
project Greeter is
for Library_Name use "greeter";
for Library_Kind use "relocatable";
for Library_Dir use "lib";
for Library_Interface use ("greeter");
for Library_Auto_Init use "true"; -- the default, I think
for Object_Dir use ".build"; -- to keep temp objects out of the way
end Greeter;
The Library_Name
attribute controls the name of the library; libgreeter.dylib
on Mac OS X, libgreeter.so
on Linux.
The Library_Kind
attribute could alternatively be "static"
, in which case the name would be libgreeter.a
. However, stand-alone libraries must be relocatable.
The Library_Dir
attribute, which you have to supply (with the two above) to make a library at all, controls where the library is created; in this case, in lib/
.
You have to supply the Library_Interface
attribute to make it a stand-alone library and generate the initialization and finalization operations that control Ada elaboration. They're called library_nameinit
and library_namefinal
- here, greeterinit
, greeterfinal
.
If Library_Auto_Init
is "false"
you have to call the initialization and finalization operations yourself, if "true"
, they're managed automagically.
OK, build the library by
gprbuild -p -P greeter
(-p
says "create any needed output directories", -P
specifies the project file).
I built greeter.c
#include <stdio.h>
extern void greeter_hello();
int main()
{
greeter__hello();
return 0;
}
using
$ gcc greeter.c -o greeter -L lib -l greeter
and run (on Linux) using
$ LD_LIBRARY_PATH=./lib ./greeter
I'll do the best I can with this, given not much Perl knowledge.
It looks to me like the Dynaloader in perl is a utility that lets you load dynamicly loadable libraries (lib*.so's on Unix systems) into a perl program.
For that to work for an Ada program, there are several things you'll need to take into account.
- You need to build your Ada program as a proper dynamic library. It looks like you did that. However, I'm no expert on this, so perhaps you missed something. I'd highly suggest looking over TFM on this.
- You need to call your Ada code properly. Ada programs generally require a process called "elaboration" be performed before any actual code may be run. To accomplish this, most Ada compilers create a special entry-point to the program, rather than just using the one associated with your "main" routine. I think Gnat's is something like
C_yourprogramname
, but don't hold me to that. Even if you are implementing a library of some sort, the elaboration should be run first (execpt in some special circumstances, that do not apply here). However, if you want a routine to be a library routine called from outside of Ada, you generally don't want a "main", so there are some extra steps. How to do this with Gnat is described in their user guide, but in general it involves telling the compiler to not make a "main", calling adainit
before running any Ada routine from outside, and calling adafinal
when you are all done.