gpt4 book ai didi

linux - 如何使用 gnat 更改 Ada 库的符号可见性?

转载 作者:太空宇宙 更新时间:2023-11-04 10:27:43 25 4
gpt4 key购买 nike

我在将程序移植到 Linux 时遇到问题,因为 Linux 默认具有公共(public)符号可见性。目前,我有一个可执行文件和一个 .so 共享对象库,它们都是用 Ada 编写的。他们共享一些文件,例如这些文件:

通用/my_program_generic.ads

generic package My_Program_Generic is
Initialized : Boolean := False;

procedure Initialize;
end My_Program_Generic;

通用/my_program_generic.adb

with Ada.Text_IO;

package body My_Program_Generic is
procedure Initialize is
begin
Ada.Text_IO.Put_Line("Initialized: " & Initialized'Img);
if not Initialized then
Initialized := True;
-- Do stuff
Ada.Text_IO.Put_Line("Did stuff!");
end if;
end Initialize;
end My_Program_Generic;

通用/my_program.ads

with My_Program_Generic;
My_Program is new My_Program_Generic;

然后可执行文件和库都从单独的代码中调用 My_Program.Initialize。输出(第一行和第二行是可执行文件,第三行是库):

Initialized: FALSE
Did stuff!
Initialized: TRUE

这里的问题是符号可见性是公开的,因此看起来可执行文件运行此函数并初始化所有内容,但随后共享对象库使用可执行文件的 My_Program.Initialized(这是 True)而不是它自己的(它是 False),无法初始化,然后使用未初始化的变量稍后崩溃。

我尝试使用 -fvisiblity=hidden 编译所有内容(来自 makefile 和 gnat 项目文件 (.gpr)),这似乎正确地将它传递给了编译器(例如它显示在命令行 gcc -c -fPIC -g -m32 -fvisibility=hidden -gnatA my_file.adb), 但它似乎没有什么不同,我找不到使用 gnat 控制可见性的任何文档。

我的操作系统是 CentOS 5.6。我无法升级到较新版本的 Linux,但我可以将我的 GCC 或 gnat 版本升级到适用于 CentOS 5.6 的任何版本。我的 GCC/gnat 版本如下:

$ gcc --version
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50)
...
$ gnatls -v
GNATLS 4.1.2 20080704 (Red Hat 4.1.2-50)
...

是的,我知道它说的是 Red Hat,但我使用的是 CentOS。 AFAIK 无论如何,它们彼此完全兼容。


可能解决我的问题所需的所有信息都在上面进行了全面解释,但这里是您可以用来在您的机器上重新创建我的二进制文件的其余代码、makefile 和 gpr 文件(用于更完整但更简单的说明) .

图书馆/my_library.ads

package My_Library is
procedure Initialize_Library;
pragma Export (DLL, Initialize_Library, "Initialize_Library");
end My_Library;

库/my_library.adb

with Ada.Text_IO;
with My_Program;

package body My_Library is
procedure Initialize_Library is
begin
Ada.Text_IO.Put_Line("Initializing Library...");
My_Program.Initialize;
end Initialize_Library;
end My_Library;

图书馆/dummy.ads

package Dummy is
end Dummy;

图书馆/my_library.gpr

project My_Library is
for source_dirs use (".","../Common");
for Library_Src_Dir use "include";
for object_dir use "obj";
for library_dir use "lib";
for library_name use "my_library";
for library_kind use "dynamic";
for library_interface use ("dummy");
for library_auto_init use "true;
-- Compile 32-bit
for library_options use ("-m32");
package compiler is
for default_switches ("Ada")
use ("-g", "-m32", "-fvisibility=hidden");
end compiler;

for Source_Files use (
"my_program_generic.ads",
"my_program_generic.adb",
"my_program.ads",
"dummy.ads",
"my_library.ads",
"my_library.adb");
end My_Library;

库/生成文件

GNATMAKE=gnatmake
LDFLAGS=-shared
TARGETBASE=libMy_Library.so
GNATMAKEFLAGS=--RTS=/usr/lib/gcc/i386-redhat-linux/4.1.2
TARGET=Debug/$(TARGETBASE)

# Phony target so make all will work
.PHONY: all
all: $(TARGET)

