浅析C/C++ library

时间:2021-07-12 18:18:45

1 背景

原来跑的好好的进程,重启后没跑多少就挂掉了,奇怪了。经过跟踪,原来是加载了一个.so文件,于是决定学习一下library相关的东东,现在和大家分享一下。

2 分类

C/C++ library可分为三类:静态库(Static Libraries)、共享库(shared Libraries)和动态加载库(dynamically loaded DL libraries)。

静态库在程序编译时会被连接到可执行程序中,程序运行时将不再需要该静态库。

共享库在程序编译时并不会被连接到可执行程序中,而是在可执行程序启动时被载入内存的,因此在程序运行时还需要动态库存在。

动态加载库在可执行程序执行的任何时候可以被加载。 DL libraries aren't really a different kind of library format (both static and shared libraries can be used as DL libraries)。

3 Static Libraries

创建方法:

ar rcs libmy_library.a file1.o file2.o

4 Shared Libraries

4.1 命名

soname: version number,由ldconfig -n $library_directory生成,程序加载依赖的名字,

library安装好之后,根据realname生成soname

realname: soname的基础上增加minor number和release number

linkername: 以so结尾,没有version number,也没有minor number和release number,

ldconfig对于开发程序时你要链接的library不做出任何假定,不会自动生成linkername

有如下的指向关系

soname -> realname

linkername -> realname

例子如下所示:

soname ----> /usr/lib/libreadline.so.3 -> /usr/lib/libreadline.so.3.0

realname ----> /usr/lib/libreadline.so.3.0

linkername ----> /usr/lib/libreadline.so -> /usr/lib/libreadline.so.3

所以一般的情况是

linkername -> soname -> realname

4.2 使用方法

当执行一个ELF文件的时候,program loader(/lib/ld-linux.so.X,程序加载器)寻找并加载ELF文件所需要的所有libraries,搜寻的目录在/etc/ld.so.conf当中指定。/etc/ld.so.preload 文件当中指示那些预加载的,优先级高于一般libraries的库,为了提高加载效率,不用每次执行一个程序都去搜索,便有了文件/etc/ld.so.cache,每次有新的libraries加入或者删除一些libraries,都应该运行ldconfig,更新文件/etc/ld.so.cache。

4.3 环境变量

4.3.1 LD_LIBRARY_PATH

LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径,注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找。设置方法如下(其中,LIBDIR1和LIBDIR2为两个库目录):

export LD_LIBRARY_PATH=LIBDIR1:LIBDIR2:$LD_LIBRARY_PATH

4.3.2 LIBRARY_PATH

LIBRARY_PATH环境变量用于在程序编译期间查找动态链接库时指定查找共享库的路径,例如,指定gcc编译需要用到的动态链接库的目录。设置方法如下(其中,LIBDIR1和LIBDIR2为两个库目录):

export LIBRARY_PATH=LIBDIR1:LIBDIR2:$LIBRARY_PATH

4.3.3 LD_PRELOAD

这个变量的作用类似于/etc/ld.so.preload

4.3.4 LD_DEBUG=files/libs/bindings/versions/help

通过这个变量的设置会显示share library函数调用时候的更加详细信息

4.4 工具

4.4.1 动态链接库管理命令ldconfig

为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig.

ldconfig命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。

ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。

4.4.2 ldd

ldd,列出某个程序依赖的shared library,不要对某个不信任的程序进行ldd,因为ldd的工作方式是:首先,设置某个变量,LD_TRACE_LOADED_OBJECTS,然后,执行这个程序。

4.5 创建shared libraries

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

-fPIC与-fpic比较,fPIC编译的文件比较大,和平台无关,fpic编译的比较小,和平台有关。不用使用-fomit-frame-pointer这个编译参数除非你不得不这样。虽然使用了这个参数获得的函数库仍然可以使用,但是这使得调试程序几乎 没有用,无法跟踪调试。需要使用“-Wl,-export-dynamic”这个选项参数。通常,动态函数库的符号表里面包含了这些动态的对象的符号。 这个选项在创建ELF格式的文件时候,会将所有的符号加入到动态符号表中。可以参考ld的帮助获得更详细的说明。如果在linux可以用``-rdynamic'' 代替``-Wl,export-dynamic''。

在开发过程中,想修改一个库,但该库也用于许多其他程序, 你不希望其他程序使用修改的库,只让一个特定的应用程序测试,一个链接选项:`rpath',它指定特定的程序被编译的运行时库搜索路径。

-Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)

5 Dynamically Loaded (DL) Libraries

使用动态加载库:

#include <stdlib.h>

#include <stdio.h>

#include <dlfcn.h>

int main(int argc, char **argv) {

void *handle;

double (*cosine)(double);

char *error;

handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);

if (!handle) {

fputs (dlerror(), stderr);

exit(1);

}

cosine = dlsym(handle, "cos");

if ((error = dlerror()) != NULL)  {

fputs(error, stderr);

exit(1);

}

printf ("%f\n", (*cosine)(2.0));

dlclose(handle);

}

编译命令为:

gcc -o foo foo.c -ldl

6 参考

http://tldp.org/HOWTO/Program-Library-HOWTO/