使用DynaLoader.pm在Perl中加载Ada共享对象

11

长期听众,第一次致电。我知道这是一个有些晦涩的问题,不要对它抱有太高期望。:-)

我有以下Ada文件:

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;

然后可以将它们编译成一个共享对象,如下所示:

gnatmake -z -fPIC greeter.adb
gcc -shared -o libgreeter.so greeter.o

这段代码可以成功编译。使用nm命令可以查看以下符号:
$ 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

现在我尝试在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, $/;
}

但是这个程序出现了以下错误信息:
./libgreeter.so: undefined symbol: __gnat_eh_personality at ./greeter.pl line 26.
有没有什么提示?是否有比DynaLoader更好/更容易使用的东西?
我在这里有一个包含所有相关文件的存储库:
2个回答

10

对于Perl方面我无法提供帮助(你需要5.14,Mac OS X只有5.12,Debian 6只有5.10)。不过,我可以帮忙构建库以便C主程序进行直接链接...

GNAT的构建过程比较复杂,有两个工具支持它:gnatmakegprbuild。到2015年9月(撰写本文时)为止,很可能gnatmake将失去构建库的功能,因此gprbuild是更好的选择。

我认为你需要一个独立的库项目(也就是一个包含控制Ada精化的初始化和终止操作;如果你不初始化Ada库,就会出现SEGV或其他不良行为)。你可以在这里找到有关如何构建一个独立库的详细信息here

我编写的greeter.gpr

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;

Library_Name属性控制库的名称,在Mac OS X上为libgreeter.dylib,在Linux上为libgreeter.so

Library_Kind属性可以选择为"static",此时名称将为libgreeter.a。但是, 独立的库必须是可重定位的

您必须提供Library_Dir属性(连同上述两个属性),以创建库并控制库所在位置;在本例中为lib/

要生成初始化和终止Ada展开控制操作并使其成为一个独立库,您必须提供Library_Interface属性。它们被称为library_nameinitlibrary_namefinal - 在这里是greeterinitgreeterfinal

如果Library_Auto_Init"false",则必须自行调用初始化和终止操作,如果为"true",则它们将自动管理。

现在,通过以下方式构建库:

gprbuild -p -P greeter

(-p选项表示"创建必要的输出目录",-P选项指定项目文件)。

我编译了greeter.c

#include <stdio.h>

extern void greeter_hello();

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

使用

$ gcc greeter.c -o greeter -L lib -l greeter

并可在 Linux 上运行

$ LD_LIBRARY_PATH=./lib ./greeter

Simon Wright++,这给了我一些很好的东西来开始。我会尝试一下,如果有效,就会接受这个答案。 - tobyink
我还没有时间尝试这个,但与此同时,我已经接受了这个答案,因为到目前为止它似乎更有前途。 - tobyink

4

鉴于我不太了解Perl知识,我会尽力处理这个问题。

在我看来,perl中的Dynaloader是一个实用程序,它允许您将动态可加载库(Unix系统上的lib*.so)加载到perl程序中。

要使Ada程序正常工作,有几件事情需要考虑:

  1. 您需要将Ada程序构建为适当的动态库。看起来您已经做到了这一点。但是,我并不是专家,因此您可能会遗漏一些东西。我强烈建议您仔细阅读相关文档。
  2. 您需要正确调用Ada代码。通常,在运行任何实际代码之前,需要执行称为“elaboration”的过程。为此,大多数Ada编译器会创建一个特殊的程序入口点,而不仅仅是使用与您的“main”例程关联的入口点。我认为Gnat的入口点类似于C_yourprogramname,但不确定。即使您正在实现某种库,也应该首先运行elaboration(除非有一些不适用于此处的特殊情况)。但是,如果您希望从外部调用例程作为库例程,则通常不需要“main”,因此需要进行一些额外的步骤。如何在Gnat中执行此操作,在用户指南中有描述,但通常涉及告诉编译器不要制作“main”,在从外部运行任何Ada例程之前调用adainit,并在完成所有操作后调用adafinal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接