目录
1.C编译过程


HelloWorld.c
#include<stdio.h>
#define FORMAT_STRING "%s"
#define MESSAGE "Hello,world!\n"
int main(int argc, char* argv[]){
printf(FORMAT_STRING, MESSAGE);
return 0;
}
(1)预处理阶段
gcc -E -P HelloWorld.c
/*...*/
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
int main(int argc, char* argv[]){
printf("%s", "Hello,world!\n");
return 0;
}
其中包含了stdio.h的所有类型定义,全局变量,函数原型都存在其中。预处理器还完整扩展了#define定义的任何宏的所有用法。
(2)编译阶段
gcc -S -masm=intel HelloWorld.c
cat HelloWorld.s
.file "HelloWorld.c"
.intel_syntax noprefix
.text
.section .rodata
.LC0:
.string "Hello,world!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 16
mov DWORD PTR -4[rbp], edi
mov QWORD PTR -16[rbp], rsi
lea rdi, .LC0[rip]
call puts@PLT
mov eax, 0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
.section .note.GNU-stack,"",@progbits
gcc通过将对printf替换为puts进行优化
(3)汇编阶段
gcc -c HelloWorld.c
file HelloWorld.o
HelloWorld.o: ELF 64-bit LSB relocatable,
x86-64, version 1 (SYSV), not stripped
ELF重定位文件,64位,LSB最低有效位,未剥夺的。
(4)链接阶段
链接时会包含额外的优化LTO(Link-Time Optimization)
gcc HelloWorld.c
file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=17d0303e23196158edff6a71e3c0af20ec6f08da, for GNU/Linux 3.2.0, not stripped
可执行文件名为a.out,动态链接,解释器/lib64/ld-linux-x86-64.so.2的文件输出会告诉你当加载到内存中执行时,哪个动态链接器将被用来解析动态库的最终关系。
2.符号和剥离的二进制文件
readelf --syms a.out
/*...*/
54: 0000000000002000 4 OBJECT GLOBAL DEFAULT 17 _IO_stdin_used
55: 0000000000004038 0 NOTYPE GLOBAL DEFAULT 26 _end
56: 0000000000001050 34 FUNC GLOBAL DEFAULT 15 _start
57: 0000000000004030 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
58: 0000000000001135 34 FUNC GLOBAL DEFAULT 15 main
59: 0000000000004030 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__
60: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
61: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@G[...]
62: 0000000000001000 0 FUNC GLOBAL HIDDEN 12 _init
main函数有一个符号,指定了当二进制文件加载到内存时将驻留的地址0x1135,还显示代码大小34字节,并指出你正在处理一个函数符号FUNC
剥离符号信息:
strip --strip-all a.out
file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=17d0303e23196158edff6a71e3c0af20ec6f08da,
for GNU/Linux 3.2.0, stripped
readelf --syms a.out
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (3)
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
6: 0000000000000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.2.5 (3)
3.反汇编二进制文件
└─$ objdump -sj .rodata HelloWorld.o
HelloWorld.o: file format elf64-x86-64
Contents of section .rodata:
0000 48656c6c 6f2c776f 726c6421 00 Hello,world!.
└─$ objdump -M intel -d HelloWorld.o
HelloWorld.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 10 sub rsp,0x10
8: 89 7d fc mov DWORD PTR [rbp-0x4],edi
b: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
f: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 16 <main+0x16>
16: e8 00 00 00 00 call 1b <main+0x1b>
1b: b8 00 00 00 00 mov eax,0x0
20: c9 leave
21: c3 ret
4.加载并执行二进制文件
当决定运行一个二进制文件时,操作系统首先要为运行的程序创建一个进程,其中包括虚拟地址空间。随后操作系统将解释器映射到进程的虚拟内存中,他知道如何加载二进制文件并执行必要的重定位。

1382

被折叠的 条评论
为什么被折叠?



