0%

深入理解计算机系统学习笔记

C 语言编译过程

  • 源文本———-预处理,处理 define,include 等
  • 预处理文本————编译
  • 汇编文本—————汇编得到二进制文件 .o
  • 目标格式—————链接
  • 可执行文件

二进制补码原理和 C 语言处理类型转换

  • 二进制补码最高位本质是一个 $-2^x$
  • 同时处理有符号和无符号整数比较时或者进行其它二元运算时,C 语言会默认无符号且均为正数。
  • 编译器处理一个 -x 的表达式时,会先读 x 然后取反,所以 -2147483648 是不合法的,应该写为 -2147483647-1

简单的汇编语言

  • 操作系统将物理内存抽象为了一个一维数组,所有汇编层面的操作均在一维数组内进行

  • 每一条汇编指令都可以被描述为两个部分,指令和操作对象,这两部分的整体可以被一个或多个字节描述,此规则本质上是一颗哈夫曼树。

  • 一个程序的汇编指令会被保存在主存上,有一个程序计数器 epi 指出当前执行到的地方。

  • 常用寄存器名有 eax,ecx,edx,ebx,esi,edi,esp,ebp。前三者的数据由当前程序保存在栈中,后三者的值由下级程序保存,最后二者为帧指针和栈指针,一般不使用。前四个寄存器可以通过 ah,al,ax 等形式访问低二字节和低一字,但都可以用 di 形式访问低一字。

  • 传送指令分为三种 movb movw movl 分别表示字节,字和双字。传送对应类型时,应该使用对应的寄存器位置。 push,pop 指令为压栈和弹栈,本质上是操作了主存和栈指针。

  • lea 指令可以快速计算 a+b*(1,2,4,8)+ca 必须为常量,b,c 必须为寄存器中的数。

  • 操作数分为三类,一类是立即数,一类是寄存器,一类是主存数据,访问格式如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $imm //立即数 imm
    eg. $0x3f

    E //寄存器 E
    eg. eax

    Imm(e1,e2,s) //主存上 Imm+e1+e2*s 位置上的数据,Imm 可以缺省,后二者可以缺省,e1 缺省时不能省略 ','
    eg. 0x3f(eax,ebx,4)
    eg. (eax)

  • testcmp 指令来控指条件,本质上,它们改变了条件寄存器内的值,test x x 等价于 cmp 0 x。执行 cmp 后,jl 等条件跳转语句会在 cmp 成立时执行,jl 表示小于时跳转,cmp x y 可以看作是 y<x 时执行 jl

  • call 指令会将返回地址 (call) 语句后那条语句的地址入栈后跳转到函数所在位置。ret 指令利用返回地址跳转回去,一般来说,用 eax 保存返回内容。

  • 由于程序寄存器中的某些值会被缓存到主存中,所以使用 gets 等不安全函数读取时,如果读取的内容过长超出了为此缓存区分别的字节后,会污染一些先前被存储在主存中的寄存器值,因为超出分配的内存后,会写在外面,通过这样的操作,我们可以改变一些系统寄存器的值,让程序跳转执行一些我们希望让它执行的代码(这样的代码通常被写在我们的输入里),这就是缓冲区攻击,通常操作是改变 ebp 的值使得 ret 操作跳到我们希望的地方。