当前所在位置:FreeBSD使用大全>>
FreeBSD连载(57):可执行程序格式
 
作者:王波
 
在FreeBSD下的可执行程序通常可分为两类,一类为使用各种解释语言编写的脚本,如sh、awk、perl、Tcl等,这些程序需要解释程序进行解释执行,小巧方便,对于实现不常使用、不要求效率的程序非常有用;另一类就是使用C等高级语言编译后产生的可执行二进制程序。

  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应用程序使用它来建立自己的图形接口。


二进制执行程序

  使用高级语言编写、并经过编译得到的二进制执行程序执行效率更高,并且只有二进制格式的执行文件才能充分利用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使用不同的格式生成执行文件。


静态连接和动态连接

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

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

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

$ cc -static -o a1 hello.c
$ cc -o a2 hello.c
$ ls -l  a1 a2
-rwxr-xr-x  1 wb  wheel  45017 Apr 18 16:26 a1
-rwxr-xr-x  1 wb  wheel   2540 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完全可以直接运行这些系统下的执行程序。

  FreeBSD能够同BSD/OS、NetBSD和OpenBSD的Intel平台上的应用程序相兼容,同为BSD家族的成员,他们非常类似。NetBSD、OpenBSD和FreeBSD同为免费系统,并且具有同样的起源,与FreeBSD的关系非常密切,因此FreeBSD能直接运行NetBSD和OpenBSD的Intel平台下的执行程序。然而NetBSD和OpenBSD 也是自由操作系统,因此它们中的应用程序也会有相应的FreeBSD版本,因此这个功能一般很少用到。BSDI是一个商业公司,因此会提供BSD/OS下的二进制执行文件,但不提供源代码。FreeBSD能够完美的运行BSD/OS下的a.out格式的执行文件,ELF执行格式的程序也能执行,但偶尔会有问题发生,因此就需要调整系统设置。

  FreeBSD也能够执行SCO Unix的执行文件,这需要使用内核的ibcs2( Intel binary compatibility system 2)选项。这需要载入一个内核可加载模块,这需要使用root身份执行ibcs2命令以载入ibcs2模块。

# ibcs2
 
 

  可以在rc.conf中设置 “ibcs2_enable=YES” ,使开机后立即载入这个模块。

  但是要执行SCO的应用程序,仅有内核支持还是不够的,还需要有SCO Unix的函数库。但SCO Unix的库函数是SCO Unix的一部分,受版权保护的。如果使用者拥有合法的SCO共享库和应用程序,就可以运行SCO Unix上的大型商业应用程序。

  同样,FreeBSD也能够运行Linux的可执行程序,与执行SCO程序类似,这也要求内核支持并载入相应的模块。rc.conf中的相应参数为 “linux_enable” 。

# linux
 
 

  但是与SCO不同的是,Linux是一种自由操作系统,其库函数为GNU开发的函数库,使用GNU通用许可保护自由使用的权利。因此FreeBSD在Packages Collection中提供了Linux的共享库,安装了这些Linux的库函数之后,就可以执行Linux的执行程序了。

# pkg_add /cdrom/packages/All/linux_lib-2.6.tgz
 
 

  FreeBSD也能执行Solaris x86和SCO Unixware的ELF格式执行程序,只是这个功能还没有加入正式发行版本。同样,这也需要相应的系统动态连接库文件,但是这些库是商业产品,因此使用起来就会受到限制。


运行Linux应用程序

  在运行Linux应用程序时,会遇到一些程序不能正确执行的情况,很多情况下并不是FreeBSD本身的问题,而是由于系统的设置不正确造成的,必须作一些设置和调整。

  由于Linux使用Elf文件格式,有些时候FreeBSD不能分清一个ELF执行文件是那种系统的执行文件,而误认为是FreeBSD的执行文件,导致不能正确执行程序。此时就需要使用brandelf命令来指定需要执行的ELF执行文件的类型,Linux 的ELF执行文件为Linux:

$ brandelf -t Linux linux_application
 
 

  另一种差异是系统文件名字的差异,例如Linux的加密口令文件为shadow,而FreeBSD使用master.passwd,Linux的控制台终端设备文件为/dev/tty0,FreeBSD 为/dev/ttyv0,对于一些要使用这些系统文件的应用程序,就造成无法正确读取相应文件的问题,这种情况可以使用符号连接来解决。

