在嵌入式Linux的開發過程中,我們經常需要用到一些軟件庫或者是測試工具,這些庫和工具大概率都是x86平台上開發的,就需要我們通過對源碼進行交叉編譯,生成能夠在開發板上使用的軟件和動態庫。
對於某些開源軟件進行編譯有兩種情況,第一種是如果我們使用buildroot根文件系統,可以在buildroot的menuconfig編譯菜單中,勾選相應的軟件,重新編譯根文件系統,編譯完成之後該根文件系統就會自動帶上對應的庫和可執行程序,第二種是buildroot中無法找到我們需要的軟件,這種情況下,只能夠進行手動交叉編譯,接下具體講解下手動交叉編譯開源軟件可能遇到的一些問題
一、使用交叉編譯工具鏈時常見問題
1.1 編譯過程中頭文件相關問題
交叉編譯時我們可能會遇到找不到頭文件的問題,我們都知道在x86平台上開發程序,程序中通過尖括號的方式包含頭文件:#include <xxx.h>他會到系統目錄下去查找所需要的頭文件,一般情況在/usr/include或者/usr/local/include路徑下,那麼對於我們的交叉編譯工具的系統目錄又是哪裏呢,我們可以通過以下命令來查看當前交叉編譯工具對應的include系統路徑在哪
echo 'main(){}' | <交叉編譯工具> -E -v -
這個命令利用 gcc 的預處理(-E)和詳細輸出(-v)功能,幫助查看編譯器查找頭文件和庫文件的路徑。適用於調試和理解交叉編譯器的工作原理,尤其是在處理不同平台和工具鏈時。下面在我的平台中舉例,我使用rk3576的SDK中自帶的交叉編譯工具,執行如上命令之後,輸出結果如下圖所示
可以看見,該命令將#include ""和#include <>會查找的路徑都打印了出來,對於源文件中通過#include ""的頭文件,該編譯器會到源文件當前路徑下查找,對與原文件中通過#include <>的頭文件,aarch64-none-linux-gnuu-gcc編譯器會去以下三個路徑中去查找,而不是x86平台下的系統路徑中去查找。如果在我們交叉編譯過程中,缺少某些頭文件,我們就可以把對應頭文件拷貝到這些目錄中去,放到任意一個路徑下就行。
/home/wzy/sdk/rk3576_linux6.1.99_release-rkr5/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/../lib/gcc/aarch64-none-linux-gnu/10.3.1/include
/home/wzy/sdk/rk3576_linux6.1.99_release-rkr5/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/../lib/gcc/aarch64-none-linux-gnu/10.3.1/include-fixed
/home/wzy/sdk/rk3576_linux6.1.99_release-rkr5/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/../lib/gcc/aarch64-none-linux-gnu/10.3.1/../../../../aarch64-none-linux-gnu/inclu
de
除了上面這種方法,也可以直接在編譯源文件的時候在後面加上-v選項查看詳細的編譯信息,例如我這裏編譯一個usb相關的應用程序,下面是我執行交叉編譯的命令,後面加上了-v來顯示具體的編譯信息
/home/wzy/sdk/rk3576_linux6.1.99_release-rkr5/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc usb1.c -o usb1 -v
執行結果如下
這裏可以看見上面所講的交叉編譯工具的頭文件系統路徑,同時也報出一個錯誤,無法找到libusb.h文件,因為我沒有吧對應頭文件拷貝到紅框中的任何一個頭文件路徑中去,也沒有通過-I命令手動指定。接下來我將通過手動指定的方式來展示加了這個選項之後的變化。
上面提到了除了使用系統目錄外,也可以自己指定目錄:編譯時用-I<path>選項指定。編譯時,編譯器會優先使用通過-I指定的路徑,因為如果指定了-I選項,該選項的路徑會放到最前面,接着上面的報錯,我加入了-I選項之後
可以看見-I選項指定的路徑優先級排到了最前面
1.2 編譯過程中庫文件問題
在交叉編譯鏈接程序時如果有這樣的提示:undefined reference to xxx,它表示 xxx 函數未定義。出現這個錯誤主要是有兩種情況,第一就是交叉編譯工具的系統庫文件路徑中沒有相應的庫,第二就是編譯源碼使用的庫的版本和當前系統中庫的版本不同,庫的差異導致報錯。解決方法要麼去寫出這個函數,或是使用庫函數,那需要在鏈接時指定庫。那麼我們如何確定交叉編譯工具對應的系統庫文件在哪裏?同樣執行相同命令確定目錄
echo 'main(){}' | <交叉編譯工具> -E -v -
當然也可以在編譯命令後加上-v選項
可以看到紅框部分打印出了LIBRARY_PATH這個宏,這個宏就表示的是交叉編譯工具在鏈接時,會到這些路徑下去查找庫文件,當我們缺少庫文件時,可以將庫文件拷貝到這些目錄下的任意一個路徑就行,同樣我們也可以自己指定鏈接庫的路徑,鏈接時用-L<path>選項指定,編譯時,編譯器會優先使用通過-L指定的路徑
當我們已經將需要鏈接的庫拷貝到對應的系統庫文件路徑下之後,或者我們已經通過-L選項指定了庫路徑之後,我們想要鏈接庫應該如何操作?例如我們想要鏈接libpthread.so,那麼我們在編譯鏈接時就需要加上-lpthread的編譯選項。就可以正常編譯程序了。
到此,我們已經知道了,編譯一個開源軟件時,如果出現了問題,應該如何查找交叉編譯工具的頭文件路徑和庫文件路徑,主要用來處理一些開源軟件編譯過程中,依賴缺失的問題,例如我現在需要編譯一個軟件,但是缺少其他軟件的庫依賴,那麼我們就需要先交叉編譯依賴庫,然後將編譯出來的庫以及對應頭文件拷貝到交叉編譯工具對應的頭文件和庫文件的系統目錄下,再編譯我們的目標軟件,這裏需要注意的一點就是,編譯出來的lib文件,有一些可能是一個庫軟連接到了另一個庫上,所以在拷貝的時候需要使用cp -drf命令來拷貝,避免破壞軟連接
那麼通過-I和-L指定路徑與將依賴頭文件與庫拷貝到已有的路徑中,有什麼區別?區別就是如果使用指定路徑的方式,在每次編譯的時候都需要重新輸一遍路徑,這對於只用編譯一次的情景還行,如果是之後的應用開發會經常用到這個庫,還是建議將庫拷貝到交叉編譯器默認的系統庫和頭文件路徑中去,避免每次編譯的時候都重新敲一遍路徑。
1.3 運行過程中找不到庫相關問題
如果在執行可執行程序的時候,出現找不到某各庫的問題,説明當前的系統庫路徑下缺少這個庫,只需要將運行需要的庫,拷貝到對應的鏈接庫目錄下就能解決這個問題
error while loading shared libraries: libxxx.so:
cannot open shared object file: No such file or directory
對應的系統庫路徑在哪?一般系統目錄就是板子上的/lib、/usr/lib目錄,當前也可以自己指定,運行程序用環境變量 LD_LIBRARY_PATH 指定,執行以下的命令來添加一個新的庫路徑
export LD_LIBRARY_PATH=/xxx_dir
程序運行時,並不需要頭文件,如果不在開發板上編譯,那麼頭文件不用拷貝到開發板上