程序的本质之五Linux系统中静态库文件的构成

操作系统:CentOS Linux release 7.7.1908

内核版本:3.10.0-1062.1.1.el7.x86_64

运行平台:x86_64

 

在Linux系统中,静态库的文件名多以.a结尾,它是一个或多个目标文件的集合。静态库文件以以下的数据结构来定义(/usr/include/ar.h):

#define ARMAG   "!<arch>\n" /* String that begins an archive file.  */
#define SARMAG  8       /* Size of that string.  */

#define ARFMAG  "`\n"       /* String in ar_fmag at end of each header.  */

struct ar_hdr
{
	char ar_name[16];       /* Member file name, sometimes / terminated. */
	char ar_date[12];       /* File date, decimal seconds since Epoch.  */
	char ar_uid[6], ar_gid[6];  /* User and group IDs, in ASCII decimal.  */
	char ar_mode[8];        /* File mode, in ASCII octal.  */
	char ar_size[10];       /* File size, in ASCII decimal.  */
	char ar_fmag[2];        /* Always contains ARFMAG.  */
};

每个静态库文件都以“!<arch>\n”等8(SARMAG)个字符作为开头。可以将这些字符看作是静态库文件的魔数。

静态库文件的内容为一个或多个目标文件的加总,而其中的每个目标文件之前,都有一个struct ar_hdr结构的数据来描述它。

struct ar_hdr结构中的所有数据都以字符的形式存储,其中未被有效数据占用的空间的值都为空格字符。

ar_name表示目标文件的文件名,常以“/”字符作为结尾。超过16个字符的文件名须要保存在ar_name值为“//              ”的数据中,而这时该目标文件的文件名为字符“/”加上长文件名所在区域的索引号,如“/18             ”。

ar_date、ar_uid、ar_gid、ar_mode和ar_size分别表示原始目标文件的时间(始于Epoch时间点的秒数)、用户ID、组ID、文件权限(如0644)以及文件的大小。其中,除ar_mode成员采用八进制的数字字符之外,其他都采用十进制的数字字符。

