【问题标题】:Linker script variable in library prevents dynamic linking库中的链接器脚本变量阻止动态链接
【发布时间】:2020-07-14 03:36:03
【问题描述】:

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

但是,这很奇怪,因为它能够在失败之前绑定其他链接器脚本变量之一。

我研究这个问题的时间太长了,所以我无法看到更大的图景。也许我在想这个错误,问题出在另一个符号上。任何帮助或指导将不胜感激!

【问题讨论】:

  • 问题可能是由libretroengine.so 中的符号引用引起的,但您并没有描述您在那里所做的事情。
  • 啊,感谢您了解这一点。这是我正在构建的库的实际名称,但我不想在问题中造成任何不必要的干扰

标签: c linux gcc


【解决方案1】:

只需使用 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 为名称为有效 C 标识符的所有部分提供了 __start___stop_ 符号。

【讨论】:

  • 这是一个非常棒且经过深思熟虑的答案!我之前曾研究过使用构造函数,但正如你所说我想要更多的控制,所以我尝试在你的第二个答案中使用提供的 _start_stop 符号(我不知道存在),程序现在完美运行!您是否碰巧知道“意外的 PLT reloc type 0x08”问题来自哪里?很高兴找出我的链接器脚本导致此失败的原因,并且我希望其他人可能遇到同样的问题。
  • @NicholasHein:唯一想到的是在 C 代码中将符号显式地设为 32 位 (uint32_t)。当您将它们定义为 void 指针或 size_t 时,错误会消失吗?
  • 嗯。我认为外部符号可以是任何类型ref。我希望链接器脚本有更多关于此的文档,但是这个解决方案解决了我的问题,所以我将它标记为已接受。再次感谢!
  • @NicholasHein:但是您的链接器脚本将符号的地址分配给符号本身;这就是为什么我怀疑它..只有测试改变类型是否修复它,才会告诉(我太懒了)。无论如何,here 是文档;它确实包含很多示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-25
  • 2020-07-16
  • 2020-01-13
  • 1970-01-01
  • 2010-11-14
相关资源
最近更新 更多