共享库学习笔记
以下内容均为翻译.英文文档好长,而且看起来很没感觉,所以翻译下来,供以后复习.
1.共享库
共享库名字
每一个共享库有一个特别的名字,叫做soname.
soname有:前缀lib,库的名字,和.so,然后是一个.版本号
一个完整的soname (不会翻译...)includes as a prefix the directory it's in
每一个共享库还有一个real name,它实际就是包含了实际库代码的文件名.
real name在soname后加上了.minor_number.release_number,最后的release_number可以省略.他们一起让你知道了你安装的库的版本.
另外,还有一个linker name,就是soname去掉版本号
管理共享库的关键是区分这些名字.
程序,当他列出他需要的共享库时,应该只列出需要的soname.
相反,当你创建一个共享库,你只需用指定的文件名创建库(具有详细的版本信息).
当你安装一个新版本的库,你向几个指定目录之一安装,然后运行ldconfig(8)程序.
ldconfig检查存在的文件,用symbolic link到real name创建soname,同时设置缓存文件/etc/ld.so.cache
ldconfig不设置linker name,一般这个是在库安装的过程中完成.
最后是一个例子.
/usr/lib/libreadline.so.3是一个完整的soname,他的symbolic link到realname如下形式
/usr/lib/libreadline.so.3.0
linker name是/usr/lib/libreadline.so ,他可能是一个指向
/usr/lib/libreadline.so.3的symbolic linker
文件系统位置
共享库必须被放到文件系统的某个位置.大多数开源软件都遵从GNU标准.
GNU标准推荐默认安装所有库到/usr/local/lib,所有命令安装到/usr/local/bin
GNU同样定义了覆盖这些默认和调用安装路径的约定.
文件系统层次标准(FHS)讨论了什么应该在哪里.根据FHS,大多数库应该安装到/usr/lib,startup需要的库应该放到/lib,不是系统的一部分的库应该放到/usr/local/lib
实践中,最新的软代码自动地把自己安装到local 目录(/usr/local),一旦代码成熟,包管理器能够覆盖默认位置的代码.注意,如果你的库访问只能通过库访问的程序,你应该把程序放到/usr/local/libexec
2.共享库怎样用
在GNU glibc-based系统,包括所有的linux系统,启动一个ELF二进制可执行文件自动时自动引起程序loader加载和运行.在linux系统,loader的名字是/lib/ld-linux.so.X(X是版本号).loader找到并且加载程序需要的其他共享库.
搜索的目录列表存放在文件/etc/ld.so.conf中.
如果你想覆盖库里面的几个函数,但是保留库中其他部分,可以向/etc/ld.so.preload中输入覆盖库的名字(.o 文件),这些preload库会优先于标准set加载.
3.环境变量
LD_LIBRARY_PATH
你可以暂时用一个不同的库代替这个方法.在linux,环境变量LD_LIBRARY_PATH是一个用冒号分隔的目录集,他的库会在标准目录集之前首先被搜索.这在debug一个新的库或者使用一个非标准库时会有用处.环境变量LD_PRELOAD列出了覆盖标准集的共享库,就像/etc/ld.so.preload做的那样.这些是/lib/ld-linux.so实现的.
LD_LIBRARY_PATH在开发和测试中很有用,但不应该被一般用户在安装过程中的一般用途中修改.
如果你不行设置LD_LIBRARY_PATH环境变量,在linux上你可以直接调用程序loader,传给他参数.例如,下面用给定的PATH代替环境变量LD_LIBRARY_PATH内容:
/lib/ld-linux.so.2 --library-path PATH EXECUTABLE
执行ld-linux.so会提供更多的帮助.最后再次提醒,这些是为debug准备的,不要在一般情况下使用.
LD_DEBUG
另外一个在GNU C loader中有用的环境变量是LD_DEBUG.这个出发了dl*函数,所以他们提供了十分详细的信息,关于他们做了什么.例如:
export LD_DEBUG=files
command_to_run
列出了处理文件和库的过程,告诉你检测到什么依赖,什么so以什么样的顺序被加载.
4.创建一个共享库
创建一个共享库比较简单.首先,创建将要进入共享库的目标文件,使用gcc -fPIC 或者 -fpic 标志.
-fPIC和-fpic选项使能了'位置独立代码'的生成,这是共享库的一项规定.你通过-Wl选项传送soname.
使用下面形式建立共享库:
gcc -shared -Wl,-soname,your_soname \
-o library_name file_list library_list
下面是一个例子,建立两个object file(a.o和b.o)然后建立一个共享库包含他们两个.
注意这份编译包含debug信息(-g),会生成warning(-Wall),他们不是必须的,但推荐这样做.
编译生成object file(使用-c),包含-fPIC选项:
gcc -fPIC -g -c -Wall a.c
gcc -fPIC -g -c -Wall b.c
gcc -shared -Wl,-soname,libmystuff.so.1 \
-o libmystuff.so.1.0.1 a.o b.o -lc
5.安装和使用共享库
一旦你建好了一个共享库,下一步就是安装它.简单的办法是复制库到标准目录(如/usr/lib)中,然后运行ldconfig
首先,在某处创建共享库.然后,你需要设置必要的symbolic link,也就是从soname到real name.
最简单的办法是运行:
ldconfig -n directory_with_shared_libraries
最后,编译你的程序,你需要告诉linker你用到的任何static和shared library.这需要使用-l选项.
如果你不能(或者不想)安装库到标准位置(你可能没有权限修改/usr/lib),需要改变安装过程.
这种情况下,你需要告诉你的程序足够信息去找到库.
有几种方法,可以用gcc的 -L 标志,这是最简单的方法,可以用rpath方法,如果你有一个特定的程序使用非标准位置的库.还可以使用环境变量控制.具体地说,可以设置LD_LIBRARY_PATH.如果你在用bash,可以这样调用my_program:
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program
你可以查看一个程序使用的共享库列表,通过ldd(1).例如,可以查看ls使用的共享库:
ldd /bin/ls
一般你可以看到一个列表,显示需要依赖的soname,跟随着这些名字解析到的目录.
实践中,你至少会看到两个依赖:
/lib/ld-linux.so.N(N通常至少为2) 这是load所以其他库的库.
libc.so.N(N通常至少为6) 这是C库.即使其他语言通常都用C库,所以大多数程序至少包括这个库.
注意:不要对你不信任的程序运行ldd.
6.不兼容的库
当一个新版本的库与旧版本是二进制不兼容的,soname就需要改变了.
在C中,有4个基本的原因导致二进制不兼容.
1)函数的行为发生改变,不满足他原始的规定说明
2)输出数据改变
3)输出函数被删除
4)输出函数接口发生改变
原文链接:http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html