在iOS App开发中,程序的链接是由Xcode中自带的LLVM来帮助我们完成的,程序员们也因此更注重业务逻辑的编写。但其实了解链接的原理能让我们对iOS的底层有更深层次的认识,也有助于我们从底层原理方面去解决各种疑难问题。
在京东商城项目中,各个业务模块会被编译成静态库后再一起链接进主工程,而现在的大部分静态插桩的方案都是在编译期进行的,这种方案不适用于京东商城。此时如果想要插桩的话该怎么办呢?
如果我们对链接的原理掌握透彻的话,以上问题就能迎刃而解。下面就让本文从静态链接与动态链接两个方面带着大家一起深入到链接的原理及其应用中来。
静态链接在没有链接之前一个程序的代码就是一个文件,显然是不利于多人合作开发和维护的。为了让代码变得更易开发和维护,代码被分成了若干个模块,每一个.c的代码源文件可以被理解成一个模块,每一个模块独立编译,再把所有编译完的文件链接起来,这个过程就是我们所说的静态链接。
01空间与地址分配每个单独的文件编译后都会生成一个符号表,静态链接后这些表会被合并成一个全局符号表。
合并的规则是相似段合并、数据段与数据段合并、代码段与代码段合并。
合并后每一个符号的的地址被确定,并写入全局符号表中。
经过空间与地址分配之后代码段中指令用到的符号地址还没有更新,想要确定符号的地址需要用到重定位表。编译后.o文件中需要重定位的符号的相关信息会存入重定位表中。
重定位表可以看作是多个relocation_info组成的数组,一个relocation_info代表了一个需要重定位符号的具体信息。链接器此时知道符号的地址,拿到了需要重定位的位置,就会去完成指令修改的工作。
// 重定位表struct relocation_info {
int32_t r_address; /* offset in the section to what is being relocated */ uint32_t r_symbolnum:24, /* symbol index if r_extern == 1 or section ordinal if r_extern == 0 */ r_pcrel:1, /* was relocated pc relative already */ r_length:2, /* 0=byte, 1=word, 2=long, 3=quad */ r_extern:1, /* does not include value of sym referenced */ r_type:4; /* if not 0, machine specific relocation type */};
举个例子,代码是在main文件中实现了一个 hook_msgSend的方法,然后在a文件中调用这个hook_msgSend这个方法。a文件编译后的.o文件中的重定位表可以确定符号的位置和需要修改指令的位置。链接之后形成的可执行文件中,分配空间后的函数地址是0x100005F08,可以在下图的符号表中找到。同时a文件里面调用hook_msgSend的指令已经被修改成0x100005F08。如下面3图所示:
重定位表中符号的位置 符号表中的地址