当前位置: 首页 > news >正文

ar 工具及 ar 库格式研究


author: hjjdebug
date: 2026年 07月 12日 星期日 11:44:14 CST
descrip: ar 工具及 ar 库格式研究.


文章目录

  • 1. 用ar 工具及 nm 工具来了解 ar 库文件
  • 2. 用自编简单工具来了解ar 库文件
    • 2.1 实验结果

ar 是文档工具, 与tar, cpio 有类似的性质,但主要用在将.o文件打包成.a文件,
同时还会生成符号索引.
我一直对这个所谓的符号索引不知所云, 所以决定花点时间认真研究一下:

1. 用ar 工具及 nm 工具来了解 ar 库文件

  1. 准备2个文件test.c, test1.c
$ cat test.cvoidfunc(){}$ cat test1.cvoidfunc1(){}
  1. 生成库文件, 一个带索引(默认带索引),一个不带索引,如下
$ gcc-c-o test.o test.c $ gcc-c-o test1.o test1.c $ ar rc libtest.a test.o test1.o $ ar rcS libtest1.a test.o test1.o//不带索引
  1. 查看区别.
    $ ar tv <库文件> 命令查看, 看不到区别
    $ nm <库文件> 命令查看, 也看不到区别
    $ nm -s <库文件> 查看, 看到了区别, 如下:
$ nm-s libtest.a 归档索引: func in test.o func1 in test1.o test.o:0000000000000000T func test1.o:0000000000000000T func1 $ nm-s libtest1.a test.o:0000000000000000T func test1.o:0000000000000000T func1

查帮助 nm -s 就是 打印armap 的意思, 就是打印文档的索引符号.
但我感觉还是对索引符号的概念不太清楚.
要想深刻的理解索引符号,需要来一段代码,并认真研读.
索引符号是函数名称与文件偏移构成的表.
这样当你知道函数名称时,通过这个表就知道在库中的偏移,为gcc 编译连接提供了信息.

2. 用自编简单工具来了解ar 库文件

下面为是对ar 库的解析文件, 很短,读懂此文件即了解了.a文件的构成.

$ cat ar_parse.c#include<stdio.h>#include<stdlib.h>#include<string.h>#include<ctype.h>#defineARMAG"!<arch>\n"#defineARFMAG"`\n"structar_hdr{charar_name[16];charar_date[12];charar_uid[6];charar_gid[6];charar_mode[8];charar_size[10];charar_fmag[2];};staticvoidtrim_space(char*buf,intlen){for(inti=len-1;i>=0;i--){if(buf[i]==' ')buf[i]='\0';elsebreak;}}// 大端4字节转本机longstaticunsignedlongbe32_to_host(constunsignedchar*p){return((unsignedlong)p[0]<<24)|((unsignedlong)p[1]<<16)|((unsignedlong)p[2]<<8)|(unsignedlong)p[3];}// 解析GNU "/"符号索引staticvoidparse_gnu_armap(FILE*fp,longmap_data_start,longmap_sz){unsignedcharbuf[4];fseek(fp,map_data_start,SEEK_SET);// 读取符号总数N(大端)if(fread(buf,1,4,fp)!=4)return;unsignedintsym_cnt=be32_to_host(buf);printf("==== GNU AR符号索引(/)解析开始 ====\n");printf("符号总数: %u\n",sym_cnt);// 读取N个成员偏移unsignedlong*offsets=malloc(sym_cnt*sizeof(unsignedlong));for(unsignedi=0;i<sym_cnt;i++){fread(buf,1,4,fp);offsets[i]=be32_to_host(buf);//读取文件偏移}// 读取符号名串表charsymname[1024];unsignedstr_off=0;for(unsignedi=0;i<sym_cnt;i++)//读取文件名称, 符号名即是文件名称{fseek(fp,map_data_start+4+sym_cnt*4+str_off,SEEK_SET);fgets(symname,sizeof(symname),fp);size_t len=strlen(symname);if(len&&symname[len-1]=='\0')len--;str_off+=len+1;printf("符号: %-12s -> 归档成员偏移: 0x%lx\n",symname,offsets[i]);}free(offsets);printf("==== 符号索引解析结束 ====\n\n");}intmain(intargc,char**argv){if(argc!=2){printf("usage: %s libtest.a\n",argv[0]);return1;}FILE*fp=fopen(argv[1],"rb");if(!fp){perror("fopen");return1;}//magic 固定长度为8 "!<arch>."charmagic[8];size_t rd=fread(magic,1,8,fp);if(rd!=8||memcmp(magic,ARMAG,8)!=0){printf("非合法AR归档文件\n");fclose(fp);return1;}printf("AR全局魔数校验通过: !<arch>\\n\n");longcur_file_offset=8;structar_hdrhdr;while(1){rd=fread(&hdr,1,sizeof(structar_hdr),fp);if(rd!=sizeof(structar_hdr))break;if(memcmp(hdr.ar_fmag,ARFMAG,2)!=0){printf("成员头archive file magic校验失败,终止解析\n");break;}charname[17]={0};memcpy(name,hdr.ar_name,16);trim_space(name,16);charsize_str[11]={0};memcpy(size_str,hdr.ar_size,10);trim_space(size_str,10);longdata_sz=atol(size_str);longhdr_sz=sizeof(structar_hdr);longfile_data_start=cur_file_offset+hdr_sz;printf("当前归档全局偏移: %ld\n",cur_file_offset);printf("成员名称:[%s]\n",name);printf("数据长度:%ld字节\n",data_sz);// 命中GNU符号索引成员 /if(strcmp(name,"/")==0){parse_gnu_armap(fp,file_data_start,data_sz);fseek(fp,file_data_start,SEEK_SET);//恢复文件指针}// 2字节对齐补齐longskip=data_sz;if(skip&1)skip++;cur_file_offset+=hdr_sz+skip;//跳到下一个文件头fseek(fp,skip,SEEK_CUR);}printf("归档完整解析完毕,当前归档全局偏移:%ld\n",cur_file_offset);fclose(fp);return0;}

