dlopen:检测具有循环依赖关系的未定义符号

时间:2016-11-08 10:12:04

标签: c shared-libraries dlopen

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

这很好用。

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

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

  

但是,后续的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无法完全加载 导致它崩溃的符号?

1 个答案:

答案 0 :(得分:0)

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

作为旁注,我很确定虚拟库的生成可以自动化,如下所示:

  • 构建lib ,不带 --no-allow-shlib-undefined
  • 使用readelf或其他任何内容提取导出的符号
  • 生成一个虚拟C(或asm)文件,该文件为前一项
  • 中的符号定义空存根
  • 构建lib和voila - 你已经构建了第一个过滤器
嗯,这听起来像今晚一个很好的迷你项目。