What are Dynamic Libraries ?
Ref: Overview of Dynamic Libraries
动态库会在使用时动态地加载到内存中。在 Linux 上,动态库以 .so
结尾,在 macOS 上是 .dylib
,而在 Windows 上是 .dll
。
Usage
以 macOS 为例,首先新建两个文件 lib.c
和 lib.h
。
1 | //lib.h |
1 | //lib.c |
然后编译出 .dylib
文件:
gcc -dynamiclib lib.c -o libtest.dylib
编写 main.c
文件来验证一下:
1 |
|
编译的时候指定要链接的动态库:
gcc main.c -L. -ltest -o main
标准的动态库都以 lib
开头,如这里的 libtest.dylib
。如果不以 lib 开头,linker 就找不到它。
Load on Runtime
在编译时制定要链接的动态库,这种用法看起来和静态库差不多。但动态库之所以动态的一个原因正是它可以在运行时动态地加载。
修改 main.c
文件:
1 |
|
其中,dlopen
用于装载和链接一个动态库,而 dlsym
会返回 symbol 的地址。
.dylib vs .a
- 动态链接会降低包体积
- 将静态的事情放在动态来做,会拖慢程序的运行速度。但是 Apple 提供了 shared library cache 来做缓存。
- 即使有缓存,仍需要查找 Procedure Linkage Table (PLT) 表。这个表记录了之前已经调用过的函数的地址。
Function Interposing
dyld
提供了一般的 loader 没有的功能:函数拦截。这样我们可以轻松地 Hook 其他动态库中的函数(比如系统调用)。而在 Linux 中,Hook 系统调用就麻烦些。
出自 Mac OS X and iOS Internals: To the Apple’s Core 中的替换掉 malloc
和 free
的例子在互联网上已经泛滥,这里就不再赘述。在这个例子中,我们 Hook 掉之前 lib.c
重的 lib_print
函数。
新建一个 libhook.c
文件。
1 | //libhook.c |
中间的宏定义来源于 /include/mach-o/dyld-interposing.h
中。我们把它编译成一个动态库:
gcc -dynamiclib libhook.c -o libhook.dylib -L. -ltest
然后,通过 dyld 的环境变量,将这个动态库强制插入已经编译好的 main 程序中:
DYLD_INSERT_LIBRARIES=libhook.dylib ./main
1 | //main.c |
我们会发现 lib_print
的实现已经被替换了,而 main 对此毫不知情,表示很无辜。