c - 库中的链接器脚本变量阻止动态链接

标签 c linux gcc

我有一个项目设置,我在其中编译和链接共享库( 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/

相关文章:

python - 指向 Ctypes 中的 c_int16 数组缓冲区的指针

linux - 我如何在批处理文件中写这个

c++ - 将 Windows 平台 C++ 移植到 POSIX (Linux) C++ - WSAGetLastError()

c++ - 跨 C++ 翻译单元的初始化

c - 强制 mac 使用 GCC,而不是 clang

c - 将 CSV 文件中的值读入变量

C密码出错

Linux 找不到 metis.h

c - 可能不一致的类型转换行为

c - 如何使用 CImg 调整屏幕中的所有点