Linux程序库(libraries)杂谈

Linux程序库(libraries)杂谈

1.     文章简介

本文主要介绍了linux系统上静态库、动态库编译方法、常见特性,以及相关的环境变量,以及动态库编程API。本文作为中级教程,未涉及教科书中常说的程序库的定义、优点等,仅为杂谈。

2.     静态库

Ø  使用ar程序打包,文件名以``.a''结尾

Ø  Object文件集合

Object文件是elf文件,包含了数据、符号、二进制代码等

Ø  优点

库版本依赖在链接阶段已经解决

由于编译、链接阶段已解决符号地址定位,所以程序启动速度较快(<5%)

Ø  编译方法

ar rcs libmy_library.a file1.o file2.o

g++ -L${LIB_PATH} –lmy_library

3.     共享库

Ø  使用gcc/ld程序生成,文件名以``.so''结尾

Ø  共享库为elf文件,包含了数据、符号、代码等

Ø  文件命名

动态库存在sonamerealname,动态库文件的位置为libpath,下面以libstdc++.so.6.0.14文件为例进行说明

文件:/usr/local/gcc4.5.1/lib/libstdc++.so.6.0.14

augustsun@linux-b47w:/usr/lib> l libstdc++.so.6

lrwxrwxrwx 1 root root 19 2010-07-24 13:49 libstdc++.so.6 -> libstdc++.so.6.0.12

 

n  soname

sonamelibstdc++.so.6,为编译时指定的名字,在动态库内部保存。

augustsun@linux-b47w:/usr/lib> readelf -a libstdc++.so.6.0.12 | grep SO

 0x0000000e (SONAME)                     Library soname: [libstdc++.so.6]

 

n  realname

realname: /usr/local/gcc4.5.1/lib/libstdc++.so.6.0.14,动态库真实的文件名,包括文件路径

n  libpath

/usr/local/gcc4.5.1/lib/

Ø  版本号以及如何支持程序的兼容

n  主版本号表明程序库版本之间的潜在不兼容性。

n  次要版本号表明只是修正了缺陷。

n  版本号实例

gcc -shared -fPIC -Wl,-soname,libd2.so.1 -o libd2.so.1.0.1 d.c

ln -s libd2.so.1.0.1 libd2.so.1

ln -s libd2.so.1 libd2.so

gcc -shared -fPIC -L. –ld2 -o libb.so b.c

编译过程中:

    链接器指定了-ld2, 会去寻找libd2.so

    如果libd2.so为符号链接,指向某个指向最新版本的符号链接,直到出现第一个主版本号文件(libd2.so.1

    所以会依赖libd2.so.1

n  兼容性总结

在大部分情形下,加载具有相同主版本号和更高次要版本号的程序库是安全的;而加载主版本号更高的程序是不安全的行为。

Ø  优点

程序共享,减少程序文件大小。

动态链接的可能优势之一在于修正缺陷。如果可以修正程序库中的缺陷,而且不必重新编译上千个程序,就 可以利用这一修正功能,这将是非常令人愉快的。有时,需要链接到某个较新的版本。

    依赖动态链接库的程序不用重新编译

Ø  编译方法

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

    编译选项:运行时动态库查找路径:-Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)

4.     共享库相关环境变量

Ø  LD_LIBRARY_PATH

动态库优先搜索路径

Ø  LD_PRELOAD

n  库中函数会覆盖其他库中函数

n  临时打补丁使用

n  其他方式

配置/etc/ld.so.preload,或者执行时命令行:/lib/ld-linux.so.2 --library-path PATH EXECUTABLE

n  例子

augustsun@linux-b47w:~/workspace> cat test.c

#include <stdio.h>

#include <string.h>

void test(const char* a, const char* b)

{

    if (strcmp(a, b) == 0) {

        printf("str('%s') equals str('%s')\n", a, b);

    } else {

        printf("str('%s') *NOT* equals str('%s')\n", a, b);

    }

}

int main(int argc, char* arvg)

{

    test("hello", "world");

    return 0;

}

 

augustsun@linux-b47w:~/workspace> ./test

str('hello') *NOT* equals str('world')

 

augustsun@linux-b47w:~/workspace> cat preload.c

#include <string.h>

int strcmp(const char* a, const char* b)

{

    return 0;

}

 

augustsun@linux-b47w:~/workspace> export LD_PRELOAD=./preload.so

