在获取函数地址时如何获得RTLD_LAZY行为?

时间:2013-07-05 14:41:21

标签: c linux

我们的系统严重依赖于使用dlopen()加载模块。我们的模块是自描述的,带有一个符号,指向与加载模块相关的一些元数据(描述,加载顺序,加载标志等)。

我们首先将指定RTLD_LAZY标志的模块加载到dlopen(),从元数据中获取我们需要的内容,然后在以后加载模块(在我们知道加载顺序应该是什么之后,它们如何应该加载等。)。

这已经有一段时间了,但我们最近发现,获取函数的地址需要在加载时解析该函数​​。我们可以通过哪些方法解决这个问题?

我已经汇总了一个很小的例子来证明这个问题。

/* foo.h */
void foo(void);

/* foo.c */
void foo(void) {}

/* bar.c */
#include "foo.h"
/* Calls foo normally */
void bar(void) { foo(); }

/* bam.c */
#include "foo.h"
static void (*f)(void);
/* Takes the address of foo */
void bam(void) { f = foo; }

/* rtld_lazy.c */
#include <dlfcn.h>
#include <stdio.h>

void check(const char *module) {
  void *mod = dlopen(module, RTLD_LAZY);
  if (mod) {
    printf("%s successfully loaded\n", module);
    dlclose(mod);
  } else {
    printf("%s failed to load: %s\n", module, dlerror());
  }
}

int main() {
  check("./bar.so");
  check("./bam.so");
  check("./foo.so");
}

使用以下输出:

./bar.so successfully loaded
./bam.so failed to load: ./bam.so: undefined symbol: foo
./foo.so successfully loaded

2 个答案:

答案 0 :(得分:2)

您应该使用dlsym()来获取符号的地址。见POSIX:

http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html

答案 1 :(得分:0)

我们已经提出了两种解决此问题的方法,具体取决于具体情况。

第一个是在调用者的模块中,为函数编写一个小的静态包装器。静态包装器将动态绑定到相关函数,您可以毫无问题地获取该函数的地址。

/* bam.c */
#include "foo.h"
static void (*f)(void);
static void foo_wrapper(void) { foo(); }
/* Takes the address of foo */
void bam(void) { f = foo_wrapper; }

如果您发现自己编写了许多这样的小包装器,您可以将一个函数返回到提供模块中。

/* foo.h */
void foo(void);
typedef void (*foo_ptr_type)(void);
foo_ptr_type foo_ptr(void);

/* foo.c */
void foo(void) {}
foo_ptr_type foo_ptr(void) { return foo; }

/* bam.c */
#include "foo.h"
static void (*f)(void);
/* Takes the address of foo */
void bam(void) { f = foo_ptr(); }