转]UNIX平台的链接库知识

来源:互联网 发布:金州勇士 知乎 编辑:程序博客网 时间:2024/06/03 10:09

 [转]UNIX平台的链接库知识 收藏

1.               nm & ldd

nm - print name list of an object file

ldd - list  dynamic  dependencies  of  executable  files  or shared objects

solaris平台上,ldd命令的执行效果为:

sun250% ldd gsmindb

        libclntsh.so.8.0 =>/export/home/oracle/lib/libclntsh.so.8.0

        libnsl.so.1 =>    /usr/lib/libnsl.so.1

        libsocket.so.1 => /usr/lib/libsocket.so.1

        libdl.so.1 =>    /usr/lib/libdl.so.1

        libc.so.1 =>    /usr/lib/libc.so.1

        libwtc8.so =>   /export/home/oracle/lib/libwtc8.so

        libgen.so.1 =>  /usr/lib/libgen.so.1

        libsched.so.1 => /usr/lib/libsched.so.1

        libaio.so.1 =>   /usr/lib/libaio.so.1

        libm.so.1 =>    /usr/lib/libm.so.1

        libmp.so.2 =>   /usr/lib/libmp.so.2

        /usr/platform/SUNW,Ultra-250/lib/libc_psr.so.1

 

sun250% ldd libclntsh.so

        libwtc8.so =>    /export/home/oracle/lib/libwtc8.so

        libnsl.so.1 =>    /usr/lib/libnsl.so.1

        libsocket.so.1 => /usr/lib/libsocket.so.1

        libgen.so.1 =>   /usr/lib/libgen.so.1

        libdl.so.1 =>     /usr/lib/libdl.so.1

        libsched.so.1 =>  /usr/lib/libsched.so.1

        libaio.so.1 =>    /usr/lib/libaio.so.1

        libm.so.1 =>     /usr/lib/libm.so.1

        libc.so.1 =>      /usr/lib/libc.so.1

        libmp.so.2 =>    /usr/lib/libmp.so.2

        /usr/platform/SUNW,Ultra-250/lib/libc_psr.so.1

可以看到gsmindb程序在运行的时候,需要动态定位并加载一系列的动态链接库,其中libclntsh.so负责管理和Oracle数据库的OCI交易。

那么,gsmindblibclntsh.so是什么关系呢?让我们来看一下nm命令的执行效果。

#nm gsmindb | more

[Index]   Value      Size    Type  Bind  Other Shndx   Name

[518]   |     87268|    1284|FUNC |GLOB |0    |9      |Do_Every_City

[352]   |    236528|    2240|FUNC |GLOB |0    |9      |Do_Every_File

[199]   |    238768|    1496|FUNC |GLOB |0    |9      |Do_Every_Rec

[391]   |    194088|    1536|FUNC |GLOB |0    |9      |Dup_Call_Check

[338]   |    217576|     168|FUNC |GLOB |0    |9      |Duration_Check

 

[372]   |    342296|       0|FUNC |GLOB |0    |UNDEF  |OCIAttrGet

[184]   |    342200|       0|FUNC |GLOB |0    |UNDEF  |OCIAttrSet

[257]   |    342152|       0|FUNC |GLOB |0    |UNDEF  |OCIBindArrayOfStruct

[157]   |    342056|       0|FUNC |GLOB |0    |UNDEF  |OCIBindByName

[339]   |    342116|       0|FUNC |GLOB |0    |UNDEF  |OCIDefineArrayOfStruct

[84]    |    341996|       0|FUNC |GLOB |0    |UNDEF  |OCIDefineByPos

[311]   |    342176|       0|FUNC |GLOB |0    |UNDEF  |OCIEnvInit

[292]   |    341936|       0|FUNC |GLOB |0    |UNDEF  |OCIErrorGet

 

#nm libclntsh.so | more

libclntsh.so:

[Index]   Value      Size    Type  Bind  Other Shndx   Name

[14547] |   1559084|      12|FUNC |GLOB |0    |9      |OCIAttrGet

[14088] |   1559112|      12|FUNC |GLOB |0    |9      |OCIAttrSet

[18742] |   1558232|      12|FUNC |GLOB |0    |9      |OCIBindArrayOfStruct

[14409] |   1558036|     104|FUNC |GLOB |0    |9      |OCIBindByName

[19945] |   1557940|      96|FUNC |GLOB |0    |9      |OCIBindByPos

[22212] |   1558156|      12|FUNC |GLOB |0    |9      |OCIBindDynamic

 

通过nm看到gsmindb需要的OCIAttrGet函数处于UNDEF状态,在libclntsh.so中恰有函数OCIAttrGet的描述。以上对应关系可以说明,在gsmindb程序中并没有OCIAttrGet函数的具体实现,OCIAttrGet函数的具体实现封装在共享库libclntsh.so中。Ldd命令执行结果中表现出来的“gsmindb程序对共享库libclntsh.so的依赖关系”,可以说明gsmindb程序在运行的时候发生的共享库libclntsh.so的动态装载过程。

下面让我们来从理论上解释一下。

 

2.               链接库基本概念

链接库可以有三种使用的形式:静态、共享和动态。静态库的代码在编译时就已连接到开发人员开发的应用程序中,而共享库只是在程序开始运行时才载入,在编译时,只是简单地指定需要使用的库函数。动态库则是共享库的另一种变化形式。动态库也是在程序运行时载入,但与共享库不同的是,使用的库函数不是在程序运行开始,而是在程序中的语句需要使用该函数时才载入。

静态库是一个目标文件的简单集合,由ar(archive,归档的意思)生成:

ar -cr libfoo.a foo.o bar.o

通常命名方式是libxxx.a

