# 计算机系统

# 第一章 综述

  • 计算机系统层次

  • 程序从源码到运行的基本过程

  • 补码

    • 正数不变
    • 负数忽略第一个数字,剩下取反加一
    • 加法:直接加;减法:转化成加法。
    • 若得到结果为负数,则一样忽略第一个数字,剩下位次取反加一
  • u%d%的区别

    前者不读取符号,即把补码的第一位当作数字;后者读取符号。

    在n位系统中,补码表示的范围为

# 第二章 数据的机器级表示与处理

# 数据的表示

  • 定点数

    • 带符号整数(signed interger)

      用补码表示和计算,最高位MSB表示符号位。最高位为符号位,正数比负数多一个。

    • 无符号整数(unsigned integer)

      数量为

    • C语言中,无符号数优先级更高,有符号和无符号进行运算时,优先按无符号处理。若溢出,无符号数结果为对模运算。

  • 浮点数

    ​ 由三部分组成:符号部分1位 指数部分8位 尾数部分23位。①先进行规格化②正数符号位为0,负数符号位为1③指数部分先加127偏移量再用相应进制表示④尾数部分只取小数点之后部分,多余的地方补0。

# 数据存储

  • 存储单位

    • 字节(8位)
    • 字:存储器访问的基本单位,16位或32位
  • 存储方式

    • 大端模式:符合人类习惯。小号地址存储高字节,大号地址存储低字节。
    • 小端模式:与大端模式相反,小号地址存储低字节,大号地址存储高字节。是x86架构的默认存储方式。
  • 对齐存储

    ​ 数据地址大小必须是数据大小的整数倍。优点:①提高CPU访问速度②减少跨存储单元造成的性能开销。

# 数据运算

  • 浮点运算

    见上。NaN表示非法计算结果。Infinity表示超出表示范围的数值。

  • 整数运算(int32位,char8位,short16位,long随系统,longlong64位)

    • 逻辑左移<<:低位补0
    • 逻辑右移>>:高位补0
    • 算数右移:高位补符号位

# 第三章(一) 程序的转换与机器级表示

# 程序转换的基本过程

  • 预处理Preprocessing

    展开宏、处理条件编译、插入头文件

  • 编译Compilation

    将高级语言代码转化为汇编代码.s文件

  • 汇编Assembly

    将汇编代码转化为机器代码.o文件

  • 链接Linking

    • 合并多个.o文件和库文件,生成可执行文件。

    • 处理符号解析和地址重定位

# 汇编语言与机器指令的对应关系

汇编语言是机器语言的符号表示,便于人类阅读。

  • 数据传送指令

    mov 源,目标 将数据从源位置传送到目标位置。

    举例:movl $5,%eax将立即数5加载到寄存器%eax

  • 算数运算指令

    • addl 源,目标 将源添加到目标,并将结果存储在目标位置。

      举例:addl %ebx,%eax%ebx的值加到%eax

    • subl 源,目标

  • 控制流指令

    • jmp 地址 无条件跳转到指定地址。
    • cmpl 源,目标 比较两个操作数,设置条件标志
    • je 地址 如果相等,跳转到地址

# 过程调用的机器级表示

函数调用和返回在机器级通过栈实现。

  • 栈的使用

    栈用于保存函数调用中的临时数据,如参数和返回地址。

    • push:将数据压入栈
    • pop:从栈顶取出数据
  • 函数调用指令

    • call 函数名:将返回地址压入栈,并跳转到函数入口。
    • ret:从栈中取出返回地址,并跳转回调用者。
  • 栈帧(Stack Frame)

    每个函数都有自己的栈帧,用于保存①参数②局部变量③返回地址

# 第三章(二) 复杂数据类型的分配与访问

# 数组的分配与访问

  • 数组的存储

    ​ 数组的存储是连续的。地址以字节计算,则元素地址计算公式:

  • 数组的访问

    ​ 根据地址计算公式,假设arr基址存储在%eax(累加器),i存储在%ecx(计数器),则其汇编语言如下:

    movl arr,%eax #将地址存到累加器中
    movl %ecx,%edx #存储计数器值到数据寄存器中
    imull $4,%edx #计算偏移量i*4
    addl %edx,%eax #将偏移量加到基址上
    movl %eax,%ebx #用基址寄存器存储地址
    

# 结构体的分配与访问

  • 结构体的对其存储

    • 每个成员的起始地址是其大小的整数倍
    • 结构体总大小要满足最大成员大小的整数倍
  • 结构体成员访问

    ​ 每个成员的位置相对于结构体基址是固定的。

# 联合体的分配与访问

​ 联合体是共享存储空间的多成员结构,所有成员起始地址相同。