augustsun@linux-b47w:~/workspace> ./test

str('hello') equals str('world')

 

其他说明:

以下代码没有调用strcmp,因为编译时已经优化掉了,即使使用了-O0优化!!可以通过gcc –S test.c来查看汇编代码。知道禁止编译优化的请mail [email protected],谢谢!

#include <stdio.h>

#include <string.h>

int main(int argc, char* arvg)

{

    if (strcmp("hello", "world") == 0) {

        printf("str('hello') equals str('world')\n", a, b);

    } else {

        printf("str('hello') *NOT* equals str('world')\n", a, b);

    }

    return 0;

}

 

 

Ø  LD_TRACE_LOADED_OBJECTS

执行程序时程序将不执行而是输出了动态库的依赖项

augustsun@linux-b47w:~> export LD_TRACE_LOADED_OBJECTS=1

augustsun@linux-b47w:~> ls

        linux-gate.so.1 =>  (0xffffe000)

        librt.so.1 => /lib/librt.so.1 (0xb78a1000)

        libselinux.so.1 => /lib/libselinux.so.1 (0xb7883000)

        libcap.so.2 => /lib/libcap.so.2 (0xb787d000)

        libacl.so.1 => /lib/libacl.so.1 (0xb7874000)

        libc.so.6 => /lib/libc.so.6 (0xb7713000)

        libpthread.so.0 => /lib/libpthread.so.0 (0xb76f9000)

        /lib/ld-linux.so.2 (0xb78c1000)

        libdl.so.2 => /lib/libdl.so.2 (0xb76f4000)

        libattr.so.1 => /lib/libattr.so.1 (0xb76ee000)

5.     动态(加载)库

   程序执行中通过dl簇函数加载。跟共享库的差别是:共享库在程序启动时加载,并且由/lib/ld-linux.so.2自动加载。

Ø  API例子及其说明

#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);

}

void *dlopen(const char *filename, int flag);

n  主要flag说明

RTLD_LAZY:代码执行到时解析(重定向)

RTLD_NOW dlopen函数返回前解析(重定向)所有符号连接

RTLD_GLOBAL 后续动态库的符号解析时可以之后此加载的符号

RTLD_LOCAL  后续动态库的符号解析时不可以之后此加载的符号 

Ø  LD_DEBUG

程序中有调用dl*函数的时候输出信息

6.     其他

Ø  库的构造和析构函数

n  不推荐使用的API

void _init(void);

void _fini(void);

 

n  推荐使用的API

void __attribute__ ((constructor)) my_init(void);

void __attribute__ ((destructor)) my_fini(void);

Ø  动态库可以被执行

通过设置编译参数-Wl,-e,FunctionName,动态库可以执行FunctionName函数。

const char

my_interp[] __attribute__((section(".interp"), used)) =

 "/lib64/ld-linux-x86-64.so.2";

 

#ifdef __cplusplus

extern "C" {

#endif

    void ShowVersion(void)

    {

        puts("version:1.20.0-build002");

        exit(0);

    }

#ifdef __cplusplus

}

#endif

 

 

Ø  库函数兼容性

n  二进制兼容Application Binary Interface)的好处

1.  直接替换版本不需要重新编译

2.  版本更新简单

n  C语言程序不兼容的原因

1.  函数的行为改变,符合于原有的规范

2.  数据结构变了

3.  原有接口不再支持

4.  函数接口变化

n  C++语言程序不兼容的原因

1.  添加新的虚函数(导致虚函数表变化)

2.  添加、删除虚函数,导致虚函数表变化

3.  外部可见的成员变量类型或者位置变化

4.  类继承变化

5.  成员变量变化,导致子类字段错乱

6.  inline的外部可见函数被删除

7.  外部可见函数变成了inline函数

8.  inline函数变化

9.  函数定义变化,导致生成的mangle名变化

10.     Others

所以,建议提供C原因接口

7.     引用

http://blog.csdn.net/haoel/article/details/1602108

http://www.360doc.com/content/09/0101/23/36491_2242478.shtml

http://en.wikipedia.org/wiki/Shared_library#Shared_libraries

http://wenku.baidu.com/view/9ef3dd649b6648d7c1c74626.html

http://hi.baidu.com/gaoxiaotiger/blog/item/d963547ff2c36e2f0cd7daa8.html

猜你喜欢

转载自f2n2z6.iteye.com/blog/1236466