2011年12月23日 星期五

快快樂樂 Makefile 入門教學

有感於身旁還是很多人對於 Makefile 感到陌生決定來發篇教學文...

要注意的是在本篇文章中所採用的詞彙與正式用語可能有點出入, 主要是為了方便理解


Makefile 對於入門者而言最重要的兩個功能是:
  1. 建置規則 : 該目標要如何完成?
  2. 檔案相依性 : 如果 xxx 更新的話, 代表我要跟著更新(或著是重新編譯)



情境模擬

在這邊先來討論為什麼我們需要 makefile 

假設我們現在有三個檔案 : a.c, b.c, c.c

編譯的時候當然可以很直接的

gcc a.c b.c c.c -o prog

但如果今天是有三十個檔案的話 :  a.c, b.c, c.c ... z.c, aa.c, bb.c, cc.c, dd.c

那麼編譯時便需要

gcc a.c, b.c, c.c ... z.c, aa.c, bb.c, cc.c, dd.c -o prog

當然如果對於一般 shell 有點熟悉的話會很直覺的想到可以

gcc *.c -o prog

大致上是沒有問題的

那幹麼要學 Makefile 阿?

隨著程式碼長大, 編譯時間開始慢慢的變長

從原本的按下去瞬間完成, 慢慢的成長到 30 秒甚至是一分鐘時

會發現按下編譯後, 會想要跑去逛個 ptt 或看個拍賣, 再去逛個奇毛新聞

