【原创】Linux下共享库嵌套依赖问题

问题场景:

  1. 动态库 librabbitmq_r.so 内部依赖动态库 libevent_core.so 和 libevent_pthreads.so ;
  2. 可执行程序 sa 依赖动态库 librabbitmq_r.so ;
  3. 在链接生成 sa 的时候希望只指定 librabbitmq_r.so 而不指定 libevent_core.so 和 libevent_pthreads.so 。

错误信息:

...
g++ ../source/authorisecfg.o ../source/bmcinst.o ../source/config.o ../source/lgsinst.o ../source/logicsrv.o ../source/logicsrvinst.o ../source/logicsrvmodulelistcfg.o ../source/main.o ../source/moduleinst.o ../source/print.o ../source/routingkeycfg.o ../source/sautils.o ../source/structself.o ../source/../../../common/source/bossutils.o
../source/../../../common/source/bossversion.o -o sa -m32 -L../../../../10-common/lib/release/linux -lrt -lwatchdogclient -lfiletransfer -losp -lkprop -ljsonconvert -ljsoncpp -ldeploycfg -lnosectionini -lrabbitmq_r -lmqwrapper -lreadwritelock -lcaptureexception -lnetconfig

/usr/bin/ld: warning: libevent_core.so, needed by ../../../../10-common/lib/release/linux/librabbitmq_r.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libevent_pthreads.so, needed by ../../../../10-common/lib/release/linux/librabbitmq_r.so, not found (try using -rpath or -rpath-link)

../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_base_free‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `evthread_use_pthreads‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_assign‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_base_dispatch‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_base_loopbreak‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_del‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_add‘
../../../../10-common/lib/release/linux/librabbitmq_r.so: undefined reference to `event_base_new‘
collect2: ld returned 1 exit status
make: *** [sa] Error 1

由错误信息可以看出,未找到的符号均属于 libevent_core.so 和 libevent_pthreads.so 内部。但这两个库确实存在于 -L../../../../10-common/lib/release/linux 路径下,但为什么链接器仍旧无法找到对应的库和符号呢?

下面的文章讨论了这个问题(下面给出部分摘录)

Why does ld need -rpath-link when linking an executable against a so that needs another so?

...

You system, through ld.so.conf, ld.so.conf.d, and the system environment, LD_LIBRARY_PATH, etc.., provides the system-wide library search paths which are supplemented by installed libraries through pkg-config information and the like when you build against standard libraries. 

...

There is no standard run-time library search path for custom shared libraries you create yourself. You specify the search path to your libraries through the -L/path/to/lib designation during compile and link. For libraries in non-standard locations, the library search path can be optionally placed in the header of your executable (ELF header) at compile-time so that your executable can find the needed libraries.

...

rpath provides a way of embedding your custom run-time library search path in the ELF header so that your custom libraries can be found as well without having to specify the search path each time it is used. This applies to libraries that depend on libraries as well. As you have found, not only is the order you specify the libraries on the command line important, you also must provide the run-time library search path, or rpath, information for each dependent library you are linking against as well so that the header contains the location of all libraries needed to run.

...

back to the semantics of ld. In order to produce a "good link", ld must be able to locate all dependent libraries. ld cannot insure a good link otherwise. The runtime linker must find and load, not just to find the shared libraries needed by a program. ld cannot guarantee that will happen unless ld itself can locate all needed shared libraries at the time the progam is linked.

...

结论就是,像这种 a.so 依赖 b.so ,而 c 依赖 a.so 的情况,在链接过程中需要通过 -rpath-link 指定所需 .so 的位置,而不能仅仅使用 -L 指定。

示例如下:

[[email protected] include_test]# ll
总用量 20
-rw-r--r-- 1 root root 149 9月  14 16:19 main.c
-rw-r--r-- 1 root root 123 9月  14 16:40 say_hello.c
-rw-r--r-- 1 root root  20 9月  14 15:58 say_hello.h
-rw-r--r-- 1 root root 416 9月  14 16:39 time_print.c
-rw-r--r-- 1 root root  69 9月  14 15:00 time_print.h
[[email protected] include_test]#

【say_hello.c】

// g++ -o say_hello.so -fpic -shared say_hello.c

#include <stdio.h>
void say_hello()
{
    printf( "Hello World!\n" );
}

【say_hello.h】

void say_hello();

【time_print.c】

// g++ -o time_print.so -fpic -shared -I. -L. time_print.c say_hello.so 

#include <time_print.h>
#include <stdio.h>
#include <say_hello.h>

int time_print( time_t tmp )
{
    int off = 0;
    time_t t;
    char buf[64] = {0};

    t = time( NULL );
    off = strftime( buf, sizeof(buf), "%d %b %H:%M:%S", localtime( &t ) );
    fprintf( stderr, "current timestamp = %s\n", buf );

    say_hello();

    return 0;
}

