目录

1.知识回顾

2.archives是什么

3.ar命令打包非链接文件

ar命令特别说明

准备工作

十六进制分析test.a

归档文件头(魔数)

文件内容

元数据

文件名(char ar_name[16])

时间戳(char ar_date[12])

UID(char ar_uid[6])

GID(char ar_gid[6])

拥有者、所属组、其他人的八进制权限(char ar_mode[8])

文件大小(char ar_size[10])

ar_fmag

所有元数据一览


1.知识回顾

之前在OS39.【Linux】动态库和静态库 自制静态库文章提到过使用ar -rc命令生成静态库,即将所有o文件的集合(不含main函数的源文件生成的o文件)打包(ar命令)为静态库

其实ar命令不仅仅能打包o文件,准确来说,ar命令的作用是"create, modify, and extract from archives",即创建、修改、解压归档文件

博主比较好奇,就研究了一下ar归档文件的格式

2.archives是什么

百度翻译对archive的解释:

man手册对archives的描述:

The GNU ar program creates, modifies, and extracts from archives.  An archive is a single file holding a collection of other files in a structure that makes it possible to retrieve the original individual files (called members of the archive).

archives定义:

1.归档是一个单一文件,以一种结构包含一组其他文件

2.这种结构可以检索出(retrieve)原始各个文件(称为归档成员文件)

man手册对文件内容的描述:

       The original files' contents, mode (permissions), timestamp, owner, and group are preserved in the archive, and can be restored on extraction.

可以看出"归档是一个单一文件,以一种结构包含一组其他文件"中的结构还含有其他内容,例如模式(权限),时间戳,所有者,所属组,还有文件的原始内容,说明: 归档文件中含有的其他文件没有经过压缩

3.ar命令打包非链接文件

从以上分析可以看出ar命令不仅仅可以打包链接文件,还可以打包非链接文件

ar命令特别说明

ar命令不是从Linux才有的,其实早在Unix就已经出现了ar命令\,可以参考Unix早期版本的源代码:

Unix版本树:

https://www.tuhs.org/cgi-bin/utree.pl

Unix V10源代码:

https://www.tuhs.org/cgi-bin/utree.pl?file=V10

准备工作

现将3个文件打包为一个归档文件

文件1: test.txt

teststring

文件2: test.bin

四字节的十六进制数据

0xAA 0xBB 0xCC 0xDD
printf '\xAA\xBB\xCC\xDD' > test.bin

文件3: test.c

int main()
{
    return 0;
}

修改test.bin的拥有者为root:

sudo chown root test.bin

test.bin添加可执行权限:

sudo chmod a+x test.bin

修改test.txt的所属组为root:

sudo chgrp root test.txt

打包为test.a:

ar -rc test.a test.txt test.c test.bin

使用file命令分析test.a:

test.a是一个ar命令生成的归档文件

十六进制分析test.a

十六进制编辑器打开test.a,这里使用Windows下的HxD:

归档文件头(魔数)

文件开头的字节就是归档文件的魔数,用来表明这是一个归档文件

是!<arch>还是!<arch>加上后面的0x0A呢? 换句话说,是下图的哪种情况?

由于ar命令是Unix引入的,那么这要查Unix源码了,在Unix V10的/cmd/asd/ar.h中:

#define	ARMAG	"!\n"
#define	SARMAG	8
#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];
};

这个ARMAG是ARchive MAGic number(归档文件魔数)的缩写,其为"!<arch>\n",需要带上\n,而且\n的ASCII码为0x0A

那么这个图是对的:

文件内容

原来没有归档时的文件内容:

对应到下图红框部分:

注意:所有数据按偶字节边界对齐,即每个成员(归档文件头 + 归档文件内容)的起始偏移必须是2的倍数,因此某些情况下,文件内容的末尾会添加0x0A用于对齐,就像上图的那样

元数据

上方代码的struct ar_hdr结构体ar_hdr是archive_header的缩写,即归档文件头,存储这每个文件的元数据(meta data),有心的读者可能会去算这个结构体的大小:

#include 
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];
};
int main()
{
	std::cout<

运行结果:

占60字节,对应到test.a,从文件名开始,到文件内容前,统计字节数:

0x43-0x08+1==0x3C==十进制的60

0x8B-0x50+1==0x3C==十进制的60

0xE5-0xAA+1==0x3C==十进制的60

发现从文件名开始,到文件内容前这部分占的字节数都是60,恰为struct ar_hdr结构体的大小

结论: 显然这部分内容就是每个文件的struct ar_hdr结构体实例化后的内容

有了这个结构体就能解析struct ar_hdr结构体的各个字段了

文件名(char ar_name[16])

下图黄框部分:

Unix源代码的/cmd/ar.c是这样打印归档文件中的各个文件名的:

char *
trim(s)
char *s;
{
	register char *p1, *p2;
	for(p1 = s; *p1; p1++)
		;
	while(p1 > s) {
		if(*--p1 != '/')
			break;
		*p1 = 0;
	}
	p2 = s;
	for(p1 = s; *p1; p1++)
		if(*p1 == '/')
			p2 = p1+1;
	return(p2);
}

结论: 文件名的结尾是/

时间戳(char ar_date[12])

下图紫框部分:

UID(char ar_uid[6])

下图橙框部分:

GID(char ar_gid[6])

下图灰框部分:

拥有者、所属组、其他人的八进制权限(char ar_mode[8])

下图蓝框部分:

文件大小(char ar_size[10])

文件大小是以ASCII码的形式呈现的,下图棕框部分:

ar_fmag

这个ar_fmag是每个归档文件头末尾必须要有的,在https://unix.org/whitepapers/ar-format.html网站中有说明:

例如在Unix V10源码的/cmd/ar.c的getdir()函数中(截取部分代码):

if (strncmp(arbuf.ar_fmag, ARFMAG, sizeof(arbuf.ar_fmag))) {
		fprintf(stderr, "ar: malformed archive (at %ld)\n", lseek(af, 0L, 1));
		done(1);
	}

会发现strncmp将arbuf.ar_fmag和宏ARFMAG比较,而ARFMAG会被替换为0x60 0x0A,也就是/cmd/asd/ar.h中定义的:

#define	ARFMAG	"`\n"
所有元数据一览

下图的每一格中的数据代表一个字节,用十六进制表示

213C617263683E0A746573742E747874
2F202020202020203020202020202020
20202020302020202020302020202020
36343420202020203131202020202020
2020600A74657374737472696E670A0A
746573742E632F202020202020202020
30202020202020202020202030202020
20203020202020203634342020202020
32392020202020202020600A696E7420
6D61696E28290A7B0A20202020726574
75726E20303B0A7D0A0A746573742E62
696E2F20202020202020302020202020
20202020202030202020202030202020
20203634342020202020342020202020
20202020600AAABBCCDD