SRCS = \
../Common/my_program_generic.ads \
../Common/my_program_generic.adb \
../Common/my_program.adb \
dummy.ads \
my_library.ads \
my_library.adb

CHOPPATH = chop
OBJPATH = obj
LIBPATH = lib

$(TARGET) : $(SRCS)
$(GNATMAKE) -Pmy_library $(GNATMAKEFLAGS)
mv $(LIBPATH)/$(TARGETBASE) $(TARGET)

# Phony target so make clean will work
.PHONY: clean
clean:
rm -rf $(TARGET) $(CHOPPATH)/*.ads $(CHOPPATH)/*.adb $(OBJPATH)/*.s $(OBJPATH)/*.o $(OBJPATH)/*.ads $(OBJPATH)/*.adb *.s $(LIBPATH)/*.so $(LIBPATH)/*.ali

执行/my_exe.adb

with Ada.Text_IO;
with My_Program;
with My_Library_Import;

procedure My_Exe is
begin
Ada.Text_IO.Put_Line("Begin main program.");
My_Program.Initialize;
My_Library_Import.Initialize_Library;
end My_Exe;

执行/my_library_import.ads

package My_Library_Import is
procedure Initialize_Library;
private
type External_Initialize_Library_Type is access procedure;
pragma Convention (DLL_Stdcall, External_Initialize_Library_Type);
External_Initialize_Library : External_Initialize_Library_Type := null;
end My_Library_Import;

执行/my_library_import.adb

with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with System;
with Interfaces.C;
with Interfaces.C.Strings;
use type System.Address;

package body My_Library_Import is
Library_Handle : System.Address := System.Null_Address;
Library_Name : String := "../Library/Debug/libMy_Library.so";

-- Interface to libdl to load dynamically linked libraries

function dlopen(
File_Name : in Interfaces.C.Strings.Chars_Ptr;
Flag : in Integer) return System.Address;
pragma Import (C, dlopen);

function dlsym(
Handle : in System.Address;
Symbol : in Interfaces.C.Char_Array) return System.Address;
pragma Import (C, dlsym);

function dlerror return Interfaces.C.Strings.Chars_Ptr;
pragma Import (C, dlerror);

function External_Initialize_Library_Type_Import is new Ada.Unchecked_Conversion(
System.Address, External_Initialize_Library_Type);

procedure Initialize_Library is
Temp_Name : Interfaces.C.Strings.Chars_Ptr;
begin
-- Load Library
Temp_Name := Interfaces.C.Strings.New_Char_Array(Interfaces.C.To_C(Library_Name));
Library_Handle := dlopen(Temp_Name, 16#101#); -- RTLD_NOW (0x0001), RTLD_GLOBAL (0x0100)
Interfaces.C.Strings.Free(Temp_Name);

-- Check for Load Library failure (did we execute from the right place?)
if (Library_Handle = System.Null_Address) then
Ada.Text_IO.Put_Line("dlerror: " &
Interfaces.C.Strings.Value(dlerror));
return;
end if;

-- Get function access
External_Initialize_Library := External_Initialize_Library_Type_Import(
dlsym(Library_Handle, Interfaces.C.To_C("Initialize_Library")));

-- Initialize library itself
External_Initialize_Library.all;
end Initialize_Library;
end My_Library_Import;

执行文件/生成文件

CC=gcc
LD=g++
GNATCHOP=gnatchop
GNATMAKE=gnatmake
RC=windres

INCLUDE_PATH = -I.

LDFLAGS=-largs -ldl -lpthread -rdynamic -lstdc++
TARGET_FILE=my_exe
GNATMAKEFLAGS=--RTS=/usr/lib/gcc/i386-redhat-linux/4.1.2
TARGET_PATH=Debug
TARGET=$(TARGET_PATH)/$(TARGET_FILE)

# Phony target so make all will work
.PHONY: all
all : $(TARGET)

SRCS = \
../Common/my_program_generic.ads \
../Common/my_program_generic.adb \
../Common/my_program.adb \
my_exe.adb \
my_library_import.ads \
my_library_import.adb

CHOPPATH = chop
OBJPATH = obj

$(TARGET) : $(SRCS)
$(GNATCHOP) $^ $(CHOPPATH) -w -r
rm -rf *.s
$(GNATMAKE) -m32 -j3 -g -gnatwA -fvisibility=hidden -D $(OBJPATH) -k $(CHOPPATH)/*.adb $(LDFLAGS) $(GNATMAKEFLAGS)
rm -rf b~$(TARGET_FILE).*
mv $(TARGET_FILE) $(TARGET)

# Phony target so make clean will work
.PHONY: clean
clean:
rm -rf $(TARGET) $(CHOPPATH)/*.ads $(CHOPPATH)/*.adb $(OBJPATH)/*.s $(OBJPATH)/*.o $(OBJPATH)/*.ads $(OBJPATH)/*.adb *.s

我没有为可执行文件 (Exe) 使用 gpr 文件。

使用 ./Debug/my_exe 从“Exe”文件夹中执行程序,带有附加文件的完整输出如下:

$ ./Debug/my_exe
Begin main program.
Initialized: FALSE
Did Stuff!
Initializing Library...
Initialized: TRUE

最佳答案

我不知道你在做什么与我不同,因为你没有告诉我们你的构建过程是什么或者你使用的是什么版本的操作系统/编译器。另外,我无法重现您的确切结果,因为您没有提供完整的演示器。

我相信答案在于最近发布的 gprbuild 的一个未记录(但令人满意)的功能(我在 macOS Sierra 和 Debian jessie 上使用了 GNAT GPL 2016 提供的功能)。

我写了一个包含实例化器的库,

with My_Program_Generic;
package Actual is new My_Program_Generic;

一个不同的副本,当然也在主程序的关闭中,另一个包只包含在库中,

package In_Library with Elaborate_Body is
end In_Library;

with Actual;
with Ada.Text_IO;
package body In_Library is
begin
Ada.Text_IO.Put_Line ("In_Library's elaboration");
Actual.Initialize;
end In_Library;

这样做的目的是避免泄露库中 Actual 的存在,否则在主程序的关闭中肯定会有两个版本。

我用这个 standalone GPR 构建了库,

library project Build is
for Library_Name use "keith";
for Library_Kind use "dynamic";
for Library_Dir use "lib";
for Library_Src_Dir use "include";
for Library_Interface use ("In_Library");
for Object_Dir use ".build";
for Source_Files use ("my_program_generic.ads",
"my_program_generic.adb",
"actual.ads",
"in_library.ads",
"in_library.adb");
end Build;

和(足够新的)gprbuild 识别出 Actual 不在 Library_Interface 中并转换它的符号,这些符号是全局,到本地!!!

“最近”是指不早于 GNAT GPL 2016 发布的版本。

您可以通过检查包含 Object_Lister 的部分的 $prefix/share/gprconfig/linker.xml 来获得用于实现此目的的方法的提示。例如,

<configuration>
<targets>
<target name="^i686.*-linux.*$" />
</targets>
<hosts>
<host name="^i686.*-linux.*$" />
</hosts>
<config>
for Object_Lister use ("nm", "-g");
for Object_Lister_Matcher use " [TDRB] (.*)";

package Linker is
for Export_File_Format use "GNU";
for Export_File_Switch use "-Wl,--version-script=";
end Linker;
</config>
</configuration>

将用于某些 Linux;看起来好像你在编译的接口(interface)单元上使用 nm -g 并将一些全局类型的符号复制到 GNU 格式的临时文件中,该文件通过 -- 传递给链接器version-script= 开关。

macOS 变体使用 -exported_symbols_list 开关以平面格式传递符号。


通常人们会使用带有 Externally_Built attribute 的 GPR 导入库,

library project Keith is
for Library_Name use "keith";
for Library_Kind use "dynamic";
for Library_Dir use "lib";
for Library_Src_Dir use "include";
for Externally_Built use "true";
end Keith;

但是 gprbuild 仍然知道库项目和使用项目中有相同的源单元并拒绝构建,让我链接到

$ gnatmake -f \
-aIlibrary/include -aOlibrary/lib \
main.adb \
-bargs -shared \
-largs -Llibrary/lib -lkeith

关于linux - 如何使用 gnat 更改 Ada 库的符号可见性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41028195/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com