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

相关推荐
船漏了就会沉1 天前
AprilTag:让机器“看见”世界坐标的视觉路标
ar·无人机
小刘数字化3 天前
告别爬塔危险:AR眼镜如何重构电力高空巡检安全标准
安全·重构·ar
北京阿法龙科技有限公司3 天前
AR智能眼镜安防应用核心指标:识别距离筑牢防线
java·开发语言·ar
私人珍藏库4 天前
[Android] PeakFinder AR v4.8.89 (山峰全景识别+增强现实山峰查看器)
android·人工智能·智能手机·ar·工具·软件
Godspeed Zhao5 天前
跨越天际:从智能汽车到 eVTOL 的适航与系统级开发37——增强现实平显(AR-HUD)与合成视觉系统(SVS)在低能见度进近中的应用
汽车·ar·evtol
粤科源兴9 天前
LM5164DDAR是什么芯片?产品特点、应用及采购指南
ar
ar01231 个月前
AR远程协助产品:重塑工业服务的未来
人工智能·ar
世界很奇妙塔1 个月前
AR/VR产业去泡沫后:从消费端娱乐,走向产业端全域赋能
ar·vr·娱乐
ar01231 个月前
工业智能化时代的AR巡检力量
人工智能·ar