动态链接库编译时的链接方式

最近在项目中遇到动态库相关的问题,情况还是 中间层的某个 liba.so库的编译中依赖了 下层libb.so库,liba.so的编译命令如下:

gcc -fPIC -shared   -o liba.so  a.c  ../../../libb.so   

此处 liba.so的编译中对 libb.so的依赖没有使用 -L ../../../ -lb  的方式给出,而是直接用相对路径 ../../../libb.so   ,此时再编译依赖 liba.so的可执行程序 exe:

gcc -o exe main.c -L {Path of liba.so} -la

这时会提示找不到  liba.so 依赖 的 libb.so,但是生成了可执行程序exe。提示找不到的原因貌似很明显,没有指定 libb.so的路径,修改exe编译命令:

gcc -o exe main.c -L {Path of liba.so} -la -L {Path of libb.so} -lb

这时还是会出现找不到 libb.so,也生成了exe,暂时先不管 提示的警告信息,拷贝到板子上运行,发现无法运行,原因正是无法找到 libb.so。

尝试将板子上libb.so的路径添加到 LD_LIBRARY_PATH中,仍然出现上述错误。

此时 使用 readelf -d去查看 liba.so,发现如下信息:


这里的 libnteec.so对应上面的libb.so,可以猜测正是在 编译 liba.so的编译命令中没有使用 -L path 和-lxxx 的方式,而直接写上上面的 绝对路径造成 无法找到 libb.so。


修改方法便是将 liba.so的编译命令修改为:

gcc -fPIC -shared   -o liba.so  a.c  -L {Path of libb.so} -lb

或者在编译liba.so 的时候就不去链接 libb.so,而是在 依赖 liba.so的可执行程序的编译时,将 libb.so添加到依赖中,如上:

gcc -o exe main.c -L {Path of liba.so} -la -L {Path of libb.so} -lb

**************************************************************************************************************************************************************************************

补充的说明,转自:

http://blog.chinaunix.net/uid-20185237-id-3006843.html


以前一直不理解连接参数-soname的用处。最近在项目实施过程中遇到共享库连接和运行的一些问题,终于才明白了设置这个参数的用意。 

下面举例说明:

gcc –O2 -o foo -Wl,-rpath=. main.c libs1.so ../x1/libs2.so

 

查看一下foo中的符号信息

$readelf -a foo | grep Shared

 0x00000001 (NEEDED)                     Shared library: [libs1.so]

 0x00000001 (NEEDED)                     Shared library: [../x1/libs2.so]

 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

 

dynamic linker在初始化foo的时候,会认为libs2.so的位置为../x1,如果libs2.so不在这个位置,那么foo是无法正常运行的,因为dynamic linker会认为libs2.so找不到。

$./foo

./foo: error while loading shared libraries: ../x1/libs2.so: cannot open shared object file: No such file or directory

 

这样就会带来麻烦,假设连接应用程序的时候某个shared library不在当前目录下,并且文件名不符合libXXX.so的形式,那么连接参数就只能使用 ../dir1/myshlib.so 这样的形式,无法变成等价的参数形式:-L../dir1 -lXXX。这样运行的是就要求myshlib.so必须放在相对路径../dir1中。如果myshlib.so以绝对路径/A/B/C/myshlib.so的形式表示,那么运行时就要求myshlib.so也放在目录/A/B/C中。

 

为了解决这个问题,ELF引入了DT_SONAME。在生成shared library的时候指定参数-soname=NAME。这样,用shared library去连接应用程序的时候,即使shared library是以什么形式给出(相对路径或者绝对路径),连接器都以NAME作为该shared library的名字。

 

gcc -shared -Wl,-soname=libs1.so.1 -o libs1.so s1.c && ln -sf libs1.so libs1.so.1

gcc -shared -Wl,-soname=libs2.so.1 -o libs2.so s2.c && ln -sf libs2.so libs2.so.1

gcc -O2 -o foo -Wl,-rpath=. main.c libs1.so ../x1/libs2.so

$readelf -a foo | grep Shared

 0x00000001 (NEEDED)                     Shared library: [libs1.so.1]

 0x00000001 (NEEDED)                     Shared library: [libs2.so.1]

 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

稍微解释一下过程:连接器在连接时发现../x1/libs2.so存在一个DT_SONAME的符号,它的值为libs2.so.1,因此,连接器取libs2.so.1作为shared library的名字,替代掉../x1.libs2.so。但是-soname=libs2.so.1并不会自动生成文件libs2.so.1,因此我们需要手动生成一个符号链接lib2.so.1,使之指向libs2.so,这样运行的时候就不会产生“找不到文件”的错误。

 

总结一下,在linux下使用shared library需要注意一下几个问题:

1.       生成shared library的时候使用-soname参数

2.       生成符号链接文件

3.       将shared library放入到LD_LIBRARY_PATH指定的目录中

你可能感兴趣的:(Linux常见问题)