源文件

当我们写完C语言代码后,通过gcc将其编译成可执行文件,包括预处理、编译、汇编和链接四个步骤。

源文件

最简单的hello.c源文件内容如下:

# include & ltstdio.h & gt//这是一行注释int main(void){ printf(& # 34;你好世界!\ n & # 34);printf(& # 34;% s \ n & # 34,_ _ DATE _ _);返回0;}对源文件中以“#”开头的元素进行预处理,比如#include #define,转换后直接插入源文件。处理后的文件通常需要。I作为文件扩展名。该步骤具体包括:

展开头文件:#include宏替换: #define条件编译:#if #elif else ifdef ifndef删除注释添加行号预定义符号常量:__DATE__ __TIME__ __TIMESTAMP__ __LINE__ __FILE__ __STDC__

Gcc可以通过以下指令得到预处理文件:gcc -E hello.c -o hello.i,hello.i的文件很长,这里截取了一小部分:

# 4 "hello.c & # 34int main(void){ printf(& # 34;你好世界!\ n & # 34);printf(& # 34;% s \ n & # 34, "2022年5月3日& # 34;);返回0;}你可以看到注释被删除了,符号常量__DATE__被展开了。

编译

编译阶段包括词法分析、语法分析、语义分析、中间代码生成、目标代码生成和优化。编译后,将生成汇编代码,通常文件扩展名为。南

Gcc可以通过以下指令获得编译后的汇编代码:gcc -S hello.c -o hello.s

默认生成的汇编代码是美国电话电报公司格式的,intel格式的汇编代码可以使用以下指令获取:gcc-s hello . c-o hello.s-ma = intel,Intel格式的hello . s的内容如下:

。文件& # 34;hello.c & # 34。intel_syntax noprefix。文字。第. rodata.LC0节:。字符串& # 34;你好世界!". LC1:。字符串& # 34;2022年5月3日& # 34;。文字。环球干线。键入main,@functionmain:.LFB0:。cfi_startproc endbr64推送rbp。cfi_def_cfa_offset 16。cfi_offset 6,-16 mov rbp,rsp。cfi_def_cfa_register 6 lea rdi,. LC0[rip]调用puts@PLT lea rdi,. LC1[rip]调用puts@PLT mov eax,0 pop rbp。cfi_def_cfa 7,8 ret。cfi_endproc。LFE0:。主要尺寸,。-主要的。ident & # 34GCC:(Ubuntu 9 . 4 . 0-1 Ubuntu 1 ~ 20 . 04 . 1)9 . 4 . 0 & # 34;。section .note.GNU-stack,& # 34;",@progbits。section .note.gnu.property,& # 34;一& # 34;。对齐8。长1f – 0f。长4f – 1f。龙50:。字符串& # 34;GNU & # 341:.对齐8。长0xc0000002。长3f-2f2:。长0x33:。align 84:汇编是根据汇编指令和机器指令的对应关系,将汇编文件翻译成目标文件。如果从源文件开始,gcc命令是gcc -c hello.c -o hello.o,如果从汇编文件开始,gcc命令是gcc -c hello。通过file命令检查目标文件hello.o:file hello.o,终端显示为:hello.o: ELF 64位L可移动,x86-64,version 1 (sysv),未剥离,说明这是一个ELF文件,ELF文件的内容会在下一篇博客中介绍。

hello.o文件的内容不能直接在编辑器中显示,但是可以通过objdump:objdump-SD hello . o-M Intel显示。

Hello.o:文件格式elf64-x86-64部分内容…哦…h =…001000000000 488d3d00000000000。….H.=………0020 b8000000 005dc3…..].章节内容。rodata:0000 68656 c6c 6f 20776 f 726 c 6421 004d 6179你好世界!。0010年5月20203320 32303232 00 3 2022。章节内容。评论:0000 00474343 3a 202855 62756 e74 7520392 e。GCC: (Ubuntu 9。0010 342 e 302d 31756275 6 e 747531 7e 32302 e 4.0-1 Ubuntu 1 ~ 20。0020 30342 e31 2920392 e 342 e 3000 04.1)9 . 4 . 0。section .note.gnu.property的内容:0000 04000000 10000000 05000000 474 e 5500。…………GNU。0010 020000 c0 04000000 03000000 0000000…………….章节内容。eh _ frame:0000 14000000 0000000 017 a5 200 01781001………zR..x..0010 1b0c 0708 90010000 1c 000000 1c 000000…………….0020 00000000 27000000 00450 e 10 8602430d….'….E….C. 0030 065e0c07 08000000。^……拆卸部分。text:000000000000000 & lt;main & gt:0:F3 0f 1e fa endbr 64 4:55 push RBP 5:48 89 E5 mov RBP,rsp 8: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x 0]# f & lt;main+0xf & gt;f: e8 00 00 00 00拨打14 & ltmain+0x 14 & gt;14: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x 0]# 1b & lt;main+0x1b & gt;1b:E8 00 00 00 00 call 20 & lt;main+0x 20 & gt;20: B8 00000000 MOVEAX,0x025: 5d POPRBP26: C3 RET此时无法确定该符号在目标文件中的虚拟地址,因为它还没有被链接。此时,如果运行hello.o,会得到一个错误:可执行文件格式错误。

链接

有两种链接:静态链接和动态链接。gcc默认使用动态链接。添加编译选项-static可以进行静态链接。在这个阶段,目标文件和它的依赖库被链接,主要包括地址和存储分配、符号绑定和重定位。Gcc命令:gcc hello.c -o hello。在objdump之后,一些内容如下:

0000000000001149 & ltmain & gt:1149:F3 0f 1e fa endbr 64 114d:55 push RBP 114 e:48 89 E5 mov RBP,rsp 1151: 48 8d 3d ac 0e 00 00 lea rdi,[rip+0x EAC]# 2004 & lt;_ IO _ stdin _ used+0x 4 & gt;1158: e8 f3 fe ff ff呼叫1050 & ltputs @ plt & gt115d: 48 8d 3d ad 0e 00 00 lea rdi,[rip+0x EAD]# 2011 & lt;_ IO _ stdin _ used+0x 11 & gt;1164:E8 E7 Fe ff ff call 1050 & lt;puts @ plt & gt1169: B8 00000000 MOVEAX,0x0 116e: 5d POPRBP116f: C3 RET与未链接的目标文件相比,虚拟地址已经确定,运行hello:

你好世界!2022年5月3日文章来自https://www.cnblogs.com/husterzxh/p/16218820.html.

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

发表回复

登录后才能评论