链接库的构成

静态链接库

先去抓一只静态链接库来看看 —— /usr/lib/libc.a

好的,实验体已就位!

file 一下:

▶ file libc.a 
libc.a: current ar archive

Linux 下的静态链接库以 .a 后缀构建文件名,并默认以 lib 作为库名前缀。由 file 的结果可知,静态链接库是由 ar 工具构建的归档文件。

那么归档文件里又是打包了些什么进去呢?binwalk 跑跑看:

▶ binwalk libc.a   
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
73904         0x120B0         ELF, 64-bit LSB relocatable, AMD x86-64, version 1 (SYSV)
75940         0x128A4         ELF, 64-bit LSB relocatable, AMD x86-64, version 1 (SYSV)
102392        0x18FF8         ELF, 64-bit LSB relocatable, AMD x86-64, version 1 (SYSV)
103108        0x192C4         ELF, 64-bit LSB relocatable, AMD x86-64, version 1 (SYSV)
……

里面塞满了 ELF 重定向文件。接下来用 ar 直接对 libc.a 进行解包:

▶ ar x libc.a
                                                                                                                                    
▶ ls
a64l.o                         gethstbynm2.o                    nice.o                              srand48.o
abort.o                        gethstbynm2_r.o                  nl_langinfo_l.o                     srand48_r.o
abs.o                          gethstbynm.o                     nl_langinfo.o                       s_scalbnf128.o
accept4.o                      gethstbynm_r.o                   noophooks.o                         s_scalbnf.o
accept.o                       gethstent.o                      nrand48.o                           s_scalbnl.o
access.o                       gethstent_r.o                    nrand48_r.o                         s_scalbn.o
acct.o                         getipv4sourcefilter.o            nsap_addr.o                         sscanf.o
……

得到了大量的 .o 目标文件,且以函数为单位构建(比如 printf 的机器码在此就构建成一个 printf.o)。所以 libc.a 静态链接库里包含的,是以函数为单位的 .o 目标文件,再与相应的函数声明(.h 头文件)相结合,便达成了“将代码打包共享出去”的目的(当然,对于静态链接库来说的“共享”,是为了使用者能 copy 一份机器码)。

IDA Pro 反汇编 printf.o

__int64 printf(__int64 a1, ...)
{
  gcc_va_list va; // [rsp+0h] [rbp-D8h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-C0h]

  va_start(va, a1);
  v3 = __readfsqword(0x28u);
  return _vfprintf_internal(stdout, a1, va, 0LL);
}

printf.o 的确只包含了 printf 的代码,printf 也的确是唯一的本地函数符号 —— 证明了以上论述的正确性。

当然 printf 本体代码这么短的原因在于它只是最外层接口,实际的打印实现代码在它所调用的内层函数中。

动态链接库

再抓来 libc-2.33.so 作为样本,照例 file 一下:

▶ file libc-2.33.so 
libc-2.33.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=54a6e404e7dc1de7c1434a00b7b1ad325b81f22a, for GNU/Linux 4.4.0, not stripped

ELF 共享文件对象。

最后更新于