在發布linux-fpga系列文章 和PowerPC平臺Linux的移植系列文章后,很多朋友發mail來問我一些執行軟件上面的問題,大部分都是和軟件所使用的鏈接庫相關的。Novell的官方網站上有一篇文章 對相關問題介紹得比較詳細,我就參照該文章寫出中文版,希望能對遇到此類問題的朋友有所幫助。
介紹
很多時候,諸如glibc這種基本庫,由于有著不同的版本存在,如果正好你手上只有適用于某個版本的packages,就會遇到各種各樣的問題。所以,如果你打算在不同版本上使用這些軟件包,就需要尋找一個比較好的移植方法。而這篇文章正是介紹相應方法的。在往下看之前,請確保你有能力安裝和配置相應的文件(因為文檔中也會提供一些提示)。下面所有的示例都基于X86硬件平臺,如果你使用的是其他體系,那么可能示例代碼和相應的技巧都需要改變。本文是在實際工程中所使用步驟的總結性描述,歡迎提出意見和指正。
背景
一個Linux程序通常都包含有執行特定功能的機器代碼,而很多功能通常都由庫文件提供。程序員朋友都知道,庫文件又分為靜態版本和動態共享版本,當一個程序被創建時,開發者就會決定是使用動態庫還是靜態庫。在使用靜態庫的程序中,可能出現不同版本或者在靜態庫的基礎架構上產生變化而引起的二進制不兼容。由于C++的成熟但是未通用標準化,經常會出現后者的情況。詳細點說,就是一些具體實現的細節,比如,如果用于創建庫的編譯器版本不同,派生繼承格(derived inheritance lattices)中的虛擬函數進行地址映射的機制,就會使相同庫擁有不同且互不兼容的版本。
共享庫倒是有很多好處,機器代碼只需要加載到內存一次;使用這個庫的不同程序都在共享它,而且只有程序具體所需要的數據才會在內存中分配空間;另外,系統管理員可以很輕松得升級共享庫,而無需接觸那些使用這個庫的程序,這一點對于安全特性來說特別重要。
說了半天,上面沒有一個特點是Linux特性,因為這些都是各種現代操作系統所使用或者支持庫的常規描述,所以就不廢話了,具體的細節建議Google。
劇本
終于進入正題了。你遇沒遇到過這種問題,辛辛苦苦down下來一個新版本程序,正準備在你新版本,最新推出的Linux系統上使用,卻發現不能運行?恩,這種問題通常發生于libc4到libc5的轉換或者libc5到libc6的轉換,后者就是通常所說的glibc (the GNU C library)。
C的庫基本上是所有UNIX/Linux系統中最重要的庫文件,因為它們扮演了所有應用程序和內核之間的接口角色。如果這種核心庫文件被改變,而這種改變又不向后兼容,那么,就可能導致整個系統都無法使用。
當然還有另外一種情況,一個程序已經在一個使用了較新版本的庫的系統中被構建,而你如今想將它安裝到你的當前系統中,當然,這種程序是不可能正常運行的,原因就是程序很可能會使用到只有較新版本的庫文件中才會擁有的新的或者改動過的特性。正如所有人都知道的那樣,包含有舊版本庫所沒有的功能的新版本庫是不會被加載以及執行的。下面以glibc舉例子:
GLIBC 版本 文件名稱
libc5 /lib/libc.so.5
libc6 /lib/libc.so.6
有時版本號還有附加后綴,比如libc.so.6.3.3.
正如在背景中所描述的那樣,由版本號大于3.3的GNU C++所編譯的C++庫的二進制不兼容性,會導致額外的復雜度,就算是在當前包括了一致的API(Application Programming Interface)和ABI(Application Binary Interface)定義的標準化環境中也同樣如此。而且,未來C++ compilers和庫的版本是否會保持完全兼容,也尚未得知。針對這個內容,LSB(Linux Standards Base)推薦所有的軟件開發商用C++開發軟件的時候,C++部分都使用靜態鏈接,不幸的是沒幾個開發商是遵循了這個推薦的。更糟糕的是,Linux的發布廠商通常都會創建自己版本的C++編譯器,卻沒有讓它們的共享庫和標準C++編譯器的一致。
總的來說,如果你想運行一個程序,卻失敗了,錯誤信息和下面這條信息類似,那么這個問題可能能夠通過本文“解決方案”部分的建議來解決。
運行程序時的錯誤信息
./a.out: relocation error: ./a.out: symbol errno, version GLIBC_2.0 not defined in file libc.so.6 with link time reference
如果你恰好遇到像上面這樣的關于symbol errno的錯誤信息,就說明你的程序被鏈接到了某個版本號低于2.3的glibc上。為了保證其線程安全的特性(每個線程盡量只訪問別的線程不訪問的變量或內存,如果硬是要訪問同一變量或內存的話,就要采用適當的互斥機制來避免由于線程切換而導致的不確定性),更新版本的glibc不再將errno作為全局變量來提供。在這種情況下,可能你不得不安裝擁有較老版本(比如2.2.5版本)的兼容環境。
但是,如果你并沒有看見上述信息,而程序還是沒有按照你所想象那樣的正常運行,那你可能就是遇到了上述介紹的C++相關問題。我們知道,運行時鏈接器(runtime linker)用于將你的程序加載到內存中,并負責解決任何和共享庫相關的依賴,它還能夠在你的系統中定位所需要的共享庫,并將它們加載到內存中,但是,這個共享庫不一定適合目前你希望運行的程序。這就是問題所在。
解決方案
等你確定了問題的源頭后,就可以創建一個兼容環境,來提供正常執行程序所需要的功能特性。在這里強烈推薦一種方法,就是在標準系統文件路徑,例如/lib和 /usr/lib外安裝附加的庫文件,如下所示 :
想移植的程序名 zoo
用于安裝兼容文件的目錄地址的前綴 /opt/compat-env/zoo
需要的庫 glibc-2.3.2-95.27
libgcc-3.2.3-42 libstdc++-3.2.3-42
所需要文件最初的源目錄 /root/zoo-src
第一步
在自行規定的目錄下創建路徑/bin和/lib:
% prefix=/opt/compat-env/zoo
% mkdir -p $prefix/bin $prefix/lib
第二步
將所需要的文件拷貝到相應的位置:
% srcdir=/root/zoo-src
% cd $prefix/bin
% cp -p $srcdir/zoo .
% cd $prefix/lib
% rpm2cpio $srcdir/glibc-2.3.2-95.27*.rpm | cpio -idvm ‘*libc*.so*’
% find . -name ‘*libc*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/glibc-2.3.2-95.27*.rpm | cpio -idvm ‘*ld*.so*’
% find . -name ‘*ld*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/libgcc-3.2.3-42*.rpm | cpio -idvm ‘*libgcc_so*.so*’
% find . -name ‘*libgcc_so*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/libstdc++-3.2.3-42*.rpm | cpio -idvm ‘*libstdc*.so*’
% find . -name ‘*libstdc*.so*’ -exec mv -v ‘{}’ . \;
% rm -rf lib usr
第三步
創建一個腳本,用于建立合適的環境變量和啟動程序:
% cd $prefix/bin
% mv zoo zoo.exec
% cat > zoo libcwait.c zoo
posted on 2008-06-09 05:18
聶文龍 閱讀(722)
評論(0) 編輯 收藏 引用