【time_print.h】

#include <time.h>
int time_print( time_t t );

【main.c 】

// g++ -o main main.c time_print.so -I. -Wl,-rpath-link,.

#include <time_print.h>

int main()
{
    time_t t;
    time_print( t );
    return 0;
}

生成两个 .so 库

[[email protected] include_test]# g++ -o say_hello.so -fpic -shared say_hello.c
[[email protected] include_test]# ll
总用量 28
-rw-r--r-- 1 root root  149 9月  14 16:19 main.c
-rw-r--r-- 1 root root  123 9月  14 16:40 say_hello.c
-rw-r--r-- 1 root root   20 9月  14 15:58 say_hello.h
-rwxr-xr-x 1 root root 6286 9月  15 19:31 say_hello.so
-rw-r--r-- 1 root root  416 9月  14 16:39 time_print.c
-rw-r--r-- 1 root root   69 9月  14 15:00 time_print.h
[[email protected] include_test]#
[[email protected] include_test]# g++ -o time_print.so -fpic -shared -I. -L. time_print.c say_hello.so
[[email protected] include_test]# ll
总用量 36
-rw-r--r-- 1 root root  149 9月  14 16:19 main.c
-rw-r--r-- 1 root root  123 9月  14 16:40 say_hello.c
-rw-r--r-- 1 root root   20 9月  14 15:58 say_hello.h
-rwxr-xr-x 1 root root 6286 9月  15 19:31 say_hello.so
-rw-r--r-- 1 root root  416 9月  14 16:39 time_print.c
-rw-r--r-- 1 root root   69 9月  14 15:00 time_print.h
-rwxr-xr-x 1 root root 7117 9月  15 19:31 time_print.so
[[email protected] include_test]#

若不指定 -rpath-link 选项,则链接失败

