目录

程序的机器级表示 | CSAPP笔记

程序的机器级表示

数据格式

C声明 Intel数据类型 汇编代码后缀 大小(字节)
char 字节 b 1
short w 2
int 双字 l 4
long 四字 q 8
char*(地址,指针) 四字 q 8
float 单精度 s 4
double 双精度 l 8
  • 八字>四字>双字>字>字节

+++

访问信息(通用寄存器)

  1. 返回值寄存器: %rax

  2. 参数寄存器

    • 第一个参数: %rdi

    • 第二个参数:%rsi

    • 第三个参数:%rdx

    • 第四个参数:%rcx

    • 第五个参数:%r8

    • 第六个参数:%r9

  3. 栈指针:%rsp

+++

操作数指示符

  • 立即数

    • 格式:$Imm

    • 操作数值:Imm

    • 表示常数值

  • 寄存器

    • 格式:r
    • 操作数值:R[r]
    • 表示某个寄存器的内容——数值或者一个指向内存位置的地址数
  • 内存引用

    • 引用组成部分:

      • 立即数偏移:Imm
      • 基址寄存器:rb
      • 变址寄存器:ri
      • 比例因子 :s
    • 操作数值:

      • 表达式

        $$ Imm(r_b,r_i,s) $$

        $$ =M[Imm+R[r_b]+R[r_i]*s] $$

    • ***M[ ]***即是在内存中寻找相对应的地址内的值

    • 单独%~~ 寄存器则是在R[%~~]中获取对应的值。

+++

数据传送指令

  • MOV类

    • 指令 效果 描述
      MOV (S ,D) D←S 传送
      movb 传送字节
      movw 传送字
      movl 传送双字
      movq 传送四字1
      movabsq (Imm , R) R←Imm 传送绝对的四字2
    • 源操作数指定的值是一个立即数,存储在寄存器 中或内存

    • 目的操作数指定的是一个位置内存地址 或者 寄存器3

    • ep.将一个值从一个内存位置 复制到 另一个内存位置

      • 第一条指令:将源值从内存位置加载到寄存器中
      • 第二条指令:将该寄存器值写入目的位置(内存位置)
  • MOVZ类

    • 无符号扩展数据传送指令,将剩余字节填充为0

    • 指令 效果 描述
      MOVZ (S ,R) R←符号扩展(S) 以符号扩展进行传送
      movzbw 将做了符号扩展的字节传送到字
      movzbl 将做了符号扩展的字节传送到双字
      movzwl 将做了符号扩展的字传送到双字
      movzbq 将做了符号扩展的字节传送到四字
      movzwq 将做了符号扩展的字传送到四字
    • 对无符号数使用

  • MOVS类

    • 符号扩展数据传送指令

    • 指令 效果 描述
      MOVS (S ,R) R←符号扩展(S) 传送符号扩展的字节
      movzbw 将做了符号扩展的字节传送到字
      movzbl 将做了符号扩展的字节传送到双字
      movzwl 将做了符号扩展的字传送到双字
      movzbq 将做了符号扩展的字节传送到四字
      movzwq 将做了符号扩展的字传送到四字
      cltq4 %rax←符号扩展(%eax) 把%eax符号扩展到%rax
    • 对有符号数使用

  • MOVS/MOVZ 类后跟 源数据大小(b、w、l) 目标数据大小(w、l、q) 一般都是从小的到大的

+++

压入和弹出栈数据

  • 栈:一种数据结构。遵循后进先出原则。

  • push:将数据压入栈中

  • pop :删除栈顶数据,弹出到数据目的

  • 栈的形式:栈是向下增长的,栈顶元素的地址是所有栈中元素地址最低的 。压栈时,栈顶指针会减少$8,而新栈顶指针会从下到上填满$8的空间。

  • 用栈指针%rsp保存着栈顶元素的地址

    指令 效果 描述
    pushq S R[%rsp]←R[%rsp]-85
    M[R[%rsp]]←S
    将四字压入栈
    popq D D←M[R[%rsp]];
    R[%rsp]←R[%rsp]+8
    将四字弹出栈
  • pushq %rbp 等价于 subq $8,%rsp //栈顶指针往下移动(减$8) movq %rbp,(%rsp) //将栈顶元素指针填入$8个字节

  • popq %rax 等价于 movq (%rsp),%rax //将栈顶元素指针传送到数据目的 addq $8, %rsp //栈顶指针往上移动(加$8)

+++

