静态链接库
先去抓一只静态链接库来看看 —— /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 共享文件对象。