首頁 > 軟體

C/C++程式連結與反組合工具objdump的使用介紹

2023-02-05 14:01:38

程式構建過程的第二個階段就是連結,連結過程輸入的是目標檔案的集合。每個目標檔案可以被看作單個原始碼檔案的二進位制儲存版本,需要為程式記憶體對映提供各種各樣的節(程式碼.text 初始化資料.data 未初始化資料.bss 和唯讀資料.rdata),連結器的最終任務是將獨立的節組合成最終的程式記憶體對映節,與此同時解析所有的參照。

連結階段

連結過程包括一系列階段(重定位、解析參照),接下來我們介紹這些階段。

1.重定位

連結過程的第一個階段僅僅進行拼接,其過程是將分散在單獨目標檔案中不同型別的節拼接到程式記憶體對映節中。

如圖,為了完成任務,需要將之前預留的空間,也就是節中從0開始的地址範圍轉換成最終程式記憶體對映中更具體的地址範圍。

2.解析參照

現在我們來看連結過程中最難的部分,將節的地址範圍線性地轉換成程式記憶體對映地址範圍。相比來說,更艱鉅的任務在於為不同的部分的程式碼建立關聯,使得程式成為一個整體。

function.h程式碼:

#pragma once
#define FIRST_OPTION
#ifdef FIRST_OPTION
#define MULTIPLIER (3.0)
#else
#define MULTIPLIER (2.0)#endif
float add_and_multiply(float x,float y);

function.c

//#include "function.h"
int nCompletionStatus = 0;
float add(float x,float y)
{
   float z = x + y;
   return z;
}
float add_and_multiply(float x,float y)
{
   float z = add(x,y);
   z *= 3;
   return z;
}

main.c

#include "function.h"
extern int nCompletionStatus;
int main(int argc,char* argv[])
{
    float x = 1.0;
    float y = 5.0;
    float z;
    z= add_and_multiply(x,y);
    nCompletionStatus =1;
    return 0;
}

在上例程式碼中

  • add_and_multiply 函數呼叫add函數,這兩個函數在同一個原始碼檔案中,這種情況下,函數add的記憶體對映地址是一個已知量,會被擴充套件成其對與function.o中程式碼節起始地址的相對偏移。
  • main函數會呼叫add_and_multiply函數,並同時參照外部變數nCompletionStatus,這個時候問題就出現了--我們不知道它們的實際程式記憶體地址,實際上編譯器會假定這些符號未來會在程序記憶體對映中存在,但是,直到生成完整記憶體對映之前,這兩個參照會一直被當成未解析參照。

該問題如圖描述:

function.o

main.o

為了解決這類問題,我們需要在連結階段就對這些參照進行解析,此時連結器需要:

  • 檢查拼接到程式記憶體對映中的節
  • 找出那些部分程式碼產生了外部呼叫
  • 計算該參照的精確地址(在記憶體對映中的地址)
  • 最後,將機器指令中的偽地址替換成程式記憶體對映的實際地址,這樣就完成了參照的解析。

3.連結範例

程式記憶體對映圖

gcc -c function.c main.c
gcc function.o main.o -o demoApp

反組合main.o檔案

objdump -D -M intel main.o

劃紅線的是跳轉自身,是因為連結器不知道函數的地址。先用偽地址代替。

反組合demoApp

objdump -D -M intel demoApp

畫紅線的位置分別是add_and_multiply 地址為11aa 和nCompletionStatus的地址。

執行下面命令檢視,看到nCompletionStatus地址為4014.

objdump -x -j .bss demoapp

到此這篇關於C/C++程式連結與反組合工具objdump的使用介紹的文章就介紹到這了,更多相關C++程式連結內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com