然後每次編譯的時間就從原本的一瞬間變成了十幾分鐘甚至半小時(疑?

但回頭分析一下上面的兩個編譯方式
  1. gcc a.c, b.c, c.c ... z.c, aa.c, bb.c, cc.c, dd.c -o prog
  2. gcc *.c -o prog
會發現每次編譯都是重新編譯每個檔案


發生這種事的時候很直覺得就會想要分開編譯成很多 .o 檔

結果編譯的過程變成


gcc -o a.o -c a.c
gcc -o b.o -c b.c
gcc -o c.o -c c.c
...
gcc -o z.o -c z.c
gcc *.o -o prog


然後你想修改 a.c, 然後重新編譯

gcc -o a.o -c a.c
gcc *.o -o prog

接著測試過後你發現有 bug , 需要更改 b.c, 然後重新編譯

gcc -o b.o -c b.c
gcc *.o -o prog

...

結果省了編譯時間多了手工時間

雖然可以避免編譯時跑去逛網頁但也很浪費時間

如果看到這邊覺得這樣的過程可以讓自己覺得很忙很認真而不想改善開發流程的話

就直接按上一頁吧



套用 Makefile !

終於要進入正題使用 Makefile 了,

使用 Makefile 主要是避免不要要的時間浪費, 以及一直重新的打字

開始介紹前再度複習 Makefile 兩個基本功能:
  1. 建置規則 : 該目標要如何完成?
  2. 檔案相依性 : 如果 xxx 更新的話, 代表我要跟著更新(或著是重新編譯)
在這邊我們想要透過 Makefile 來建置程式

那要怎樣才能把剛才所作的事換成用 Makefile 作勒?

就開始一步一步分析並從頭開始寫, 我們的目標是要建置 prog 這隻程式

打開一個 Makefile 並且打入

prog:

接著打入 make 他會跟你抱怨, 不知道怎麼建立 prog

make: Nothing to be done for `prog'.

所以這邊要開始進入 Makefile 第一個重要的功能了, 如何加入建置規則?

方式是在該目標下一行先敲一下 tab 然後開始寫指令

要注意, 一定要是 tab 而不能是空白

prog:
    gcc a.o b.o c.o -o prog

在這邊為了簡化還是先退回只有三個檔案

然後按下 make

gcc a.o b.o c.o -o prog

喔喔喔喔喔喔喔喔 現在只打 4 個字就取代 23 個字了!

但目前其他三的檔案還是沒有建立規則, 大致受上一樣畫葫蘆

prog:
    gcc a.o b.o c.o -o prog

a.o:
    gcc a.c -o a.o -c

b.o:
    gcc b.c -o b.o -c

c.o:
    gcc c.c -o c.o -c

在按下 make 後你可能會發現, 他跟你說 prog 已經是最新版了

make: `prog' is up to date.

現在就來修改 a.c 檔案的內容後重新按 make

...

你會發現

他還是跟你說 prog 已經是最新版了

make: `prog' is up to date.

主要原因是我們沒有告訴 make 檔案相依性

他不知道什麼時候該重新建置目標

要加入檔案相依性只要在目標後面加入所相依的檔案名稱即可

prog: a.o b.o c.o 
    gcc a.o b.o c.o -o prog

a.o: a.c 
    gcc a.c -o a.o -c

b.o: b.c 
    gcc b.c -o b.o -c

c.o: c.c 
    gcc c.c -o c.o -c


加入完相依性後再按 make 他就會重新編譯 a.o 跟 prog 了!


gcc a.c -o a.o -c
gcc a.o b.o c.o -o prog



所以用 Makefile 有啥好處 ?

從上面的例子可以看出使用 Makefile 來建置程式的好處 :

  1. 節省打編譯指令的時間
  2. 建立相依性後, 可自動偵測是否需重建
其實在現代的多核心機器下還可以利用 Makefile 的內建功能來達到平行建置

只要你在建置時在 make 後面加入 -jn 其中 n 帶入數字,

例如

make -j4

代表一次平行執行四個任務, 也就是最多同時會編譯四個檔案!

這個功能則是一般手動編譯較難以達成的, 並且可以非常有效的節省編譯時間



Makefile 常見錯誤訊息

一般而言寫 Makefile 最容易看到下列的錯誤訊息

Makefile:2: *** missing separator.  Stop.

但看到後請不要慌張, 這個訊息通常是你的建置規則前面沒有用 tab 所造成的

尋著他後面提示的行號去檢查很快就可以解決



雜談

Makefile 在 linux 環境及泛 Unix 家族底下都是常用的建置工具

但是事實上 Makefile 可以作的更多, 不只是編譯建置程式

在很多情況下我們可以利用 Makefile 的平行建置功能來達到平行執行某些工具

例如如果要跑 benchmark 之類的, 也給他寫成 Makefile

然後在 i7 上面 make -j8 就整個很開心

2011年12月14日 星期三

Android prelink in ICS

在 Android ICS 中已經將 apriori 及 soslim 都丟掉
言下之意就是 prelink 在 Android 中不再出現

根據 Commit 裡面的機制就是說硬體已經進步
所以不缺那點時間跟省下來的空間XD...

而且不是 gcc 那串 toolchain 的東西, 要分開維護很麻煩

並且對於 Address-Space-Layout Randomization 的有效性大大降低

2011年11月30日 星期三

libelf for arm

對於 arm 的 object file 使用 elfutils-libelf 時

取用 Symbol 需要注意 Symbol address 的部份

在 ELF for the ARM®  Architecture 的第 4.6.3 的部份有提到

Symbol Values 
  • In addition to the normal rules for symbol values the following rules shall also apply to symbols of type STT_FUNC: If the symbol addresses an ARM instruction, its value is the address of the instruction (in a relocatable object, the offset of the instruction from the start of the section containing it). 
  • If the symbol addresses a Thumb instruction, its value is the address of the instruction with bit zero set (in a relocatable object, the section offset with bit zero set). 
  • For the purposes of relocation the value used shall be the address of the instruction (st_value & ~1).
ARM 跟 thumb 的 Symbol 區別方式是看 Symbol Table 中 Symbol Address 的最低位元來判斷, 所以取用請記得 st_value & ~1, 免得取到的 Address 差一.

2011年3月30日 星期三

cuda-memcheck : CUDA Debug 好幫手

在撰寫 CUDA 程式的時候最煩惱的就是 Debug 困難

即使現在有了 cuda-gdb 也是相當的困難

而在 CUDA toolkit 裡面有附贈一隻叫 cuda-memcheck 的小程式

可以幫助你檢查程式中有無下列兩種錯誤:


1. Out of boundary memory access
2. Misalignment

那接下來先了解一下上面兩個錯誤是啥

第一個錯誤 Out of boundary memory access 很好理解

比如說你的陣列長度只有 10 但是你卻存取到了 10 以上

int a[10];
a[11] = 1111; // Out of boundary memory access !!

這件事情在傳統 C 語言上面很好察覺,

但在 CUDA 中通常 index 都是跟 threadIdx 以及 blockIdx 加起來之類的

一不小心就會寫錯

而第二個錯誤 Misalignment 則是一般在 x86 PC 上寫程式幾乎不會遇到的事情

原因在於 x86 硬體允許你作這件事, 在嵌入式系統的世界比較容易遇到這種事

alignment 這件事簡單來講就是記憶體存取時位址必須為 x 的倍數

例如 4 - byte alignment 的話, 位址必須能被 4 整除

合法的 4 - byte alignment  位址如 : 0x00ffff, 0x0, 0x8 等等
不合法的 4 - byte alignment  位址如 : 0x01, 0xff06, 0xa 等等

但通常這種情只會發生在你有對指標作些特殊處理時才會發生

例如

char p[10];
int *a = (int*)p+1;

之類的運算, 通常在寫影像處理相關的程式時
比較有可能寫出這類的程式碼
在寫 CUDA 時請盡量避開

在了解 cuda-memchech 的功能後來看看如何使用

假設你有一個 CUDA 程式 a.out

那你只要下

cuda-memcheck a.out

就可以開始檢查

如果有錯誤的話會吐出類似下面的訊息


========= CUDA-MEMCHECK
========= Invalid __global__ read of size 4
=========     at 0x00000038 in f
=========     by thread (0,0,0) in block (0,0)
=========     Address 0xfb00000228 is out of bounds
=========
========= ERROR SUMMARY: 1 error


在這個時候你會發現雖然他告訴你 out of bounds
但是根本不知道在那一行出錯阿!!!

這時候你重新編譯你的程式

nvcc -G oob.cu

加個 -G 然後

cuda-memcheck a.out

重新檢查


========= CUDA-MEMCHECK
========= Invalid __global__ read of size 4
=========     at 0x000000b0 in oob.cu:3:f
=========     by thread (0,0,0) in block (0,0)
=========     Address 0xfb00000228 is out of bounds
=========
========= ERROR SUMMARY: 1 error

就會報告錯誤的行數了!

另外有個 --continue flag

cuda-memcheck --continue ./a.out

這時候他檢測到錯誤的時候就會試著繼續跑跑看了

Misalignment 錯誤訊息差不多也是這樣就不浪費版面了