動態(tài)鏈接和延遲綁定

現(xiàn)在的程序幾乎都是動態(tài)鏈接了 + 延遲綁定了. 這樣可以節(jié)省寶貴的內(nèi)存空間還能提升運(yùn)行時的效率. 之前也零零散散地看了很多相關(guān)的文章. 今天就系統(tǒng)地總結(jié)一遍吧.

got表和plt表

首先從got表和plt表講起. 以一個調(diào)用libc中write()函數(shù)為例. 來分析一下流程:

基礎(chǔ)知識

  1. got表屬于數(shù)據(jù)段, 是可寫的. 表中存儲的是指針. plt屬于代碼段, 其中每一項(xiàng)都存儲了三個匯編指令.
  2. 當(dāng)系統(tǒng)根據(jù)一個文件創(chuàng)建一個進(jìn)程的時候, dynamic linker會把got表的第二項(xiàng)和第三項(xiàng)初始化為特殊的值, 具體是什么后面會解釋.
  3. 對于每個重定位的函數(shù), 其在got表和plt表中分別有一項(xiàng)存儲該函數(shù)動態(tài)鏈接時需要使用的程序. 我們假設(shè) PLT[2], 和GOT[4]存儲libc 中 write 的對應(yīng)信息

動態(tài)鏈接流程

  1. 當(dāng)這個程序里面調(diào)用write()的時候就會跳到plt表中write對應(yīng)的項(xiàng).這兒就是PLT[2], 其中有三行匯編碼
jmpq *GOT[4] # write 函數(shù)在got表中對應(yīng)的項(xiàng)
pushq $0x01 # write()對應(yīng)的編號, 記為reloc_arg.程序中的每個重定位函數(shù)都有一個獨(dú)一無二的編號, 根據(jù)這個編號可以計算這個函數(shù)對應(yīng)的got表的偏移, 動態(tài)鏈接需要的信息等等.
jmpq  4005a # PLT[0]

而GOT[4]表中初始值都是指向PLT[2]的第二行代碼, 當(dāng)程序剛加載完畢的時候, 每個重定位函數(shù)對應(yīng)的got表中的地址都指向?qū)?yīng)的plt表的第二個指令.

  1. 進(jìn)入 PLT[0], 其中代碼如下:
pushq *GOT[1] #一個特殊的地址, 指向動態(tài)鏈接所需要的信息, 后面會解釋, 記為 link_map
jmpq *GOT[2] # dynamic linker 的地址

GOT[2] 中存儲的就是_dl_runtime_resolve函數(shù)的地址了.
就相當(dāng)于執(zhí)行了_dl_runtime_resolve(link_map, reloc_arg).

  1. _dl_runtime_resolve()會根據(jù)reloc_arg計算出got表地址, 需要重定位函數(shù)的名稱等信息, 然后根據(jù)這些信息找到函數(shù)的真實(shí)運(yùn)行地址. 最后把got表中這個函數(shù)對應(yīng)的項(xiàng)got[4]修改為真實(shí)地址.

  2. dynamic linker()執(zhí)行結(jié)束之后就會跳轉(zhuǎn)到write()函數(shù)里面.

  3. 之后在調(diào)用write()的時候仍然先跳到plt[2], 然后跳到*got[4], 此時got[4]中的地址就是write()函數(shù)的真實(shí)地址了. 因?yàn)橹辉诘谝淮螆?zhí)行的時候才綁定真實(shí)的地址, 所以叫做延遲綁定(lazy binding)

dynmic linker工作流程

看完前面的內(nèi)容, 對動態(tài)鏈接的過程已經(jīng)大致了解了. 接下來我們深入分析一下最關(guān)鍵的一步:調(diào)用dynmic linker修改got表內(nèi)容.

我畫了如下示意圖來更直觀地表達(dá)動態(tài)鏈接的過程, 具體過程后面解釋.
.dynamic是elf文件中的一個section, 其中包含了動態(tài)鏈接所需要的信息. 比如一些指向其它section的指針. 可以參考這個文檔
.dynstr是elf文件中的一個section. 其包含的需要重定位的函數(shù)的名稱. dynamic linker可以根據(jù)這些名稱找到真實(shí)的運(yùn)行時地址進(jìn)而修改got表
.dynsym section是一個結(jié)構(gòu)體數(shù)組, 結(jié)構(gòu)體定義如下:

typedef struct
{
  Elf32_Word    st_name;   /* Symbol name (index in .synstr) */
  Elf32_Addr    st_value;  /* Symbol value */
  Elf32_Word    st_size;   /* Symbol size */
  unsigned char st_info;   /* Symbol type and binding */
  unsigned char st_other;  /* Symbol visibility under glibc>=2.2 */
  Elf32_Section st_shndx;  /* Section index */
} Elf32_Sym;