[[email protected] include_test]# g++ -o main main.c time_print.so -I. -L.
/usr/bin/ld: warning: say_hello.so, needed by time_print.so, not found (try using -rpath or -rpath-link)
time_print.so: undefined reference to `say_hello()‘
collect2: ld 返回 1
[[email protected] include_test]#

若指定 -rpath-link 选项,则可以成功链接

[[email protected] include_test]# g++ -o main main.c time_print.so -I. -L. -Wl,-rpath-link,.
[[email protected] include_test]# ll
总用量 44
-rwxr-xr-x 1 root root 6999 9月  15 19:37 main
-rw-r--r-- 1 root root  149 9月  14 16:19 main.c
-rw-r--r-- 1 root root  123 9月  14 16:40 say_hello.c
-rw-r--r-- 1 root root   20 9月  14 15:58 say_hello.h
-rwxr-xr-x 1 root root 6286 9月  15 19:31 say_hello.so
-rw-r--r-- 1 root root  416 9月  14 16:39 time_print.c
-rw-r--r-- 1 root root   69 9月  14 15:00 time_print.h
-rwxr-xr-x 1 root root 7117 9月  15 19:31 time_print.so
[[email protected] include_test]#

查看一下共享库依赖关系

[[email protected] include_test]# ldd say_hello.so
        linux-vdso.so.1 =>  (0x00007fffb53ff000)
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f56915d4000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f5691350000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f5691139000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f5690da5000)
        /lib64/ld-linux-x86-64.so.2 (0x000000388c400000)
[[email protected] include_test]#
[[email protected] include_test]# ldd time_print.so
        linux-vdso.so.1 =>  (0x00007fff50cfa000)
        say_hello.so => not found
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007ff0f7829000)
        libm.so.6 => /lib64/libm.so.6 (0x00007ff0f75a5000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff0f738f000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff0f6ffa000)
        /lib64/ld-linux-x86-64.so.2 (0x000000388c400000)
[[email protected] include_test]#
[[email protected] include_test]# ldd main
        linux-vdso.so.1 =>  (0x00007fffc55ff000)
        time_print.so => not found
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003899800000)
        libm.so.6 => /lib64/libm.so.6 (0x000000388dc00000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003898000000)
        libc.so.6 => /lib64/libc.so.6 (0x000000388c800000)
        /lib64/ld-linux-x86-64.so.2 (0x000000388c400000)
[[email protected] include_test]#

执行程序

[[email protected] include_test]# ./main
./main: error while loading shared libraries: time_print.so: cannot open shared object file: No such file or directory
[[email protected] include_test]#
[[email protected] include_test]# LD_LIBRARY_PATH=. ./main
current timestamp = 15 Sep 19:41:00
Hello World!
[[email protected] include_test]#

最后给出一个 rpath 递归问题的讨论: 《Resursive linking with rpath

时间: 2024-07-29 12:46:06

【原创】Linux下共享库嵌套依赖问题的相关文章

Linux下共享库嵌套依赖问题 (转载)

转自:http://my.oschina.net/moooofly/blog/506466 问题场景: 动态库 librabbitmq_r.so 内部依赖动态库 libevent_core.so 和 libevent_pthreads.so : 可执行程序 sa 依赖动态库 librabbitmq_r.so ; 在链接生成 sa 的时候希望只指定 librabbitmq_r.so 而不指定 libevent_core.so 和 libevent_pthreads.so . 错误信息: ... g

linux下共享库的注意点之-fpic

在编译共享库必须加上-fpic.这是为什么呢? 首先看一个简单的例子: #include <stdio.h> int fun1() { printf("fun1\n"); } 先不加-fpic的情况下生成库,反汇编查看fun1的机器码 0000044c <fun1>: 44c: 55 push %ebp 44d: 89 e5 mov %esp,%ebp 44f: 83 ec 18 sub $0x18,%esp 452: c7 04 24 b2 04 00 00

【Linux笔记】细说linux系统下共享库的命名规范和使用方法

1. Shared Library的优势 共享库,又称动态库或so文件,顾名思义,它可以在可执行文件启动时加载或进程运行期被调用.使用共享库有很多好处,例如(包含但不限于下面提到的场景): 1) 减少了依赖共享库的模块的大小,因为它们不必把共享库提供的功能的实现代码静态编译到自己的模块代码中. 2) 在同一台机器上运行的多个进程会在内存中共享同一份动态库,操作系统采用的这种内存布局方式可以极大地节省机器内存资源. 3) 若很多模块依赖了以共享库形式提供的同一个底层库,则底层库升级时,只需升级该s

LINUX总结第13篇:LINUX下动态库及版本号控制

感觉讲得挺详细 注: ln 命令用法 ln –s 源文件 目标文件 (目标文件即为软链接文件) 可用ls -l查看软链接文件具体指向哪个文件 目录[-] 1. File libhello.c 2. File libhello.h 3. File main.c 前言 针对同一动态组件的不同版本链接和加载. 一.概念                  DLL HELL字面意思是DLL"灾难",是由于com组件(动态库)升级引起的程序不能运行的情况.        原因         有三

Linux下静态库与动态库

一.基本概念 1.1.什么是库        在 windows 平台和 linux 平台下都大量存在着库. 本质上来说库是一种可执行的二进制代码(但不可以独立执行),可以被操作系统载入内存执行. 由于 windows 和 linux 的平台不同(主要是编译器.汇编器和连接器 的不同),因此二者库的二进制是不兼容的. 本文仅限于介绍 linux 下的库. 1.2. 库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同: 静态库的代码在编译过程中已经被

linux下静态库和动态库一些东西

http://www.cnblogs.com/changefuture/archive/2011/12/22/2297460.html Linux  动态链接库和静态库示例 文件预览 文件目录树如下,如你所见,非常简单. libtest/ |-- lt.c |-- lt.h `-- test.c 代码 #lt.c /* lt.c * */ #include <stdio.h> void myprint(void) { printf("Linux library test!\n&quo

谈谈Linux下动态库查找路径的问题 ldconfig LD_LIBRARY_PATH PKG_CONFIG_PATH

谈谈Linux下动态库查找路径的问题 ldconfig LD_LIBRARY_PATH  PKG_CONFIG_PATH 转载自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=23069658&id=4028681 学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续“上路”.回想起自己当年刚接触Linux时,不管是用源码包编译程序,还是程序运行时出现的和动态库的各种恩恩怨怨,心里那真叫一个难受.那时候脑袋里曾经

Linux下的库操作工具-nm、ar、ldd、ldconfig和ld.so

Linux下的库操作工具-nm.ar.ldd.ldconfig和ld.so 1.nm [options] file 列出file中的所有符号 [option] -c 将符号转化为用户级的名字 -s 当用于.a文件即静态库时,输出把符号名映射到定义该符号的模块或成员名的索引 -u 显示在file外定义的符号或没有定义的符号 -l 显示每个符号的行号,或为定义符号的重定义项 2.ar {dmpqrtx} [member] archive file 用于操作高度结构化的存档文件(.a) [option

linux下动态库的编写和调用

linux下动态库的编写和调用 linux下编写和调用一个简单的动态库大概分为以下几个步骤: - 创建动态库程序文件 add.c int add(int a,int b) { return a+b; } 创建引用头文件 head.c #ifndef _HEAD_ #define _HEAD_ int add(int a,int b); #endif 生成目标文件 生成要加编译器选项 -fpic gcc -fpic -c add.c 然后生成动态库 注意使用链接器选项 -shared gcc -s