正在學習嵌入式linux開發,求推薦詳細介紹makefile和鏈接腳本語法相關的書籍或者資料。?

另外,求大神們指點一下鏈接腳本內的段地址是怎麼傳給c語言的,比如uboot中cmd段的段地址是在鏈接腳本定義的,它是怎麼傳給c語言的?


【學Makefile】

看這個:GNU make

這個僅需要一定的腳本基礎,但非正相關。可以以這個為基礎學習其他擴展知識

【學ld script】

看這個:Using LD, the GNU linker

這個需要一定的ELF和彙編基礎,但非正相關。可以以這個為基礎學習其他擴展知識

【段地址傳遞給C語言】

這個涉及很多基礎知識,先為你建立一個基本的邏輯,其他細節,自己去找資料。

首先,一個C程序變成一個可執行程序,需要經過兩個步驟,首先是編譯(CC),然後是鏈接(LD)。前者把一個獨立的C文件翻譯為.o文件,後者把多個.o文件關聯在一起,形成一個完整的程序。

CC是把用C語言描述的程序翻譯成指令和數據,放到section中,數據放到一個section中很簡單,但翻譯指令這個是缺信息的,因為如果你要使用一個地址(變數或者函數都行)的時候,這個地址是多少?這取決於你把這段程序放在什麼地方,也取決於其他section放在什麼地方(當你索引的符號在本section之外)。

CC用兩種方法解決這個問題,一種是使用相對地址,函數foo1調foo2,如果兩個函數在同一個section中,只要調用一個相對當前地址多大距離的地址即可。這種情況CC自己就可以解決。

第二種是要使用其他section(本.o或者其他.o的不同段都是如此)的符號,CC對此沒有任何辦法,在這種位置上,CC只能放置一個所謂「重定位項」,等待ld去完成後續過程。

ld的工作是把所有的.o文件拼裝為一個完整的可執行程序,即把所有.o的位置放到一起,它可以根據需要把多個段拼成一個段,也可以按順序一個段一個段順序排列。這些所有的行為,都可以在ld script中描述,ld script可以制定程序從什麼地址開始,每個段放在什麼地方,哪些合併,哪些獨立排放等等。

ld script還有一個能力,可以定義任意的符號名,從而任意增加符號,並指定這個符號等於多少。

比如,你在你的.c中,說extern void * addr。CC會認為這個地址在其他.o中,它會建立一個重定位項。但你不需要在其他.o中定義它,你完全可以在ldscript中直接定義這個符號名,讓它指向某個section的首地址。這樣,這個索引的地址在ld後,就會被賦予一個地址,訪問這個地址,這個地址就指向需要的section的開頭。

uboot中的section (".u_boot_cmd")就是這樣實現的。


你是說ld script,這部分看我寫的開源書籍吧,http://tinylab.org/open-c-book


http://Github.com + http://google.com


從某種程度上說,你這句話是錯誤的。這裡並沒有一個傳遞的過程。僅僅是編譯器在編譯的時候「固化」每一段的地址空間而已。

我忽然理解你的問題了,等我回去補充。

-----------------------------------------------------------

--------------------這裡是分割線--------------------

-----------------------------------------------------------

首先,先上一個典型的cmd參數:

setenv bootcmd tftp 50008000 zImage ;bootm 50008000

需要理解的是,這句話的意思是,將一個代碼段放置在內存的50008000中,然後從50008000中執行這段代碼。

又或者,有時候,可能會在u-boot中執行以下命令:

tftp 50008000 xxx。。。。

然後執行

go 50008000運行下載到這個地址的代碼段

為的是將一個代碼文件下載到內存的某一個區域。我覺得你的懷疑點就是,為什麼要將代碼段下載到這個地址,為什麼下載到其它的地址就不可以。

下面,來看一個典型的裸板程序(因為假如是運行在操作系統上的程序,會由操作系統做內存的關係映射,你的疑問應該也就不再存在了)。

假設有這樣一個代碼(helloworld.c):

void helloworld(){
printf("helloworld");
// 需要注意的是,因為是裸板程序,其實是不能執行printf函數的
// 這裡僅僅是演示,一個類比而已
// 真實的代碼這裡應該指定某些硬體的寄存器地址等
}

假設以上就是一個最基本的嵌入式C語言代碼。和通常的桌面程序有所不同,嵌入式C語言代碼並不要求一定要存在一個main函數,只需要在編譯的時候指定代碼的起始地址就可以了。比如(helloworld.lds):

SECTIONS
{
. = 0x50008000;
.text :
{
helloworld.o (.text);
}
};

這裡就定義了由上面的C語言源文件在做鏈接的時候的代碼段的地址。所以在運行的時候需要從這裡開始運行。

(我說的和你問的是一回事嗎)


之前學過編譯原理,所以連接腳本和makefile對於我來說也只是語法問題。看看GNUmake手冊足矣


光看書效果不大,還是從零開始寫一個項目的makefile比較靠譜,寫的過程中遇到什麼問題、技術一步一步學習解決:Makefile工程實踐:從零開始一步一步寫項目的Makefile視頻課程_共21課時_嵌入式_嵌入式Linux_視頻教程在線自學__51CTO學院_專業的IT技能學習平台


《跟我一起寫 Makefile》陳皓


github搜索makefile,多看幾個,不懂再google


網上有一份 左耳朵陳皓翻譯的 一份make的文件,先把那個看了再說


嵌入式前景如何?


推薦How to write makefile,有中英文版,一般都看這本


這個不需要書。


推薦閱讀:

怎麼看待 Linus 和 Richard Stallman 對 C++ 的態度?
Linux是否有類似於IIS統一管理網站的工具?
最逼近Mac OS X的Linux系統有哪些?
Linux 各個發行版與 OS X 相比哪個寫代碼更舒服?為什麼?
vmware, visual studio, steam dota2這種需要極大內存的程序是如何分配內存的?

TAG:Linux | 嵌入式系統 | 嵌入式開發 | linux學習 |