二进制或程序由机器代码和数据组成。在这种情况下,您放入源代码的字符串,编译器也只是字节的数据,并且由于它的使用方式被认为是只读数据,因此取决于可能落在 .rodata 或 .text 中的编译器或编译器可能使用的其他名称。Gcc 可能会称它为 .rodata。程序本身是 .text 格式的。链接器出现,当它链接事物时,它会为 .text、.data、.bss、.rodata 和您可能拥有的任何其他项目找到一个位置,然后将这些点连接起来。在你调用 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 <puts@plt>
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)
编译器将对该指令进行编码,但将地址保留为零或填充
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)
取消链接它必须只填充此地址/偏移量,以便链接器稍后填写。
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。编译器足够聪明,知道一个 const 即使它是全局的也不会改变,所以它可以将该值硬编码到数学中,而不需要从 ram 中读取,但是它必须从 ram 中读取另外两个变量,因此会生成一条指令链接器在链接时填写地址零。
在您的情况下,您的只读/常量数据是字节的集合,它不是数学运算,因此源文件中定义的字节被放置在内存中,因此它们可以作为 printf 的第一个参数指向。
二进制文件不仅仅是机器代码。并且编译器和链接器可以将东西放在内存中以供机器代码获取,机器代码本身不必编写将由机器代码的其余部分使用的每个值。