2012-01-15 22 views
4

我只是有一個有趣的想法。我使用objdump來轉儲一個簡單的二進制文件,我在二進制文件中看到很多函數。是否有可能創建另一個與這些功能鏈接的C程序?假設我知道輸入和輸出的參數。是否有可能鏈接一些正常的二進制功能?

一些詳細信息: 文件1:test.c的

#include <stdio.h> 

int add(int x,int y) 
{ 
    return x+y; 
} 

int main(int argc, const char *argv[]) 
{ 
    printf("%d\n",add(3,4)); 
    return 0; 
} 

文件2:test1.c

#include <stdio.h> 

int main(int argc, const char *argv[]) 
{ 
    printf("%d\n",add(8,8)); 
    return 0; 
} 

gcc test.c -o test.exe 
gcc test1.c test.exe -o test1.exe 

輸出:

ld: in test.exe, can't link with a main executable 
collect2: ld returned 1 exit status 

回答

1

從實際的角度來看,對象(.o)文件和可執行文件幾乎沒有區別。目標文件可以包含未綁定的符號,其中可執行文件不能。可執行文件必須包含一個入口點,其中目標文件沒有這樣的限制。該可執行文件具有更完整的標題。該可執行文件還解決了所有跳轉偏移問題,因爲它已經通過鏈接解析階段。某些功能可能已被永久插入。

所以是的,理論上你可以創建一個可執行文件,它調用另一個可執行文件的函數,但不僅僅是一個普通的連接線。你的主要問題是第二個可執行文件不能有一個入口點 - 一個main函數 - 並且仍然與原文鏈接(因爲名字會碰撞)。

如果您的目標只是調用原始函數,我建議您使用與您似乎暗示的直接鏈接不同的方法。如果製作共享庫並將其放入LD_PRELOAD環境變量中,然後調用原始可執行文件,則可以使用庫來有效地掛接程序條目(可能通過_main符號),然後調用備用程序例程。因爲這個庫與原來的二進制文件一起加載,所以你可以調用所有的原始函數...

但是,從二進制文件調用函數的最簡單方法是隻與目標文件鏈接而不是可執行文件。

+0

我對共享庫和LD_PRELOAD方法感興趣。有什麼更詳細的地方可以閱讀嗎? – Patrick 2012-01-15 07:12:39

+1

http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick可能會幫助你... – Jason 2012-01-15 07:18:06

+1

以下是一些更多信息:http://lca2009.linux.org.au/slides /172.pdf – Jason 2012-01-15 07:20:47

1

當然,簡單地寫一個頭文件爲要使用正確的函數簽名的函數提供聲明,然後包含e在您調用函數的C代碼模塊中的頭文件。然後編譯並鏈接到另一個目標文件以創建最終的可執行文件。

雖然假設您已經轉儲的對象文件中的函數遵循ABI和您正在使用的平臺/編譯器的調用約定(我知道一個看起來很明顯),但它不能包括它自己的入口點(即,一個main()函數)。關於第二點,目標文件必須基本上是獨立函數的「庫」。這意味着你不能鏈接到一個可執行文件。

+0

我知道我可以很容易地鏈接到.o文件。雖然,我想知道我是否可以用可執行文件來做到這一點。 – Patrick 2012-01-15 06:52:36

+1

啊,你沒有提到你的目標文件包含'main()'...這是行不通的,它必須是一個函數庫的目標文件 – Jason 2012-01-15 06:53:56

+0

有人應該提到編譯器之間的區別。 – Mikhail 2012-01-15 06:55:16

2

恐怕不是。

編譯後的二進制文件已由鏈接器通過重定位階段處理,鏈接器將代碼中的每個符號引用與運行時地址相關聯。

你可以做一個簡單的實驗,找出差異,這裏是一個程序,它輸出的「Hello World」:

// main.c 
#include <stdio.h> 

int main() 
{ 
    printf("Hello World!"); 
    return 0; 
} 

使用gcc -c您可以將源代碼編譯成可重定位目標:

$ GCC -c main.o

$ readelf -s main.o 

Symbol table '.symtab' contains 10 entries: 
    Num: Value Size Type Bind Vis  Ndx Name 
    0: 00000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 00000000  0 FILE LOCAL DEFAULT ABS main.c 
    2: 00000000  0 SECTION LOCAL DEFAULT 1 
    3: 00000000  0 SECTION LOCAL DEFAULT 3 
    4: 00000000  0 SECTION LOCAL DEFAULT 4 
    5: 00000000  0 SECTION LOCAL DEFAULT 5 
    6: 00000000  0 SECTION LOCAL DEFAULT 7 
    7: 00000000  0 SECTION LOCAL DEFAULT 6 
    8: 00000000 29 FUNC GLOBAL DEFAULT 1 main 
    9: 00000000  0 NOTYPE GLOBAL DEFAULT UND printf 

你可以從這裏main函數的值看爲0x0,這意味着它尚未搬遷和可林與他人一起。

但是當你編譯文件與gcc命令,以生成可執行文件之一:

$ gcc main.c 
$ readelf -s a.out | grep main 
    2: 00000000  0 FUNC GLOBAL DEFAULT UND [email protected]_2.0 (2) 
    39: 00000000  0 FILE LOCAL DEFAULT ABS main.c 
    51: 00000000  0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_ 
    62: 080483c4 29 FUNC GLOBAL DEFAULT 13 main 

現在你可以看到主函數的地址已經搬遷到0x80483c4,這是函數的運行時地址碼。生成a.out不能再與其他人鏈接,因爲可能存在運行時地址衝突。

一般而言,重定位階段無法恢復,因爲一些符號信息在階段後丟失。

欲瞭解更多詳情,我建議您閱讀Computer System: A Programmer's Prospective書中的鏈接章節,其中涵蓋了很多鏈接和重新定位。

+0

感謝您推薦這本書!我會研究它。 – Patrick 2012-01-15 07:13:11

+0

+1的詳細解釋。 – 2012-01-15 07:18:23

相關問題