ar_fmag中的值固定为“`\n”两个字符。

使用ar命令即可查看静态库中所有目标文件的struct ar_hdr结构中的值,如:

$ ar tv /usr/lib/libc_nonshared.a
rw-r--r-- 1005/135   1480 Aug  7 08:08 2019 elf-init.oS
rw-r--r-- 1005/135   1284 Aug  7 08:08 2019 atexit.oS
rw-r--r-- 1005/135   1284 Aug  7 08:08 2019 at_quick_exit.oS
rw-r--r-- 1005/135   1248 Aug  7 08:08 2019 stat.oS
rw-r--r-- 1005/135   1252 Aug  7 08:08 2019 fstat.oS
rw-r--r-- 1005/135   1252 Aug  7 08:08 2019 lstat.oS
rw-r--r-- 1005/135   1228 Aug  7 08:08 2019 stat64.oS
rw-r--r-- 1005/135   1236 Aug  7 08:08 2019 fstat64.oS
rw-r--r-- 1005/135   1236 Aug  7 08:08 2019 lstat64.oS
rw-r--r-- 1005/135   1252 Aug  7 08:08 2019 fstatat.oS
rw-r--r-- 1005/135   1256 Aug  7 08:08 2019 fstatat64.oS
rw-r--r-- 1005/135   1280 Aug  7 08:08 2019 mknod.oS
rw-r--r-- 1005/135   1264 Aug  7 08:08 2019 mknodat.oS
rw-r--r-- 1005/135   1116 Aug  7 08:08 2019 warning-nop.oS
rw-r--r-- 1005/135   1228 Aug  7 08:08 2019 stack_chk_fail_local.oS

静态库除了包含目标文件的数据之外,还有可能包含了两类特殊的数据。这两类特殊的数据也都以struct ar_hdr结构数据开头。在Linux系统中没有找到关于它们的定义,这里是通过源码(binutils-2.27/binutils/elfcomm.c文件中的setup_archive函数)分析获得。

1、第一类特殊数据(符号)

紧跟静态库文件魔数(即ARMAG)之后,其中,ar_hdr. ar_name的值为“/               ”或“/SYM64/         ”(空白处表示相应数量的空格字符);ar_hdr. ar_size的值为该类特殊数据的总大小(2字节对齐,也就是说如果总大小为奇数个字节则须要加1);其他成员有数值,但可忽略。

紧跟struct ar_hdr结构数据之后,接着依次为符号数量、每个符号所在的目标文件位置的索引号(即该目标文件开头(包括struct ar_hdr数据)相对于静态库文件开头的偏移量)、所有符号的名称(均以空字符结尾)。

符号数量和符号索引号的长度跟ar_hdr. ar_nam有关,当ar_hdr. ar_name为“/               ”时,它们的长度为4个字节,当ar_hdr. ar_name为“/SYM64/         ”时,它们的长度为8个字节。符号数量和符号索引号的值均以大端字节序存储。

使用readelf命令即可查看第一类特殊数据的值,如:

$ readelf -c /usr/lib/libc_nonshared.a
Index of archive /usr/lib/libc_nonshared.a: (34 entries, 0x1fe bytes in the symbol table)
Contents of binary /usr/lib/libc_nonshared.a(elf-init.oS) at offset 0x336
  __libc_csu_init
  __x86.get_pc_thunk.bx
  __libc_csu_fini
Contents of binary /usr/lib/libc_nonshared.a(atexit.oS) at offset 0x93a
  atexit
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(at_quick_exit.oS) at offset 0xe7a
  at_quick_exit
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(stat.oS) at offset 0x13ba
  __stat
  __x86.get_pc_thunk.bx
  stat
Contents of binary /usr/lib/libc_nonshared.a(fstat.oS) at offset 0x18d6
  __fstat
  __x86.get_pc_thunk.bx
  fstat
Contents of binary /usr/lib/libc_nonshared.a(lstat.oS) at offset 0x1df6
  __lstat
  __x86.get_pc_thunk.bx
  lstat
Contents of binary /usr/lib/libc_nonshared.a(stat64.oS) at offset 0x2316
  stat64
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(fstat64.oS) at offset 0x281e
  fstat64
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(lstat64.oS) at offset 0x2d2e
  lstat64
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(fstatat.oS) at offset 0x323e
  fstatat
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(fstatat64.oS) at offset 0x375e
  fstatat64
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(mknod.oS) at offset 0x3c82
  __mknod
  __x86.get_pc_thunk.bx
  mknod
Contents of binary /usr/lib/libc_nonshared.a(mknodat.oS) at offset 0x41be
  mknodat
  __x86.get_pc_thunk.bx
Contents of binary /usr/lib/libc_nonshared.a(warning-nop.oS) at offset 0x46ea
  __warn_memset_zero_len
Contents of binary /usr/lib/libc_nonshared.a(stack_chk_fail_local.oS) at offset 0x4b82
  __stack_chk_fail_local
  __x86.get_pc_thunk.bx

从上面输出的信息可知,该静态库包含34个符号、每个符号的索引号(如符号__libc_csu_init所在目标文件elf-init.oS相对静态库libc_nonshared.a的偏移量为0x336个字节),还可以知道所有符号名称的总长度为0x1fe字节。

还可以通过执行$ hexdump -C /usr/lib/libc_nonshared.a命令来佐证上述的结论,如下图所示:

文件开头8个魔数字符,其中0x0a对应的字符为“\n”;然后是60个字节的struct ar_hdr数据,其中第一类特殊数据的长度为650个字节;再然后是符号的数量为34(即0x22)个;再然后是符号__libc_csu_init的索引号为0x336,索引号的长度也是4个字节。4 + 34 * 4 + 0x1fe刚好等于650个字节。

2、第二类特殊数据(长文件名)

紧跟第一类特殊数据之后就是第二类特殊数据。它的ar_hdr. ar_name的值固定为“//              ”;ar_hdr. ar_size表示该类特殊数据的总大小(也是2字节对齐);其他成员的值全为空格字符。

通过执行$ hexdump -C /usr/lib/libc_nonshared.a命令来获取第二类特殊数据的值,如下图所示:

其中,该静态库第二类特殊数据的长度为44个字节,内容为目标文件(即文件名长度超过16个字符)的长文件名,每个长文件名都以“\n”为结尾。长文件名at_quick_exit.oS的索引号为0,stack_chk_fail_local.oS的索引号为18。索引号为长文件名首字符在该类特殊数据的内容的偏移量,从0开始计数。所以目标文件at_quick_exit.oS在它的ar_hdr.ar_name中填写“/0              ”,stack_chk_fail_local.oS填写“/18             ”。at_quick_exit.oS的文件名如下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值