我的二進制或程序是由機器代碼和數據組成的。在這種情況下,你在源代碼中放入的字符串,編譯器也僅僅是字節的數據,以及由於它被如何使用而被認爲是隻讀數據,所以取決於可能以.rodata或.text寫入的編譯器或編譯器可能使用的其他名稱。海灣合作委員會可能會把它叫做.rodata。程序本身在.text中。鏈接器出現時,鏈接時找到.text,.data,.bss,.rodata和其他任何可能的項目,然後連接點。在你調用printf的情況下,鏈接器知道它把字符串,字節數組放在了什麼位置,並且它被告知它的名字是什麼(一些內部的臨時名字毫無疑問),並且printf調用被告知這個名字,所以鏈接器在調用printf之前修改指令以將地址抓取到格式字符串。
Disassembly of section .text:
0000000000400430 <main>:
400430: 53 push %rbx
400431: bb 0a 00 00 00 mov $0xa,%ebx
400436: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40043d: 00 00 00
400440: bf e4 05 40 00 mov $0x4005e4,%edi
400445: e8 b6 ff ff ff callq 400400 <[email protected]>
40044a: 83 eb 01 sub $0x1,%ebx
40044d: 75 f1 jne 400440 <main+0x10>
40044f: 31 c0 xor %eax,%eax
400451: 5b pop %rbx
400452: c3 retq
400453: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40045a: 00 00 00
40045d: 0f 1f 00 nopl (%rax)
Disassembly of section .rodata:
00000000004005e0 <_IO_stdin_used>:
4005e0: 01 00 add %eax,(%rax)
4005e2: 02 00 add (%rax),%al
4005e4: 48 rex.W
4005e5: 65 6c gs insb (%dx),%es:(%rdi)
4005e7: 6c insb (%dx),%es:(%rdi)
4005e8: 6f outsl %ds:(%rsi),(%dx)
4005e9: 2c 20 sub $0x20,%al
4005eb: 77 6f ja 40065c <__GNU_EH_FRAME_HDR+0x68>
4005ed: 72 6c jb 40065b <__GNU_EH_FRAME_HDR+0x67>
4005ef: 64 21 00 and %eax,%fs:(%rax)
編譯器將已編碼的該指令,但可能留下的地址爲0或者一些填充
400440: bf e4 05 40 00 mov $0x4005e4,%edi
,以便鏈接可以在以後的填充它。 gnu反彙編器試圖分解沒有意義的.rodata(和.data等)塊,所以忽略它試圖解釋從地址0x4005e4開始的字符串的指令。
鏈接對象的解體前顯示了這兩個部分的.text和.RODATA
Disassembly of section .text.startup:
0000000000000000 <main>:
0: 53 push %rbx
1: bb 0a 00 00 00 mov $0xa,%ebx
6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
d: 00 00 00
10: bf 00 00 00 00 mov $0x0,%edi
15: e8 00 00 00 00 callq 1a <main+0x1a>
1a: 83 eb 01 sub $0x1,%ebx
1d: 75 f1 jne 10 <main+0x10>
1f: 31 c0 xor %eax,%eax
21: 5b pop %rbx
22: c3 retq
0000000000000000 <.rodata.str1.1>:
0: 48 rex.W
1: 65 6c gs insb (%dx),%es:(%rdi)
3: 6c insb (%dx),%es:(%rdi)
4: 6f outsl %ds:(%rsi),(%dx)
5: 2c 20 sub $0x20,%al
7: 77 6f ja 78 <main+0x78>
9: 72 6c jb 77 <main+0x77>
b: 64 21 00 and %eax,%fs:(%rax)
斷開鏈接它只是墊這個地址/鏈接器偏移到後來練習I.
10: bf 00 00 00 00 mov $0x0,%edi
還注意該對象只包含在.rodata中的字符串。與圖書館和其他項目鏈接,使它成爲一個完整的程序清楚地增加了更多.rodata,但鏈接器管理所有這些。
也許更容易看到這個例子
void more_fun (unsigned int, unsigned int, unsigned int);
unsigned int a;
unsigned int b=5;
const unsigned int c=7;
void fun (void)
{
more_fun(a,b,c);
}
拆解爲對象
Disassembly of section .text:
0000000000000000 <fun>:
0: 8b 35 00 00 00 00 mov 0x0(%rip),%esi # 6 <fun+0x6>
6: 8b 3d 00 00 00 00 mov 0x0(%rip),%edi # c <fun+0xc>
c: ba 07 00 00 00 mov $0x7,%edx
11: e9 00 00 00 00 jmpq 16 <fun+0x16>
Disassembly of section .data:
0000000000000000 <b>:
0: 05 .byte 0x5
1: 00 00 add %al,(%rax)
...
Disassembly of section .rodata:
0000000000000000 <c>:
0: 07 (bad)
1: 00 00 add %al,(%rax)
...
以任何理由,你必須掛靠看到.bss段。這個例子的要點是函數的機器碼在.text中,未初始化的全局在.bss中,初始化的全局是.data,而const初始化的全局是.rodata。編譯器足夠聰明,知道常量即使是全局也不會改變,所以它可以將該值硬編碼到數學中,而不需要從RAM中讀取,但是它必須從RAM讀取的其他兩個變量纔會生成一條指令地址零在鏈接時由鏈接器填充。
在你的情況你的只讀/ const數據是一個字節的集合,它不是一個數學運算,所以你的源文件中定義的字節被放置在內存中,以便它們可以被指向作爲printf的第一個參數。
還有更多的二進制不只是機器碼。編譯器和鏈接器可以將內容放置在內存中以供機器代碼獲取,機器代碼本身不必編寫將被其餘機器代碼使用的每個值。
它是在你的二進制文件中,當操作系統啓動你的程序時,它被加載到內存中,就像程序的機器代碼一樣。請注意,'[esp]'與'esp'不同,前者訪問內存,並不改變'esp'本身。 – Jester
它加載到內存中。我假定該字符串的內存地址從0x8048441開始,到0x80484e0結束。請記住一個字符串是一個整數列表。 – Xorifelse
@Jester啊,所以「mov DWORD PTR [esp],0x80484e0」實際上並不是指向一個新的地址,而只是將0x80484e0寫到它當前指向的地址? –