admin管理员组

文章数量:1130349

《Computer Systems: A Programmer’s Perspective》第三章 Machine-Level Representation of Programs


3.1 A Historical Perspective(历史回顾)

  • 早期程序直接编写机器代码,现代使用高级语言(如C)编写,编译器/汇编器将其翻译为机器指令。
  • 汇编语言是机器语言的助记符(例如 movl 代替机器码)。
  • 指令集架构(ISA)定义了处理器支持的操作。

扩展:了解不同架构(如 x86, x86-64, ARM)之间的指令风格差异。


3.2 Program Encodings(程序编码)

3.2.1 Machine-Level Code

  • 使用 x86-64 汇编语言进行解释。
  • 编译器生成 .s 文件(汇编),汇编器再转换成机器码(ELF目标文件)。

3.2.2 Code Examples

long fun(long x) {
  return x + 1;
}

编译为:

fun:
    addq $1, %rdi
    movq %rdi, %rax
    ret

3.2.3 Formatting

  • 汇编中常见指令格式:操作数, 目的
  • 所有整数运算基于64位寄存器(如 %rax, %rdi

3.3 Data Formats(数据格式)

  • 整数、浮点数、指针等数据类型在内存中按字节存储。
  • 使用两种基本数据布局:小端序(little-endian) 和 大端序(big-endian)。

3.4 Accessing Information(访存方式)

3.4.1 Operand Specifiers

  • Immediate(立即数):$0x10
  • Register:%rax
  • Memory:8(%rbp), (%rax,%rbx,4)

3.4.2 Data Movement Instructions

  • mov(数据移动),lea(地址计算),push/pop(栈操作)

3.4.3 示例:将数组元素加倍

void scale(long *a, long n) {
  for (long i = 0; i < n; i++) a[i] *= 2;
}

3.4.4 Stack Data

  • push:将数据入栈
  • pop:从栈中弹出数据

3.5 Arithmetic and Logical Operations

3.5.1 lea(Load Effective Address)

  • 通常用于地址计算,速度比乘法快
lea (%rdi,%rdi,2), %rax ; rax = rdi*3

3.5.2 一元与二元操作

  • neg, not, add, sub, imul, and, or, xor

3.5.3 位移操作

  • shl, shr, sar

3.5.5 特殊操作

  • 整除、模:idiv, mul

3.6 控制结构(Control Flow)

3.6.1 Condition Codes

  • 标志位(Z, S, OF, CF)反映算术/逻辑结果

3.6.3 跳转指令

  • jmp, je, jne, jg, jle, 等

3.6.5 条件执行(if/else)

  • 通过条件跳转控制执行路径

3.6.6 条件移动

  • cmovxx 指令避免分支,提高性能(对现代CPU友好)

3.6.7 循环

  • for, while 均转为 cmp + jump + label 组合

3.6.8 Switch 语句

  • 使用跳转表(jump table)优化

3.7 Procedures(过程/函数调用)

3.7.1 运行时栈结构

  • 函数调用过程中维护返回地址、局部变量、保存的寄存器等。

3.7.2 Control Transfer

  • callret 控制跳转与返回

3.7.3 Data Transfer

  • 参数传递通过寄存器 %rdi, %rsi, %rdx, %rcx, %r8, %r9

3.7.4 栈上局部变量

  • 通过 subq 分配栈空间,局部变量通过偏移寻址

3.7.6 递归调用

  • 每次调用都独立分配栈帧,堆栈不断增长

3.8 数组

3.8.1 基本原则

  • 元素访问公式:base + index * element_size

3.8.3 多维数组

  • a[i][j] 转换为 *(a + i * cols + j)

3.8.5 可变长度数组

  • 栈帧中动态分配,注意对齐和安全性

3.9 混合结构体

3.9.1 struct

  • 成员按声明顺序排列,有 padding
  • 访问某成员通过偏移量

3.9.2 union

  • 所有成员共享一块内存

3.9.3 对齐(Alignment)

  • CPU 更快读取对齐的数据;结构体可能插入 padding 增加对齐

3.10 指针与调试

3.10.1 理解指针

  • 指针是变量地址的抽象;间接访问通过 *pp->x

3.10.2 使用 GDB

  • 设置断点、打印变量、反汇编指令

3.10.3 缓冲区溢出

  • 超出数组边界写入会破坏栈帧、控制流

3.10.4 缓冲区溢出防护

  • 栈随机化(ASLR)、栈保护符号(canary)

3.10.5 支持变长栈帧

  • printf 需要处理不定参数

3.11 浮点代码

3.11.1 移动与转换

  • movss, movsd, cvtsi2ss, cvttss2si

3.11.2 浮点过程

  • 与整数函数类似,浮点参数/返回值通过 SSE 寄存器传递

3.11.3 算术运算

  • addss, mulss, divsd 等对应 float/double 运算

3.11.6 比较与分支

  • 使用 ucomiss, ucomisd 进行无序比较(支持 NaN)

总结(3.12)

  • 本章深入分析 C 语言程序如何映射到机器级语言。
  • 理解指令执行、函数调用、内存访问与布局、控制结构实现,为性能优化、安全分析、逆向工程等打下基础。

《Computer Systems: A Programmer’s Perspective》第三章 Machine-Level Representation of Programs


3.1 A Historical Perspective(历史回顾)

  • 早期程序直接编写机器代码,现代使用高级语言(如C)编写,编译器/汇编器将其翻译为机器指令。
  • 汇编语言是机器语言的助记符(例如 movl 代替机器码)。
  • 指令集架构(ISA)定义了处理器支持的操作。

扩展:了解不同架构(如 x86, x86-64, ARM)之间的指令风格差异。


3.2 Program Encodings(程序编码)

3.2.1 Machine-Level Code

  • 使用 x86-64 汇编语言进行解释。
  • 编译器生成 .s 文件(汇编),汇编器再转换成机器码(ELF目标文件)。

3.2.2 Code Examples

long fun(long x) {
  return x + 1;
}

编译为:

fun:
    addq $1, %rdi
    movq %rdi, %rax
    ret

3.2.3 Formatting

  • 汇编中常见指令格式:操作数, 目的
  • 所有整数运算基于64位寄存器(如 %rax, %rdi

3.3 Data Formats(数据格式)

  • 整数、浮点数、指针等数据类型在内存中按字节存储。
  • 使用两种基本数据布局:小端序(little-endian) 和 大端序(big-endian)。

3.4 Accessing Information(访存方式)

3.4.1 Operand Specifiers

  • Immediate(立即数):$0x10
  • Register:%rax
  • Memory:8(%rbp), (%rax,%rbx,4)

3.4.2 Data Movement Instructions

  • mov(数据移动),lea(地址计算),push/pop(栈操作)

3.4.3 示例:将数组元素加倍

void scale(long *a, long n) {
  for (long i = 0; i < n; i++) a[i] *= 2;
}

3.4.4 Stack Data

  • push:将数据入栈
  • pop:从栈中弹出数据

3.5 Arithmetic and Logical Operations

3.5.1 lea(Load Effective Address)

  • 通常用于地址计算,速度比乘法快
lea (%rdi,%rdi,2), %rax ; rax = rdi*3

3.5.2 一元与二元操作

  • neg, not, add, sub, imul, and, or, xor

3.5.3 位移操作

  • shl, shr, sar

3.5.5 特殊操作

  • 整除、模:idiv, mul

3.6 控制结构(Control Flow)

3.6.1 Condition Codes

  • 标志位(Z, S, OF, CF)反映算术/逻辑结果

3.6.3 跳转指令

  • jmp, je, jne, jg, jle, 等

3.6.5 条件执行(if/else)

  • 通过条件跳转控制执行路径

3.6.6 条件移动

  • cmovxx 指令避免分支,提高性能(对现代CPU友好)

3.6.7 循环

  • for, while 均转为 cmp + jump + label 组合

3.6.8 Switch 语句

  • 使用跳转表(jump table)优化

3.7 Procedures(过程/函数调用)

3.7.1 运行时栈结构

  • 函数调用过程中维护返回地址、局部变量、保存的寄存器等。

3.7.2 Control Transfer

  • callret 控制跳转与返回

3.7.3 Data Transfer

  • 参数传递通过寄存器 %rdi, %rsi, %rdx, %rcx, %r8, %r9

3.7.4 栈上局部变量

  • 通过 subq 分配栈空间,局部变量通过偏移寻址

3.7.6 递归调用

  • 每次调用都独立分配栈帧,堆栈不断增长

3.8 数组

3.8.1 基本原则

  • 元素访问公式:base + index * element_size

3.8.3 多维数组

  • a[i][j] 转换为 *(a + i * cols + j)

3.8.5 可变长度数组

  • 栈帧中动态分配,注意对齐和安全性

3.9 混合结构体

3.9.1 struct

  • 成员按声明顺序排列,有 padding
  • 访问某成员通过偏移量

3.9.2 union

  • 所有成员共享一块内存

3.9.3 对齐(Alignment)

  • CPU 更快读取对齐的数据;结构体可能插入 padding 增加对齐

3.10 指针与调试

3.10.1 理解指针

  • 指针是变量地址的抽象;间接访问通过 *pp->x

3.10.2 使用 GDB

  • 设置断点、打印变量、反汇编指令

3.10.3 缓冲区溢出

  • 超出数组边界写入会破坏栈帧、控制流

3.10.4 缓冲区溢出防护

  • 栈随机化(ASLR)、栈保护符号(canary)

3.10.5 支持变长栈帧

  • printf 需要处理不定参数

3.11 浮点代码

3.11.1 移动与转换

  • movss, movsd, cvtsi2ss, cvttss2si

3.11.2 浮点过程

  • 与整数函数类似,浮点参数/返回值通过 SSE 寄存器传递

3.11.3 算术运算

  • addss, mulss, divsd 等对应 float/double 运算

3.11.6 比较与分支

  • 使用 ucomiss, ucomisd 进行无序比较(支持 NaN)

总结(3.12)

  • 本章深入分析 C 语言程序如何映射到机器级语言。
  • 理解指令执行、函数调用、内存访问与布局、控制结构实现,为性能优化、安全分析、逆向工程等打下基础。

本文标签: 第三章读书笔记ProgrammerSystemsperspective