ATT汇编语言学习笔记

汇编语言复习

转眼已经毕业8年了,刚毕业的时候学习的汇编知识发现已经遗忘了不少,确实在实际工作中用到的机会不是太多,但是偶尔还是可以帮助解决问题发现事情的本质,所以决定花些时间复习下,加之之前使用的书籍是 ,主要使用的编译器是masm,由于目前主要在linux平台进行开发,所以还是希望进一步熟悉ATT语法,这次使用<汇编语言程序设计>来学习,这本书主要基于linux操作系统,使用ATT格式讲解,是一本公认好书,但是中文版真的翻译的惨不忍睹。

官方参考手册

官方手册

intel 寄存器列表

通用寄存器

这些寄存器通常用于存储指令的操作数,但是有些指令会指定特有的寄存器来承担特定的功能。

寄存器名称 功能描述
EAX Accumulator for operands and results data
EBX Pointer to data in the DS segment
ECX Counter for string and loop operations
EDX I/O pointer
ESI Pointer to data in the segment pointed to by the DS register; source pointer for the string operations
EDI Pointer to data(or destination) in the segment pointed to by the ES register; destination pointer for string operations
ESP Stack pointer(in the SS segment)
EBP Pointer to data on the stack (in the SS segment)
  • 以上为32位寄存器 去掉E为16位寄存器
  • 在64位模式下除了提供了R前缀的64位通用寄存器(如 RAX),还提供了R8-R15 8个64位通用寄存器

段寄存器

16位时代,为了扩展地址总线到20位,会将段寄存器的16位左移4位加上一个16位的地址作为最终的寻址地址,32位时代,段寄存器承担起了映射表择子(selector)的作用,用于指明特定的内存段条目。64位模式下由于地址空间巨大,一般不在使用了

主要包括
| 名称 | 作用 |
| :—– | :————- |
| CS | 代码段寄存器 |
| DS | 数据段寄存器 |
| ES | 扩展段寄存器 |
| SS | 堆栈段寄存器 |
| FS | |
| GS | |

标志寄存器

有 FLAG(16bit) EFLAG(32bit) RFLAG(64bit)三个版本对应不同的模式
这是一个32位长度的寄存器,包括的标志如下

名称 位置 作用
CF 0 带位表示
PF 2
AF 4
ZF 6
SF 7
TF 8
IF 9
DF 10 方向标识,控制流语句的执行方向,(MOVS CMPS SCAS 等)
OF 11
IOPL 12 13
NT 14
RF 16
VM 17
AC 18
VIF 19
VIP 20
ID 21

intel 指令格式

在intel手册的第二卷第二章给出了intel处理器指令的格式,包括现有的intel64和IA-32处理器都是使用的这种指令格式,了解指令的格式有助于我们理解汇编语言中的指令规律

Intel指令主要由四个部分组成

  1. 可选的指令前缀
  2. 操作码
  3. 可选的修饰符 (ModR/M SIB 移位)
  4. 可选的数据元素(立即数)
指令前缀 操作码 ModR/M SIB 移位 数据元素
0-4字节 1-3字节 0-1字节 0-1字节 0-4字节 0-4字节
  1. 操作码
    定义指令的基本功能,告诉CPU这条指令要完成什么任务

  2. 指令前缀
    按照功能可以分成四组,每组一个字节,所以这个位置最多占4个字节,这四组分别是

    1. 锁定前缀和重复前缀
    2. 段覆盖前缀和分支提示前缀
    3. 操作数长度覆盖前缀
    4. 地址长度覆盖前缀
前缀名称 作用
锁定前缀 表示指令独占共享内存区 用于多核和超线程处理器
重复前缀 表示重复的功能,多用于字符串处理等
段覆盖前缀 用于覆盖默认的段寄存器? 第二章会讲
分支提示前缀 尝试提示处理器程序在跳转语句中最可能的路径 (likely unlikely)
操作数长度覆盖前缀 通知处理器程序将在16位和32位操作数之间切换,使程序在使用大操作数的时候能通知处理器,提高数据赋值速度
地址长度覆盖前缀 通知处理器程序将在16位和32位地址之间切换
  1. 修饰符
    修饰符用来定义执行的指令涉及的寄存器和内存位置,修饰符包含在三个单独的值中

    • 寻址方式说明符(ModR/M)字节
    • 比例-索引-基址(SIB)字节
    • 1\2\4个地址移位字节
    1. ModR/M字节包含三个字段
    • Mod专用于定义指令中使用的寄存器或寻址方式(寄存器或内存)
    • reg/opcode 用于两个功能 1, 指定寄存器 2. 扩展操作码的宽度
    • r/m 用于两个功能 1. 指定另一个寄存器 2. 和Mod组合定义寻址方式
      | Mod | reg/opcode | r/m |
      |:—-|:———–|:—-|
      | 2位 | 3位 | 3位 |
    1. SIB字节也由3个部分组成
      | 比例 | 索引 | 基址 |
      |——|——|——|
      | 2位 | 3位 | 3位 |
      比例字段指定操作的比例因子
      索引字段指定内存访问中所用的索引寄存器
      基址字段指定内存访问中所用的基址寄存器

    2. 地址移位字节
      用于指定对于ModR/M和SIB字节中定义的内存位置的偏移

  2. 数据元素
    指令使用的数据,一般是立即数或内存位置

汇编程序由三部分组成

1. 指令 被汇编器转换成机器码
2. 数据 包括静态内存区、堆栈、bss段
3. 命令 以句点开头,完成指示会汇编器生成一系列指令(.if .for),或者指明汇编器预留空间(.long .ascii)等任务
    * .section用于定义段落,所有汇编语言程序都必须包括3个段落
        1. 数据段 程序存储数据元素的内存区,声明后该区域不能扩展,该区域的数据元素可以被初始化,且存储于程序映像中
        2. bss段  该段只是ELF格式中一个段长度的描述,该段在加载器加载的时候会被内核填充为0
        3. 文本段 映像中存储程序指令码的内存区域

系统构成

三条总线

  1. 控制总线 负责发送指令至硬件设备
  2. 地址总线 负责在例如从内存读取数据的时候告知内存控制器要读取的内存地址是多少
  3. 数据总线 负责传输数据,例如向内存读写数据的具体内容

三条总线链接 处理器 内存 输入输出设备
上述总线还是比较抽象的,可能只是概念上的,例如和磁盘的串口通讯,可能同时发送了指令和地址信息。又或者GPIO程序员使用的方式就更为自由

2005年在Pentium上面引入的NetBurst技术是一个在程序优化的时候需要注意到的技术,在intel手册第一卷第二章第二节里面描述了这种技术
NetBurst主要包括的技术有

  1. 指令预取和解码
  2. 分之预测
  3. 乱序指令处理
  4. 指令隐退(Retirment Unit) 参见手册 2.2.2.3

因为现代处理器的较大吞吐量,造成了CPU资源浪费在从内存获取数据上,为了避免这种情况,在CPU上面提供了可以更快访问的存储单元,这就是流水线技术(pipelinig),同一时间流水线上的不同处理单元有的部分进行预取,有的部分进行指令执行,有的部分进行数据回写

IA32平台使用两级缓存实现流水线操作,第一个是L1一级缓存,CPU内部需要指令或数据的时候通过它进行请求,它会返回命中结果或根据预取算法预取他们。这样其实存在一个缺陷,就是执行分支语句时候不能总是正确预测。