跳到主要内容
GCC 编译系列:静态库工具 AR 详解 | 极客日志
C
GCC 编译系列:静态库工具 AR 详解 综述由AI生成 详细介绍 GCC 工具链中的归档工具 ar,阐述其创建、修改和提取静态库档案的功能。内容涵盖 ar 命令的基础操作指令、常用参数修饰符及插件选项,并通过 Makefile 示例展示静态库的自动化构建流程。此外,文章还分析了重新组合二进制文件时的符号可见性、ABI 兼容性及链接顺序等工程实践要点,旨在帮助开发者深入理解 Linux 静态链接机制。
赛博朋克 发布于 2026/4/6 更新于 2026/5/20 27 浏览1. AR 工具概述
1.1 背景介绍
GCC 中的 AR 命令全称是 Archive,是一个用于创建、修改和提取档案(archive)文件的工具。档案文件通常用于将多个目标文件打包成一个文件,以便于管理和分发。
在 GCC 工具链体系中,ar 并非编译器本身的一部分,而是 GNU Binutils 中的归档工具,其核心职责是将多个目标文件(通常是 .o 文件)按特定格式封装为一个归档文件,即静态库(.a)。这种归档文件本质上是一个简单的文件容器,内部仍然是标准的 ELF 目标文件结构,只是在文件头部增加了归档元信息,便于索引与管理。与链接阶段直接输入多个 .o 相比,使用归档文件能够简化依赖管理,使模块化构建更加清晰。
从构建流程来看,ar 通常出现在'编译 → 归档 → 链接'这一链条中。例如:
gcc -c foo.c bar.c
ar rcs libmylib.a foo.o bar.o
gcc main.c -L. -lmylib -o app
其中 r 表示替换或添加成员,c 表示创建新归档,s 用于生成符号索引。这个符号索引(symbol table)对链接器至关重要,它允许 ld 在解析未定义符号时快速定位所需的目标文件成员,而无需线性扫描整个归档。
在机制层面,静态库的'按需提取'特性是其关键优势。链接器在处理 .a 文件时,并不会整体展开库中的所有对象,而是仅将满足未解析符号引用的目标文件纳入最终可执行文件。这种策略避免了无谓的代码膨胀,同时也解释了链接顺序敏感的问题:若依赖库顺序错误,符号可能无法被正确解析。
在工程实践中,ar 常与 ranlib 配合使用(尽管现代 ar rcs 已自动生成索引),并可通过 ar t、ar x 等命令查看或提取归档内容。相比动态库 .so,静态库在链接阶段即被整合进最终产物,部署时不再依赖外部库文件,但代价是体积增大且无法在运行时共享代码段。因此,在嵌入式开发、单体部署场景中静态归档尤为常见,而在通用 Linux 发行环境中则更多依赖动态链接机制。
1.2 基础使用
以下是 AR 命令的一些常见用法:
(1)创建档案文件 :
ar rc libmylib.a file1.o file2.o file3.o
上述命令会创建一个名为 libmylib.a 的档案文件,并将 file1.o、file2.o 和 file3.o 三个目标文件打包进去。
(2)查看档案文件内容 :
ar t libmylib.a
该命令会列出 libmylib.a 档案文件中包含的所有目标文件。
(3)向档案文件中添加目标文件 :
ar r libmylib.a file4.o
该命令会将 file4.o 目标文件添加到 libmylib.a 档案文件中。如果档案文件不存在,则会创建一个新的档案文件。
(4)从档案文件中提取目标文件 :
ar x libmylib.a file2.o
该命令会从 libmylib.a 档案文件中提取出 file2.o 目标文件。
(5)删除档案文件中的目标文件 :
ar d libmylib.a file3.o
该命令会从 libmylib.a 档案文件中删除 目标文件。
file3.o
AR 命令还有其他一些选项和用法,可以通过 man ar 命令查看完整的文档。
1.3 档案 (archive) 在 GNU 工具链语境下,archive 本质上是一种面向链接阶段优化的容器格式,而不是通用压缩打包工具。其内部结构以全局头和若干成员头为基础,每个成员通常是标准 ELF 可重定位目标文件(relocatable object)。ar 在归档时会保留成员的权限位、时间戳及 UID/GID 等元数据,因此它不仅是逻辑聚合,更是一种'可还原'的二进制封装机制,这也是其被归类为二进制实用程序的原因。
真正影响链接性能的是符号索引(armap)。当使用 ar rcs libfoo.a *.o 时,s 选项会为归档生成一个符号到成员对象的映射表。链接器 ld 在解析未定义符号时,可通过该索引直接定位目标成员,而无需顺序扫描全部对象文件。若缺失索引,虽仍可链接,但会显著增加解析开销,尤其在大型静态库场景下。索引可通过 nm -s libfoo.a 查看,本质上是一个特殊成员。
薄档案(thin archive)则体现了构建系统优化思想。使用 ar rcT libfoo.a *.o 创建的 thin archive 并不复制对象内容,而仅保存对原始 .o 的路径引用,因此体积小、创建快,适合大型工程的中间构建产物。其'扁平'特性意味着嵌套添加时不会形成层级结构,而是直接展开成员。需要注意的是,成员路径以归档文件为基准保存,移动构建目录可能导致失效。
类型 内容存储 体积 可移植性 典型场景 普通 archive 拷贝对象文件 较大 强 发布静态库 thin archive 引用原始对象 极小 依赖路径 本地构建
在实践中,archive 机制与'按需提取'策略结合,使库内对象顺序不再影响符号解析;只要索引存在,成员间可自由互调。这种设计与 ELF 重定位模型协同工作,构成了 GCC 静态链接体系的基础。
2. 命令参数介绍 ubuntu->~:$ ar --help
Usage: ar [emulation options] [-]{dmpqrstx}[abcDfilMNoOPsSTuvV] [--plugin <name>] [member-name] [count] archive-file file...
ar -M [<mri-script]
commands:
d - delete file(s) from the archive
m[ab] - move file(s) in the archive
p - print file(s) found in the archive
q[f] - quick append file(s) to the archive
r[ab][f][u] - replace existing or insert new file(s) into the archive
s - act as ranlib
t[O][v] - display contents of the archive
x[o] - extract file(s) from the archive
command specific modifiers:
[a] - put file(s) after [member-name]
[b] - put file(s) before [member-name] (same as [i])
[D] - use zero for timestamps and uids/gids (default)
[U] - use actual timestamps and uids/gids
[N] - use instance [count] of name
[f] - truncate inserted file names
[P] - use full path names when matching
[o] - preserve original dates
[O] - display offsets of files in the archive
[u] - only replace files that are newer than current archive contents
generic modifiers:
[c] - do not warn if the library had to be created
[s] - create an archive index (cf. ranlib)
[l <text> ] - specify the dependencies of this library
[S] - do not build a symbol table
[T] - deprecated, use --thin instead
[v] - be verbose
[V] - display the version number
@<file> - read options from <file>
--target=BFDNAME - specify the target object format as BFDNAME
--output=DIRNAME - specify the output directory for extraction operations
--record-libdeps=<text> - specify the dependencies of this library
--thin - make a thin archive
optional: --plugin <p> - load the specified plugin
2.1 AR 操作指令 ar d libtest.a file1.o file2.o
该命令将从 libtest.a 归档文件中删除 file1.o 和 file2.o 文件。
(2)移动 (m[ab]) ,在归档文件中移动文件的位置。
ar mab libtest.a file1.o file2.o
该命令将 file1.o 和 file2.o 文件移动到归档文件的末尾(b 选项)或者开头(a 选项)。
(3)打印 (p) ,打印归档文件中指定文件的内容。
该命令将显示 libtest.a 归档文件中 file1.o 文件的内容。
(4)快速追加 (q[f]) ,将文件快速追加到归档文件的末尾。
ar q libtest.a file3.o file4.o
该命令将 file3.o 和 file4.o 文件追加到 libtest.a 归档文件的末尾。如果使用 f 选项,则即使归档文件不存在也会创建它。
(5)替换或插入 (r[ab][f][u]) ,替换归档文件中已有的文件,或插入新文件。
ar r libtest.a file1.o file5.o
该命令将用 file5.o 替换 libtest.a 归档文件中的 file1.o,如果 file5.o 不存在,则将其插入到归档文件中。选项 a 和 b 分别表示将文件插入到归档的开头或末尾,f 选项表示即使归档文件不存在也会创建它,u 选项表示只有当文件比归档中的同名文件更新时才替换。
(6)符号表 (s) ,类似于 ranlib 命令,用于创建或更新归档文件的符号表。
该命令将创建或更新 libtest.a 文件的符号表,加速对归档文件中符号的访问。
(7)内容列表 (t[O][v]) ,显示归档文件的内容列表。
该命令将显示 libtest.a 归档文件中的文件列表。v 选项提供详细输出,O 选项按照归档文件中的顺序显示文件列表。
(8)提取 (x[o]) ,从归档文件中提取指定的文件。
ar x libtest.a file1.o file2.o
该命令将从 libtest.a 归档文件中提取 file1.o 和 file2.o 文件。o 选项表示提取文件时保留原始的日期。
2.2 AR 通用命令修饰符 (1)不警告 ([c]) ,在创建归档文件时,如果归档文件不存在,AR 不会显示警告信息。这在脚本或自动化流程中很有用,可以避免不必要的警告输出。
ar cr libtest.a file1.o file2.o
(2)符号表索引 ([s]) ,在创建归档文件时,AR 会同时创建归档文件的符号表索引,类似于 ranlib 命令的功能。这样可以加速对归档文件中符号的访问,特别是在大型项目中使用归档文件作为库时非常有用。
ar rs libtest.a file1.o file2.o
(3)依赖关系 ([l]) ,指定当前库文件的依赖关系。这个选项可以在归档文件中记录其所依赖的其他库文件,方便管理复杂的库依赖关系。
ar rl "libdep1.a libdep2.a" libtest.a file1.o file2.o
(4)不生成符号表 ([S]) ,在创建归档文件时,AR 不会生成符号表。这可以减小归档文件的大小,但会影响对归档文件中符号的访问效率。
ar rS libtest.a file1.o file2.o
(5)详细输出 ([v]) ,在执行操作时,AR 会显示详细的信息,包括正在处理的文件名、操作结果等。这对于调试和理解 AR 的行为非常有帮助。
ar rvx libtest.a file1.o file2.o
(6)版本号 ([V]) ,显示 AR 的版本号。这个选项可以用于检查当前系统中 AR 的版本,以确保兼容性。
(7)读取选项文件 (@) ,从指定的文件中读取 AR 命令选项。这个功能可以将一组常用的 AR 选项存储在文件中,然后通过 @ 选项来引用,从而简化 AR 命令的编写。
(8)目标文件格式 (--target=BFDNAME) ,指定目标对象文件格式为 BFDNAME。这个选项可以让 AR 适应不同的目标平台和文件格式,提高其灵活性和可移植性。
ar --target=elf64-x86-64 r libtest.a file1.o file2.o
(9)提取输出目录 (--output=DIRNAME) ,指定提取操作的输出目录。这个选项可以将提取的文件放置在指定的目录中,而不是当前工作目录,方便管理提取出的文件。
ar --output=extracted_files x libtest.a
(10)记录依赖关系 (--record-libdeps=) ,指定当前库文件的依赖关系,与 [l] 选项类似。这个选项提供了另一种记录库依赖关系的方式,可以根据个人喜好选择使用。
ar --record-libdeps="libdep1.a libdep2.a" r libtest.a file1.o file2.o
(11)瘦归档文件 (--thin) ,创建瘦归档文件,即只存储文件的路径而不存储文件内容。这种归档文件可以大大减小归档文件的大小,特别适用于存储大量小文件的场景。但是,使用瘦归档文件时,需要确保原始文件在提取时可用。
ar --thin r libtest.a file1.o file2.o
2.3 plugin 选项 AR 的 --plugin 选项允许 AR 加载额外的插件,以支持更多的文件格式,包括包含链接时优化 (Link-Time Optimization, LTO) 信息的目标文件。这个功能可以显著扩展 AR 的应用范围和灵活性,特别是在使用 LTO 等高级编译优化技术时。
ar --plugin name [other options] [member...]
其中,name 是要加载的插件名称。例如,要加载名为 liblto_plugin.so 的插件,可以使用以下命令:
ar --plugin liblto_plugin.so r libtest.a file1.o file2.o
需要注意的是,--plugin 选项只在工具链启用了插件支持时可用。如果在构建工具链时没有启用插件支持,则无法使用该选项。
如果没有通过 --plugin 选项指定要加载的插件,但工具链启用了插件支持,那么 AR 会自动搜索 ${libdir}/bfd-plugins 目录下的插件文件。AR 会按照字母顺序遍历该目录下的文件,并使用第一个声明支持当前目标文件的插件。这种机制可以简化插件的管理和使用,无需每次都显式指定插件名称。
例如,假设 ${libdir}/bfd-plugins 目录下有以下插件文件:
liblto_plugin.so.0.0.0
my_custom_plugin.so
other_plugin.so
当使用 AR 操作包含 LTO 信息的目标文件时,如果没有通过 --plugin 选项指定插件名称,AR 会自动选择 liblto_plugin.so.0.0.0 插件,因为它在字母顺序上优先于其他插件。
需要特别注意的是,AR 的 --plugin 选项使用的插件搜索目录与 ld 的 -plugin 选项不同。为了让 AR 使用 ld 的插件,需要将插件文件复制到 ${libdir}/bfd-plugins 目录下。对于基于 GCC 的编译,ld 的插件文件通常名为 liblto_plugin.so.0.0.0,而基于 Clang 的编译则使用 LLVMgold.so。GCC 插件通常向后兼容早期版本,因此只需复制最新版本的插件文件即可。
3. 使用技巧
3.1 Makefile 创建静态库 在 Makefile 编译流程中,AR 工具通常用于创建和管理静态库文件 (.a 文件)。静态库是一组目标文件 (.o 文件) 的集合,可以在链接阶段被其他目标文件或可执行文件引用。通过将常用的函数、类等代码编译为静态库,可以提高代码的重用性、模块化和可维护性。
在 Makefile 中,AR 工具的作用主要体现在以下两个方面:
创建静态库 :将一组 .o 文件打包成一个 .a 静态库文件。
更新静态库 :向已有的 .a 静态库文件中添加、删除或替换 .o 文件。
以下是在 Makefile 中使用 AR 工具的典型实现形式:
LIBRARY = libmylib.a
OBJECTS = file1.o file2.o file3.o
CC = gcc
CFLAGS = -Wall -c
AR = ar
ARFLAGS = rcs
all: $(LIBRARY)
$(LIBRARY) : $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJECTS) $(LIBRARY)
LIBRARY 变量定义了要创建的静态库的名称,这里是 libmylib.a。
OBJECTS 变量定义了静态库所包含的目标文件,这里是 file1.o、file2.o 和 file3.o。
CC 和 CFLAGS 变量定义了编译器和编译选项,用于编译源代码文件生成目标文件。
AR 和 ARFLAGS 变量定义了 AR 工具和操作选项。ARFLAGS 中的 r 表示替换或添加目标文件,c 表示在必要时创建静态库,s 表示创建目标文件索引以加快访问速度。
all 目标是默认目标,依赖于 $(LIBRARY),表示创建静态库。
$(LIBRARY) 目标的规则描述了如何从目标文件 $(OBJECTS) 创建静态库。$@ 表示目标名称,即 $(LIBRARY),$^ 表示所有的依赖文件,即 $(OBJECTS)。
%.o: %.c 是一个隐含规则,描述了如何从 .c 源文件编译生成 .o 目标文件。
clean 目标用于清理生成的中间文件和静态库文件。
当在命令行中执行 make 命令时,Makefile 中的规则将被依次执行,最终生成静态库文件 libmylib.a。
3.2 重新组合二进制文件 从工程实践角度看,静态库本质上只是若干 ELF 目标文件的归档容器,ar 并不会改变 .o 的符号结构与重定位信息。因此通过 ar x 将特定目标文件解包,本质上是把原有编译单元重新暴露给链接阶段,这种做法在无法获取源代码但又需要局部替换实现时尤为常见。
需要注意,提取前可用 ar t libtest.a 或 nm libtest.a 查看成员及符号分布,避免误提取无关模块。
ar t libtest.a
nm -C libtest.a | grep target_symbol
ar x libtest.a file1.o file2.o
重新组合时,编译器会先将 file3.c、file4.c 编译为临时目标文件,再与已有的 file1.o、file2.o 一并交由链接器处理。因此符号可见性、重复定义以及 ABI 兼容性是关键风险点。
例如若新源文件中定义了与原库中同名的全局符号,链接阶段会产生 multiple definition 错误;若原库使用特定编译选项(如 -fPIC、-fno-exceptions、不同的 -D 宏),而新编译单元未保持一致,可能导致 ABI 不匹配甚至运行期崩溃。
gcc -c file3.c -O2 -fPIC
gcc -o newbinary file1.o file2.o file3.o file4.o
在更复杂场景下,例如需要'覆盖'库中某个实现,可选择不提取该 .o,而是自行编译一个同名符号的新目标文件,并通过链接顺序控制解析优先级。
GNU ld 采用从左到右的符号解析策略,因此目标文件应置于库之前。此外,若原静态库内部存在相互依赖关系,简单抽取部分 .o 可能破坏依赖闭环,需要结合 ldd、readelf -Ws 分析符号引用关系,确保重组后的链接图完整。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online