和我一起学软件架构:C编译流程
引言我们基于两个材料进行实验一个简单的C语言代码) (GNU工具-GCC)源代码// hello.c#includestdio.h#definePI3.14159intmain(){doubleradius5.0;doubleareaPI*radius*radius;printf(Area %f\n,area);return0;}GCC说明在Linux 环境下gcc命令将源代码转换为可执行文件通常需要经过四个主要阶段预处理、编译、汇编和链接。这个四个过程分别由预处理器、编译器、汇编器、链接器完成这四个过程被同一个命令gcc包装了起来每个阶段都有明确的输入、输出和核心任务。先看一下gcc 命令参数useruser-VirtualBox:~/test$ gcc --help ... ... -v Display the programs invoked by the compiler. -E Preprocess only; do not compile, assemble or link. -S Compile only; do not assemble or link. -c Compile and assemble, but do not link. -o file Place the output into file. ... ...1. 第一阶段预处理输入文件 hello.c输出文件 hello.i#0hello.c#0built-in#0command-line#1/usr/include/stdc-predef.h134#0command-line2#1hello.c......执行命令gcc -v -E hello.c -o hello.i/usr/lib/gcc/x86_64-linux-gnu/11/cc1 是真正的预处理器useruser-VirtualBox:~/test$ gcc-v-Ehello.c-ohello.i... /usr/lib/gcc/x86_64-linux-gnu/11/cc1-E-quiet-v-imultiarchx86_64-linux-gnu hello.c-ohello.i-mtunegeneric-marchx86-64...预处理器行为预处理器负责处理所有以#开头的指令展开宏定义#define插入头文件内容#include处理条件编译#ifdef、#if等删除注释2. 第二阶段编译输入文件 hello.i输出文件 hello.s.filehello.c.text.section.rodata.LC2:.stringArea %f\n.text.globl main.type main,functio main:.LFB0:.cfi_startproc endbr64 pushq%rbp.cfi_def_cfa_offset16.cfi_offset6,-16movq%rsp,%rbp.cfi_def_cfa_register subq $16,%rsp movsd.LC0(%rip),%xmm0执行命令gcc -S -v hello.i -o hello.susr/lib/gcc/x86_64-linux-gnu/11/cc1 是真正的编译器useruser-VirtualBox:~/test$ gcc-S-vhello.i-ohello.s Using built-in specs.COLLECT_GCCgcc usr/lib/gcc/x86_64-linux-gnu/11/cc1-fpreprocessedhello.i-quiet-dumpbasehello.i -dumpbase-ext .i-mtunegeneric-marchx86-64-version-ohello.s...编译器行为将高级语言转换为汇编指令。此阶段是编译器最复杂的部分包含词法分析将源代码拆分成记号token语法分析构建语法树语义分析检查类型、作用域等中间代码生成与优化生成独立于机器的中间表示如 RTL、GIMPLE并执行优化如常量折叠、死代码消除目标代码生成将优化后的中间表示映射为特定 CPU 架构的汇编指令-marchx86-64 指定了生成的汇编代码使用 x86-64 基础指令集 --with-abim64 以及 --with-multilib-listm32,m64,mx32 决定了默认的 ABI 为 64 位 System V ABI常见于 x86_64 Linux并支持生成 32 位、64 位和 x32 代码。ABI 规定了函数调用约定、参数传递方式、数据结构对齐等 Target: x86_64-linux-gnu 中的 linux 部分 系统调用接口由操作系统内核定义GCC 生成的代码通过 C 库如 glibc间接使用系统调用。这里的 linux 表示目标系统为 Linux系统调用号、参数传递方式等遵循 Linux 内核的约定3. 第三阶段汇编输入文件 hello.s输出文件 hello.o 即二进制机器码但尚未链接hello.o:file format elf64-x86-64Disassembly of section.text:0000000000000000main:0:55push%rbp1:4889e5 mov%rsp,%rbp4:4883ec10sub $0x10,%rsp8:f20f1005000000movsd0x0(%rip),%xmm0 #10main0x10f:0010:f20f1145f8 movsd%xmm0,-0x8(%rbp)15:f20f1045f8 movsd-0x8(%rbp),%xmm01a:f20f5945f8 mulsd-0x8(%rbp),%xmm01f:f20f5905000000mulsd0x0(%rip),%xmm0 #27main0x2726:0027:f20f1145f0 movsd%xmm0,-0x10(%rbp)2c:f20f1045f0 movsd-0x10(%rbp),%xmm0执行命令gcc -v -c hello.s -o hello.oas 是真正的汇编器useruser-VirtualBox:~/test$ gcc-v-chello.s-ohello.o Using built-in specs.COLLECT_GCCgcc Target: x86_64-linux-gnuCOLLECT_GCC_OPTIONS-v-c-ohello.o-mtunegeneric-marchx86-64as-v--64-ohello.o hello.s汇编器行为将汇编指令转换为机器码。汇编器如 GNUas将每条助记符翻译成对应的 CPU 指令同时处理符号如标签的地址计算生成一个可重定位的目标文件ELF 格式。目标文件中包含代码段.text数据段.data、.rodata符号表记录了函数和全局变量的名称及位置但地址是相对的4. 第四阶段链接输入文件 hello.o输出文件 hello.elf 即可执行程序经过了链接使用objdump -d hello可以查看已重定位的代码0000000000001149 main: 1149: 55 push %rbp 114a: 48 89 e5 mov %rsp,%rbp 114d: 48 83 ec 10 sub $0x10,%rsp 1151: f2 0f 10 05 df 0e 00 movsd 0xedf(%rip),%xmm0 # 2038 _IO_stdin_used0x8 1158: 00 1159: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp) 115e: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0 1163: f2 0f 59 45 f8 mulsd -0x8(%rbp),%xmm0 1168: f2 0f 59 05 d0 0e 00 mulsd 0xed0(%rip),%xmm0 # 2040 _IO_stdin_used0x10 116f: 00 1170: f2 0f 11 45 f0 movsd %xmm0,-0x10(%rbp) 1175: f2 0f 10 45 f0 movsd -0x10(%rbp),%xmm0 117a: 48 8d 3d 87 0e 00 00 lea 0xe87(%rip),%rdi # 2008 _IO_stdin_used0x8 1181: b8 01 00 00 00 mov $0x1,%eax 1186: e8 c5 fe ff ff callq 1050 printfplt 118b: b8 00 00 00 00 mov $0x0,%eax 1190: c9 leaveq 1191: c3 retq执行命令gcc hello.o -v -o hello/usr/lib/gcc/x86_64-linux-gnu/11/collect2 是真正的链接器seruser-VirtualBox:~/test$ gcc hello.o-v-ohello Using built-in specs.COLLECT_GCCgcc Target: x86_64-linux-gnuCOLLECT_GCC_OPTIONS-v-ohello-mtunegeneric-marchx86-64-dumpdirhello./usr/lib/gcc/x86_64-linux-gnu/11/collect2-plugin/usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt-fresolution/tmp/ccXJXVQE.res -plugin-opt-pass-through-lgcc -plugin-opt-pass-through-lgcc_s -plugin-opt-pass-through-lc -plugin-opt-pass-through-lgcc -plugin-opt-pass-through-lgcc_s --build-id --eh-frame-hdr-melf_x86_64 --hash-stylegnu --as-needed -dynamic-linker链接器行为符号解析与重定位。链接器如ld将多个目标文件和库合并为一个可执行文件主要完成符号解析将每个符号引用与一个符号定义关联起来例如printf的实现在libc.so中重定位将每个符号的最终虚拟地址填入代码中合并相同的节section生成完整的可执行文件
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2426674.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!