读CSAPP之Procedure、Stack Frame

今年是离开校园的第六年,这六年来我一直在从事应用软件的设计、开发工作,大部分时间是在与高级编程语言、设计模式、业务逻辑打交道。它们大多流于表面,久而久之,与技术底层疏远了,诸如计算机组成原理、汇编语言、编译原理、数据结构以及算法慢慢得生疏,时至今日,向上碰到天花板,向下触到花岗岩。五年是一个契机,趁着下一个五年开始之际,我计划用三个月至半年时想间,重新学习这些知识,以期达到巩固基础,厚积薄发的目的。

本篇是我阅读《Computer System: A Programmer’s Perspective》一书的笔记,该书和与之搭配的《Professional Assembly Language》是我当下阅读计划的一部分。

Procedure

过程调用包括将数据(以参数和返回值的形式)和控制从代码的一部分传递到另一部分,同时,在进入时为过程的局部变量分配空间,并在退出时释放空间。

IA32只提供了对进入过程和退出过程的控制,而数据传递、变量的分配和释放则要通过操作程序栈来实现。x86-64则是通过寄存器而不是栈来传递数据,这样极大地减少了存储器读写操作的数量。

Stack Frame

栈(stack)在过程调用中起到了重要作用,包括了传递过程参数、存储返回信息、保存寄存器和本地存储。为单个过程分配的那部分栈成为栈帧(stack frame),其的最顶端是以两个指针界定,%ebp为帧指针,%esp为栈指针,程序执行时,大多数信息是相对于帧指针。

IA32中,栈向下增长,栈顶元素的地址是所有栈中元素地址最低的,栈指针%esp保存着栈顶元素的地址。

C对于数组引用是不进行任何边界检查,而且局部变量和状态信息(寄存器值和返回地址)都存放在栈中,一旦对越界的数组元素的写操作破坏了状态信息。则程序一旦重新加载寄存器或者执行ret指令,则会出现严重的错误。

栈随机化的思想是使栈的位置在程序每次运行时都有变化,程序开始时,在栈上分配一段0~n 字节之间的随机大小的空间。

地址空间布局随机化(Address-Sapce Layout Randomization),每次运行时,程序的不同部分,例如程序代码、库代码、栈、全局变量和堆数据,都会被加载到存储器的不同区域。

栈保护者的思想是在栈帧中任何局部缓冲区与栈状态之间存储一个特殊的“金丝雀”值,在回复寄存器状态和从函数返回之前,程序检查这个值是否被改变。

栈必须是既可读又可写,同时 x86 体系结构将读和执行访问权限合并成了一个1位的标志,因此栈上的字节也都是可执行的。x86-64 为内存保护引入了 NX (No-eXecute)位,将读和执行模式分开,由此栈就可以被标记为可读、可写,但不可执行。

Leave a comment

Your comment