从程序员角度看ELF (4)
时间:2007-04-09 来源:Echo CHEN
★5.5 Linux下的ELF
在Linux下ELF的执行有独特的特性,这些特性对Linux的使用者来说是很有用的。一些Linux自己的扩展跟Solaris ELF的执行很类似。
★5.5.1 ELF的宏(macros)
<gnu-stabs.h>中,定义了能维护标号的一些宏。
elf_alias(name1,name2)
为标号name1定义一个化名name2.当文件中标号名已经被定义的时候应该是有很用的。
weak_alias(name1,name2)
为标号name1定义一个弱化名name2。仅当name2没有在任何地方定义时,连接器就会用name1解析name2相关的符号。在文件中定义的标号name1也会同样处理。
elf_set_element(set,symbol)
强迫标号成为set集合的元素。为每个set集合创建一个section.
symbol_set_declare(set)
在该模块中宣告一个集合set.事实上宣告了两个标号:
1 一个set的开始标号
extern void * const __start_set
2 一个set的结尾标号
extern void * const __stop_set
symbol_set_first_element(set)
返回一个指针(void * const *),指向set集合第一个元素。
symbol_set_end_p(set,ptr)
假如ptr(void * const *)逐渐增加指向set的最后一个元素,就返回为true.
使用这些宏,程序员能任意从不同的源文件中创建列表。
★5.5.2 library(库)的定位和搜索路径
在Linux下,大部分系统的library库被安装在/usr/lib目录下。只有一些基本的共享库被安装在/lib目录下。例如:libc.so,libcurses.so,libm.so,libtermcap.so(各个版本对应的文件会有些不同),在其他部分被mount上之前,那些文件是启动Linux系统所必须的。连接器默认的搜索路径是
/lib,/usr/lib,/usr/local/lib,/usr/i486-linux/lib。
环境变量LD_LIBRARY_PATH也可保存目录列表,用(:)分开,该变量被动态连接器检查并用该变量指出的目录查找共享库。例如:/usr/X11R6/lib:/usr/local/lib:告诉动态连接器查找共享库除了现在在默认的目录查找外,然后在/usr/X11R6/lib目录,然后再是/usr/local/lib目录,然后再是当前目录。
新的环境变量ELF_LD_LIBRARY_PATH扮演着和LD_LIBRARY_PATH类似的角色。因为LD_LIBRARY_PATH也被老的a.out DLL linux的共享库使用。为了避免来自DLL连接器的不必要的警告,对于在Linux下ELF的动态连接器来说,最好使用LD_LIBRARY_PATH环境变量。
另外一个特性是/etc/ld.so.conf文件,该文件包含了一些目录列表。
例如:
/usr/X11R6/lib
/usr/lib
/usr/kerberos/lib
/usr/i486-linux-libc5/lib
/usr/lib/gconv
/usr/lib/qt-2.1.0/lib
/usr/lib/qt-1.45/lib
程序ldconfig将把/etc/ld.so.conf文件中列出的搜索目录中的所有的共享库存储到/etc/ld.so.cache中。假如共享库已经被从默认的目录中移走,Linux ELF动态连接库将在/etc/ld.so.cache文件中找该共享库。
★5.5.3 共享库的版本
在ELF系统上,假如两个共享库有同样的应用程序二进制接口(ABI)的子集的话,对那些仅仅使用那些ABI子集的程序来说,这两个共享库是可以互相通用的(当然那两个共享库有同样的函数功能)。
当一个库被改变,只要新的ABI和前面一个版本的共享库有100%的兼容的话,所有和前面版本连接的程序在新的共享库下也能很好的运行。为了支持这,foo库必须小心的维护:
1.这个共享库应该如下构造:
[alert7@redhat62 dl]# gcc -shared -Wl,-soname,libfoo.so.major \
-o libfoo.so.major.minor.patch-level libfoo.o
动态连接器运行时将试着定位和映象libfoo.so.major而不管事实上用的共享
文件名libfoo.so.major.patch-level。
2.一个符号连接应该指向正确的共享库
[alert7@redhat62 dl]# ln -s libfoo.so.major.minor.patch-level \
libfoo.so.major
3.当ABI改变和原来版本不兼容的时,主(major)版本号应该升级。
当搜索共享库的时候,Linux连接器将使用最新的共享库(它们有最高的major,minor和patch level的版本号)。
★5.5.4 共享(shared)库和静态(static)库的混合连接
默认情况下,假如共享库可用,连接器会使用共享库。但是-Bdynamic和-Bstatic提供了很好控制库的方法。它们可以决定用共享库还是用静态库。
传-Bdynamic和-Bstatic选项给连接器,如下操作:
# gcc -o main main.o -Wl,-Bstatic \
-lfoo -Wl,-Bdynamic -lbar
# gcc -o main main.o -Wl,-Bstatic
告诉连接器所有的库(象libc等等)都使用静态的版本。
★5.5.5 装载附加的共享库
在ELF系统上,为了执行一个ELF文件,内核要把控制权交给动态连接器ld-linux.so.1(在linux上动态连接器是ld-linux.so.1,版本不同也会不同的,在默认的redhat6.2上是/lib/ld-linux.so.2)。在绝对路径/lib/ld-linux.so.1 以二进制存放着。假如动态连接器不存在,没有哪个ELF可执行文件能运行。
动态连接器执行以下一个步骤完成从程序到进程映象:
1.分析可执行文件中的动态信息section,决定需要哪些库。
2.定位和映象(map)那些共享库,并且分析它们动态信息section决定是否需要附加的共享库。
3.为可执行程序和那些需要的共享库执行重定位。
4.调用共享库中提供的任何初始化函数并且安排共享库提供的清除(cleanup)函数在共享库卸栽出进程空间的时候运行。
5.传控制给程序
6.为应用程序提供函数的迟延装定服务
7.为应用程序提供动态转载服务。
环境变量LD_PRELOAD设置共享库名或者用":"把文件名隔开。动态连接器在任何那些请求的共享库之前把环境变量LD_PRELOAD的共享库装载到进程地址空间去。例如:
# LD_PRELOAD=./mylibc.so myprog
这里./mylibc.so将第一时间map到程序myprog的空间。因为动态连接器在找寻标号的时候总是使用第一次碰到的标号,所以我们可以使用LD_PRELOAD来覆盖标准共享库中的函数。这个特性对程序员来说是很有用的,可用来在还没有建好整个共享库的时候对单个函数功能先做调试实验。
我们可以这样:
#gcc -c -fPIC -O3 print.c
#gcc -shared print.o -o print.so.1.0
创建自己的共享连接库
★5.5.6 Linux下动态装载(Dynamic loading)
_dlinfo是动态连接接口库的一个函数。它列出所有映射到执行程序和通过dlopen打开的每个共享库。它的输出类试以下:
List of loaded modules
00000000 50006163 50006200 Exe 1
50007000 5000620c 50006200 Lib 1 /lib/elf/libd1.so.1
5000a000 500062c8 50006200 Lib 2 /lib/elf/libc.so.4
50000000 50006000 00000000 Int 1 /lib/elf/ld-linux.so.1
500aa000 08006f00 08005ff0 Mod 1 ./libfoo.so
Modules for application (50006200):
50006163
5000620c /lib/elf/libdl.so.1
500062c8 /lib/elf/libc.so.4
50006000 /lib/ld-linux.so.1
Modules for handle 8005ff0
08006f00 ./libfoo.so
500062c8 /lib/elf/lib.so.4
50006163
5000620c /lib/elf/libd1.so.1
500062c8 /lib/elf/libc.so.4
50006000 /lib/elf/ld-linux.so.1
以上可被用来解释动态的连接和动态的装载。
在linux支持ELF上配置的GCC假如使用了-rdynamic选项,它将把-export-dynamic传给连接器。强烈建议使用动态装载。这就是为什么在我们的Makefile例子中使用了LDFLAGS=-rdynamic。暂时,这个选项只能在linux下使用。但是-Wl,-export-dynamic能在其他的平台上把-export-dynamic
传给GNU的连接器。
你能在GNU link editor的[3]和[4]部分找到它详细的描述。
在Linux下ELF的执行有独特的特性,这些特性对Linux的使用者来说是很有用的。一些Linux自己的扩展跟Solaris ELF的执行很类似。
★5.5.1 ELF的宏(macros)
<gnu-stabs.h>中,定义了能维护标号的一些宏。
elf_alias(name1,name2)
为标号name1定义一个化名name2.当文件中标号名已经被定义的时候应该是有很用的。
weak_alias(name1,name2)
为标号name1定义一个弱化名name2。仅当name2没有在任何地方定义时,连接器就会用name1解析name2相关的符号。在文件中定义的标号name1也会同样处理。
elf_set_element(set,symbol)
强迫标号成为set集合的元素。为每个set集合创建一个section.
symbol_set_declare(set)
在该模块中宣告一个集合set.事实上宣告了两个标号:
1 一个set的开始标号
extern void * const __start_set
2 一个set的结尾标号
extern void * const __stop_set
symbol_set_first_element(set)
返回一个指针(void * const *),指向set集合第一个元素。
symbol_set_end_p(set,ptr)
假如ptr(void * const *)逐渐增加指向set的最后一个元素,就返回为true.
使用这些宏,程序员能任意从不同的源文件中创建列表。
★5.5.2 library(库)的定位和搜索路径
在Linux下,大部分系统的library库被安装在/usr/lib目录下。只有一些基本的共享库被安装在/lib目录下。例如:libc.so,libcurses.so,libm.so,libtermcap.so(各个版本对应的文件会有些不同),在其他部分被mount上之前,那些文件是启动Linux系统所必须的。连接器默认的搜索路径是
/lib,/usr/lib,/usr/local/lib,/usr/i486-linux/lib。
环境变量LD_LIBRARY_PATH也可保存目录列表,用(:)分开,该变量被动态连接器检查并用该变量指出的目录查找共享库。例如:/usr/X11R6/lib:/usr/local/lib:告诉动态连接器查找共享库除了现在在默认的目录查找外,然后在/usr/X11R6/lib目录,然后再是/usr/local/lib目录,然后再是当前目录。
新的环境变量ELF_LD_LIBRARY_PATH扮演着和LD_LIBRARY_PATH类似的角色。因为LD_LIBRARY_PATH也被老的a.out DLL linux的共享库使用。为了避免来自DLL连接器的不必要的警告,对于在Linux下ELF的动态连接器来说,最好使用LD_LIBRARY_PATH环境变量。
另外一个特性是/etc/ld.so.conf文件,该文件包含了一些目录列表。
例如:
/usr/X11R6/lib
/usr/lib
/usr/kerberos/lib
/usr/i486-linux-libc5/lib
/usr/lib/gconv
/usr/lib/qt-2.1.0/lib
/usr/lib/qt-1.45/lib
程序ldconfig将把/etc/ld.so.conf文件中列出的搜索目录中的所有的共享库存储到/etc/ld.so.cache中。假如共享库已经被从默认的目录中移走,Linux ELF动态连接库将在/etc/ld.so.cache文件中找该共享库。
★5.5.3 共享库的版本
在ELF系统上,假如两个共享库有同样的应用程序二进制接口(ABI)的子集的话,对那些仅仅使用那些ABI子集的程序来说,这两个共享库是可以互相通用的(当然那两个共享库有同样的函数功能)。
当一个库被改变,只要新的ABI和前面一个版本的共享库有100%的兼容的话,所有和前面版本连接的程序在新的共享库下也能很好的运行。为了支持这,foo库必须小心的维护:
1.这个共享库应该如下构造:
[alert7@redhat62 dl]# gcc -shared -Wl,-soname,libfoo.so.major \
-o libfoo.so.major.minor.patch-level libfoo.o
动态连接器运行时将试着定位和映象libfoo.so.major而不管事实上用的共享
文件名libfoo.so.major.patch-level。
2.一个符号连接应该指向正确的共享库
[alert7@redhat62 dl]# ln -s libfoo.so.major.minor.patch-level \
libfoo.so.major
3.当ABI改变和原来版本不兼容的时,主(major)版本号应该升级。
当搜索共享库的时候,Linux连接器将使用最新的共享库(它们有最高的major,minor和patch level的版本号)。
★5.5.4 共享(shared)库和静态(static)库的混合连接
默认情况下,假如共享库可用,连接器会使用共享库。但是-Bdynamic和-Bstatic提供了很好控制库的方法。它们可以决定用共享库还是用静态库。
传-Bdynamic和-Bstatic选项给连接器,如下操作:
# gcc -o main main.o -Wl,-Bstatic \
-lfoo -Wl,-Bdynamic -lbar
# gcc -o main main.o -Wl,-Bstatic
告诉连接器所有的库(象libc等等)都使用静态的版本。
★5.5.5 装载附加的共享库
在ELF系统上,为了执行一个ELF文件,内核要把控制权交给动态连接器ld-linux.so.1(在linux上动态连接器是ld-linux.so.1,版本不同也会不同的,在默认的redhat6.2上是/lib/ld-linux.so.2)。在绝对路径/lib/ld-linux.so.1 以二进制存放着。假如动态连接器不存在,没有哪个ELF可执行文件能运行。
动态连接器执行以下一个步骤完成从程序到进程映象:
1.分析可执行文件中的动态信息section,决定需要哪些库。
2.定位和映象(map)那些共享库,并且分析它们动态信息section决定是否需要附加的共享库。
3.为可执行程序和那些需要的共享库执行重定位。
4.调用共享库中提供的任何初始化函数并且安排共享库提供的清除(cleanup)函数在共享库卸栽出进程空间的时候运行。
5.传控制给程序
6.为应用程序提供函数的迟延装定服务
7.为应用程序提供动态转载服务。
环境变量LD_PRELOAD设置共享库名或者用":"把文件名隔开。动态连接器在任何那些请求的共享库之前把环境变量LD_PRELOAD的共享库装载到进程地址空间去。例如:
# LD_PRELOAD=./mylibc.so myprog
这里./mylibc.so将第一时间map到程序myprog的空间。因为动态连接器在找寻标号的时候总是使用第一次碰到的标号,所以我们可以使用LD_PRELOAD来覆盖标准共享库中的函数。这个特性对程序员来说是很有用的,可用来在还没有建好整个共享库的时候对单个函数功能先做调试实验。
我们可以这样:
#gcc -c -fPIC -O3 print.c
#gcc -shared print.o -o print.so.1.0
创建自己的共享连接库
★5.5.6 Linux下动态装载(Dynamic loading)
_dlinfo是动态连接接口库的一个函数。它列出所有映射到执行程序和通过dlopen打开的每个共享库。它的输出类试以下:
List of loaded modules
00000000 50006163 50006200 Exe 1
50007000 5000620c 50006200 Lib 1 /lib/elf/libd1.so.1
5000a000 500062c8 50006200 Lib 2 /lib/elf/libc.so.4
50000000 50006000 00000000 Int 1 /lib/elf/ld-linux.so.1
500aa000 08006f00 08005ff0 Mod 1 ./libfoo.so
Modules for application (50006200):
50006163
5000620c /lib/elf/libdl.so.1
500062c8 /lib/elf/libc.so.4
50006000 /lib/ld-linux.so.1
Modules for handle 8005ff0
08006f00 ./libfoo.so
500062c8 /lib/elf/lib.so.4
50006163
5000620c /lib/elf/libd1.so.1
500062c8 /lib/elf/libc.so.4
50006000 /lib/elf/ld-linux.so.1
以上可被用来解释动态的连接和动态的装载。
在linux支持ELF上配置的GCC假如使用了-rdynamic选项,它将把-export-dynamic传给连接器。强烈建议使用动态装载。这就是为什么在我们的Makefile例子中使用了LDFLAGS=-rdynamic。暂时,这个选项只能在linux下使用。但是-Wl,-export-dynamic能在其他的平台上把-export-dynamic
传给GNU的连接器。
你能在GNU link editor的[3]和[4]部分找到它详细的描述。
相关阅读 更多 +