ICS 复习 2024-06-14 未分类 1 条评论 427 次阅读 # Representing Program ![[Pasted image 20240614024159.png]] ### Translating Program 1. 预处理阶段 Preprocessing phase (cpp):把 \#include 的内容插入源代码 2. 编译阶段Compilation phase (cc1): C 语言翻译为汇编语言 3. 组装阶段 Assembly phase (as):把汇编语言翻译为机器语言指令 4. 连接阶段 Linking phase: 把机器语言连接库,生成可执行文件 ### C语言中的数据类型对应的长度 ![[Pasted image 20240614031218.png]] ### C 中的无符号陷阱 如果有符号数和无符号数在一个表达式参与运算,他们都会被转化为无符号数 ```cpp int foo = -1; unsigned bar = 1; bool result = (foo < bar); // false ``` 在这个例子中,`foo` 是一个有符号整数,值为 `-1`,而 `bar` 是一个无符号整数,值为 `1`。 在比较 `foo` 和 `bar` 时,`foo` 将被转换为无符号整数。这是因为在进行有符号和无符号整数比较时,C++ 会将有符号整数转换为无符号整数。转换后的值是基于类型的宽度。例如,在一个典型的32位系统中,无符号整数的最大值是 `2^32 - 1`,也就是 `4294967295`。 所以,`-1` 转换为无符号整数后,值将是 `4294967295`。 因此,比较 `(foo < bar)` 实际上是比较 `4294967295 < 1`,显然这不成立,结果是 `false`。 总结:在这段代码中 `(foo < bar)` 的结果是 `false`。 ![[Pasted image 20240614032351.png]] ### 有符号数和无符号数转换 每个位不变,但加减 $2^w$ ![[Pasted image 20240614032138.png]] ### 延长和截断(int, short, long 转换) 短->长:符号位扩展 长->短:直接截断 ![[Pasted image 20240614032548.png]] ### 位数保留和新增 二进制(T,U)加法处理:舍去超出范围的部分 左移: 右侧补0 右移: 左侧逻辑补0,算术补**符号位** 乘法处理:舍去多余位 ### 大端和小端存储方式 #### 整数 IA32,x86-64 小端 Sun 大端 ![[Pasted image 20240614034556.png]] #### 字符没有序问题,0作为终止符 ![[Pasted image 20240614034827.png]] # Machine Programming ### 操作数类型 #### 立即数 `$0x400, $-533` 类似 C 语言常数,但前缀 $ #### 寄存器 `%rax`, `%r13` - x86-64 有 16个寄存器 - `%rsp` 有特殊用途,保留 - 一些寄存器在特定指令有特殊用途,通常作为通用寄存器 #### 内存 `(%rax)` `8(%rax)` 获取存储在寄存器中的内存地址对应内存的值 ##### 内存地址操作格式 最通用的内存寻址形式是 `D(Rb, Ri, S)`,其中每个部分的含义如下: - **D**: 常量“偏移量”(Displacement),可以是 1, 2, 或 4 字节的常量值。 - **Rb**: 基址寄存器(Base Register),可以是任意的 16 个整数寄存器之一。 - **Ri**: 索引寄存器(Index Register),可以是任意整数寄存器,但不能是 `%rsp`(堆栈指针寄存器)。 - **S**: 缩放因子(Scale),可以是 1, 2, 4 或 8。 ###### 缩放因子为什么是这些数字? 缩放因子(Scale)只能是 1, 2, 4 或 8,这是因为这些值通常用于数组索引和指针运算。在编程中,数组元素的大小通常是 1 字节(char),2 字节(short),4 字节(int 或 float),或 8 字节(double 或 long long)。通过使用这些缩放因子,可以方便地进行数组元素的访问。 ![[Pasted image 20240614041408.png]] ### `movq` 指令 `movq src, dst` #### 可用的操作 不能用一条指令完成内存到内存的数据转移 ![[Pasted image 20240614041026.png]] ### `leaq` 指令 `leaq src, dst` 只计算地址,不访问地址 `leaq` 指令将源操作数 `Src` 所表示的地址计算结果存储到目标寄存器 `Dst` 中 - 可以用来计算形式如 `x + k * y` 的算术表达式,其中 `k` 是 `1, 2, 4, 或 8` - 计算地址而不进行内存访问 ### 一些算术指令 #### 双操作数 | 指令格式 | 计算方式 | 说明 | | ----------------- | -------------------- | ------------------------- | | `addq Src, Dest` | `Dest = Dest + Src` | 加法,将Src加到Dest上 | | `subq Src, Dest` | `Dest = Dest - Src` | 减法,从Dest中减去Src | | `imulq Src, Dest` | `Dest = Dest * Src` | 乘法,将Src乘以Dest | | `salq Src, Dest` | `Dest = Dest << Src` | 左移,将Dest左移Src位,等同于`shlq` | | `sarq Src, Dest` | `Dest = Dest >> Src` | 算术右移,将Dest算术右移Src位,保留符号位 | | `shrq Src, Dest` | `Dest = Dest >> Src` | 逻辑右移,将Dest逻辑右移Src位,不保留符号位 | | `xorq Src, Dest` | `Dest = Dest ^ Src` | 按位异或,将Src和Dest进行按位异或运算 | | `andq Src, Dest` | `Dest = Dest & Src` | 按位与,将Src和Dest进行按位与运算 | | `orq Src, Dest` | `Dest = Dest \| Src` | 按位或,将Src和Dest进行按位或运算 | *左移右移是 s(shift)+a/h(算术/逻辑)+l/r(左移右移)* #### 单操作数 | 指令格式 | 计算方式 | 说明 | |------------------|-----------------|------------------------------| | `incq Dest` | `Dest = Dest + 1` | 增量,将Dest加1 | | `decq Dest` | `Dest = Dest - 1` | 减量,将Dest减1 | | `negq Dest` | `Dest = -Dest` | 取负,将Dest取反 | | `notq Dest` | `Dest = ~Dest` | 取反,将Dest按位取反 | ### C 编译指令 `gcc –Og p1.c p2.c -o p` `-Og` 是调试友好编译优化 `-o` 指定输出 ### 特殊寄存器 **`%rsp`(Stack Pointer)**:堆栈指针,指向当前堆栈的顶部。 **`%rip`(Instruction Pointer)**:指令指针,指向下一条将要执行的指令(一般不可访问) ### 处理器状态 状态码: - **CF(Carry Flag)**:进位标志,用于无符号算术运算中的进位 - **ZF(Zero Flag)**:零标志,当操作结果为零时设置 - **SF(Sign Flag)**:符号标志,当操作结果为负时设置 - **OF(Overflow Flag)**:溢出标志,用于有符号算术运算中的溢出 ### `cmp` 比较指令 类似 `sub` 指令,根据 `b - a` 结果设置条件码 - **CF(Carry Flag)**:当 `b < a` 时设置,用于无符号比较。 - **ZF(Zero Flag)**:当 `b == a` 时设置。 - **SF(Sign Flag)**:当 `b - a` 为负数(结果小于 0,视为有符号数)时设置。 - **OF(Overflow Flag)**:当有符号运算溢出时设置,即当 `b > 0 && a < 0 && (b - a) < 0` 或 `b < 0 && a > 0 && (b - a) > 0` 时设置。 不会改变 `b` ### `test` 指令 类似 `add` 指令,计算 `b&a` 并设置状态码 - **ZF(Zero Flag)**:当 `a & b == 0` 时设置。 - **SF(Sign Flag)**:当 `a & b` 的结果为负数时设置(即最高有效位为1)。 `test %rX, %rX` 可以判断 `%rX` 是否为 `0` ### `setX a` 指令 `setX` 指令用于根据条件码设置目标寄存器的**低位字节**为 0 或 1。条件码的状态决定了目标寄存器的低位字节是被设置为 1 还是保持为 0 | 指令 | 条件 | 描述 | | ------- | ------------- | --------- | | `sete` | ZF | 等于 / 零 | | `setne` | ~ZF | 不等于 / 非零 | | `sets` | SF | 负 | | `setns` | ~SF | 非负 | | `setg` | ~(SF^OF)&~ZF | 大于(有符号) | | `setge` | ~(SF^OF) | 大于等于(有符号) | | `setl` | (SF^OF) | 小于(有符号) | | `setle` | (SF^OF) \| ZF | 小于等于(有符号) | | `seta` | ~CF&~ZF | 高于(无符号) | | `setb` | CF | 低于(无符号) | ![[Pasted image 20240614044657.png]] ### `movzbl` 指令 `movzbl` 指令将一个8位寄存器的值零扩展到32位寄存器中,并清零高位。 ```assembly movzbl %al, %eax ``` `%rax` = `0x12345678000000ab` `movzbl %al, %eax` 操作后的寄存器状态: - `%al` 仍然是 `0xab`。 - `%eax` 被设置为 `0x000000ab`(零扩展后)。 - `%rax` 被设置为 `0x00000000000000ab`(高32位被清零)。 ### `jX a` 指令 `jX` 指令用于根据条件码跳转到 `a` 地址 | 指令 | 条件 | 描述 | | ----- | ------------- | ------------------ | | `jmp` | 1 | 无条件跳转 | | `je` | ZF | 等于 / 零 | | `jne` | ~ZF | 不等于 / 非零 | | `js` | SF | 负 | | `jns` | ~SF | 非负 | | `jg` | ~(SF^OF)&~ZF | 大于(有符号) | | `jge` | ~(SF^OF) | 大于等于(有符号) | | `jl` | (SF^OF) | 小于(有符号) | | `jle` | (SF^OF) \| ZF | 小于等于(有符号) | | `ja` | ~CF&~ZF | 高于(无符号) | | `jb` | CF | 低于(无符号) | 标签: none 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
真好呢