GCC 编译 | 命令参数、编译流程与链接机制

GCC 编译 | 命令参数、编译流程与链接机制 注本文为 “GCC 编译” 相关合辑。图片清晰度受引文原图所限。略作重排未整理去重。如有内容异常请看原文。GCC 的使用简介与命令行参数说明delphiwcdj 原创于 2011-06-19 20:29:00 发布本文阐述 GCC 编译器的基础用法与拓展功能涵盖编译配置项、告警输出、库文件调用、调试配置、交叉编译等内容适用于学习 GCC 编译流程、规范代码编写的开发人员。参考资料《GNU gcc嵌入式系统开发》作者董文军1. GCC 的基本用法调用 GCC 编译器时需要指定对应参数与待处理文件名称。不同类型编译参数的先后顺序不会改变执行结果同类参数的顺序会影响执行逻辑。若使用多个-L参数指定库文件检索目录GCC 会依照参数书写顺序依次检索对应目录。GCC 多数参数由多字符构成不支持单字符参数合并写法该类参数在 Linux 系统中称作短参数。例如-dr与分开书写的-d -r具备不同语义。GCC 内置编译参数数量超过 100 个下文仅介绍日常使用频次较高的参数。GCC 标准调用格式gcc [options] [filenames]options代表编译参数filenames代表待处理文件名称。常用参数说明如下1.1-c仅执行编译操作不执行链接流程。编译器读取.c等源码文件生成后缀为.o的目标文件。该参数多用于编译不含程序入口的子程序文件。1.2-o output_filename指定输出文件名称为output_filename输出文件名称不可与源文件名称一致。未配置该参数时GCC 默认生成可执行文件a.out。1.3-g生成 GNU 调试工具gdb所需的符号信息。对源码进行调试操作时必须添加该参数。1.4-O开启编译与链接阶段的代码优化。经过优化处理的可执行文件运行效率会提升但编译、链接耗时会相应增加。同时优化操作会对调试工作造成干扰可能出现运行表现与源码逻辑不一致的情况。该参数一般用于软件正式版本的编译打包。1.5-O2执行强度高于-O的编译与链接优化整体编译、链接流程耗时更长。1.6-Idirname将dirname指向的目录加入头文件检索路径该参数作用于预处理阶段。C 语言头文件引入分为两种形式#includestdio.h#includestdio.h尖括号形式预处理程序cpp优先在系统默认头文件目录如/usr/include中检索文件双引号形式预处理程序cpp首先在当前工作目录检索文件检索失败后再检索-I指定的目录。若项目头文件分散在多个目录中需要多次使用-I参数依次配置检索路径。1.7-Ldirname将dirname指向的目录加入库文件检索路径该参数作用于链接阶段。默认状态下链接程序ld在系统默认目录如/usr/lib中检索库文件。配置该参数后链接程序会优先检索-L指定目录再检索系统默认目录。库文件存放于多个目录时需多次使用该参数配置路径。1.8-lname链接名称为libname.a的函数库库文件取自系统默认目录或-L参数指定的目录。示例-lm表示链接数学函数库libm.a。1.9 基础使用示例现有 C 语言源码文件test.c代码内容如下#includestdio.hintmain(void){printf(Hello world\n);return0;}生成可执行文件的基础指令gcc test.c-otest完整编译流程分为三步调用预处理程序cpp完成宏展开、头文件内容嵌入等预处理操作调用编译程序ccl与汇编程序as将预处理后的源码转换为目标代码调用链接程序ld将目标代码整合为可执行文件。默认配置下预处理、编译、链接流程会一次性执行。1.10 分步执行编译流程GCC 支持在编译的四个阶段中断执行对应参数如下-E执行预处理后终止流程生成后缀为.i的预处理文件-c执行编译后终止流程生成后缀为.o的目标文件-S执行汇编后终止流程生成后缀为.s的汇编文件。分步执行指令示例# 预处理gcc-Etest.c-otest.i# 编译生成目标文件gcc-ctest.c-otest.o# 汇编生成汇编文件gcc-Stest.c-otest.s# 链接生成可执行文件gcc test.o-otest1.11 多文件与库链接示例项目包含testmain.c、testsub.c两个源码文件程序调用系统数学库编译指令如下gcc testmain.c testsub.c-lm-otest其中-lm用于链接系统数学库libm.a。1.12 工程编译说明源码数量较多的工程单次使用一条 GCC 指令完成全量编译会造成资源损耗。若仅修改单个文件该方式会重复编译所有未变更文件。针对该场景可搭配make工具完成增量编译。2. 告警提示配置项GCC 集成完备的错误检测与告警输出能力可辅助开发人员规范代码编写。2.1-pedantic编译不符合 ANSI/ISO C 语言标准的源码时输出对应告警信息。#includestdio.hvoidmain(void){longlongintvar1;printf(It is not standard C code!/n);}以违规代码为例该参数可检测出以下问题main函数返回值声明为void标准规范要求声明为int使用long long定义 64 位整数属于 GNU 语法扩展不符合 ANSI/ISO C 标准main函数执行结束前未编写return语句。2.2-WallGCC 多数告警类参数以-W为前缀-Wall可启用绝大多数告警检测规则输出全面的告警信息。编译输出的告警不属于程序错误但会提升故障出现概率。建议编译源码时固定添加-Wall参数便于排查隐性代码问题。2.3-Werror将所有告警信息判定为错误触发告警时编译流程立即终止。该参数适用于自动化编译场景强制对存在问题的代码进行修正。2.4-Wcast-align检测指针地址对齐异常。若无需地址对齐的指针指向要求地址对齐的变量编译器会输出告警。例如char *类型指针指向int *类型地址int类型数据通常要求地址可被 2 或 4 整除。2.5 其他常用参数-v输出 GCC 完整执行流程--target-help展示当前 GCC 支持的 CPU 架构-Q输出编译统计信息与所有函数名称。3. 库文件操作配置项Linux 平台下的软件开发通常需要调用第三方库文件。从代码开发角度库文件由头文件.h与库文件.so/.a组成。系统默认头文件存放于/usr/include/库文件存放于/usr/lib/文件路径自定义时需要通过编译参数配置检索规则。3.1-I向头文件检索路径中新增自定义目录。3.2-L向库文件检索路径中新增自定义目录适用于库文件未存放在系统默认路径的场景。3.3-lLinux 平台库文件命名遵循统一规则文件名以lib作为前缀。使用-l参数时可省略该前缀例如-lfoo会自动链接libfoo.so文件。3.4-staticLinux 库文件分为静态链接库后缀.a与动态链接库后缀.so。二者区别在于代码加载时机静态库代码在编译阶段载入程序动态库代码在程序运行阶段载入程序。GCC 默认优先使用动态链接库动态库缺失时才会检索静态库。添加-static参数可强制使用静态链接库。3.5-shared生成共享目标文件该文件可与其他目标文件链接整合为可执行文件。4. 调试配置项gdbGNU Debugger与 GCC 配合使用可在 Linux 平台搭建完整的程序调试环境。4.1-g与-ggdb默认编译模式下GCC 不会向二进制文件写入调试符号信息以此控制文件体积。需要调试时可使用-g或-ggdb参数生成调试符号。-g支持分级配置调试信息粒度格式为-g[数字]共分为三个等级-g1仅保留函数调用回溯、堆栈转储相关信息不包含局部变量、行号信息-g2默认等级包含符号表、代码行号、局部变量与外部变量信息-g3在-g2基础上额外保留源码宏定义信息。补充说明函数调用回溯追踪程序运行过程中的函数调用链路堆栈转储以十六进制格式保存程序运行现场。调试参数会增大二进制文件体积同时增加程序运行开销仅建议在开发、测试阶段使用。4.2-p与-pg向二进制文件植入性能剖析数据用于定位程序性能瓶颈。4.3-save-temps保留编译流程产生的中间文件。示例指令gcc test.c-otest-save-temps执行后除可执行文件test外还会保留预处理文件test.i、汇编文件test.s等中间文件。5. 交叉编译配置项常规编译场景中GCC 生成的目标代码适配当前运行主机的硬件架构。GCC 支持交叉编译可生成适配其他 CPU 架构的目标代码。嵌入式系统开发常以 X86 架构 PC 作为宿主机借助 GCC 交叉编译功能完成嵌入式设备的程序开发。本文不展开详述具体参数配置。GCC 的基本使用Balaaam 原创于 2022-03-26 17:18:23 发布1. GCC 概述GCCGNU Compiler Collection由 Richard Stallman 开发是 GNU 项目的核心工具之一该工具最初为 C 语言编译器GNU C Compiler现阶段支持 C、C、Java、Ada、COBOL 等多种编程语言GCC 具备跨平台特性支持主流硬件架构同时兼容 MMIX 等小众架构软件采用模块化设计可扩展新增编程语言与 CPU 架构支持GCC 属于自由软件。2. GCC 编译流程完整编译分为四个阶段预处理、编译、汇编、链接。C 语言源码为人类可读格式运行前需要转换为机器指令并按照可执行文件格式打包存储至磁盘。3. GCC 常用参数与示例编译指令示例# 预处理gcc-Ehello.c-ohello.i# 编译生成汇编文件gcc-Shello.i-ohello.s# 汇编生成目标文件gcc-chello.s-ohello.o# 链接生成可执行文件gcc hello.o-ohello# 一次性完成编译与链接gcc hello.c-ohello# 编译生成目标文件gcc-chello.c gcc-chello.c-ohello.o4. 多文件编译项目文件hello_fn.h、hello_fn.c、main.c# 一次性编译链接gcc hello_fn.c main.c-onewhello# 分步独立编译gcc-Wall-cmain.c-omain.o gcc-Wall-chello_fn.c-ohello_fn.o gcc-Wallmain.o hello_fn.o-onewhello5. 头文件与库文件作用头文件.h定义常量、声明系统函数与库函数调用接口库文件预编译完成的函数集合由一组关联函数组成用于实现通用功能。例如 ncurses 库用于界面绘制dbm 库用于数据库访问。6. 系统默认检索路径头文件路径/usr/include及其子目录/usr/local/include及其子目录库文件路径/usr/lib/usr/local/lib7. 外部库调用示例源码文件calc.c#includemath.h#includestdio.hintmain(){doublexpow(2.0,3.0);printf(The cubed if %f\n,x);return0;}编译指令gcc-Wallcalc.c-ocalc-lm-lm用于链接libm.so或libm.a数学库。8. 静态库与共享库静态库后缀.a编译链接阶段库代码完整嵌入可执行文件。程序运行时不再依赖原静态库文件共享库后缀.so仅在可执行文件中记录函数入口地址库代码在程序运行时动态加载。多个程序可共用同一份共享库文件共享库优势减小可执行文件体积借助操作系统虚拟内存机制实现内存资源复用。9. 静态库生成与使用项目文件hello_fn.h、hello_fn.c、main.c# 编译生成目标文件gcc-Wallhello_fn.c-ohello_fn.o# 打包为静态库ar 为 GNU 归档工具ar rcs libhello.a hello_fn.o# 链接静态库生成可执行文件gcc-Wallmain.c libhello.a-omain# 通过 -L、-l 参数调用静态库gcc-Wall-L.main.c-omain-lhello10. 库文件检索优先级-I、-L参数指定的目录从左至右检索环境变量C_INCLUDE_PATH、LIBRARY_PATH指定的目录系统默认目录。11. 共享库生成与使用编译参数说明-shared指定生成共享库格式-fPIC生成位置无关代码适配共享库运行特性共享库命名规则libXXX.so。示例指令# 生成共享库gcc-shared-fPIChello.o-olibhello.so# 链接共享库gcc main.o-omain-L.-lhello12. 共享库运行配置程序运行时加载共享库可选择以下三种配置方式将.so文件复制至系统共享库目录/usr/lib配置环境变量LD_LIBRARY_PATH修改配置文件ld.so.conf执行ldconfig更新缓存。GCC 简介和命令行参数说明KuoGavin 于 2021-05-12 14:01:15 发布本文介绍 GCC 编译器基础用法、告警配置、库文件操作、调试配置、交叉编译并区分 GCC 与 G 的使用差异。链接阶段会完成数据段合并合并只读数据以节省内存与地址回填对应符号解析、地址重定位两大流程。内存布局说明栈区域向低地址扩展堆区域向高地址扩展共享库占用中间地址空间。1. GCC 基本用法调用规则与前文一致基础调用格式gcc [options] [filenames] \text{gcc [options] [filenames]}gcc [options] [filenames]参数说明-c仅编译不链接由.c源码生成.o目标文件多用于子程序编译-o output_filename自定义输出文件名称未配置时默认生成a.out-g生成gdb调试所需符号信息-O开启编译与链接优化提升程序运行效率增加编译耗时干扰调试工作-O2高阶代码优化编译、链接耗时进一步增加-Idirname新增头文件检索目录区分两类头文件引入规则-Ldirname新增库文件检索目录链接阶段优先检索该目录-lname链接库文件省略库文件前缀lib示例-lm对应libm.a/libm.so编译示例源码文件test.c#includestdio.hintmain(void){printf(Hello world\n);return0;}基础编译指令gcc test.c-otest分步编译指令# 预处理gcc-Etest.c-otest.i# 汇编gcc-Stest.c-otest.s# 编译生成目标文件gcc-ctest.c-otest.o# 链接gcc test.o-otest多文件库链接示例gcc testmain.c testsub.c-lm-otest大型工程建议搭配make工具实现增量编译。2. 告警提示配置项参数说明-v输出 GCC 完整执行流程--target-help展示支持的 CPU 架构-Q输出编译统计信息与函数名称-pedantic检测不符合 ANSI/ISO C 标准的代码并输出告警-Wall启用全量告警检测规则-Werror告警升级为错误触发告警则终止编译-Wcast-align检测指针地址对齐异常测试代码#includestdio.hvoidmain(void){longlongintvar1;printf(It is not standard C code!\n);}该代码存在多处语法规范问题对应不同参数的运行效果参考下图-pedantic参数运行效果-Wall参数运行效果-Werror参数运行效果告警信息可辅助排查隐性故障推荐常态化使用-Wall参数。3. 库文件操作配置项参数说明-I新增头文件检索目录-L新增库文件检索目录-l链接指定库文件省略前缀lib-static强制使用静态链接库-shared生成共享目标文件4. 调试配置项参数说明-g/-ggdb生成调试符号支持三级调试信息粒度划分-p/-pg植入性能剖析数据用于性能调优-save-temps保留编译中间文件调试参数会增大文件体积与运行开销仅用于开发调试阶段。5. 交叉编译配置项GCC 支持生成跨架构目标代码嵌入式开发场景常使用该功能具体参数不作展开。6. GCC 与 G 对比6.1 统一编译流程预处理生成.i文件编译转换为汇编代码生成.s文件汇编生成.o目标文件链接生成可执行文件。6.2 支持的文件后缀源码类.c、.CC 语言.cxx、.ccC 语言.mObjective-C.i预处理 C 文件.ii预处理 C 文件.s、.S汇编文件头文件.h目标/库文件.o目标文件.a库文件。6.3 常见认知误区纠正误区说明GCC 仅编译 C 代码G 仅编译 C 代码二者均可编译 C/C 代码。后缀为.c的文件GCC 按 C 语法解析G 按 C 语法解析后缀为.cpp的文件二者均按 C 语法解析。编译阶段 G 会调用 GCCC 项目通常使用 G 完成链接操作。GCC 不会定义__cplusplus宏G 一定会定义该宏用于标识代码解析语法。.c文件使用 GCC 编译时宏不生效其余场景宏生效。编译使用 GCC链接必须使用 G编译可任选 GCC / G链接可使用 G或使用gcc -lstdc。C 标准库不会由 GCC 自动链接因此 C 项目多选用 G 完成链接。链接器、链接过程及相关概念解析KuoGavin 于 2021-05-15 22:54:45 发布链接是将代码、数据片段整合为单一文件的过程生成的文件可加载至内存并运行。该流程由链接器执行按照执行时机分为三类编译时链接源代码转换为机器码阶段适用于静态库、动态库、可重定位目标文件加载时链接程序载入内存运行阶段适用于静态库、动态库、可重定位目标文件运行时链接程序运行过程中由应用主动触发仅适用于动态库。1. 编译器驱动程序编译器驱动程序会按需调用预处理器、编译器、汇编器、链接器。示例存在main.c、sum.c两个文件main.c调用sum.c内函数编译指令gcc-Og-oprog main.c sum.c源文件至可执行文件的编译流程静态链接流程生成可执行文件prog后执行指令./prog。Shell 识别该文件为可执行文件调用操作系统加载器完成程序运行。2. 目标文件目标文件分为三类可重定位目标文件包含二进制代码与数据可与其他同类文件合并Linux 后缀.o可执行目标文件包含二进制代码与数据可直接载入内存运行无固定后缀共享目标文件特殊可重定位文件支持加载时、运行时动态链接Linux 后缀.soWindows 后缀.dll。文件转换流程文本源文件 ⟶ ccl as 可重定位文件 ⟶ ld 可执行目标文件 \text{文本源文件} \stackrel{\text{ccl \ as}}{\longrightarrow} \text{可重定位文件} \stackrel{\text{ld}}{\longrightarrow} \text{可执行目标文件}文本源文件⟶ccl as​可重定位文件⟶ld​可执行目标文件目标模块为二进制字节序列目标文件是存储在磁盘中的目标模块单个目标文件可包含多个目标模块。主流系统文件格式Linux 与 Unixx86-64使用 ELF 格式Windows 使用 PE 格式MacOS 使用 Mach-O 格式。下文基于 ELF 格式展开说明。2.1 可重定位目标文件.o2.2 可执行目标文件ELF 可执行文件设计为适配内存加载程序头部表记录文件片段与内存段的映射关系。加载器将文件内容从磁盘复制至内存并跳转到程序入口地址执行该过程称为加载。Linux 程序可通过execve函数调用加载器。程序运行内存映像2.3 共享目标文件.so/.dll静态库存在维护成本高、内存占用大的问题共享库可解决上述问题。共享库又称共享目标文件支持多进程共用同一份库代码。共享库共享特性文件系统中单个库文件可被多个可执行文件引用内存中库的代码段仅保留一份副本供所有调用进程共用。共享库编译与调用生成共享库指令gcc-shared-fpic-olibvector.so addvec.c multvec.c参数说明-fpic生成位置无关代码是共享库的必备配置-shared指定输出为共享库格式。链接共享库生成可执行文件gcc-oprog21 main2.c ./libvector.so可执行文件运行流程加载器载入程序文件识别.interp节启动动态链接器动态链接器完成共享库重定位、符号引用解析移交程序控制权程序正常运行。运行时动态加载接口Linux 提供专用接口支持程序运行时加载共享库dlopen加载并链接指定共享库dlsym根据符号名称获取函数地址dlerror获取接口调用的错误信息。Java JNI 技术基于该套接口实现本地 C/C 函数调用。3. 链接器的核心任务3.1 符号解析符号分为函数、全局变量、静态变量三类。符号解析的作用是将每一处符号引用匹配到唯一的符号定义。符号与符号表每个可重定位目标文件包含符号表.symtab记录文件内定义与引用的符号局部非静态变量不记录在符号表中。目标模块内的符号分为三类全局定义符号本模块定义其他模块可引用对应非静态 C 函数、全局变量外部符号本模块引用其他模块定义局部符号仅本模块可见对应带static修饰的函数、全局变量。符号表包含节索引、符号类型等字段存在三类伪节标识ABS不可重定位符号UNDEF未定义符号COMMON未初始化全局变量。多重全局符号解析规则按照定义形式区分强符号、弱符号函数、已初始化全局变量为强符号未初始化全局变量为弱符号。解析规则不允许存在多个同名强符号出现则编译报错同名符号包含一个强符号与多个弱符号时选用强符号存在多个同名弱符号时随机选择其中一个。上述规则易引发隐性逻辑错误编写代码时需规避全局符号重定义。静态库符号解析静态库后缀.a通过ar工具打包生成链接器仅复制程序实际引用的目标模块。静态库创建指令ar rcs libvector.a addvec.o multvec.o链接指令gcc-static-oprog2c main2.o ./libvector.a gcc-static-oprog2c main2.o-L.-lvector链接器维护三个集合待合并目标文件集合E、未解析符号集合U、已定义符号集合D。按照命令行顺序扫描文件扫描目标文件将文件加入集合E同步更新U、D扫描静态库匹配U中的未解析符号将匹配成功的模块加入E更新U、D扫描结束后若U非空则链接报错。命令行中目标文件与库文件的顺序需要匹配依赖关系。3.2 重定位编译器、汇编器默认从地址 0 开始生成代码与数据。符号解析完成后链接器明确各代码段、数据段的大小重定位环节会为所有符号分配运行时内存地址并修改符号引用地址。重定位条目汇编器生成目标文件时针对外部符号、全局变量生成重定位条目指导链接器修改地址引用。代码段重定位条目存放于.rel.text数据段重定位条目存放于.rel.data。ELF 格式定义多种重定位类型两类基础类型R_X86_64_PC3232 位 PC 相对地址重定位R_X86_64_3232 位绝对地址重定位。重定位执行步骤节与符号重定位合并同类数据段、代码段为所有节、符号分配运行时内存地址引用重定位遍历所有重定位条目按照寻址类型修改符号引用地址指向最终内存位置。4. 总结链接分为编译时、加载时、运行时三种形式目标文件包含可重定位、可执行、共享三类符号解析、重定位是链接器的两项核心工作静态库会完整复制引用模块库文件与目标文件的顺序会影响链接结果动态库基于位置无关代码实现多进程共享支持加载时链接与运行时动态加载加载器负责将可执行文件载入内存动态链接器完成共享库的地址解析与绑定。referencegcc 的使用简介与命令行参数说明_gcc -l -share -fpic -g-CSDN 博客https://blog.csdn.net/delphiwcdj/article/details/6555073GCC 编译器详解-CSDN 博客https://blog.csdn.net/weixin_50697073/article/details/123759516gcc 简介和命令行参数说明_gcc -t-CSDN 博客https://blog.csdn.net/yueguangmuyu/article/details/116703618链接器、链接过程及相关概念解析-CSDN 博客https://blog.csdn.net/yueguangmuyu/article/details/116710102