计算机系统基础--csapp

汇编

Posted by 汤汤 on November 3, 2024

来自《深入理解计算机系统》CMU-CSAPP课程 一、汇编代码
二、程序转换与机器级表示

汇编代码

1.区分x86、ARM、MIPS架构

01.细节比较

| 特征 | x86 架构 | ARM 架构 | MIPS 架构 | | — | ———– | ———- | ———- | | 架构类型 | CISC | RISC | RISC | | 寄存器名 | eax, ebx, esp, eip 等 | r0r15, sp, lr, pc | $t0$t9, $s0$s7, $zero, $ra, $sp 等 | | 语法风格 | Intel(常见)或 AT\&T| ARM 语法(类 Intel 风格)| 类似于 MIPS 自己的风格 | | 典型指令 | mov, push, call, jmp | ldr, str, mov, bl, b | lw, sw, li, jal, jr| | 调用指令 | call| bl (Branch with Link)| jal (Jump and Link)| | 返回指令 | ret | bx lr | jr $ra| | 注释符号 | ; | @ | #| | 字节序 | 小端(默认)| 可大端可小端 | 多为大端,也支持小端 |

02.二进制反编译工具
  • ELF 文件可用 readelf -h 查看 Machine 字段判断是 ARM, MIPS, Intel 80386

2.区分Intel与AT&T汇编

01.细节比较

| 特征 | Intel 语法 | AT\&T 语法 | | ———– | ————— | ———— | | 汇编器 | MASM, NASM, IDA 默认 | GNU as(GAS)、objdump, GCC 默认 | |操作数顺序| 目标 ← 源(Destination first) | 源 → 目标(Source first) | | 寄存器前缀 | 无前缀,如:eax | 以 % 开头,如:%eax| | 立即数前缀 | 无前缀 | 以 $ 开头,如:$0x10 | | 内存引用 | DWORD PTR [eax] | (%eax) | | 语法风格 | 类似 C 语言赋值 | 函数式语法风格 | | 注释符 | ; | # |

Intel 更接近 C 语言赋值顺序: mov eax, 1 就像 eax = 1

02.常见二进制编译与反编译工具的默认语法

| 环境 | 默认语法 | | ——————————- | —————- | | NASM、MASM、IDA Pro | Intel | | GCC、objdump、GAS | AT\&T | | Ghidra / Binary Ninja / Radare2 | 可自定义,默认多数为 Intel |

程序转换与机器级表示

写在前面

数的表示
  • 原码:正数符号位为0;负数符号位为1
    • 问题:无法进行直接加减运算
  • 反码:【正数】反码与原码相同;【负数】将原码除符号位以外取反
    • 问题:0有两种表示方法:+0(0000 0000)和-0(1111 1111)
  • 补码:【正数】补码与原码相同;【负数】反码加1
    • 注意:求负数的补码步骤:将其绝对值的二进制表示取反后加1
    • 原码表示范围-127~+127;补码表示范围-128~+127。因此表示-128时时没有办法从原码-反码-加1这个步骤得到补码的,因为一开始就超出了原码的表示范围。
      整数溢出
  • 加法
    • 两正数相加,超过正数表示范围
    • 两负数相加,超过负数表示范围
  • 减法
    • 正减负
    • 负减正
  • 乘法
    • 两数相乘。一般CPU不会直接设置OF溢出标志,需要程序员检查或设置指令
      寄存器
  • 控制寄存器:CR0~CR4
  • 8位寄存器:AL CL DL BL AH CH DH BH
  • 16位寄存器:AX CX DX BX SP BP SI DI
  • 32位寄存器:
    • 8个通用寄存器:EAX ECS EDX EBX ESP EBP ESI EDI
    • 段寄存器:ES CS CC DS FS GS(段寄存器的值和偏移量组合成内存单元的物理地址)
    • 指令指针寄存器:EIP
    • 标志寄存器:EFLAGS
  • 64位寄存器:16个通用寄存器,用rdi,rsi,rdx,rcx,r8,r9保存1 $\sim$ 6个参数,用rax保存返回值

    比较及循环结构

标志寄存器EFLAGS/RFLAGS

包含多个标志位

  • CF-carry flag:表示无符号运算的进位或借位
    • 无符号数加法产生的进位,或无符号数减法产生的借位。都将CF设置为1,表示无符号数运算的溢出。
  • ZF-zero flag:若结果为0,则ZF=1
  • SF-sign flag:运算结果的符号。若结果为负,则SF为1
    • 可以用作条件跳转、分支控制等操作
  • OF-overflow flag:表示有符号数运算发生溢出

显式设置标志位的指令

cmp指令

减法操作,只影响标志位

test指令

按位与操作,只影响标志位(ZFSF)。 通常用于检查特定位的状态(如检查某个位是否为1;检查某个值是否为0)。

  • test ax 1检查ax的最低位是否为1。如果是,则ZF为0。
  • test %rax %rax%RAX的值为0时,结果为0,则ZF设置为1。
  • test指令将运算结果的最高位赋值给SF

如何使用条件码(标志位)

理论上可以直接从条件寄存器上读取,但一般不这样做【读取或写入标志寄存器通常需要借助 PUSHFPOPF或类似的指令,这些操作通常会导致不必要的性能开销。】。

可以使用条件跳转指令配合cmp或test指令。

setcc

根据标志寄存器的某些标志位设置目的寄存器的值(通常是8位寄存器、或者是16位、32位寄存器的低8位,也就是单个字节的值) 用来判断某些条件是否成立。

  • setcc只能修改目的寄存器单个字节的值,如果要扩展到32位、64位,需要配合mov指令 movzbl %al %eaxz表示零扩展(对应s表示符号扩展);前缀b表示源操作数的位宽是1个字节;前缀l表示目的操作数的位宽是long类型4个字节。