文章目录
前言
Hello World!
写了这么久的Hello World!它是如何实现的?
一、源码
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
编译可执行程序:
orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ gcc helloworld.c
运行:
orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ ./a.out
Hello World!
二、生成汇编代码
orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ gcc -O0 -S helloworld.c
-O0
Reduce compilation time and make debugging produce the expected results.
减少编译时间(意味着更少的优化),且使调试产生预期的结果。
这个是默认的。
-S
Stop after the stage of compilation proper; do not assemble.
编译阶段后停止,不做汇编。
生成汇编文件,文件名以后缀.s代替原来的后缀.c和.i等等。
三、汇编代码
.file "helloworld.c"
.text
.section .rodata //只读区
.LC0: //位置标记,用来计算Hello world!的位置
.string "Hello World!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64 //end branch,不知道啥用,不管
pushq %rbp //保存上一级帧指针到栈中
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp //将rsp复制到rbp中,此时rbp为当前函数的帧指针
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi //将rip + LC0的地址,放到rdi中
call puts@PLT //调用puts函数,因此处printf只有一个参数,所以此时printf和puts等价,puts自带一个'\n'输出
movl $0, %eax //eax赋值为0, return 0;
popq %rbp //从栈中恢复rbp成上一级帧指针
.cfi_def_cfa 7, 8
ret //返回
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
四、反汇编与硬编码
- 查看main函数反汇编代码
命令:objdump --disassemble=main a.out
Disassembly of section .text:
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 8d 3d ac 0e 00 00 lea 0xeac(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
1158: e8 f3 fe ff ff callq 1050 <puts@plt>
115d: b8 00 00 00 00 mov $0x0,%eax
1162: 5d pop %rbp
1163: c3 retq
Q: Hello World!的位置2004 怎么来的?
A: rip + 0xeac,rip等于下一条的指令位置即1158 + 0xeac = 2004。
Q: 为什么是下一条,而不是当前条?
A: 首先,cpu执行一条指令,简单分为三个步骤,①取指令,②译码,③执行。
rip在第②步之后指向下一条指令。所以第③步执行时rip为下一条指令的位置。
- 查看rodata区数据
命令:objdump -z -d --section=.rodata a.out
a.out: file format elf64-x86-64
Disassembly of section .rodata:
0000000000002000 <_IO_stdin_used>:
2000: 01 00 02 00 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 ....Hello World!
2010: 00 .
2004开始:
48 | 65 | 6c | 6c | 6f | 20 | 57 | 6f | 72 | 6c | 64 | 21 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
‘H’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | ’ ’ | ‘W’ | ‘o’ | ‘r’ | ‘l’ | ‘d’ | ! | ‘\0’ |
Q: 为什么’\n’不在里面?
A: puts输出自带一个’\n’。