算术和逻辑操作

  • 加载有效地址

    • 指令 效果 描述
      leaq S,D D←&S 加载有效地址
    • *加载有效地址(load effective address)*指令 leaqmovq的变形。但它没有引用内存 ,而是将有效地址写入到目标操作数(&s) 为后面的内存引用 产生指针

    • leaq的特殊用法: 若%rdx的值为 X,那么指令 **leaq 7(%rdx,%rdx,4),%rax ** 等同于将%rax的值设为 5X+7

      • 因为寄存器%rdx存储的是立即数X,但**M[R[%rdx]]本质是用寄存器%rdx内的值 X 作为地址引用 ** 到内存M[X],而内存 **M[X]**的地址就是 X本身,所以可以用此作为一种简洁的算术操作。
  • 一元操作

    • 只有一个操作数

    • 指令 效果 描述
      INC D D←D+1 加1
      DEC D D←D-1 减1
      NEG D D← -D 取负
      NOT D D←~D 取补
  • 二元操作

    • 有两个操作数

    • 指令 效果 描述
      ADD S,D D←D+S
      SUB S,D D←D-S
      IMUL S,D D←D*S
      XOR S,D D←D^S 异或
      OR S,D D←D|S
      AND S,D D←D&S
  • 移位操作

    • 指令 效果 描述
      SAL k,D D←D«k 左移
      SHL k,D D←D«k 左移(等同于SAL)
      SAR k,D D←D» A k 算术右移
      SHR k,D D←D»L k 逻辑右移
    • k是移位量,D是要移位的数。

    • 移位量可以是 立即数 也可以是 放在单字节寄存器%cl6中,不管放在寄存器哪个字节,只有最低的m位才会起作用。一个字节(2bits)位移量能达到2^8^ -1=255。对w位长的数据操作,位移量由%cl寄存器的低m位决定,2^m^ = w高位会被忽略

    • 每个移位指令都有相对应的 b\w\l\q 后缀,对应不同的要移位的数的大小。

+++

特殊的算数操作

  • 两个64位有无符号的整数相乘,得到的乘积需要128位来表示。x86-64对 128位(16bits)数提供支持。 将一对寄存器 %rdx , %rax 组成一个128位 的八字。

  • R[%rdx]:R[%rax] 储存八字,包括乘积的结果 被除数。

  • 特别的,在除法中,**R[%rax]储存除法的结果R[%rdx]**储存除法的余数。

  • 指令 效果 描述
    imulq S
    mulq S
    R[%rdx]:R[%rax]←S×R[%rax] 有符号全乘法
    无符号全乘法
    cqto R[%rdx]:R[%rax]←符号扩展(R[%rax]) 转换为八字7
    idivq S
    divq S
    R[%rdx]←R[%rdx]:R[%rax] mod S
    R[%rax]←R[%rdx]:R[%rax] ÷ S
    有符号除法
    无符号除法
  • 对于 unsigned __int128类型储存乘积。

    1
    2
    3
    
    void store_uprod(uint128_t *dest,uint64_t x,uint64_t y){
    	*dest=x*(uint128_t)y;
    }
    
    • 汇编代码是
    1
    2
    3
    4
    5
    6
    
    //dest in %rdi, x in %rsi, y in %rdx
    
    movq	%rsi,%rax	//乘数放入%rax
    mulq	%rdx		//y与%rax的数相乘,放入R[%rdx]:R[%rax]
    movq	%rax,(%rdi) //(小端法)低位放在小地址上
    movq	%rdx,8(%rdi)//(小端法)高位放大地址上
    
    • 存储乘积需要两个movq指令。
  • 对于除法

    • 通常来说被除数是64位的值。所以%rdx中的位应该是全0(无符号运算) 或者是 %rax的符号位(有符号元素)%rdx的值会默认设置为0,当进行有符号运算时,需要用cqto7 符号扩展%rax的数。**R[%rax]储存除法的结果R[%rdx]**储存除法的余数。

+++

控制

  • 条件码

    • 等价于t=a+b 用ADD指令

      条件码 表达式示例 效果 描述 标志
      CF (unsigned)t <(unsigned)a 无符号溢出 最近的操作最高位产生了进位,
      可用来检测无符号溢出
      进位标志
      ZF t==0 最近操作的结果为0 零标志
      SF t<0 负数 最近的操作得到的结果为负数 符号标志
      OF (a<0==b<0)&&(t<0!=a<0) 有符号溢出 最近的操作导致补码溢出(正/负 溢出标志
    • 条件码寄存器是在CPU中维护的一组但各位寄存器。

    • 可以描述最近的算术或逻辑操作的属性。可以用来检测这些寄存器来执行条件分支指令。

    • 除了leaq指令不会改变任何条件码,因为只进行地址计算。除此之外其他指令都会设置条件码。



  1. 常规的movq指令只能以表示为32位的补码数字立即数作为源操作数,然后用符号扩展得到64位的值,放到目的位置。 ↩︎

  2. movabsq 将任意64为立即数Imm作为源操作数,且只能以寄存器为目的。 ↩︎

  3. x86-64中加了一条限制,传送指令两个操作数不能 指向内存位置。 ↩︎

  4. cltq只能作用于寄存器%eax和%rax ↩︎

  5. 指针的字节大小为8 ↩︎

  6. 寄存器%cl从单字节到四字分别是:%cl、%cx、%ecx、%rcx ↩︎

  7. cqto(x86-64叫cqo),会隐含的读出%rax的符号位,复制到%rdx所有位。此指令不需要操作数。 ↩︎ ↩︎