【软件开发底层知识修炼】十一 链接器-链接脚本
上一篇文章學(xué)習(xí)了鏈接器之-main函數(shù)不是第一個執(zhí)行的函數(shù):main函數(shù)不是第一個執(zhí)行的函數(shù)
今天繼續(xù)學(xué)習(xí)鏈接器,學(xué)習(xí)鏈接是如何動作的,從而引入鏈接腳本的概念。本文就學(xué)習(xí)鏈接腳本的概念。
1、鏈接腳本的作用
我們都知道可重定位文件經(jīng)過鏈接器鏈接后最終形成可執(zhí)行文件。這個鏈接的過程大概就是分為符號解析和重定位。
那么鏈接器到底是如何工作的呢?這取決于鏈接腳本。我們可以看下圖:
幾個可重定位文件與相應(yīng)的庫文件進行鏈接,鏈接器經(jīng)過鏈接器的指導(dǎo),最終形成可執(zhí)行程序。
在學(xué)習(xí)鏈接鏈接腳本的大致格式之前,先練總結(jié)一下鏈接腳本的幾個作用(實際上這些作用我們都知道,只不過今天才知道鏈接腳本的存在而已):
-
鏈接腳本用于描述鏈接器處理目標(biāo)文件(可重定位文件)與庫文件的方式
- 合并各個目標(biāo)文件中的段
- 重定位各個段的起始地址
- 重定位各個符號的最終地址(這個地址實際上是段內(nèi)偏移地址)
2、鏈接腳本的格式
鏈接腳本也就是一個腳本文件,語法比較簡單,下面我們直接看一個例子,來說明一個鏈接腳本大致有哪些內(nèi)容:
上述的的描述還是很全面很仔細的。我們主要注意一下幾點:
- 各個段的鏈接地址必須符合具體平臺的規(guī)則,比如在Intel處理器上與在Amd處理器上,或許各個段在整個地址空間的位置就會有一些差別
- 鏈接腳本中能夠直接定義標(biāo)識符并制定存儲地址
- 鏈接腳本能夠直接定義源代碼(需要編譯的.c .c++程序)中的標(biāo)識符的地址
- 我們主要學(xué)習(xí)Linux系統(tǒng),在Linux中代碼段(.text段的地址范圍為[0x08048000,0x08049000])
我們現(xiàn)在可能還不理解上面的幾條注意事項,但是經(jīng)過下面的例子,就一定可以理解了:
我們寫了如下的C程序與鏈接腳本:
8-1.c
#include <stdio.h>int s1; extern int s2;int main() {printf("&s1 = %p\n", &s1);printf("&s2 = %p\n", &s2);return 0; }8-1.lds
SECTIONS {.text 0x08048400:{*(.text)}. = 0x01000000;s1 = .;. += 4;s2 = .;.data 0x0804a800:{*(.data)}.bss :{*(.bss)} }我們看上面的程序,在C程序中有一個extern int s2; 這個s2不是這個C文件的,是外部文件的,很明顯,我們只有兩個文件,另一個就是我們指定的鏈接腳本文件。
使用下述命令進行編譯:
- gcc 8-1.c 8-1.lds -o lyy
生成可執(zhí)行程序lyy (注意如果編譯的時候不加上我們自己定義的鏈接腳本,編譯就會出錯)
運行:
- ./lyy
執(zhí)行結(jié)果為:
很明顯,s1的地址由于我們再鏈接腳本中指定了:
. = 0x01000000;s1 = .;所以s1的地址是:0x01000000;
而由于在鏈接腳本中有如下的兩句話:
. += 4;s2 = .;所以s2的地址為:0x01000004;
如果我們不使用鏈接腳本指定這兩個變量的地址,那么他們的地址就是隨機的,這也符合我們平時的結(jié)果。
3、借助鏈接腳本修改程序的入口函數(shù)
不知道是否還記得在上一篇文章中:點擊查看。我們學(xué)習(xí)了程序的執(zhí)行流程,知道了main函數(shù)并不是真正的第一個開始執(zhí)行的函數(shù)。而且我們有辦法在編譯程序的時候改變第一個執(zhí)行的程序。那個時候使用的是在編譯的時候指定入口函數(shù)的地址,就像下面這樣:
- gcc -e program -nostartfiles program.c -o program
今天我們來學(xué)習(xí)另一種方法,來修改程序的入口函數(shù)。
那就是在鏈接腳本中指定,大概格式如下:
下面給出一個例子,這個例子中沒有main函數(shù),我們自己指定一個函數(shù),然后將它設(shè)為入口函數(shù):
8-2.c
#include <stdio.h> #include <stdlib.h>int program() {printf("D.T.Software\n");exit(0); }8-2.lds
ENTRY(program)SECTIONS {.text 0x08048400:{*(.text)} }使用下面命令進行編譯(也可以先編譯輸出目標(biāo)文件,然后進行鏈接,下面的命令直接一步完成而已):
- gcc -nostartfiles 8-2.c 8-2.lds -o lyy2
沒有報錯,生成了可執(zhí)行文件lyy2
運行程序結(jié)果為:
很明顯,我們利用這個方法成功修改了這個C程序的入口函數(shù)。
為了更加深入,我們可以看看該可執(zhí)行程序的符號信息:
使用以下命令查看可執(zhí)行程序lyy2
- nm lyy2
由于上面的8-2.lds鏈接腳本指定代碼段其實地址為: 0x08048400,而我們的可執(zhí)行程序的入口函數(shù)(program函數(shù))的地址其實就是代碼段(.text)地址,所以如上圖,T代表代碼段,地址為0x08048400。很完美的解釋。
4 、 默認的鏈接腳本
上面我們學(xué)習(xí)了鏈接腳本的各種知識,但是我們平時并沒有使用它或者看到它。但是這并不意味著學(xué)習(xí)它就沒有用處。它對于我們理解整個系統(tǒng)原理有很大幫助。
可以使用下面的命令查看默認的鏈接腳本:
- ld --verbose > defaults.lds
上述命令將默認的鏈接腳本輸出到文件defaults.lds文件中,我們可以打開defaults.lds文件來查看默認鏈接腳本文件。
5、總結(jié)
記住,我們在學(xué)習(xí)的內(nèi)容是可以讓你走的更遠,走的更高的鋪墊。或許對你產(chǎn)生不了直接的影響,但是絕對會對你將來的學(xué)習(xí)之路產(chǎn)生深遠的影響。不要一口吃一個胖子,慢慢來,從應(yīng)用軟件做起,深入學(xué)習(xí)底層原理!你的未來一定更加美好!!!
本文參考狄泰軟件學(xué)院相關(guān)課程
想學(xué)習(xí)的可以加狄泰軟件學(xué)院群,
群聊號碼:199546072
學(xué)習(xí)探討加個人(可以免費幫忙下載CSDN資源):
qq:1126137994
微信:liu1126137994
總結(jié)
以上是生活随笔為你收集整理的【软件开发底层知识修炼】十一 链接器-链接脚本的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 欧几里得算法(即辗转相除法)的时间复杂度
- 下一篇: 【OS学习笔记】十二 现代处理器的结构和