我有一个项目设置,我在其中编译和链接共享库( libexample.so ),链接器脚本如下所示:
SECTIONS
{
.modules : {
__MODULES_START = .;
KEEP(*(.mod*));
__MODULES_END = .;
}
...
}
我在我的代码中使用这些来加载编译到库中的模块。extern uint32_t __MODULES_START;
extern uint32_t __MODULES_END;
unsigned int init_mods (void) {
void (*p)(void) = (void *)&__MODULES_START;
...
}
当我在我的 Makefile 中编译库时build/%.o: %.c
gcc -o $@ -c $< -fPIC -g -Os -Iinclude
bin/libexample.so: $(OBJS)
gcc -o $@ $^ -shared -fPIC -lc -T$(LINKER_SCRIPT)
它构建和链接得很好,当我尝试将库链接到另一个调用“init_mods”的项目时它可以工作。build/%.o: %.c
gcc -o $@ -c $< -fPIE -g -Os -Iinclude -I../libexample/include
bin/untitled-program: $(OBJS)
gcc -o $@ $^ -fPIE -lc -lexample -Lbin '-Wl,-rpath,$$ORIGIN'
但是,当我运行可以找到库的程序时,我收到以下链接错误:/bin/untitled-program: error while loading shared libraries: /blah/blah/libexample.so: unexpected PLT reloc type 0x08
当我阅读共享库时,我在符号表中得到了两个定义Symbol table '.symtab' contains 223 entries:
Num: Value Size Type Bind Vis Ndx Name
...
154: 0000000000000050 0 NOTYPE GLOBAL DEFAULT 2 __MODULE_INIT_END
...
222: 0000000000000028 0 NOTYPE GLOBAL DEFAULT 2 __MODULE_INIT_START
所以我想知道我的问题是否与 NOTYPE 有关,但我无法找到有关这方面的文档。为了解释为什么我认为我的问题与链接器脚本变量有关,当我在打开链接器调试的情况下运行程序时,其中一个是最后一个出现的。
$ LD_DEBUG=all ./untitled-program
...
23856: symbol=__MODULE_END; lookup in file=./bin/untitled-program [0]
23856: symbol=__MODULE_END; lookup in file=/usr/lib/libc.so.6 [0]
23856: symbol=__MODULE_END; lookup in file=./bin/libexample.so [0]
23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_END'
...
23856: symbol=__MODULE_START; lookup in file=./bin/untitled-program [0]
23856: symbol=__MODULE_START; lookup in file=/usr/lib/libc.so.6 [0]
23856: symbol=__MODULE_START; lookup in file=./bin/libexample.so [0]
23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_START'
./bin/untitled-program: error while loading shared libraries: ./bin/libexample.so: unexpected PLT reloc type 0x08
但是,这很奇怪,因为它能够在失败之前绑定(bind)其他链接器脚本变量之一。我一直在研究这个问题太久了,所以我很难看到更大的图景。也许我在想这个错误,问题出在另一个符号上。任何帮助或指导将不胜感激!
最佳答案
只需使用 GCC 构造函数属性标记您的模块初始化函数(它与 C++ 构造函数无关!),它将在 init_array
中包含其地址部分;然后动态链接器将在 main()
之前执行它,或者在加载动态库时立即。
static void my_mod_init(void) __attribute__((constructor));
static void my_mod_init(void)
{
/* Initialize this module, hook up to the library */
}
这样做的好处是,因为动态链接器会自动执行这些,所以当您加载带有此类模块的动态库时,这些也会运行,例如dlopen(path, RTLD_NOW | RTLD_GLOBAL)
.如果您想在自己的控制下复制功能,
然后让每个模块将一个 init 函数地址数组声明到一个特殊的部分,比如“mod_inits”。定义一些辅助宏:
#define MERGE2_(a, b) a ## b
#define MERGE2(a, b) MERGE2_(a, b)
#define MODULE_INIT(func) \
static void *MERGE2(_init_, __LINE__) \
__attribute__((section ("mod_inits"), used)) = &func
然后,在您的模块源文件中,创建一些函数:static void hello(void) {
puts("Hello!");
}
MODULE_INIT(hello);
static void also(void) {
puts("Also hello!");
}
MODULE_INIT(also);
在库文件中,扫描并执行标有 MODULE_INIT()
的任何编译单元中的所有函数:extern void *__start_mod_inits;
extern void *__stop_mod_inits;
void run_mod_inits(void)
{
for (void **ptr = &__start_mod_inits; ptr < &__stop_mod_inits; ptr++) {
void (*func)(void) = *ptr;
func(); /* Could pass parameters, if they have the same prototype */
}
}
您不需要任何链接器文件,因为 gcc 提供了 __start_
和 __stop_
名称为有效 C 标识符的所有部分的符号。
关于c - 库中的链接器脚本变量阻止动态链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62887667/