链接来自不同共享对象(.so)文件的同名符号

时间:2016-06-13 20:05:43

标签: c gcc linker shared-libraries ld

让我们说libl1.solibl2.solibl3.so都包含函数f

在没有使用{{1>的情况下,是否真的无法将所有这些f(从libl1.solibl2.solibl3.so)链接到我的程序中}}?

我甚至同意使用一些包装器库 - 但我仍然需要最终链接到dlopenlibl1.solibl2.so本身(而不是他们修改后的副本)

P.S。:尝试澄清this问题。

1 个答案:

答案 0 :(得分:0)

稍加注意,可以将dlopen与链接到应用程序的共享库一起使用。

dlopen尝试返回已加载的共享对象的句柄。只要共享对象被正确命名,如果由于链接符号而加载它,则使用dlopen将不会导致它再次加载。

一个例子可以说明这一点。

以下是三个几乎完全相同的库。每个定义一个名为f的函数和另一个具有唯一名称的函数。 (它们还有非导出的初始值设定项,我添加了这些,以便加载可见。):

file f1.c

#include <stdio.h>
int f() { return puts("I'm f1::f"); }
int f1_g() { return puts("I'm f1_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f1"); }

file f2.c

#include <stdio.h>
int f() { return puts("I'm f2::f"); }
int f2_g() { return puts("I'm f2_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f2"); }

file f3.c

#include <stdio.h>
int f() { return puts("I'm f3::f"); }
int f3_g() { return puts("I'm f3_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f3"); }

这些编译成共享对象,指定每个共享对象的soname:

for i in f1 f2 f3; do
  gcc -Wall --shared -Wl,--soname=lib$i.so -fPIC -o lib$i.so $i.c
done

这是一个显式引用两个唯一函数名的main函数,但依赖于dlopen / dlsym来使用具有通用名称的函数。 (它指的是两个唯一的名称,而不是三个,以显示加载行为的差异,但仍然是透明的)。请注意,使用dlopen加载的库名称不包含斜杠,因此它们将与已加载的具有相同soname的库匹配。

file main.c

#include <stdio.h>
#include <dlfcn.h>
extern int f1_g();
extern int f2_g();
extern int f3_g();
int (*f1_f)();
int (*f2_f)();
int (*f3_f)();
int main() {
  puts("Loading f1");
  f1_f = dlsym(dlopen("libf1.so", RTLD_NOW), "f");
  puts("Loading f2");
  f2_f = dlsym(dlopen("libf2.so", RTLD_NOW), "f");
  puts("Loading f3");
  f3_f = dlsym(dlopen("libf3.so", RTLD_NOW), "f");
  f1_f();
  f1_g();
  f2_f();
  f2_g();
  f3_f();
  // f3_g(); /* We don't explicitly use f3_g, so f3.so is not linked */
  return 0;
}

现在,我们编译并运行它:

$ # Need to specify -L in the build and LD_LIBRARY_PATH when running
$ # because . is not in the default library search path.
$ gcc -o main -Wall main.c -L. -lf1 -lf2 -lf3 -ldl
$ LD_LIBRARY_PATH=. ./main
Initializing f2
Initializing f1
Loading f1
Loading f2
Loading f3
Initializing f3
I'm f1::f
I'm f1_g
I'm f2::f
I'm f2_g
I'm f3::f

因此,f1f2会在main开始之前加载(并初始化),因为明确使用的函数f1_gf2_g需要它们。当请求这些共享对象的句柄时(使用dlopen),不会再次加载库。但是,dlopenlibf3.so的调用未在可执行文件中找到共享对象,因此通过调用dlopen加载(并初始化)。

随后,调用所有函数并获取各种f函数的预期版本。