午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

(轉(zhuǎn)載)GCC_ELF:程序的啟動階段動態(tài)鏈接器 都干了些什么?

 happy123god 2012-05-07

一個動態(tài)鏈接的可執(zhí)行文件包含了所有的鏈接器信息。運行時動態(tài)鏈接器要重定位文件,并解析未定義的符號。
  • 文件的.dynsym節(jié),即動態(tài)符號表,包含了一個文件導(dǎo)入(imported)或?qū)С?exported)的所有的符號。
  • 文件的.dynstr節(jié),即動態(tài)符號字符串表,包含了上述符號的名字字符串。
  • 文件的.hash(或是.gnu.hash)節(jié),包含的是一個哈希表,連接器可以用它更快的查找符號。
  • 文件的.dynamic節(jié),也即DYNAMIC段(segment),包含的是動態(tài)鏈接器所需的文件的信息。這個段是包含在數(shù)據(jù)段里的,但ELF程序頭表(program header table)包含到它的鏈接。所以鏈接器可以很快找到它。
.dynamic節(jié)也是一個列表,里面包含一個數(shù)值、一個指針、加上這個條目的類型標(biāo)簽。這些類型標(biāo)簽有的只在可執(zhí)行文件里有,有的只在庫里有,有的則是共有的。

  • NEEDED: 這個文件所依賴的庫名。(通常可執(zhí)行文件都有,有時庫也有。可以多次出現(xiàn))
  • SONAME: "shared object name", 鏈接器要用到的文件的名字(庫中才有)。
  • SYMTAB, STRTAB, HASH, SYMENT, STRSZ,: point to the symbol table, associated string and hash tables, size of a symbol table entry, size of string table. (共有的)
  • PLTGOT: points to the GOT, or on some architectures to the PLT (共有的)
  • REL, RELSZ, and RELENT or RELA, RELASZ, and RELAENT: pointer to, number of, and size of relocation entries. REL entries don't contain addends, RELA entries do. (Both.)
  • JMPREL, PLTRELSZ, and PLTREL: pointer to, size, and format (REL or RELA) of relocation table for data referred to by the PLT. (Both.)
  • INIT and FINI: pointer to initializer and finalizer routines to be called at program startup and finish. (Optional but usual in both.)
  • A few other obscure types not often used.
一個程序的結(jié)構(gòu)也可以如這樣描述,先是頭,然后是只讀區(qū),然后是讀寫區(qū),最后是BSS區(qū)。但BSS區(qū)通常不存在。

read-only pages: 
.hash 
.dynsym 
.dynstr 
.plt 
.text 
.rodata

read-write pages: 
.data 
.got 
.dynamic

.bss

一、文件的加載

這一過程有點長,但很直觀。

首先:啟動動態(tài)鏈接器

一個程序啟動之后,系統(tǒng)先把文件內(nèi)容映射到內(nèi)存中,然后.....

首先,系統(tǒng)通過.interp節(jié)找到動態(tài)鏈接器,并將其加載到內(nèi)存空間,再啟動鏈接器。還要把一個輻助向量傳到動態(tài)鏈接器的??臻g里:

AT_PHDR, AT_PHENT, and AT_PHNUM:程序頭表的地址,每個表項的大小和表項數(shù)。

AT_ENTRY: 程序的入口地址

AT_BASE: 動態(tài)鏈接器加載的基地址

然后,動態(tài)鏈接器找到自己的.got節(jié),其中的第一條目指向動態(tài)鏈接器自己的.dynamic節(jié)。然后動態(tài)鏈接器先重定位自己的變量和必要函數(shù)。使自己可用。(動態(tài)鏈接器ld.so的重要函數(shù)都以_dt_開頭,一段特殊的代碼會直接找到這種字符串開頭的符號,并解析它們)

最后動態(tài)鏈接器初始化一系統(tǒng)的符號表,其中有指針指向動態(tài)鏈接器自己的符號表和程序的符號表。從概念上來講,一個進(jìn)行的程序文件和所有的庫是共享一個符號表的。但是鏈接器不沒有把所有的符號表混合在一起,而是用一個鏈表把他們串起來。每個文件都有自己的hash表。鏈接器查找符號時只計算一次哈希值,然后逐個哈希表去匹配。

然后找到需要的庫