​ 特点:①只有一个成员占用存储空间②联合体大小等于最大成员大小。

# 第三章(三) 越界访问与缓冲区溢出

# 定义

  • 越界访问:①超出数组地址范围②指针指向地址不合法

  • 缓冲区溢出:程序向固定大小的内存空间中写入超出其容量的数据,覆盖其相邻内存。(导致未定义行为)

    危害:①程序崩溃②安全漏洞③数据篡改

# 缓冲区溢出的工作机制

​ 常见的攻击机制为覆盖栈上的返回地址,使其返回时跳转到攻击者控制的地址。

+------------------+
| 返回地址         |  <-- 被覆盖的目标
+------------------+
| 参数             |
+------------------+
| 局部变量         |  <-- 溢出的源
+------------------+

# 保护机制

  • 编写安全代码

    • 避免直接操作裸指针。
    • 在数组操作时检查边界条件。

    使用安全函数

    • 替代 strcpy 的函数:strncpy
    • 替代 gets 的函数:fgets

    启用编译器保护

    • 堆栈保护:通过在栈帧中插入 canary 值,防止返回地址被修改。
    • 地址空间布局随机化(ASLR):随机化栈、堆和代码段的地址,增加攻击难度。

    使用现代语言和工具

    • 选择内存安全的编程语言(如 Python、Rust)。
    • 使用工具检测潜在的内存问题(如 Valgrind)。

# 第四章 (一)程序的链接

# 链接基本概念

  • 静态链接:在编译时将所有依赖的目标文件和库文件合并到一个可执行文件中
    • 优点:速度快
    • 缺点:体积大
  • 动态链接:程序运行时加载共享库(.so.dll文件)
    • 优点:体积小,库复用
    • 缺点:需要额外的运行开销

# 目标文件格式

对于linux标准文件格式ELF格式,包括 .text 代码段 .data 已初始化数据段 .bss 未初始化数据段

  • 可重定位目标文件(relocatable object file).o.obj
    • 包含代码和数据,地址未确定(需要通过链接器确定)
    • 每个.o文件由一个源文件生成
  • 可执行目标文件(executable object file).exe
    • 包含代码和数据,地址已确定
    • 可直接加载到内存执行
  • 共享目标文件(shared object file).so.dll
    • 特殊的可重定位文件,运行时加载

# 符号解析与重定位

  • 符号解析:将符号的引用(调用符号的位置)与定义(符号的位置,即全局变量地址)关联起来的过程。
  • 重定位:将程序从虚拟地址空间映射到实际内存地址的过程。
    • 重定位表:记录需要修改地址的地方
    • 重定位过程:①合并多个目标的代码段和数据段②修正符号的引用地址为实际地址

# 动态链接的机制

  • 延迟绑定:动态链接的函数在首次调用时才绑定地址。

    实现:

    • 在调用动态库函数时,跳转到一个中间层PLT。
    • PLT调用动态链接器(动态加载函数地址)
  • 优点

    • 减少可执行文件体积:程序不包含库代码
    • 提高代码复用率

# 第四章 (二)深入链接操作与ELF文件结构

# 链接的具体操作

预处理-编译-汇编-链接

gcc -E main.c -o main.i 输出预处理文件,展开所有宏和include
gcc -S main.i -o main.s 输出汇编代码,人类可读的代码main.s
gcc -c main.s -o main.o 输出目标文件main.o,包含机器指令和数据
gcc -c lib.c -o lib.o
gcc main.o lib.o -o program 输出可执行文件

最后一步的链接两大核心过程:

  • 符号解析:链接器在标准库中查找printf的定义,并与main相关联。
  • 重定位:将main.o和lib.o中的.text.data段合并,计算实际内存地址,并更新引用地址。

# ELF文件结构

主要段如下:

  • .text :存放代码,只读
  • .data :存放已初始化的全局变量和静态变量
  • .bss :存放未初始化的全局变量和静态变量;运行初始化为0,不占用文件大小
  • 符号表 .symtab :列出所有符号信息,包括定义和引用
  • 重定位表 .rel.text.rel.data:列出需要重定位的地址信息

# 动态链接库的加载

  • ELF动态链接文件结构

    动态链接库是ELF文件的特殊形式,包含三张表

    • .dynsym 动态符号表,列出库中可导出的符号
    • .plt (Procedure Linkage Table)延迟绑定时的跳转表
    • .got (Global Offset Table)存储全局变量和函数指针
  • 动态链接库加载过程

    • 程序运行时,操作系统加载ELF文件
    • 动态链接器查找并加载所有依赖的共享库
    • 使用PLT和GOT实现函数调用的延迟绑定