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

to94eoyn  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(214)

假设libl1.solibl2.solibl3.so都包含函数f
如果不使用dlopen,是否真的没有办法将所有这些f(从libl1.solibl2.solibl3.so)链接到我的程序中?
我甚至同意使用一些 Package 器库--但是我仍然需要最终链接到libl1.solibl2.solibl3.so本身(而不是它们的修改副本)。
P.S.:这是一个试图澄清this问题。

dm7nw8vv

dm7nw8vv1#

只要稍加小心,就可以将dlopen与链接到应用程序的共享库一起使用。
dlopen试图返回一个已经加载的共享对象的句柄。只要共享对象的名称正确,如果它是因为链接符号而被加载的,使用dlopen将不会导致它被第二次加载。
一个例子可以使这一点更加清楚。
下面是三个几乎相同的库,每个库都定义了一个名为f的函数和另一个具有唯一名称的函数(它们也有非导出的初始化器,我添加了这些初始化器,以便加载可见):

文件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"); }

文件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"); }

文件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的已加载库匹配。

文件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

因此,加载了f1f2main启动之前,因为显式使用的函数f1_gf2_g需要这些共享对象(并对其进行初始化)。(使用dlopen),则不会再次加载库。但是,对libf3.sodlopen调用不会在可执行文件中找到共享对象,因此它由对dlopen的调用加载(并初始化)。
随后,调用所有函数,并获得各种f函数的预期版本。

相关问题