GCC编译(6)静态库工具AR

GCC编译(6)静态库工具AR
GCC编译(6)静态库工具AR


Author: Once Day Date: 2026年2月20日



一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…



漫漫长路,有人对你微笑过嘛…



全系列文章可参考专栏:
编译构建工具链_Once-Day的博客-ZEEKLOG博客



参考文章:ar(1) - Linux manual page【Linux】ar命令:用于创建、修改和提取静态库(archive)-ZEEKLOG博客Linux命令学习手册-ar - 知乎Linux ar命令介绍 和常用示例 - Link_Z - 博客园

文章目录

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 tar x 等命令查看或提取归档内容。相比动态库 .so,静态库在链接阶段即被整合进最终产物,部署时不再依赖外部库文件,但代价是体积增大且无法在运行时共享代码段。因此,在嵌入式开发、单体部署场景中静态归档尤为常见,而在通用 Linux 发行环境中则更多依赖动态链接机制。

1.2 基础使用

以下是AR命令的一些常见用法:

(1)创建档案文件

ar rc libmylib.a file1.o file2.o file3.o 

上述命令会创建一个名为libmylib.a的档案文件,并将file1.ofile2.ofile3.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. 命令参数介绍

AR的命令帮助信息如下:

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的常用操作指令说明:

(1)删除(d),从归档文件中删除指定的文件。

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)打印§,打印归档文件中指定文件的内容。

ar p libtest.a file1.o 

该命令将显示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命令,用于创建或更新归档文件的符号表。

ar s libtest.a 

该命令将创建或更新libtest.a归档文件的符号表,加速对归档文件中符号的访问。

(7)内容列表(t[O][v]),显示归档文件的内容列表。

ar tv libtest.a 

该命令将显示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 通用命令修饰符

下面是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的版本,以确保兼容性。

ar V 

(7)读取选项文件(@),从指定的文件中读取AR命令选项。这个功能可以将一组常用的AR选项存储在文件中,然后通过@选项来引用,从而简化AR命令的编写。

ar @options.txt 

(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等高级编译优化技术时。

使用--plugin选项的基本语法如下:

 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工具的作用主要体现在以下两个方面:

  1. 创建静态库:将一组.o文件打包成一个.a静态库文件。
  2. 更新静态库:向已有的.a静态库文件中添加、删除或替换.o文件。

以下是在Makefile中使用AR工具的典型实现形式:

# 定义静态库的名称 LIBRARY = libmylib.a # 定义静态库所包含的目标文件 OBJECTS = file1.o file2.o file3.o # 定义编译器和编译选项 CC = gcc CFLAGS = -Wall -c # 定义AR工具和操作选项 AR = ar ARFLAGS = rcs # 默认的目标:创建静态库 all: $(LIBRARY) # 创建静态库的规则 $(LIBRARY): $(OBJECTS) $(AR) $(ARFLAGS) $@ $^ # 编译目标文件的规则 %.o: %.c $(CC) $(CFLAGS) $< -o $@ # 清理生成的文件 clean: rm -f $(OBJECTS) $(LIBRARY) 

在上面的Makefile示例中:

  • LIBRARY变量定义了要创建的静态库的名称,这里是libmylib.a
  • OBJECTS变量定义了静态库所包含的目标文件,这里是file1.ofile2.ofile3.o
  • CCCFLAGS变量定义了编译器和编译选项,用于编译源代码文件生成目标文件。
  • ARARFLAGS变量定义了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.anm libtest.a 查看成员及符号分布,避免误提取无关模块。

ar t libtest.a nm -C libtest.a |grep target_symbol ar x libtest.a file1.o file2.o 

重新组合时,编译器会先将 file3.cfile4.c 编译为临时目标文件,再与已有的 file1.ofile2.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 可能破坏依赖闭环,需要结合 lddreadelf -Ws 分析符号引用关系,确保重组后的链接图完整。






Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注!

(。◕‿◕。)感谢您的阅读与支持~~~

Read more

Java智能客服系统实战:基于Spring Boot与NLP的高效实现方案

最近在做一个智能客服系统的项目,之前也调研过不少方案,发现传统的客服系统确实有不少痛点。今天就来分享一下我们团队基于 Spring Boot 和 NLP 技术,从零搭建一套高效智能客服系统的实战经验,希望能给有类似需求的同学一些参考。 1. 为什么需要智能客服?传统方案的痛点 在项目初期,我们维护的是一个基于规则引擎的客服系统。它的工作原理很简单:预先设定好一堆“关键词-回复”的匹配规则。用户提问时,系统就去遍历这些规则,找到匹配度最高的那条,然后给出预设的回复。 这套系统初期跑起来还行,但随着业务发展,问题越来越明显: 1. 规则爆炸,维护噩梦:每增加一个业务场景,就要手动添加一堆规则。比如“怎么退货”、“我要退款”、“退货流程是什么”,本质上是一个意图,却需要写三条甚至更多规则。规则库越来越臃肿,维护成本指数级上升。 2. 缺乏语义理解,死板僵硬:规则引擎只能做字面匹配。用户说“这个玩意我不想要了,能退吗?”,如果规则里只写了“退货”,很可能就匹配不上,

By Ne0inhk
【MySQL】第八节—表的增删改查,吃透这篇就够了(下)

【MySQL】第八节—表的增删改查,吃透这篇就够了(下)

Hi,我是云边有个稻草人-ZEEKLOG博客个人主页,今天结束表的增删改查,继续! 《MySQL》本篇文章所属专栏—持续更新中!   目录 三、Update 3.1【将孙悟空同学的数学成绩变更为 80 分】 3.2【将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分】 3.3【将总成绩倒数前三的 3 位同学的数学成绩加上 30 分】 3.4【将所有同学的语文成绩更新为原来的 2 倍】 四、Delete 4.1 删除数据 【删除孙悟空同学的考试成绩】 【删除总分倒数第一的同学信息】 【删除整张表数据】 4.2 截断表 五、去重数据表,插入查询结果 六、

By Ne0inhk

Spring AI

目录 基本概念 什么是 AI 模型(Model) 大语言模型  (LLM) 提示词 (Prompt) 词元(Token) Spring AI 是什么 快速入门 环境要求 申请 API Key 项目创建 接口编写 核心接口 ChatModel  ChatClient 消息类型 SystemMessage UserMessage AssistantMessage 输出格式 结构化输出 流式输出 SSE 协议介绍 SSE 数据格式 data event id retry SSE 使用示例 Flux Advisors 基本概念 什么是 AI AI:也就是 人工智能(

By Ne0inhk
使用 VS Code 连接 MySQL 数据库

使用 VS Code 连接 MySQL 数据库

文章目录 * 前言 * VS Code下载安装 * 如何在VS Code上连接MySQL数据库 * 1、打开扩展 * 2、安装MySQL插件 * 3、连接 * 导入和导出表结构和数据 前言 提示:这里可以添加本文要记录的大概内容: 听说VS Code不要钱,功能还和 Navicat 差不多,还能在上面打游戏 但是没安装插件是不行的 发现一个非常牛的博主 还有一个非常牛的大佬 提示:以下是本篇文章正文内容,下面案例可供参考 VS Code下载安装 VS Code下载安装 如何在VS Code上连接MySQL数据库 本篇分享是在已有VS Code这个软件的基础上,数据库举的例子是MySQL 1、打开扩展 2、安装MySQL插件 在搜索框搜索 MySQL和 MySQL Syntax,下载这三个插件 点击下面的插件,选择【install】安装

By Ne0inhk