应用程序在使用链接库的时候,通常只需要告诉链接库的名字即可,这个名字就是libxxx.a中的xxx,例如ld -lfoo。意思是告诉ld,连接一个名字为libfoo.a或者libfoo.so的库。如果你的库名字不遵循libxxx.a的格式,ld就找不到,会给应用开发造成麻烦。

另外,静态的意思是每个用到该库的应用程序都拥有一份自己的库拷贝,应用程序运行的时候,即使将库删除也没有问题,因为应用程序自己已经有了自己的拷贝。

UNIX系统的动态库实际上应该叫做共享库,只是很多人从windowsDynamic Linked Library这个词学习过来,把UNIX的共享库称做动态库。所有应用程序共享一份动态库拷贝,需要在LD_LIBRARY_PATH这个环境变量中正确的设置动态库所在的位置,否则应用程序运行时会报告找不到动态库。

 

3.               链接库相关的编译知识

3.1    基于GCC链接库编译

3.1.1    SunOSLinux

1.       建立静态链接库

gcc -c neupass.c

ar rc ./libneupass.a neupass.o

2.       建立动态链接库

gcc -fPIC -G -o ./libneupass.so neupass.c

3.       静态库编译样例

gcc testit.c -L. –lneupass

4.       动态库编译样例

gcc -L. -lneupass testit.c

 

3.1.2    HP-UX

1.       建立静态链接库

gcc -c neupass.c

ar rc ./libneupass.a neupass.o

2.       建立动态链接库

gcc -fPIC -shared -o ./libneupass.sl neupass.c

3.       静态库编译样例

gcc testit.c -L. -lneupass

4.       动态库编译样例

gcc -L. -lneupass testit.c

 

3.1.3    AIX

1.      建立静态链接库

gcc -c neupass.c

ar rc ./libneupass.a neupass.o

2.      建立动态链接库

gcc -shared -o ./libneupass.so neupass.c

3.      静态库编译样例

gcc testit.c -L. –lneupass

4.      动态库编译样例

gcc -Xlinker -bdynamic -Xlinker -brtl -L. -lneupass testit.c

注:

AIX系统上连接的时候,如果需要联接*.so文件,那么必须在链接的时候,选择brtl选项。

3.2    GCC编译的option

     -G   传递给联接器的参数,建议使用`-symbolic'  or  `-shared'

 

     -fPIC

          If supported for the  target  machine,  emit  position-

          independent code, suitable for dynamic linking, even if

          branches need large displacements.

 

     Linker Options

     -shared

          Produce a shared object which can then be  linked  with

          other  objects  to form an executable.  Only a few sys-

          tems support this option.

     -Wl,option

          Pass option as an option to the linker.  If option con-

          tains  commas, it is split into multiple options at the

          commas.

 

3.3        ldoption

-B dynamic代表动态模式,-B static代表静态模式。

在动态模式下,*.so*.a类型的库文件都可能会被联接。在库文件的查找路径中,只需要找到*.so*.a文件中的任何一个即停止查找。如果在搜索路径的某个目录下,*.so*.a文件同时存在,*.so文件优先。

在静态模式下,只有*.a类型的静态库文件会被查找和联接。

3.4    链接库的搜索顺序

LD_LIBRARY_PATH的概念如下:

A list of directories in which to search for libraries specified with the -l option. Multiple directories are separated by a colon. In the most general case, it will contain two directory lists separated by a semi-colon.

Example: dir1a:dir1b:dir1c;dir2a:dir2b:dir2c

If ld is called with any number of occurrences of -L, as in:

ld ... -Lpath1 –Lpath2 ... -Lpathn ...

then the search path ordering is:

dir1a:dir1b:dir1c path1 path2... pathn dir2a:dir2b:dir2c LIBPATH

When the list of directories does not contain  a  semicolon, it is interpreted as dirlist2.

按照目前的编程习惯,一般设置LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=dir2a:dir2b:dir2c

所以,链接库的搜索顺序是:

path1 path2 pathn LD_LIBRARY_PATH(dir2a:dir2b:dir2c) LIBPATH

3.5    同链接库有关的环境变量

对于HP-UXLPATH的环境变量限定的是在编译的时候搜索库文件的路径,程序还可以设定RPATH作为运行时搜索库文件的路径。

普通情况下,LD_LIBRARY_PATH设定之后,需要动态加载链接库的程序即可正常工作。但是,在某些特定的应用场合,还需要设定另一个特定的变量,在HP-UX系统下是SHLIB_PATH,在AIX系统下是LIBPATH,设定的内容可以保持和LD_LIBRARY_PATH一致。

 

3.6    注意事项

如果连接的是动态库的话,那么-llibs放在*.o的前面和后面都会正常链接。

但是如果连接的是静态库的话,那么-llibs一般应当放在*.o的后面,才能够保证正常链接。

例如:libneupass.a是静态库

#gcc -o a.out -L. -lneupass testit.o

未定义                  文件中的

符号                    在文件中

encode                   testit.o

ld: 致命的: 符号参照错误. 没有输出被写入a.out

collect2: ld returned 1 exit status

但是:

#gcc -o a.out -L. testit.o -lneupass

运行正常

以上问题,并不是在每个平台上都出现。

 

以上符号参照错误的原因:

cc -lm foo.c

这里foo.c用到了数学库中的符号,但是链接器无法正确解析。

因为当搜索到libm.a时,来自foo.c的数学函数符号尚未出现,因此不需要析取libm.a的任何模块。接下来foo.o链接进来,增加了一批尚未成功解析的符号,但已经没有libm.a可供使用了,因此链接库libm.a必须在foo.o之后被搜索到。

再例如:有几个库文件A.aB.acommon.a,前两者A.aB.a用到了定义在common.a中的例程,如果把common.a放在前面,链接器报告存在无法解析的符号名,放在最后则无问题。

原创粉丝点击