57 FreeBSD连载:可执行程序格式( 二 )


在FreeBSD中,a.out格式的执行文件可以支持压缩执行格式,这使得使用gzip压缩过的a.out格式的执行文件也能立即执行,当前还不能对ELF格式的文件提供这种支持 。
FreeBSD的文件格式从aout到ELF的转变是渐变的,首先是在3.0-RELEASE中将执行程序的缺省格式转变为ELF格式,内核文件还保持aout格式,直至FreeBSD-3.1,全部执行文件格式才缺省设置为ELF格式 。
转向ELF也造成很多相关程序的转变,如原有的Boot Loader不支持ELF格式的内核,3.1-RELEASE就升级到新Boot Loader;而原有的可加载模块lkm为aout格式,也需要转向ELF格式的modules 。新可加载模块的位置为/modules目录,并使用kldload、klduload、kldstat来进行管理 。(aout格式的模块管理命令为modload、modstat和modunload) 。
a.out和ELF格式使用的库文件也是不同的,使用ELF执行文件格式的FreeBSD 3.x中,/usr/lib下为ELF格式的函数库,而用一个子目录/usr/lib/aout存放a.out格式的函数库,用于兼容2.2.x之前版本的FreeBSD程序 。但这给一些使用包装技术的软件(一些中文外挂系统)造成了一些小麻烦 。对不同格式的执行文件要使用不同的包装库,系统不会将与程序本身格式不同的连接库连接到程序上,对应的错误信息为 “bad magic”,指出文件格式的不同 。
由于3.x之后的缺省格式为elf格式,为了生成a.out格式的文件,必须在编译和连接时使用 -aout参数,告诉编译器gcc和连接器ld使用不同的格式生成执行文件 。
静态连接和动态连接
在操作系统发展的早期,除了内核提供的接口,所有的库函数都要连接到程序中,这样所有的程序都可以直接在系统内核下运行 。然而事实上大部分程序都会使用一些相同的库函数,尤其是在使用高级语言编程的时候,通常都使用同样的库 。例如,C语言编写的程序通常都使用printf函数进行输出,使用scanf读入用户输入内容 。如果每个库函数都连接到用户程序中,这样每个程序都会包括这个函数的一个拷贝,就浪费了内存空间 。
因此,现代操作系统使用动态连接的技术,不将常用的库直接编译进每个程序中,而是保留相应的接口,在内核载入程序时,再使用动态连接程序将库载入并和执行程序连接起来 。这就是动态连接的技术,由于库和程序是分别载入的,因此多个程序可以共享一个库的同一个拷贝,节约了资源 。
不论对于a.out格式还是ELF格式,FreeBSD均支持动态连接,因此应用程序缺省就使用动态连接的方式 。如果想使用静态连接,可以在应用程序编译连接时,指定-static连接选项,将目标程序连接成静态连接的执行文件 。由于库代码被连接进执行文件中,静态连接的执行文件要比动态连接的执行文件要大 。
$ cc -static -o a1 hello.c$ cc -o a2 hello.c$ ls -la1 a2-rwxr-xr-x1 wbwheel45017 Apr 18 16:26 a1-rwxr-xr-x1 wbwheel2540 Apr 18 16:27 a2在FreeBSD下,共享库被放到/etc/ld-config设定的目录下,通常为/usr/lib,每个库文件使用.so和库的版本号结尾 。例如,libc.so.3.1为一个标准C库函数的动态共享库文件 。对于a.out格式的执行文件,其动态库文件位于/usr/lib/aout目录下 。
可以使用程序ldd来确定一个程序使用的动态连接库:
bash-2.02$ ldd /usr/bin/vi/usr/bin/vi:libcurses.so.2 => /usr/lib/libcurses.so.2 (0x2808e000)libtermcap.so.2 => /usr/lib/libtermcap.so.2 (0x2809a000)libc.so.3 => /usr/lib/libc.so.3 (0x2809f000)其他系统的执行文件
很多其他Unix系统,例如BSD/OS和Linux,也是运行在Intel平台上的系统,那么执行程序中的处理器指令是完全相同的,不同之处只在于应用程序的格式、应用程序与操作系统的接口、库文件等 。事实上由于同为Unix系统,这些差异也很小,因此通过调整内核的一些参数设置,FreeBSD完全可以直接运行这些系统下的执行程序 。