本节书摘来自异步社区《逆向工程权威指南》一书中的第3章3.5节MIPS,作者【乌克兰】Dennis Yurichev(丹尼斯),更多章节内容可以访问云栖社区“异步社区”公众号查看。
3.5 MIPS3.5.1 全局指针Global pointer全局指针是MIPS软件系统的一个重要概念。我们已经知道,每条MIPS指令都是32位的指令,所以单条指令无法容纳32位地址(指针)。这种情况下MIPS就得传递一对指令才能使用一个完整的指针。在前文的例子中,GCC在生成文本字符串的地址时,就采用了类似的技术。
从另一方面来说,单条指令确实可以容纳一组由寄存器的符号、有符号的16位偏移量(有符号数)。因此任何一条指令都可以构成的表达式,访问某个取值范围为“寄存器−32768”~“寄存器+32767”之间的地址(总共69KB)。为了简化静态数据的访问操作,MIPS平台特地为此保留了一个专用的寄存器,并且把常用数据分配到了一个大小为64KB的内存数据空间里。这种专用的寄存器就叫作“全局指针”寄存器。它的值是一个指针,指向64KB(静态)数据空间的正中间。而这64KB空间通常用于存储全局变量,以及printf()这类由外部导入的的外部函数地址。GCC的开发团队认为:获取函数地址这类的操作,应当由单条指令完成;双指令取址的运行效率不可接受。
在ELF格式的文件中,这个64KB的静态数据位于.sbss 和.sdata之中。“.sbss”是small BSS(Block Started by Symbol)的缩写,用于存储非初始化的数据。“.sdata”是small data的缩写,用于存储有初始化数值的数据。
根据这种数据布局编程人员可以自行决定把需要快速访问的数据放在.sdata、还是.sbss数据段中。
有多年工作经验的人员可能会把全局指针和MS-DOS内存(参见本书第49章)、或者MS-DOS的XMS/EMS内存管理器联系起来。这些内存管理方式都把数据的内存存储空间划分为数个64KB区间。
全局指针并不是MIPS平台的专有概念。至少PowerPC平台也使用了这一概念。
3.5.2 Optimizing GCC下面这段代码显示了“全局指针”的特色。
指令清单3.18 Optimizing GCC 4.4.5 (汇编输出)
1 $LC0: 2 ; \000 is zero byte in octal base: 3 .ascii "Hello, world!\012\000" 4 main: 5 ; function prologue. 6 ; set the GP: 7 lui $28,%hi(__gnu_local_gp) 8 addiu $sp,$sp,-32 9 addiu $28,$28,%lo(__gnu_local_gp) 10 ; save the RA to the local stack: 11 sw $31,28($sp) 12 ; load the address of the puts() function from the GP to $25: 13 lw $25,