動態(tài)鏈表器自己的初始化完成之后,它就開始為程序加載庫文件。程序的program header table有一個條目指向程序的DYNAMIC段,也即.dynamic節(jié)。它里面包含的是動態(tài)鏈接信息。包含一個DT_STRTAB項,指向.dynstr節(jié),和多個ST_NEEDED項,是需要的庫,其名字指向字符號表(.dynstr)中的偏移:

例子:

readelf -d test得到的.dynamic節(jié)條目表

readefl -S test得到的節(jié)頭表

找到需要的庫名字之后,動態(tài)鏈接器就會去系統(tǒng)里找相同名字的庫文件,這是一個相當(dāng)復(fù)雜的過程。比如說在上面的例子中,需要的庫文件是libc.so.6(C庫,版本6),找它的過程需要去系統(tǒng)的庫路徑個逐個搜索??赡苷业降膸烀忠膊煌耆粯?,比如說可能會找到lib.so.6.2,即后面還帶了更小的版本號。

鏈接器查找?guī)煳募奈恢冒ㄈ缦聨滋帲?/span>

  • .dynamic節(jié)可能包括一個DT_RPATH類型的條目,指明的是一系列冒號分隔的路徑。這是通過鏈接編輯器(link editor)在程序鏈接階段加進(jìn)去的。
  • 如果系統(tǒng)定義了環(huán)境變量LD_LIBRARY_PATH, 同上,也會進(jìn)這里面去找。(但安全起見,如果程序是sset-uid的,這一步會跳過。)
  • 查找?guī)炀彺嫖募?nbsp;/etc/ld.so.conf 里面有庫文件名也有路徑名,如果在這里能找到,就在這里找。大多數(shù)的庫都是在這里找到的。
  • 如果上面三步都沒找到,那就找 /usr/lib.要是還找不到就出錯退出了。
找到需要的庫之后,動態(tài)鏈接器找到庫文件,打開它,把它映射進(jìn)內(nèi)存。讀它的文件頭,進(jìn)而找到程序頭,再找到.dynamic節(jié)所在的DYNAMIC段,然后通過.dynamic節(jié),把庫中的符號表加到全局符號表鏈中,如果這個庫還依賴其它的庫,就再加載其他的庫。

最終,動態(tài)鏈接器找到了所有的庫,并且擁有了一個邏輯上的全局符號表。

初始化共享庫

動態(tài)鏈接器重新訪問每個庫,處理它的重定位條目,添寫其GOT(全局偏移表),并對數(shù)據(jù)區(qū)的符號進(jìn)行重定位。加載階段的重定義包括:

  • R_386_GLOB_DAT, 用于初始化指向定義于其他庫中的符號地址的GOT項。
  • R_386_32, a non-GOT 對其它庫中靜態(tài)數(shù)據(jù)的引用。
  • R_386_RELATIVE, 本地定義的數(shù)據(jù)或字符串
  • R_386_JMP_SLOT, 初始化用于PLT的GOT條目,下面詳述。
如果庫文件中有.init節(jié),則會調(diào)用這一節(jié),來對庫進(jìn)行一些庫自己的初始化。如是有.fini節(jié),那么退出時會執(zhí)行。執(zhí)行完.init庫的初始化就完了,可以使用了。

惰型過程鏈接(原名是lazy procedure linkage,沒找到通用的翻譯方法,自己取的名字)

為什么說是惰型的呢?因為這種鏈接方式很拖拉,不到用時不鏈接。過程鏈接是說這種鏈接是面向函數(shù)的。

有些函數(shù),在程序運行過程中很少會調(diào)用,比如說錯誤處理什么的。函數(shù)依賴的庫還會依賴其他庫,但依賴的其它庫的函數(shù)也很少會用到。如果所有的都加載的話,可能你系統(tǒng)里的所有庫都必須加載到內(nèi)存中去。那機(jī)器就沒法跑了。

所以對于不常用的函數(shù),我們在不用他的時候,就不加載他所有的庫。用時再說。

這樣也能加快程序啟動時間,很多庫都不用加載了嘛。

但那些沒有解析的符號怎么辦?先不管,留著用到時再綁定。但這些符號可不能放到原來的地方了,我們需要另外的處理方法,和存儲方法。

