请教在用户空间共享动态链接库的方法
在用户空间里要运行两个应用程序,这两个应用程序都要动态链接一个动态链接库,这个链接库编译出来是应该放在哪里,和这两个程序的空间有关系吗,这两个程序都只有一个进程,他们的进程空间相互独立,所以要如何才能得到可访问的链接库的地址,这是一个图形界面的例子,库是图形界面的库。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
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交易。
那么,gsmindb和libclntsh.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系统的动态库实际上应该叫做共享库,只是很多人从windows的Dynamic Linked Library这个词学习过来,把UNIX的共享库称做动态库。所有应用程序共享一份动态库拷贝,需要在LD_LIBRARY_PATH这个环境变量中正确的设置动态库所在的位置,否则应用程序运行时会报告找不到动态库。
3. 链接库相关的编译知识
3.1 基于GCC的链接库编译
3.1.1 SunOS、Linux
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 ld的option
-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-UX,LPATH的环境变量限定的是在编译的时候搜索库文件的路径,程序还可以设定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.a、B.a、common.a,前两者A.a和B.a用到了定义在common.a中的例程,如果把common.a放在前面,链接器报告存在无法解析的符号名,放在最后则无问题。