欢迎光临
我们一直在努力

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 语言头文件引入分为两种形式:

#include <stdio.h>
#include "stdio.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,代码内容如下:

    #include <stdio.h>
    int main(void)
    {
    printf("Hello world\\n");
    return 0;
    }

    生成可执行文件的基础指令:

    gcc test.c -o test

    完整编译流程分为三步:

  • 调用预处理程序 cpp,完成宏展开、头文件内容嵌入等预处理操作;
  • 调用编译程序 ccl 与汇编程序 as,将预处理后的源码转换为目标代码;
  • 调用链接程序 ld,将目标代码整合为可执行文件。
  • 默认配置下,预处理、编译、链接流程会一次性执行。

    1.10 分步执行编译流程

    GCC 支持在编译的四个阶段中断执行,对应参数如下:

  • -E:执行预处理后终止流程,生成后缀为 .i 的预处理文件;
  • -c:执行编译后终止流程,生成后缀为 .o 的目标文件;
  • -S:执行汇编后终止流程,生成后缀为 .s 的汇编文件。
  • 分步执行指令示例:

    # 预处理
    gcc -E test.c -o test.i
    # 编译生成目标文件
    gcc -c test.c -o test.o
    # 汇编生成汇编文件
    gcc -S test.c -o test.s
    # 链接生成可执行文件
    gcc test.o -o test

    1.11 多文件与库链接示例

    项目包含 testmain.c、testsub.c 两个源码文件,程序调用系统数学库,编译指令如下:

    gcc testmain.c testsub.c -lm -o test

    其中 -lm 用于链接系统数学库 libm.a。

    1.12 工程编译说明

    源码数量较多的工程,单次使用一条 GCC 指令完成全量编译会造成资源损耗。若仅修改单个文件,该方式会重复编译所有未变更文件。针对该场景,可搭配 make 工具完成增量编译。

    2. 告警提示配置项

    GCC 集成完备的错误检测与告警输出能力,可辅助开发人员规范代码编写。

    2.1 -pedantic

    编译不符合 ANSI/ISO C 语言标准的源码时,输出对应告警信息。

    #include <stdio.h>

    void main(void)
    {
    long long int var = 1;
    printf("It is not standard C code!/n");
    }

    在这里插入图片描述

    以违规代码为例,该参数可检测出以下问题:

  • main 函数返回值声明为 void,标准规范要求声明为 int;
  • 使用 long long 定义 64 位整数,属于 GNU 语法扩展,不符合 ANSI/ISO C 标准;
  • main 函数执行结束前未编写 return 语句。
  • 2.2 -Wall

    GCC 多数告警类参数以 -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 -l

    Linux 平台库文件命名遵循统一规则,文件名以 lib 作为前缀。使用 -l 参数时可省略该前缀,例如 -lfoo 会自动链接 libfoo.so 文件。

    3.4 -static

    Linux 库文件分为静态链接库(后缀 .a)与动态链接库(后缀 .so)。二者区别在于代码加载时机:静态库代码在编译阶段载入程序,动态库代码在程序运行阶段载入程序。

    GCC 默认优先使用动态链接库,动态库缺失时才会检索静态库。添加 -static 参数可强制使用静态链接库。

    3.5 -shared

    生成共享目标文件,该文件可与其他目标文件链接整合为可执行文件。

    4. 调试配置项

    gdb(GNU 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 -o test -save-temps

    执行后除可执行文件 test 外,还会保留预处理文件 test.i、汇编文件 test.s 等中间文件。

    5. 交叉编译配置项

    常规编译场景中,GCC 生成的目标代码适配当前运行主机的硬件架构。GCC 支持交叉编译,可生成适配其他 CPU 架构的目标代码。

    嵌入式系统开发常以 X86 架构 PC 作为宿主机,借助 GCC 交叉编译功能完成嵌入式设备的程序开发。本文不展开详述具体参数配置。


    GCC 的基本使用

    Balaaam 原创于 2022-03-26 17:18:23 发布

    1. GCC 概述

  • GCC(GNU Compiler Collection)由 Richard Stallman 开发,是 GNU 项目的核心工具之一;
  • 该工具最初为 C 语言编译器(GNU C Compiler),现阶段支持 C、C++、Java、Ada、COBOL 等多种编程语言;
  • GCC 具备跨平台特性,支持主流硬件架构,同时兼容 MMIX 等小众架构;
  • 软件采用模块化设计,可扩展新增编程语言与 CPU 架构支持;
  • GCC 属于自由软件。
  • 2. GCC 编译流程

    完整编译分为四个阶段:预处理、编译、汇编、链接。

    请添加图片描述

    C 语言源码为人类可读格式,运行前需要转换为机器指令,并按照可执行文件格式打包存储至磁盘。

    3. GCC 常用参数与示例

    请添加图片描述

    编译指令示例:

    # 预处理
    gcc -E hello.c -o hello.i
    # 编译生成汇编文件
    gcc -S hello.i -o hello.s
    # 汇编生成目标文件
    gcc -c hello.s -o hello.o
    # 链接生成可执行文件
    gcc hello.o -o hello
    # 一次性完成编译与链接
    gcc hello.c -o hello
    # 编译生成目标文件
    gcc -c hello.c
    gcc -c hello.c -o hello.o

    4. 多文件编译

    项目文件:hello_fn.h、hello_fn.c、main.c

    # 一次性编译链接
    gcc hello_fn.c main.c -o newhello

    # 分步独立编译
    gcc -Wall -c main.c -o main.o
    gcc -Wall -c hello_fn.c -o hello_fn.o
    gcc -Wall main.o hello_fn.o -o newhello

    5. 头文件与库文件作用

  • 头文件(.h):定义常量、声明系统函数与库函数调用接口;
  • 库文件:预编译完成的函数集合,由一组关联函数组成,用于实现通用功能。例如 ncurses 库用于界面绘制,dbm 库用于数据库访问。
  • 6. 系统默认检索路径

    头文件路径

    • /usr/include 及其子目录
    • /usr/local/include 及其子目录

    库文件路径

    • /usr/lib
    • /usr/local/lib

    7. 外部库调用示例

    源码文件 calc.c:

    #include <math.h>
    #include <stdio.h>

    int main()
    {
    double x = pow(2.0, 3.0);
    printf("The cubed if %f\\n", x);
    return 0;
    }

    编译指令:

    gcc -Wall calc.c -o calc -lm

    -lm 用于链接 libm.so 或 libm.a 数学库。

    8. 静态库与共享库

  • 静态库(后缀 .a):编译链接阶段,库代码完整嵌入可执行文件。程序运行时不再依赖原静态库文件;
  • 共享库(后缀 .so):仅在可执行文件中记录函数入口地址,库代码在程序运行时动态加载。多个程序可共用同一份共享库文件;
  • 共享库优势:减小可执行文件体积,借助操作系统虚拟内存机制,实现内存资源复用。
  • 9. 静态库生成与使用

    项目文件:hello_fn.h、hello_fn.c、main.c

    # 编译生成目标文件
    gcc -Wall hello_fn.c -o hello_fn.o
    # 打包为静态库,ar 为 GNU 归档工具
    ar rcs libhello.a hello_fn.o
    # 链接静态库生成可执行文件
    gcc -Wall main.c libhello.a -o main
    # 通过 -L、-l 参数调用静态库
    gcc -Wall -L . main.c -o main -lhello

    10. 库文件检索优先级

  • -I、-L 参数指定的目录(从左至右检索);
  • 环境变量 C_INCLUDE_PATH、LIBRARY_PATH 指定的目录;
  • 系统默认目录。
  • 11. 共享库生成与使用

    编译参数说明

  • -shared:指定生成共享库格式;
  • -fPIC:生成位置无关代码,适配共享库运行特性;
  • 共享库命名规则:libXXX.so。
  • 示例指令

    # 生成共享库
    gcc -shared -fPIC hello.o -o libhello.so
    # 链接共享库
    gcc main.o -o main -L . -lhello

    12. 共享库运行配置

    程序运行时加载共享库,可选择以下三种配置方式:

  • 将 .so 文件复制至系统共享库目录 /usr/lib;
  • 配置环境变量 LD_LIBRARY_PATH;
  • 修改配置文件 ld.so.conf,执行 ldconfig 更新缓存。

  • GCC 简介和命令行参数说明

    KuoGavin 于 2021-05-12 14:01:15 发布

    本文介绍 GCC 编译器基础用法、告警配置、库文件操作、调试配置、交叉编译,并区分 GCC 与 G++ 的使用差异。

    img

    链接阶段会完成数据段合并(合并只读数据以节省内存)与地址回填,对应符号解析、地址重定位两大流程。

    img

    内存布局说明:栈区域向低地址扩展,堆区域向高地址扩展,共享库占用中间地址空间。

    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:

    #include <stdio.h>
    int main(void)
    {
    printf("Hello world\\n");
    return 0;
    }

    基础编译指令:

    gcc test.c -o test

    分步编译指令:

    # 预处理
    gcc -E test.c -o test.i
    # 汇编
    gcc -S test.c -o test.s
    # 编译生成目标文件
    gcc -c test.c -o test.o
    # 链接
    gcc test.o -o test

    多文件+库链接示例:

    gcc testmain.c testsub.c -lm -o test

    大型工程建议搭配 make 工具实现增量编译。

    2. 告警提示配置项

    参数说明
    -v 输出 GCC 完整执行流程
    –target-help 展示支持的 CPU 架构
    -Q 输出编译统计信息与函数名称
    -pedantic 检测不符合 ANSI/ISO C 标准的代码并输出告警
    -Wall 启用全量告警检测规则
    -Werror 告警升级为错误,触发告警则终止编译
    -Wcast-align 检测指针地址对齐异常

    测试代码:

    #include <stdio.h>
    void main(void)
    {
    long long int var = 1;
    printf("It is not standard C code!\\n");
    }

    该代码存在多处语法规范问题,对应不同参数的运行效果参考下图:

    img -pedantic 参数运行效果

    img -Wall 参数运行效果

    img -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、.C(C 语言);.cxx、.cc(C++ 语言);.m(Objective-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++ 会调用 GCC,C++ 项目通常使用 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 -o prog main.c sum.c

    源文件至可执行文件的编译流程

    img 静态链接流程

    生成可执行文件 prog 后,执行指令 ./prog。Shell 识别该文件为可执行文件,调用操作系统加载器完成程序运行。

    2. 目标文件

    目标文件分为三类:

  • 可重定位目标文件:包含二进制代码与数据,可与其他同类文件合并,Linux 后缀 .o;
  • 可执行目标文件:包含二进制代码与数据,可直接载入内存运行,无固定后缀;
  • 共享目标文件:特殊可重定位文件,支持加载时、运行时动态链接,Linux 后缀 .so,Windows 后缀 .dll。
  • 文件转换流程:

    文本源文件

    ccl & as

    可重定位文件

    ld

    可执行目标文件

    \\text{文本源文件} \\stackrel{\\text{ccl \\& as}}{\\longrightarrow} \\text{可重定位文件} \\stackrel{\\text{ld}}{\\longrightarrow} \\text{可执行目标文件}

    文本源文件ccl & as可重定位文件ld可执行目标文件

    目标模块为二进制字节序列,目标文件是存储在磁盘中的目标模块,单个目标文件可包含多个目标模块。

    主流系统文件格式:Linux 与 Unix(x86-64)使用 ELF 格式,Windows 使用 PE 格式,MacOS 使用 Mach-O 格式。下文基于 ELF 格式展开说明。

    2.1 可重定位目标文件(.o)

    2.2 可执行目标文件

    ELF 可执行文件设计为适配内存加载,程序头部表记录文件片段与内存段的映射关系。

    加载器将文件内容从磁盘复制至内存,并跳转到程序入口地址执行,该过程称为加载。Linux 程序可通过 execve 函数调用加载器。

    程序运行内存映像

    2.3 共享目标文件(.so / .dll)

    静态库存在维护成本高、内存占用大的问题,共享库可解决上述问题。共享库又称共享目标文件,支持多进程共用同一份库代码。

    共享库共享特性:

  • 文件系统中,单个库文件可被多个可执行文件引用;
  • 内存中,库的代码段仅保留一份副本,供所有调用进程共用。
  • 共享库编译与调用

    生成共享库指令:

    gcc -shared -fpic -o libvector.so addvec.c multvec.c

    参数说明:

    • -fpic:生成位置无关代码,是共享库的必备配置;
    • -shared:指定输出为共享库格式。

    链接共享库生成可执行文件:

    gcc -o prog21 main2.c ./libvector.so

    img

    可执行文件运行流程:

  • 加载器载入程序文件,识别 .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 -o prog2c main2.o ./libvector.a
    gcc -static -o prog2c 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_PC32:32 位 PC 相对地址重定位;
  • R_X86_64_32:32 位绝对地址重定位。
  • 重定位执行步骤
  • 节与符号重定位:合并同类数据段、代码段,为所有节、符号分配运行时内存地址;
  • 引用重定位:遍历所有重定位条目,按照寻址类型修改符号引用地址,指向最终内存位置。
  • 4. 总结

  • 链接分为编译时、加载时、运行时三种形式,目标文件包含可重定位、可执行、共享三类;
  • 符号解析、重定位是链接器的两项核心工作;
  • 静态库会完整复制引用模块,库文件与目标文件的顺序会影响链接结果;
  • 动态库基于位置无关代码实现多进程共享,支持加载时链接与运行时动态加载;
  • 加载器负责将可执行文件载入内存,动态链接器完成共享库的地址解析与绑定。

  • reference

    • gcc 的使用简介与命令行参数说明_gcc -l -share -fpic -g-CSDN 博客 https://blog.csdn.net/delphiwcdj/article/details/6555073
    • GCC 编译器详解-CSDN 博客 https://blog.csdn.net/weixin_50697073/article/details/123759516
    • gcc 简介和命令行参数说明_gcc -t-CSDN 博客 https://blog.csdn.net/yueguangmuyu/article/details/116703618
    • 链接器、链接过程及相关概念解析-CSDN 博客 https://blog.csdn.net/yueguangmuyu/article/details/116710102
    赞(0)
    未经允许不得转载:171主机测评 » GCC 编译 | 命令参数、编译流程与链接机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址