全局變量
實驗品
小白鼠(global.c):
#include <stdio.h>
int i = 1;
int main()
{
++i;
printf("%d\n",i);
return 0;
}
i
的定義被放在了函數體的外邊,
i
就成為了全局變量,程序運行的結果我不關心,
我關心的是 i 最後變成了什麼。
反彙編
這次悟空的火眼金睛也不給力了(後面我們會看到), 我們得反彙編可執行文件才能看到最終結果:
[lqy@localhost temp]$ gcc -o global global.c
[lqy@localhost temp]$ objdump -s -d global > global.txt
[lqy@localhost temp]$
- gcc -o global global.c 是編譯 global.c 生成可執行文件 global
- objdump -s -d global > global.txt 是反彙編 global
- -s 參數可以將所有段的內容以十六進制的方式打印出來
- -d 參數可以將所有包含指令的段反彙編
global.txt 是將標準輸出輸出到 global.txt 文件 (專業點的話,叫"輸出重定向")
objdump 是 linux 下一款反彙編工具, 能夠反彙編目標文件、可執行文件
這樣操作後,global 文件的反彙編結果輸出到了 global.txt 文件中,打開 global.txt(文件比較長,我的有 357 行), 定位到 main 函數:
080483c4 <main>:
80483c4: 55 push %ebp
80483c5: 89 e5 mov %esp,%ebp
80483c7: 83 e4 f0 and $0xfffffff0,%esp
80483ca: 83 ec 10 sub $0x10,%esp
80483cd: a1 64 96 04 08 mov 0x8049664,%eax
80483d2: 83 c0 01 add $0x1,%eax
80483d5: a3 64 96 04 08 mov %eax,0x8049664
80483da: 8b 15 64 96 04 08 mov 0x8049664,%edx
80483e0: b8 c4 84 04 08 mov $0x80484c4,%eax
80483e5: 89 54 24 04 mov %edx,0x4(%esp)
80483e9: 89 04 24 mov %eax,(%esp)
80483ec: e8 03 ff ff ff call 80482f4
80483f1: b8 00 00 00 00 mov $0x0,%eax
80483f6: c9 leave
80483f7: c3 ret
粗體部分標出了 ++i 的反彙編結果: 全局變量 i 最後變成了絕對地址為 0x8049664 的內存塊(大小為4字節)。 注意這裡的 0x8049664 不是指立即數 0x8049664, 如果要表示值為 0x8049664 的立即數,應該寫為 $0x8049664。
悟空的弱點
通過火眼金睛:
gcc -S -o global.s global.c
我們看到 ++i 的彙編形式是:
movl i, %eax
addl $1, %eax
movl %eax, i
悟空,你太讓我們失望了!
目標文件中的全局變量
目標文件也可以用 objdump 來反彙編:
[lqy@localhost temp]$ gcc -c -o global.o global.c
[lqy@localhost temp]$ objdump -s -d global.o > global.txt
[lqy@localhost temp]$
global.o 反彙編出來的 global.txt 就沒那麼長了, 只有 35 行,其中 ++i 部分的結果是:
9: a1 00 00 00 00 mov 0x0,%eax
e: 83 c0 01 add $0x1,%eax
11: a3 00 00 00 00 mov %eax,0x0
怎麼會這樣?怎麼不是 0x8049664 呢? 這就是目標文件 和 可執行文件的區別了: 全局變量在目標文件中只有一個冒牌地址, 在鏈接後(各目標文件協商(為自己的全局變量爭一塊地盤) 完畢)才填入最終的絕對地址。
目標文件和可執行文件
目標文件是編譯後的產物, 已經完成了 C語言 到 機器碼 的轉變, 但是部分機器碼的操作數還需要在鏈接過程中修正。
可執行文件是由多個目標文件和 C 運行庫鏈接而成的, C 運行庫提供了諸如 printf 等函數的實現。
linux 下目標文件(默認擴展名是.o)和可執行文件都是 ELF 格式(文件內容按照一定格式進行組織)的二進制文件; 類似的,Windows 下 VISUAL C++ 編譯出來的目標文件 (擴展名是.obj)採用 COFF 格式,而可執行文件 (擴展名是.exe)採用 PE 格式, ELF 和 PE 都是從 COFF 發展而來的。
因為 linux 下目標文件和可執行文件的內容格式是一樣的, 所以 objdump 既可以反彙編可執行文件也可以反彙編目標文件。
小結
全局變量也變成無名無姓的內存地址了, 而且還是常量!
這次又介紹了一個工具:objdump。 所有後面要用到的工具和使用方法都介紹完了:gcc、objdump, ……嗯……(好像還不夠)……至少主要的都介紹完了O(∩_∩)O~……