交叉編譯與靜態連結問題
問題一:交叉編譯生成可執行檔案,無法在目標板上執行
一、問題描述
arm-linux-gcc -o name.c
編譯程式正常,但可執行檔案無法在開發板上執行。提示找不到該檔案
二、問題排除
1. 用gcc編譯,可在虛擬機器里正常執行,因此不是c檔案的問題。
2. 試過絕對路徑後,確定不是檔案存在的問題,而是這個檔案並不能被執行.
三、問題分析
通過排除法將問題定位到動態連結庫上.
在主機上用arm-linux-gcc-static -o 來進行靜態編譯.然後將新產生的檔案傳到目標板上.可以發現通過靜態編譯的檔案明顯比動態編譯的要大.
然後再次執行./hello 可以看到螢幕上出現了久違的
原因:核心檔案與可執行程式使用了不同的編譯器進行編譯,導致動態連結庫對應不上。
如何徹底解決,而不是每次都使用靜態編譯?
既然是動態庫引起的問題,那麼應該和編譯器的版本有關. 一般編譯應用程式對編譯器版本沒有限制,而編譯Linux核心和 u-boot時,手冊上會指定編譯器。
實驗:目標板上linux核心使用4.3.3編譯的,而我們剛才編譯hello可執行檔案是使用3.4.1編譯的。
解決方案:將$PATH中的原來包含3.4.1/bin 的路徑改為 4.4.3/bin . 修改的方法網上有很多.(直接export PATH=”想要的路徑”)
再次編譯hello檔案,下載.
目標板上執行,成功顯示”Hello,World!”.
可以看到系統中動態庫的支援和編譯器還是有關的.建議編譯核心和編譯可執行檔案的編譯器保持一致。
問題二: “執行arm-linux-gcc命令,提示No such file or directory”
在ubuntu16.04上安裝arm的交叉編譯器arm-linux-gcc,環境變數配置好以後,執行arm-linux-gcc命令,總提示No such file ordirectory。然後去arm-linux-gcc所在的目錄下,發現不缺少任何檔案。而且環境變數配置也是正確的(環境變數很easy,只要配置個path路徑就行),因為arm-
分析了好久,才發現是ubuntu版本的問題。
本人的ubuntu是64位,而下載的這些交叉編譯器是32位的。因此需要安裝ia32-libs庫,如果apt-get 搜不到這個庫的話,可以安裝lib32ncurses5庫,也是一樣的。本人安裝了後者,解決了該問題。
結論:
簡單粗暴的解決方式→用同一個編譯器編譯核心和可執行程式。程式直接用靜態編譯即可無壓力執行。
基礎知識:
1. 連結方法
程式的靜態連結還是動態連結是根據編譯器的連結引數指定的。
l 靜態連結方法:#pragmacomment(lib, "test.lib") ,靜態連結的時候,載入程式碼就會把程式會用到的動態程式碼或動態程式碼的地址確定下來。
l 動態連結方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用這種方式的程式並不在一開始就完成動態連結,而是直到真正呼叫動態庫程式碼時,載入程式才計算(被呼叫的那部分)動態程式碼的邏輯地址,然後等到某個時候,程式又需要呼叫另外某塊動態程式碼時,載入程式又去計算這部分程式碼的邏輯地址,所以,這種方式使程式初始化時間較短,但執行期間的效能比不上靜態連結的程式。
2. 靜態連結和動態連結
(1)靜態連結:
就是在編譯連結時直接將需要的執行程式碼拷貝到呼叫處,優點就是在程式釋出的時候就不需要的依賴庫,也就是不再需要帶著庫一塊釋出,程式可以獨立執行,但是體積可能會相對大一些。(所謂庫就是一些功能程式碼經過編譯連線後的可執行形式。)
(2)動態連結:
就是在編譯的時候不直接拷貝可執行程式碼,而是通過記錄一系列符號和引數,在程式執行或載入時將這些資訊傳遞給作業系統,作業系統負責將需要的動態庫載入到記憶體中,然後程式在執行到指定的程式碼時,去共享執行記憶體中已經載入的動態庫可執行程式碼,最終達到執行時連線的目的。優點是多個程式可以共享同一段程式碼,而不需要在磁碟上儲存多個拷貝,缺點是由於是執行時載入,可能會影響程式的前期執行效能。
3. 靜態庫和動態庫
靜態庫和應用程式編譯在一起,在任何情況下都能執行。而動態庫是動態連結,顧名思義就是在應用程式啟動的時候才會連結,所以,當用戶的系統上沒有該動態庫時,應用程式就會執行失敗。再看它們的特點:
Ø 動態庫:
1.共享:多個應用程式可以使用同一個動態庫,啟動多個應用程式的時候,只需要將動態庫載入到記憶體一次即可;
2.開發模組好:要求設計者對功能劃分的比較好。
Ø 靜態庫:
程式碼的裝載速度快,執行速度也比較快,因為編譯時它只會把你需要的那部分連結進去,應用程式相對比較大。但是如果多個應用程式使用的話,會被裝載多次,浪費記憶體。