2.1 实验结果

$./ar_parse libtest.a AR全局魔数校验通过:!<arch>\n 当前归档全局偏移:8成员名称:[/]数据长度:24字节====GNU AR符号索引(/)解析开始====符号总数:2符号:func->归档成员偏移:0x5c符号:func1->归档成员偏移:0x560====符号索引解析结束====当前归档全局偏移:92成员名称:[test.o/]数据长度:1224字节 当前归档全局偏移:1376成员名称:[test1.o/]数据长度:1224字节 归档完整解析完毕,当前归档全局偏移:2660

配合nm -s libtest.a, 再配合010editor 查看二进制文件, 可对它的符号索引区有清晰的认识
对ar文件打包的格式也有了认识, 不仅知其然,而其知其所以然.

http://www.jsqmd.com/news/1178686/

相关文章:

  • 想搭私有 Wiki 和知识库?Tiki Wiki Docker Compose 部署实测
  • 2026最新泰州本地漏水检测公司本地精选权威推荐:正规防水补漏公司优选口碑TOP5:卫生间/厨房/阳台/飘窗/地下室渗漏水维修师傅上门 - 即刻修防水
  • StreamCap:如何用开源工具解决40+平台直播录制难题?
  • MP2672A双节锂电池充电管理IC设计与应用
  • numpy.std默认ddof=0的陷阱:样本标准差误用导致模型偏差
  • ANSYS Fluent三维流体仿真:从网格划分到激光熔池实战指南
  • 深入解析C++循环结构:while与do...while的本质差异与应用场景
  • Altium Designer 23 最小系统 PCB 设计:从原理图到布局的 5 个关键步骤
  • 基于MCP3202与PIC18F4685的锂电池电压平衡系统设计
  • YimMenu完整指南:免费GTA5增强工具快速上手教程
  • VSCode 1.127.0 命令行启动与工作区配置:3种高效开发环境搭建实战
  • AD74412R与PIC18LF24K50在工业控制中的高效信号采集方案
  • 深度解析:SMAPI在macOS平台上的跨平台模组加载架构实现方案
  • 靠谱的管壳式换热器品牌有哪些?适配工业冷却场景的推荐 - myqiye
  • 劳力士中国官方售后服务中心|地址与联系电话权威信息公示(2026年7月最新) - 劳力士服务中心
  • 2026 年至今,开鲁靠谱的执法服装供应商选型指南,穿了这身衣服,他竟敢直呼局长姓名? - 行业推荐【认证官】
  • C++性能调优实战:30分钟定位瓶颈与五大场景优化
  • Lua-Protobuf 深度解析:类型加载、int64处理与性能优化实战
  • 5分钟快速上手:MediaCrawler多平台数据采集完整指南
  • MATLAB深度学习实战:从CNN到GAN的完整工作流与模型部署
  • 原来专业音响系统公司背后还有这么多大学问?搞懂了吗?
  • QQ音乐加密文件转换指南:实现个人音乐库跨平台自由播放
  • Tailwind CSS v4.3 从入门到实战Vite 安装、响应式布局、暗黑模式与主题配置
  • 玖杰汽车制造性价比怎么样 - myqiye
  • 毕业必备!2026AI论文工具大盘点(覆盖 99% 毕业生论文需求)
  • 2026最新洛阳本地漏水检测公司本地精选权威推荐:正规防水补漏公司优选口碑TOP5:卫生间/厨房/阳台/飘窗/地下室渗漏水维修师傅上门 - 即刻修防水
  • GAT vs GCN vs GraphSAGE:3 大图神经网络核心算子效率与效果对比
  • 2026年7月最新厦门劳力士官方售后客户服务电话及线下网点地址 - 劳力士官方服务中心
  • DevToys工具开发中的XSS防御:安全输入处理实战指南
  • 《雷暴区域》影评:极端环境悬疑中的人性与技术叙事