假设我完全控制的libA
取决于libC.so.2
。同时,与我的libB
可以共存的第三方libA
取决于libC.so.1
。
正常的动态链接不起作用,因为libA
或libB
都将为libC
中的符号接收错误的实现。如何使libA
与libB
一起使用,而对libA
的构建管道进行最少的修改?
答案 0 :(得分:0)
我已经搜索并测试了一下...
首先,变化中的libC
:
// libC1.c => libC.so.1
int c(void) { return 21; }
// libC2.c => libC.so.2
int c(void) { return 42; }
然后libA
和libB
:
// libA.c => libA.so | gcc -fPIC -shared -o libA.so libA.c -l:libC.so.1 -L.
extern int c(void);
int a(void) { return c(); }
// libB.c => libB.so | gcc -fPIC -shared -o libB.so libB.c -l:libC.so.2 -L.
extern int c(void);
int b(void) { return c(); }
请注意,以上两种情况我都直接指定了正确的.so
文件(-l:libC.so.1
和-l:libC.so.2
)。现在libA
和libB
都指向正确的libC
,但是有一个问题:两个libC
都导出符号c
!
因此...
extern int a(void);
extern int b(void);
#include <stdio.h>
int main() {
printf("a => %d, b => %d\n", a(), b());
}
...将愉快地打印a => 21, b => 21
。原因是,一旦动态链接程序加载了libC
中的一个,符号c
(在 both libA
和libB
中均未定义) )(对于libA
和libB
都解析为已加载的那个)。
dlopen
似乎是唯一的方法有两种方法:
libA
和libB
修改应用程序使用dlopen
自己加载两个库,RTLD_LOCAL
使加载的符号(因此也依赖于加载的库的符号) 对应用程序不可见(或更高版本)调用dlopen
)。
#include <stdio.h>
#include <assert.h>
#include <dlfcn.h>
int (*a)(void);
int (*b)(void);
int main() {
void * const a_handle = dlopen("libA.so", RTLD_NOW | RTLD_LOCAL);
// you could dlopen("libC.so.2", RTLD_NOW | RTLD_GLOBAL) here to "select"
// the correct symbol `c` for the following, too.
void * const b_handle = dlopen("libB.so", RTLD_NOW | RTLD_LOCAL);
assert(a_handle); // real error handling here please!
assert(b_handle);
*(void **)(&a) = dlsym(a_handle, "a");
*(void **)(&b) = dlsym(b_handle, "b");
assert(a); // real error handling here please!
assert(b);
printf("a => %d, b => %d\n", a(), b());
}
然后在(main2.c
)上方进行编译,链接和运行
# gcc main2.c -ldl
# LD_LIBRARY_PATH=. ./a.out
a => 21, b => 42
libA
在libA
的源代码中,无论您从funC
调用函数libC
的哪个位置,都需要将对funC_impl
的调用替换为: / p>
int (*funC_impl)(char *, double); // for a funC(char *, double) which returns an int
// and somewhere during initialization:
void * const c_handle = dlopen("libC.so.2", RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
// check c_handle != NULL
*(void **)(&funC_impl) = dlsym(c_handle, "funC");
// check for errors! (dlerror)
当然,还有每个功能的所有内容……当然也不能以这种方式控制libB
。
-Bsymbolic
might help,但我无法使其正常工作如果运行LD_LIBRARY_PATH=. LD_DEBUG=all ./a.out 2>&1
(对于答案顶部的版本),则这是输出的一部分:
10545: Initial object scopes
10545: object=./a.out [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=linux-vdso.so.1 [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545: scope 1: linux-vdso.so.1
10545:
10545: object=./libA.so [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=./libB.so [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=./libC.so.1 [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=./libC.so.2 [0]
10545: scope 0: ./a.out ./libA.so ./libB.so /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/libc.so.6 ./libC.so.1 ./libC.so.2 /nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2
10545:
10545: object=/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27/lib/ld-linux-x86-64.so.2 [0]
10545: no scope
10545:
问题在于,对于libA
和libB
而言,初始范围都依次包含库libC.so.1
和libC.so.2
。因此,当在c
和libA
的每个符号中解析符号libB
时,它首先会查看libC.so.1
,找到符号并对其进行处理。
现在缺少的“所有”是一种更改此“初始对象范围”的方法。
答案 1 :(得分:0)
如果您可以更改libC.so.2
中的符号名称,则可以使用Implib.so的重命名功能。例如。将所有libC.so.2
符号更改为具有MYPREFIX_
前缀:
$ cat mycallback.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
#endif
void *mycallback() {
void *h = dlmopen(LM_ID_NEWLM, "libxyz.so", RTLD_LAZY | RTLD_DEEPBIND);
if (h)
return h;
fprintf(stderr, "dlmopen failed: %s\n", dlerror());
exit(1);
}
$ implib-gen.py --dlopen-callback=mycallback --symbol_prefix=MYPREFIX_ libC.so.2
$ ... # Link your app with libC.so.2.tramp.S, libC.so.2.init.c and mycallback.c, keep libC.so.1 unchanged
libC.so.2
标头中的函数名称也将需要更新(通常在vim中是简单的s///
)。
Implib.so的工作原理是为有问题的库中的每个符号生成一堆包装器(在这种情况下为libC.so.2
),然后在内部(通过dlsym
)将调用转发到其实际实现。