# cd /etc
# ln -s master.passwd shadow
# cd /dev
# ln -s ttyv0 tty0
 
 

  此外,Linux动态连接库的版本不合适,也是很多Linux程序不能正常运行的主要原因。Linux使用GNU开发的glib作为库函数,编译器gcc首先实现了glibc1 ,用于兼容标准的C库函数libc5。随着gcc的发展,它发展到了glibc 2,也被称为libc6。这两个不同版本的库有一定的差异,glib2功能更强,但由于出现时间还短,不是所有的Linux版本都使用glibc2。有的Linux版本还在使用glibc1,如 Slackware 3.6,而另一些Linux如RedHat 5.0,Debin 2.0都使用glibc2,因此运行Linux应用程序的时候,首先就要确认其使用的C库函数版本。除了标准的C库之外,Linux应用程序使用的其他动态连接库也会存在不一致的问题。

  因此如果Linux应用程序使用的库和FreeBSD安装的Linux共享库版本不一致,那么就不会正常执行这些Linux程序。Linux是一个非常活跃的开发系统,某些版本的Linux常常等不及系统稳定就将应用程序及其使用的库升级,因此为了运行要求新动态连接库的Linux应用程序,就必须及时更新Linux的共享库。除了及时更新FreeBSD正式发行的Linux共享库(这个共享库通常为一个稳定版本,不会一味求新)之外,为了跟上Linux升级的脚步,还必须手工安装Linux的库和其他执行文件。

  当前FreeBSD提供的Linux动态连接库为Linuxlib-2.6,提供了对glibc2库及其他一些共享库的支持,然而众多的应用程序还会使用其他各种动态连接库。 FreeBSD的软件库只能支持常用的Linux应用程序,为了彻底解决Linux应用程序的动态连接库更新问题,并不能依靠FreeBSD提供的软件包。还是应该使用Linux的方式,按照Linux的方式安装所需要的Linux动态连接库。

  FreeBSD下用于与Linux相兼容的程序和库文件统统位于/usr/compat/linux 目录下(根目录下有它的一个连接/compat/linux,compat目录为兼容其他系统执行文件的目录),为了安装新的Linux文件,首先要将这个目录清除。

# cd /usr/compat/linux
# rm -fr *
 
 

  安装新的Linux兼容文件可以通过多种方式来完成,由于当前最流行的 Linux系统通常使用RPM的包管理方式,因此可以使用RPM包来安装Linux文件。这需要首先安装包管理程序rpm,这个程序有FreeBSD版本(此时还无法运行Linux程序)。可以在Packages Collection中找到这个软件。

  然后就要为安装Linux的RPM包初始化rpm,使rpm不使用根目录作为系统的起始路径,而使用/usr/compat/linux作为所有的rpm包的根进行安装,这就要使用 --root参数指定相对的根目录,以将Linux执行程序与FreeBSD执行执行程序区分开。rpm在/usr/compat/linux/var/local/lib/rpm中记录包的安装信息。

# mkdir -p /usr/compat/linux/var/local/lib/rpm
# rpm --initdb --dbpath /usr/compat/linux/var/local/lib/rpm
# rpm -i --ignoreos --root /usr/compat/linux ld.so-1.9.5-7.i386.rpm
# rpm -i --ignoreos --root /usr/compat/linux ldconfig-1.9.5-3.i386.rpm
# rpm -i --ignoreos --root /usr/compat/linux glib-1.0.1-3.i386.rpm
# rpm -i --ignoreos --root /usr/compat/linux glibc-2.0.7-17.i386.rpm
 
 

  当使用rpm -I安装好所有必须的系统支持文件,以及需要执行的应用程序,这样安装完毕之后,在通过调整一些参数,Linux程序就能正常执行了。主要需要根据应用程序的错误提示,修改FreeBSD和Linux在文件和目录结构上的不同,如Linux 执行程序的位置,应该更改为/usr/compat/linux/usr/bin目录或其他相应的路径,而非原有根目录下的路径,以使得Linux程序内部只调用Linux执行程序。只有一些对执行程序的格式不敏感的程序,可以使用/bin或/usr/bin下的相应FreeBSD执行程序。通过这样的设置之后,在FreeBSD下就能运行包括Oracle 8 for Linux在内的大型应用软件。

  但是FreeBSD并不可能运行所有的Linux程序,有的应用程序程序对Linux内核有一定要求,需要内核的特定版本的支持。一般来讲,对于这些对Linux特定内核有要求的应用程序,FreeBSD也就无法提供支持了。

  不管怎样,虽然在FreeBSD下运行Linux执行程序没有太大的问题,然而毕竟 Linux是一个独立的操作系统,处理问题的风格与FreeBSD不同,因此除了较小的程序之外,安装支持文件、必要的目录结构等参数的调整是免不了的。

未完,待续。。。
 
来源:http://freebsd.online.ha.cn/

声明:本站的文章和软件是本人从网上收集整理的(除本人的作品之外),所有版权属于作者,
如有侵犯您的权益,请指出,本站将立即改正,谢谢.

Copyright 2000 www.newok.com