FreeBSD可執行程式格式(轉)

gugu99發表於2007-08-11
FreeBSD可執行程式格式(轉)[@more@]

  在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不同,因此除了較小的程式之外,安裝支援檔案、必要的目錄結構等引數的調整是免不了的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-944718/,如需轉載,請註明出處,否則將追究法律責任。

相關文章