每個重定位函數(shù)在其中有一項(xiàng), 可以根據(jù)這個找到重定位函數(shù)的名稱.
.rel.plt section也是一個結(jié)構(gòu)體數(shù)組, 結(jié)構(gòu)體定義如下:

typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Word;
typedef struct
{
  Elf32_Addr    r_offset;               /* 該項(xiàng)對應(yīng)的got表的項(xiàng)的地址 */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;
#define ELF32_R_SYM(val) ((val) >> 8)   //計算該項(xiàng)在.dynsym中的index
#define ELF32_R_TYPE(val) ((val) & 0xff) 
 .dynmic                                                    .dynstr
+-------> +-----------------+        +-------------------------------> +------------+
          | ................|        |                                 |  ........  |
          |                 |        |                                 +------------+
          |                 |        |                      +---------->  "read"    |
          +-----------------+        |                      |          +------------+
          |    STRTAB       +--------+    .dynsym           |          | .........  |
          +-----------------+           +--------------+    |          +------------+
          |    SYSTAB       +---------> |  .........   |    |
          +-----------------+           +--------------+    |
          |   PLTRELSZ      |           |  name_index  +----+
          +-----------------+           +--------------+
          |    PLTREL       |           |              | index = Elf32_R_SYM(r_info)
          +-----------------+           +--------------+ <-------------------+
          |    RELENT       |           |              |                     |
          +-----------------+           +--------------+                     |
          |    JMPREL       +---+       |  .........   |                     |
          +-----------------+   |       +--------------+                     |
          |                 |   |               .rel.plt                     |
          |                 |   +-----------------------> +--------------+   |
          |                 |                        +----+    r_offset  |   |
          |                 |         .got.plt       |    +--------------+   |
          |                 |       +-------------+  |    |    r_info    +---+
          |                 |       |    ......   |  |    +--------------+
          |                 |       +-------------+  |    | ...........  |
          |                 |       | read@got    | <+    |              |
          |                 |       +-------------+       |              |
          +-----------------+       |  .......    |       |              |
                                    |             |       +--------------+
                                    |             |
                                    |             |
                                    +-------------+

最后結(jié)合動態(tài)鏈接器的源碼分析具體鏈接過程(源碼可見: glibc/elf/dl-runtime.c: _dl_fixup 函數(shù))

_dl_fixup(struct link_map *l, ElfW(Word) reloc_arg)
{
    // 首先通過參數(shù)reloc_arg計算重定位入口扫沼,這里的JMPREL即.rel.plt,reloc_offset即reloc_arg
    const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
    // 然后通過reloc->r_info找到.dynsym中對應(yīng)的條目
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
    // 這里還會檢查reloc->r_info的最低位是不是R_386_JUMP_SLOT=7
    assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
    // 接著通過strtab+sym->st_name找到符號表字符串算柳,result為libc基地址
    result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    // value為libc基址加上要解析函數(shù)的偏移地址鼠证,也即實(shí)際地址
    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
    // 最后把value寫入相應(yīng)的GOT表?xiàng)l目中
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}

參考博客:
ROP之return to dl-resolve
Executable and Linkable Format (ELF)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甸饱,一起剝皮案震驚了整個濱河市婆硬,隨后出現(xiàn)的幾起案子涨缚,更是在濱河造成了極大的恐慌家制,老刑警劉巖粟害,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕴忆,死亡現(xiàn)場離奇詭異,居然都是意外死亡悲幅,警方通過查閱死者的電腦和手機(jī)套鹅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汰具,“玉大人卓鹿,你說我怎么就攤上這事×衾螅” “怎么了吟孙?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵澜倦,是天一觀的道長。 經(jīng)常有香客問我杰妓,道長藻治,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任巷挥,我火速辦了婚禮桩卵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倍宾。我一直安慰自己雏节,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布高职。 她就那樣靜靜地躺著钩乍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怔锌。 梳的紋絲不亂的頭發(fā)上寥粹,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機(jī)與錄音埃元,去河邊找鬼排作。 笑死,一個胖子當(dāng)著我的面吹牛亚情,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哈雏,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼楞件,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了裳瘪?” 一聲冷哼從身側(cè)響起土浸,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彭羹,沒想到半個月后黄伊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡派殷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年还最,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毡惜。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡拓轻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出经伙,到底是詐尸還是另有隱情扶叉,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站枣氧,受9級特大地震影響溢十,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜达吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一张弛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宗挥,春花似錦乌庶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搪桂,卻和暖如春透敌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踢械。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工酗电, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人内列。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓撵术,卻偏偏與公主長得像,于是被迫代替她去往敵國和親话瞧。 傳聞我的和親對象是個殘疾皇子嫩与,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容