1. 程式人生 > >總結(三)

總結(三)

input 輸入 題解 變化 red 虛擬 sym lib 根據

靜態鏈接

鏈接過程主要包括地址和空間的分配、符號決議、重定位

[toc]
技術分享圖片

空間和地址分配

分配的空間是指虛擬地址空間的分配。

  • 將這連個文件編譯之後鏈接在一塊兒成為ab的過程中,a.o和b.o的內容如何合並,換句話說如何在ab中儲存兩個.o文件的內容?
    相似段合並: 將所有文件中的代碼段.text合並在一塊兒,.data合並到一塊兒等等。
  • 將文件合並之後之前文件的相對偏移地址發生變化,如何在新的文件中對其內容進行定位?
    在鏈接的過程中,合並多個文件會根據所的信息對其內容堿性符號解析和重定位。

綜上所述,鏈接器分配空間的策略是采用相似段合並,並采用兩步鏈接的方法。

  1. 空間地址分配 掃描輸入文件,獲得段長度屬性位置,將輸入文件中的符號定義和引用都收集起來放到全局符號表中。 鏈接器獲得輸入文件的段長度,並將它們合並起來,計算輸出文件的各個段長度和位置,建立映射關系。
  2. 符號解析和重定位 使用第一步的信息,對合成的輸出文件的符號信息進行解析調節,調整代碼中的地址等。
    技術分享圖片

查看各個文件的屬性,可以看到ab的.text的大小正好是兩個文件的.text大小和
符號地址的確定
因為各個符號在段內的相對位置是固定的,所以其實在合並之後的各個符號的地址就一定確定了,在段基址加上一個偏移值,或者原本的偏移值加上一個偏移值。

符號解析和重定位

重定位 起始對於符號地址以及各個代碼段的地址在上面部份已經解決,但是在代碼段中的引用的外部符號的地址會在新的輸出文件中發生改變,所以代碼段中的引用外部符號的地址也需要進行改變,這個也就是重定位的過程。
技術分享圖片

0x14 向esi傳入0x00 和 0x21 的main的地址也是0x00, 這些在a.o中作為符號保留位置,在鏈接成為ab之後,他們的地址被改寫
技術分享圖片

可以看到shared的地址是0x00601000所以填入00 10 60 00
call指令是下一個指令的地址加上調用的偏移值所以 swap地址等於 0040010d + 00000002 = 40010f

問題: 如何確定在重定位的時候的每個符號的地址信息,就是如何找到代碼段中哪些地方需要修改?

使用重定位表
對於可重定位的ELF文件來說,他必須包含重定位表,用來專門保存重定位信息(重定位包含但不局限於代碼段)。
可以使用objdump的-r選項查看重定位表信息,重定位的數據結構:
技術分享圖片

r_offset 重定位入口地址
r_info 重定位符號類型

符號解析
使用objdump -s 查看符號信息符號表 .symtab

指令修正方式

  • 絕對尋址修正 S+A
    S是符號的實際地址, A 是修正位置的值,比如shared的修正
  • 相對尋址修正 S + A - P

S是符號的實際地址, P是被修正的位置地址, A是修正位置的值
A是目的地址相對於下一條指令的偏移量
我的理解是 如果D是要填的值的話, 那麽有使用call調用函數的地址是D+(-P+A) = S
所以有D = S + A -P

COMMON塊

用於解決符號沖突問題,是對弱符號機制問題解決得辦法
對於多個符號定義類型不一致情況:

  • 兩個或者兩個以上得強符號類型不一致
  • 一個強符號,其他的弱符號,類型不一致
  • 兩個或者兩個以上弱符號類型不一致
    解決辦法:
    第一種情況連接器報錯, 後面兩種情況,參照強符號弱符號得處理方式來進行處理。

API 和 ABI


可以使用ar命令查看lib.a這樣得文件內部所存在得.o文件得種類
每個.o文件中只包含一個函數,這樣來避免浪費,因為鏈接器鏈接靜態庫的單位是目標文件

鏈接控制腳本

使用鏈接器得方法:

  1. 使用命令行 ld collect2之類
  2. 將連接指令存放在目標文件裏面
  3. 使用連接控制腳本
    ENTRY(函數名) 入口函數
    STARTUP(filename) 第一個輸入文件
    SEARCH_DIR(path) 將路徑加入ld鏈接器得庫查找目錄
    INPUT(file,file,...) 將指定文件作為鏈接輸入文件
    INCLUDE filename 指定文件包含進本鏈接腳本
    PROVIDE(symbol) 在鏈接腳本中定義某個符號

Windows上得可執行文件PE類型,大體和Linux得Elf一樣

總結(三)