FreeBSD下的可执行程序格式(转)

FreeBSD下的可执行程序格式(转)[@more@]在 FreeBSD下的可执行程序通常可分为两类,一类为使用各种解释语言编写的脚本,如sh、awk、perl、Tcl等,这些程序需要解释程序进行解释执行,小巧方便,对于实现不常使用、不要求效率的程序非常有用;另一类就是使用C等高级语言编译后产生的可执行二进制程序。

1) 解释脚本程序

使用 file命令可以确认文件的类型,包括可以确定一个程序是可直接执行的二进制程序,还是解释脚本程序。


$ file a.out

a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically

linked (uses shared libs), not stripped

$ file /etc/rc

/etc/rc: Bourne shell script text executable


Unix之所以功能强大,原因之一就在于它提供了强大的再开发能力。这不仅与提供了高级语言C的编译器有关,而且也与提供了很多种能以解释方式执行的简单脚本语言有关。解释程序脚本的特点是方便性、简单灵活,而且也比较容易学习入手。很多情况下,需要完成的工作任务功能比较单一,并不需要频繁运行,而且要求快速编写出来,这就适合使用解释型语言编写,并且解释程序本身就具备处理文本和字符串的便捷性,并能够和很多现有程序通过系统提供的管道、环境变量等方式结合起来,使得它们非常适合实现文本处理功能。

解释语言的缺点是每次运行程序时都要载入语言的解释器,解释执行程序,因而效率较低,并且不能直接操纵内存和 I/O设备,不适合编写大型程序和对效率要求较高的场合。

每个解释脚本程序的第一行指出该脚本程序使用的解释器,例如一个普通的 shell程序的第一行为:


#! /bin/sh

不同的解释语言可用在不同的方面,最常用的有 shell解释程序,依据使用shell的不同,也分为不同的shell脚本,基本上也分为sh和csh两种不同的风格。系统管理中经常使用shell程序来执行一些日常管理任务,很多软件也使用shell程序来提供辅助安装和设置任务。perl也是一种常用的、功能强大的解释语言,它兼有解释性程序的方便性和高级编程语言的强大功能,使程序员能在很短的时间内写出非常有效的程序。因此perl得到了众多程序员的支持,通过为perl开发了更多的程序模块,进一步使得perl的处理能力变得更为强大。当前perl已经成为了最流行的一种解释语言,尤其在编写Web服务器上的CGI程序方面,更是处于无可争议的地位。Tcl/tk是另一种解释语言,它能用在X Window系统下,使用描述语言显示不同的X控件,因此很多X应用程序使用它来建立自己的图形接口。

2) 二进制执行程序

使用高级语言编写、并经过编译得到的二进制执行程序执行效率更高,并且只有二进制格式的执行文件才能充分利用 Unix系统提供的全部功能。同样系统内核也是一个特殊格式的二进制执行文件。

早期的 Unix使用a.out格式作为它的执行文件格式,随着Unix的发展,又出现了其他几种执行文件的格式,当前最重要的执行文件格式为ELF格式,采用这种格式的最初想法是为了在不同平台间采用相同的执行文件格式,并实现动态共享连接库。虽然ELF文件格式并没有达到AT&T最初设想的全部目的,但这种文件格式却成为最流行的执行文件格式。除此之外,实际使用的文件格式还有一种较老的COFF格式,这种格式是在Unix System V R3.2中使用的,当前只有老版本的SCO Unix中还在使用它,而SCO也正逐渐转向ELF格式。

FreeBSD可以同时支持这两种执行文件的格式,FreeBSD 2.2.x之前的版本使用a.out格式作为缺省的执行文件格式,到FreeBSD 3.x之后ELF格式成为缺省的执行文件格式,并且以后会彻底转向ELF。事实上在FreeBSD下的a.out格式具备了相当多的特性,如动态连接等ELF格式具备的特性,也有一些ELF格式不具备的特性,如压缩执行文件格式。但由于FreeBSD中使用的编译器gcc决定不再支持a.out格式的缘故,因此FreeBSD也必须转向ELF格式。这也是当前还支持a.out格式的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使用不同的格式生成执行文件。

3) 静态连接和动态连接

在操作系统发展的早期,除了内核提供的接口,所有的库函数都要连接到程序中,这样所有的程序都可以直接在系统内核下运行。然而事实上大部分程序都会使用一些相同的库函数,尤其是在使用高级语言编程的时候,通常都使用同样的库。例如, C语言编写的程序通常都使用printf函数进行输出,使用scanf读入用户输入内容。如果每个库函数都连接到用户程序中,这样每个程序都会包括这个函数的一个拷贝,就浪费了内存空间。

因此,现代操作系统使用动态连接的技术,不将常用的库直接编译进每个程序中,而是保留相应的接口,在内核载入程序时,再使用动态连接程序将库载入并和执行程序连接起来。这就是动态连接的技术,由于库和程序是分别载入的,因此多个程序可以共享一个库的同一个拷贝,节约了资源。

不论对于 a.out格式还是ELF格式,FreeBSD均支持动态连接,因此应用程序缺省就使用动态连接的方式。如果想使用静态连接,可以在应用程序编译连接时,指定-static连接选项,将目标程序连接成静态连接的执行文件。由于库代码被连接进执行文件中,静态连接的执行文件要比动态连接的执行文件要大。
QUOTE: