2016年9月18日 星期日

Embedded Linux: 如何使用現成的 C code?

前面的文章開頭提到,FAE 說要盡量使用現成的 C code,但卻沒提到要怎麼做。這裡就來分享一下個人心得(該公司應該聘請小弟當 FAE 才對 XDDD)



通常 Embedded Linux 新手可以分為兩個族群:
  1. 沒接觸過 Linux
  2. 有用過 Linux,但沒接觸過 Embedded Linux

1. 沒接觸過 Linux


通常開發 Embedded Linux 有兩種作法:
  1. 團隊共用一台 Linux Sever 進行開發,每個成員都自己的帳號,用 samba 傳送檔案,用 ssh or telnet 下命令編譯。
  2. 在 Windows 上安裝 VirtualBox 或 VMware
2. 現在似乎是主流,畢竟現在 SSD 跟 DRAM 都很便宜,而且 1. 常常碰到如果很多人一起 build code 效率大幅下降的問題。因為小弟這 3-4 年都是用 2. 作法,就不講 1. 了(而且我也不會XD)。

安裝 VirtualBox/VMware + Ubuntu 這邊就不提了,基本的 Linux 指令、bash 教學市面上也是多如牛毛,不需要在這裡浪費篇幅。

在跨入 Embedded Linux 前,請先放下越級打怪的想法,最起碼要先知道如何在 x86 Linux 上編譯 C 程式。在過去這些知識都相當零散,比較完整的資料小弟印象中早年有 O'Reilly 出版的 Programming with GNU Software,現在比較好的入門指南則是 21 世紀 C 語言



這本書告訴你在 Linux 上開發 C 程式所需的編譯、測試、除錯等基本知識。但要靠這本書學全 Linux C 程式開發是不可能的,比方說書上提供的 makefile 太過簡單,在稍具規模的案子根本不能用,要補全細節還是得看 GNU Make 一類的書。不過本書還是非常值得推薦,甚至個人覺得現在學校教 C 語言就應該這麼教,而不是像筆者 20 年前在學校那樣用一些業界少用甚至根本不用的語法來折磨學生。

2. 有用過 Linux,但沒接觸過 Embedded Linux


到這個階段代表你已經度過階段 1.,會寫簡單的 makefile,也知道 autotools 是幹嘛的。到這個階段,也應該聽過 cross compiler、toolchain,這邊就不講 toolchain 安裝了,因為每種 target 玩法都不太一樣,有的只能裝在 Ubuntu、有的只能裝在 64bit Linux 上,請務必看清楚手冊,免得像小弟一樣先前裝完 toolchain 才發現只支援 64bit Ubuntu @#$^^...

這裡只講新手最常碰到的 3 個問題(朋友也曾經拿這個問我過)

  1. 如何確認編譯出來的執行檔為目標板的格式?
  2. 相依性
  3. 安裝

2.1 如何確認編譯出來的執行檔為目標板的格式?


關於問題 1. ,因為現在很多 Linux open source project 都是用 autotools 作為建置系統,如果你有直接下載這些原始碼直接編譯安裝過,對下面這三步驟應該不陌生

  1. ./configure
  2. make
  3. make install
有些人剛開始以為只要這樣就能工作:

./configure --host=arm-linux

結果做出來的檔案還是錯的,比較萬無一失的作法是(請自行把下面的路徑換成你的環境):

export PATH=$PATH:/usr/local/arm-none-linux-gnueabi/bin
export CPPFLAGS="-I/usr/local/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc/usr/include"
export CFLAGS="-I/usr/local/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc/usr/include"
export LDLIBS=-L/your_path
export AR=arm-none-linux-gnueabi-ar
export AS=arm-none-linux-gnueabi-as
export LD=arm-none-linux-gnueabi-ld
export RANLIB=usr/local/arm-none-linux-gnueabi/arm-none-linux-gnueabi/bin/ranlib
export CC=arm-none-linux-gnueabi-gcc
export NM=arm-none-linux-gnueabi-nm
./configure --host=arm-linux

如果你跟筆者一樣是個嚴謹自律的工程師,當然不是這樣就算了,我們還是要確認一下編譯出來的結果,Linux 有個指令可以辨認檔案格式:





如果 file 指令無法辨識你的執行檔,就只好出絕招了:






這邊有一點特別要注意的是,不要用錯用成了 x86 objdump,不然你會得到錯誤的結果:





看起來好像對,卻少了該有的細節。

2.2 相依性


最常見的就是這個編譯出來的執行檔,需要其他的 .so 才能執行,那要怎麼知道關聯到哪些 .so?江湖一點訣,說破不值錢,就這三板斧:

objdump -a -x program | grep NEEDED
objdump -a -x program | grep SONAME
objdump -a -x program | grep RPATH

以 curl 為例:





這樣我們就知道了 curl 需要 libcurl.so 與 libc.so。

有一點要特別注意的是,要確認這些現成的 C code 引用了正確的 header file,因為在筆者身上曾經發生過一種情形,就是編譯成功,檔案格式也正確,但最後執行時崩潰,因為 include 到錯誤的 header file,也就是可能引用到了 x86 Linux 上的 header file。

當時的情形是用了很多巨集來判別該呼叫 epoll_create 還是 epoll_create1,但因為 daily build sever 設定有誤,本該呼叫 epoll_create 卻變成呼叫 epoll_create1,而 target 上的 libc 沒有那麼新,所以就崩潰了。這個問題可以用 strace 來找出是哪個 system call 呼叫失敗,她也是您 Embedded Linux 上的得力助手之一。

這方面的知識可以參考 Binary Hack

2.3 安裝


前面我們可以看到最後一個步驟是 make install,也就是安裝,當然有時只有一個執行檔,那簡單的拷貝就行了,但是稍微有點規模的程式還有些額外的設定檔、.so 檔要搞定,在 21 世紀 C 語言裡作者提供了一個簡單的辦法,就是安裝在自己的 ~/root 目錄下,只要去觀察 ~/root 就知道該複製哪些檔案到 target 上,詳細過程請自行查閱該書第一章:使用函式庫原始碼(即使系統管理員不同意) Page.24。

尾聲


移植現成的 C code 還有很多東西要注意,比方說還有一種最常見的是記憶體對齊的問題,以現在用的最多的 ARM SoC 來說,你必須注意原來的 C code 有沒有記憶體對齊的問題,例如指向 32bit integer 的指標,填入的位址必須對齊 4 bytes,如果您拿到的 C code 從來沒有在  ARM 上面驗證過,那您得小心了,也許這部份  compiler 會幫您處理掉,但追蹤問題時不失於一個檢查的方向。

另外筆者在移植 cups 時也踢到鐵板過,因為該 target 沒有正確實做權限切換,到最後筆者只好修改 cups 原始碼 :(

相較於 Windows 開發,Linux 可說是一條荊棘之路,但好在一切都是公開的,只要願意走下去總有見到光明的那天

沒有留言:

張貼留言