ar 工具及 ar 库格式研究


author: hjjdebug

date: 2026年 07月 12日 星期日 11:44:14 CST

descrip: ar 工具及 ar 库格式研究.


文章目录

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

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

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

  1. 准备2个文件test.c, test1.c
cpp 复制代码
$ cat test.c
void func()
{
}
$ cat test1.c
void func1()
{
}
  1. 生成库文件, 一个带索引(默认带索引),一个不带索引,如下
cpp 复制代码
$ 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 <库文件> 查看, 看到了区别, 如下:
cpp 复制代码
$ nm -s libtest.a

归档索引:
func in test.o
func1 in test1.o

test.o:
0000000000000000 T func

test1.o:
0000000000000000 T func1
$ nm -s libtest1.a

test.o:
0000000000000000 T func

test1.o:
0000000000000000 T func1

查帮助 nm -s 就是 打印armap 的意思, 就是打印文档的索引符号.

但我感觉还是对索引符号的概念不太清楚.

要想深刻的理解索引符号,需要来一段代码,并认真研读.

索引符号是函数名称与文件偏移构成的表.

这样当你知道函数名称时,通过这个表就知道在库中的偏移,为gcc 编译连接提供了信息.

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

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

cpp 复制代码
$ cat ar_parse.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define ARMAG     "!<arch>\n"
#define ARFMAG    "`\n"

struct ar_hdr {
    char ar_name[16];
    char ar_date[12];
    char ar_uid[6];
    char ar_gid[6];
    char ar_mode[8];
    char ar_size[10];
    char ar_fmag[2];
};

static void trim_space(char *buf, int len)
{
    for (int i = len - 1; i >= 0; i--)
    {
        if (buf[i] == ' ')
            buf[i] = '\0';
        else
            break;
    }
}

// 大端4字节转本机long
static unsigned long be32_to_host(const unsigned char *p)
{
    return ((unsigned long)p[0] << 24) |
           ((unsigned long)p[1] << 16) |
           ((unsigned long)p[2] << 8)  |
           (unsigned long)p[3];
}

// 解析GNU "/"符号索引
static void parse_gnu_armap(FILE *fp, long map_data_start, long map_sz)
{
    unsigned char buf[4];
    fseek(fp, map_data_start, SEEK_SET);

    // 读取符号总数N(大端)
    if (fread(buf, 1, 4, fp) != 4)
        return;
    unsigned int sym_cnt = be32_to_host(buf);
    printf("==== GNU AR符号索引(/)解析开始 ====\n");
    printf("符号总数: %u\n", sym_cnt);

    // 读取N个成员偏移
    unsigned long *offsets = malloc(sym_cnt * sizeof(unsigned long));
    for (unsigned i = 0; i < sym_cnt; i++)
    {
        fread(buf, 1, 4, fp);
        offsets[i] = be32_to_host(buf); //读取文件偏移
    }

    // 读取符号名串表
    char symname[1024];
    unsigned str_off = 0;
    for (unsigned i = 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");
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("usage: %s libtest.a\n", argv[0]);
        return 1;
    }

    FILE *fp = fopen(argv[1], "rb");
    if (!fp)
    {
        perror("fopen");
        return 1;
    }

	//magic 固定长度为8 "!<arch>."
    char magic[8];
    size_t rd = fread(magic, 1, 8, fp);
    if (rd != 8 || memcmp(magic, ARMAG, 8) != 0)
    {
        printf("非合法AR归档文件\n");
        fclose(fp);
        return 1;
    }
    printf("AR全局魔数校验通过: !<arch>\\n\n");

    long cur_file_offset = 8;
    struct ar_hdr hdr;

    while (1)
    {
        rd = fread(&hdr, 1, sizeof(struct ar_hdr), fp);
        if (rd != sizeof(struct ar_hdr))
            break;

        if (memcmp(hdr.ar_fmag, ARFMAG, 2) != 0)
        {
            printf("成员头archive file magic校验失败,终止解析\n");
            break;
        }

        char name[17] = {0};
        memcpy(name, hdr.ar_name, 16);
        trim_space(name, 16);

        char size_str[11] = {0};
        memcpy(size_str, hdr.ar_size, 10);
        trim_space(size_str, 10);
        long data_sz = atol(size_str);
        long hdr_sz = sizeof(struct ar_hdr);
        long file_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字节对齐补齐
        long skip = 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);
    return 0;
}

2.1 实验结果

cpp 复制代码
$ ./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文件打包的格式也有了认识, 不仅知其然,而其知其所以然.

相关推荐
YH行业报告分析5 小时前
2026 HOE光聚合物材料产业化窗口:RGB多色薄膜如何打通AR光波导量产闭环?
ar
YH行业报告分析6 小时前
2026 HOE材料产业化提速:RGB多色光聚合物如何驱动AR光波导与HUD光学升级?
ar
各类产品分享5 天前
工厂 AR 巡检系统架构设计与落地难点解析
系统架构·ar·ar巡检
虹科数字化与AR6 天前
安宝特AR电力解决方案:硬件、三大系统、部署架构与应用场景
经验分享·ar
各类产品分享7 天前
工业现场 AR 巡检系统架构设计与落地实践
系统架构·ar·ar巡检
各类产品分享8 天前
AR 辅助巡检系统落地:技术架构、工作流设计与实施局限
架构·ar·ar巡检
骑着蜗牛撵大象3279 天前
链路层不只是 MAC:以太网帧、ARP 与 802.1Q VLAN 的逐跳拆解
计算机网络·macos·ar·vlan·mac地址·链路层
各类产品分享19 天前
汽车总装线 AR 巡检方案:从二维码触发到远程协作的技术落地
汽车·ar·ar巡检
视+老张9 天前
商场AR导航不是画一条线:长泰广场如何把“找店”拆成眼前的下一步
ar·室内导航·easyar·商场导航
各类产品分享110 天前
工业现场 AR 巡检系统的技术架构与落地难点解析
架构·ar·ar巡检