存儲方法叫Procedure Linkage Table.PLT。每個動態(tài)綁定的程序或庫都有一個plt表。里面包含的條目是程序或庫調(diào)用的非本地例程。當(dāng)程序或庫調(diào)用到這個例程的時候,都會跳入到plt的相應(yīng)條目中去,相應(yīng)的條目都是幾條簡單的指令,如果這個函數(shù)例程還沒有綁定,就是調(diào)用動態(tài)鏈接器進(jìn)行綁定。如是不是第一次調(diào)用,那么函數(shù)就己經(jīng)綁定了,就是直接跳轉(zhuǎn)到函數(shù)中去。


Figure 3:
 PLT structure in x86 code 
Special first entry


PLT0: pushl GOT+4 
jmp *GOT+8

Regular entries, non-PIC code:


PLTn: jmp *GOT+m 
push #reloc_offset 
jmp PLT0

Regular entries, PIC code:


PLTn: jmp *GOT+m(%ebx) 
push #reloc_offset 
jmp PLT0

PLT中的第一個條目叫做PLT0,它的代碼與其他PLTn不同。PLT0就是那個調(diào)用動態(tài)鏈接器的代碼。但第一次調(diào)用某個函數(shù)時,PLTn都會跳到PLT0上來,然后用PLT0的代碼來調(diào)用動態(tài)鏈接器。

在程序裝載階段,動態(tài)鏈接器會在GOT中自動加上兩個條目:在GOT的第二個字上,和第三個字上,即GOT+4和GOT+8。依次放入的是庫鑒定代碼(這個代碼可以知道當(dāng)前要調(diào)用一個未解析的符號的程序或庫是什么)和動態(tài)鏈接器的符號解析函數(shù)地址。每個PLTn條目第一個指令都是一個跳轉(zhuǎn)指令,跳轉(zhuǎn)到*GOT+m上去。每個PLTn條目都對應(yīng)著一個GOT上的條目,那個GOT上的條目初始化時是一個指向這個PLT上跳轉(zhuǎn)指令的下一條指令,是push指令,也就是說開始時是PLTn跳到GOT+m后馬上跳回來執(zhí)行下面的push指令(這塊可以看看上面的指令圖)。push指令就是push #reloc_offset,它入棧的是重定向表中的一個條目,這個條目的類型為 R_386_JMP_SLOT。這個條目的目地地址是符號表中一個符號,這個符號表條目的value項正指向GOT+m,也就是說現(xiàn)將要解析的符號解析為GOT+m.

這種安排很詭怪。但也很巧妙。當(dāng)?shù)谝淮芜M(jìn)入PLTn時,第一條指令跳到GOT+m后什么也沒做就返回來接著執(zhí)行push指令了。這個push指令將要解析的符號的重定位表上的條目偏移入棧。它的目的地址是符號表中的條目,條目的目的地址則是GOT+m,下一步我們把解析出來的真正地址填到GOT+m就可以了。

PLTn的第三條指令就是jump PLT0,

PLT0: pushl GOT+4 

jmp *GOT+8
這里將鑒定程序或庫的代碼入棧,然后調(diào)用GOT+8指定的符號解析例程。
這里有一點東西要多說一下:
當(dāng)我們調(diào)用一個call指令的時候,它將eip入棧,然后跳到調(diào)用的函數(shù)里,函數(shù)返回時將eip出棧,接著執(zhí)行eip所指位置的指令。
在這里也用了這個小技巧,這里先入棧的是#reloc_offset,然后入棧的是GOT+4,然后jump到符號解析例程。
想想:符號解析例程返回時會有什么操作?就是出棧一個數(shù),然后按這個數(shù)指定的位置執(zhí)行指令,也就是說這里GOT+4里的內(nèi)容會pop eip。
這樣,進(jìn)入符號解析例程之后,符號解析例程在鏈在一起的那些符號表里找到要解析的符號,然把符號地址存到GOT+m里,返回。然后先pop出庫簽定例程,確定是哪個程序做的調(diào)用,然再pop出重定位表,去重定位表中重新調(diào)用那個函數(shù),這時再進(jìn)入PLTn時,就直接接跳到GOT+m到跳到相應(yīng)的函數(shù)上去了,不會再執(zhí)行PLTn中的下兩條指令了。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多