【问题标题】:dlopen: detect undefined symbols with cyclic dependenciesdlopen:检测具有循环依赖关系的未定义符号
【发布时间】:2017-03-21 22:02:47
【问题描述】:

我有一些具有循环依赖关系的共享库,我想用dlopen 加载它们。为此,我使用 RTLD_LAZY | RTLD_GLOBAL 标志调用 dlopen

这很好用。

现在,我想检查所有共享库中的所有符号是否都已完全解析,因此我再次使用RTLD_NOW 调用dlopen,如果无法解析所有符号,这将失败。

至少,我是这样理解手册页的:

但是,随后的 dlopen() 调用会加载相同的共享对象 RTLD_NOW 可能会强制对早先加载的共享对象进行符号解析 RTLD_LAZY。

这是一个简单的例子来说明我的问题:

main.h

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

#include "h.h"

#define CNT 3
static const char *libs[CNT] = {"./liba.so", "./libb.so", "./libc.so"};

typedef int (*f_ptr)();

int main() {
    void *h[CNT];
    f_ptr f[CNT];

    for (int i = 0; i < CNT; i++) {
        printf("dlopen: %s\n", libs[i]);
        h[i] = dlopen(libs[i], RTLD_LAZY | RTLD_GLOBAL);
        if (!h[i]) {
            fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
            exit(EXIT_FAILURE);
        }
    }

    for (int i = 0; i < CNT; i++) {
        printf("dlopen again: %s\n", libs[i]);
        void *xx = dlopen(libs[i], RTLD_NOW | RTLD_GLOBAL);
        if (!xx) {
            fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
            dlclose(h[i]);
            h[i] = 0;
        } else {
            dlclose(xx);
        }
    }

    for (int i = 0; i < CNT; i++) {
        if (h[i]) {
            f[i] = (int (*)())dlsym(h[i], "init");

            if (!f[i]) {
                fprintf(stderr, "%d %s: %s\n", __LINE__, libs[i], dlerror());
                exit(EXIT_FAILURE);
            }
        }
    }

    for (int i = 0; i < CNT; i++) {
        if (f[i]) {
            printf("%s %08d\n", libs[i], f[i]());
        }
    }

    return 0;
}

交流

#include "h.h"

int init() { return 1 + b_(); }
int a_() { return 10; }

b.c

#include "h.h"

int init() { return 100 + a_(); }
int b_() { return 1000; }

c.c

#include "h.h"

int init() { return 10000 + x_(); }
int c_() { return 100000; }

h.h

int init();
int a_();
int b_();
int x_();

CMakeLists.txt

project(xx)

add_library(a SHARED a.c)
add_library(b SHARED b.c)
add_library(c SHARED c.c)

add_executable(main main.c)
target_link_libraries(main dl)

编译运行:

$ mkdir b && cd b && cmake .. && make && ./main
...
[100%] Built target b
dlopen: ./liba.so
dlopen: ./libb.so
dlopen: ./libc.so
dlopen again: ./liba.so
dlopen again: ./libb.so
dlopen again: ./libc.so
./liba.so 00001001
./libb.so 00000110
./main: symbol lookup error: ./libc.so: undefined symbol: x_

如何检测到 libc.so 无法完全加载 - 在调用之前 导致它崩溃的符号?

【问题讨论】:

  • 在我工作的地方,如果有未定义的符号,我们总是将链接器标志设置为错误来构建共享库。所以这不是问题。不确定 cmake 的魔法符文是什么,但你想要的链接器标志是 --no-allow-shlib-undefined
  • 按照我的理解,那么这个选项会不会允许共享库之间循环依赖?还是我错过了什么?
  • 是的,不会。但你真的想要吗?

标签: c shared-libraries dlopen


【解决方案1】:

理想情况下,您希望按照其他人的建议(通过--no-allow-shlib-undefined)将符号交叉匹配的负担放在静态链接器上,而不是将其延迟到启动。现在循环依赖有点复杂,但 Solaris(其中 Linux shlibs 是一个惨淡的副本)提供了一个聪明的解决方案 - shlib filters。这个想法是您链接到虚拟 shlib,它们导出同样的虚拟符号并具有与最终生产库相同的 sonames。这些当然只在链接时用于验证依赖关系。

附带说明,我很确定虚拟库的生成可以自动化,如下所示:

  • 构建库没有 --no-allow-shlib-undefined
  • 使用readelf 或其他任何方式提取导出的符号
  • 生成一个虚拟 C(或 asm)文件,该文件为上一项中的符号定义空存根
  • 构建 lib 和瞧 - 你已经构建了你的第一个过滤器

嗯,这听起来像是今晚的一个不错的小项目。

【讨论】:

    猜你喜欢
    • 2014-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-06
    • 2012-10-10
    相关资源
    最近更新 更多