目录

一、概述

二、x86_64 (AT&T)

2.1 数据格式

2.2 寄存器

2.2.1格式

2.2.2 调用规范

2.3  寻址

2.4 指令

2.4.1 数据传送指令

2.4.2 栈操作

2.4.3 算术&逻辑操作

2.4.4 乘法&除法

2.4.5 控制——比较指令

2.4.6 控制——跳转

2.4.7 控制——循环

2.4.8 过程


一、概述

CSAPP第三章笔记,备忘。

二、x86_64 (AT&T)

2.1 数据格式

这样使用后缀指示数的大小

char b byte
short w word
int l long word / doubleword
long q quad word
char * q quad word

2.2 寄存器

2.2.1格式

2.2.2 调用规范

%rax 返回值

%rdi, %rsi, %rdx, %rcx, %r8, %r9

前6个参数
%rbx, %rbp, %r12, %r13, %r14, %r15 被调用着保存
%r10, %r11 调用者保存
%rsp 栈指针
%rip PC
   
   

2.3  寻址

operand操作数分三种:

  • 立即数,使用$, 如$-577
  • 寄存器,使用%,如%rax
  • 内存

2.4 指令

2.4.1 数据传送指令

  • mov S, D    S->D

常用的有movb,movw, movl, movq, movabsq, 源操作数S可以为立即数,寄存器,或者内存,目的操作数可以时寄存器或者内存

  • 两个操作数不能同时是内存,如立即数和内存。
  • 寄存器大小一定要与指令后缀(b/w/l/q)匹配,且两个操作数都是寄存器时宽要匹配
  • mov指令只会填充指令后缀指示大小的内存和寄存器,例外是movl 以寄存器作为目的操作数时,会将高4字节置0
  • movabsq处理64位立即数,而常规的movq只能将32bit补码表示的立即数做源操作数,再符号扩展成64bit。movabsq能以64bit立即数做源操作数,只能以寄存器做目的操作数
  • MOVZ S, R   零扩展S->R

常用的指令movzbw,movzbl, movzbq, movzwl, movzwq

  • 目的操作数一定是寄存器,源操作数是内存或者寄存器
  • 有符号
  • 并没有movzlq指令,可以使用目的操作数是寄存器的movl指令实现,会把高4字节置0
  • MOVS S, R 符号扩展S->R

常用的指令movsbw, movsbl, movsbq, movswl, movswq, movslq, cltq

  • 目的操作数一定是寄存器,源操作数是内存或者寄存器
  • 用于补码
  • cltq 是 符号扩展(%eax)->%rax

2.4.2 栈操作

满递减的,栈顶时出口,在这种情况下,在小地址。

  • pushq S

rsp = rsp - 8,S-> (rsp)

  • popq D

(rsp)->D, rsp = rsp + 8

  • 每条指令占一个字节
  • %rsp总是指向栈顶

2.4.3 算术&逻辑操作

  • leaq S, D     &S -> D   加载有效地址
  • INC D
  • DEC D
  • NEG D
  • NOT D
  • ADD S, D
  • SUB S, D
  • IMUL S, D
  • XOR S, D
  • OR S, D
  • AND S, D
  • SAL k, D
  • SHL k,D
  • SAR k, D 算术右移,有符号
  • SHR k, D  逻辑右移,无符号
  • leaq 目的操作数必须时寄存器,实际上根本没有访问内存,只是把其地址取出,如leaq 7(%rdx, %rdx, 4), %rax,那么%rax = 5* %rdx,leaq能执行加法和有限形式的乘法,在编译简单的算术表达式时非常有用
  • 一元操作数可以时地址也可以时寄存器
  • 二元操作数中的第二个操作数既是源也是目的操作数,第一个操作数可以是立即数,寄存器和内存,第二个操作数只能是寄存器和内存,当其为内存时,既要从内存读取数,也要将最终的结果写回内存
  • 除了leaq外,其余指令的指令后缀形式和上面相同
  • 移位操作的移位量可以时立即数也可以放在%cl中

2.4.4 乘法&除法

  • imulq S    有符号全乘法, S * %rax -> %rdx:%rax ,乘数分别时操作数和%rax,高位在%rdx, 低位在%rax
  • mulq S    无符号全乘法,操作同上
  • clto          转换为8字, 将%rax 符号扩展到%rdx:%rax
  • idivq  S   有符号除法被除数在%rdx:%rax中, 除数为S,商在 %rax中,余数在%rdx中
  • divq  S    无符号除法,操作同上
  • 16字称 oct word
  • imulq也可以用双操作数
  • clto除法时用,用于扩展被除数

2.4.5 控制——比较指令

CPU中提供“测试数据值,然后根据测试结果来改变控制流或数据流”的底层机制。CPU中有单bit的条件码(condition code)寄存器,描述了最近算术或者逻辑的结果属性,通过检测这些寄存器来执行条件分支指令,如下:。

  • CF: Carry Flag,进位标志,最近操作产生进位,用来检查无符号操作溢出
  • ZF: Zero Flag,最近操作产生的结果为0
  • SF: Sign Flag,  最近操作产生的结果为负数
  • OF: Overflow Flag,最近的操作导致一个补码溢出(正溢出或负溢出)

t = a + b的情况:

  • CMP S1, S2

比较指令,基于S2 - S1改变condition code,相关的指令有cmpb, cmpw, cmpl, cmpq

  • TEST S1, S2

测试指令,基于 S1 & S2改变conditon code, 相关的指令有testb,testw,testl,testq,通常用法两个操作数都是自身,检测正数、负数、0(test %rax, %rax)

上述的指令描述了“根据condition code的某种组合,将某一数设置成0或1”

2.4.6 控制——跳转

  • jmp Lable                      直接跳转,跳转由标号指出,标号是跳转编码的一部分。
  • jmp  *%rxx/*(%rxx)        间接跳转,跳转由寄存器或者内存中读出

  • 有条件跳转只能是直接跳转

2.4.6.1 跳转指令的编码:

  1. PC relative,将目标指令的地址与紧跟在跳转指令后面那条指令的地址之间的差作为编码,执行时,PC的值是跳转指令的下一条指令,而不是跳转本身的地址,这种惯例可以追述到早期计算机系统,当时处理器会将更新PC作为执行这条指令的第一步。在链接后,跳转的编码是不会改变的,因此程序可以放在内存中的不同的位置,这样的代码是地址无关的。jmp相对值+下一条指令的地址就是跳转的最终地址
  2. 绝对地址,用4个字节直接指定目标

2.4.6.2 C语言与汇编的对应关系

条件控制实现条件分支:

if (test-expr) {then-statement
}else {else-statement
}
//汇编指令通常会描述成如下c-style    t = test-exprif (!t) {goto false;}then-statementgoto done;
false:else-statemnet
done:
  • 汇编器会为then和else语句产生各自的代码块,插入条件和无条件分支,保证正确执行。
  • 可以看到就是产生原条件相反的值,然后插入无条件分支

条件传送实现分支跳转

条件指令会引起branch prediction miss,惩罚时间较长,会冲洗流水线,条件传送实现了一种无须分支指令实现跳转的方法,它需要先计算出不同的结果,根据条件选择结果。当然 ,条件传送也有其局限性,如需要计算每个分支的结果,在某些时候,可能很大的开销。特别的,有时候不能对两个分支同时求值,可能会引起错误

2.4.7 控制——循环

do-while

do {body-statement
} while (test-expr)//对应的汇编很容易理解(c-style)
loop:body-statementt = test-exprif (t)goto loop

在分析汇编循环代码时,看看能不能找到寄存器和局部变量的对应关系,另外“看看循环前如何初始化寄存器,循环中如何更新和测试寄存器,循环结束时又如何使用寄存器”

while循环

while (test-expr) {body-statement
}
//第一种 jump to middle
goto test
loop:body-statement
test:t = test-exprif (t)goto loop
//第二种 guarded-do
t = test-expr
if (!t)goto done
loop:body-statementt = test-exprif (!t)goto loop
done:
  • 第一种相当于在do-while基础上先使用无条件跳转直接先进行一次条件测试。
  • 第二种先进行条件判断,如果成立就执行do-while流程,使用较高等级(如O1)会用这种方式,

for 循环

for循环实际上可以和while循环互换

for (init-expr, test-expr, update-expr) {body-statement
}
//等价的while
init-expr
while (test-expr) {body-statementupdate-expr
}

按照上面有两种方法,就不展开了。

switch 语句

switch语句一般通过跳转表实现,如果case 的情况可以集中在小范围内,可以通过跳转表组成的索引来定位,实现上如下图所示:

2.4.8 过程

函数调用规范,需要考虑如下几个方面:

  • 传递数据:参数传递和返回值,参数传递一般会通过寄存器传递,参数较多时利用栈传递,在C中是由右向左压栈。返回值保存在固定的寄存器中。
  • 传递控制:控制PC指针,调用时确定过程地址,调用完成时指向调用时的下一条指令。
  • 分配/释放空间:除了参数,还需对局部变量,返回地址,被调方可能用到的寄存器分配和释放空间。
  • callq Lable              直接跳转
  • callq *operand        间接跳转
  • retq
  • call相当于执行 push 下一条指令地址,jmp Lable
  • ret相当于pop %rip

函数调用时栈的示意:

  • 栈的内存布局顺序要记牢
  • 当寄存器不足以存放数据时,需要使用栈上的空间,如参数,局部变量,有时候需要引用局部变量的地址或者处理数组结构体等变量时,都要在栈上分配空间

函数调用时栈的特性是:调用时分配,完成后释放,这为函数链式调用,递归调用等提供了机制

2.4.9 数组的表示

  • T A[N]                                 &A[n] = A + n = A + n * sizeof(T)
  • A[n - 8] / *(A + n + 8)           movq (8 * sizeof(T))(A, n, sizeof(T)), %reg
  • &A[n - 8]  / A + n - 8            leaq  -(8 * sizeof(T)(A, n, sizeof(T)), %reg                   addr = A + sizeof(T) * (8 - n)
  • 注意一下指针的运算
  • T A[R][C]                           A[r][c]  = *(A + r) + c = A + r *sizeof(T) * C + c * sizeof(T)
  • 使用伸缩和加法特性得到值

2.4.10 异构——结构体和联合

typedef struct node {void *value;struct node *next;
}node_t;node_t *node;
//假设node存储在%rdi中
node = node->next;
movq 8(%rdi), %rdi&node = node->next;
leaq 8(%rdi), %rdi

程序说明了在进行node = node->next,这样的操作时取的时node_t 中next域的值,node是指针变量

  • 结构体使用指针+偏移的形式
  • 对齐原则:任何K字节的基本对象的地址必须是K的倍数

编程基础(二)——汇编相关推荐

  1. python编程基础(二)~python安装设置 和 pip packages安装与使用

    目录 背景 1. 手动安装python.更改mac默认python版本 2. 手动安装pip 3. 安装package 4. package的导入 5. 升级.降级.更新package 6. 删除pa ...

  2. 第三课.python编程基础(二)

    在最开始,我记录一个内容:TPU,TPU是谷歌生产的专用于tensor计算的处理器.下面进入正式部分 条件判断与三元表达式 python中只有if,if else,if elif,if elif el ...

  3. Java编程基础二:java基本语法

    第二章 基本语法 1.关键字和保留字 1.1关键字 定义:被java语言f赋予了特殊含义,用作专门用途的字符串 特点:关键字中所有的字母都是小写 用于定义 数据类型 的关键字 class interf ...

  4. C++编程基础二 04-默认实参

    1 // C++函数和类 04-默认实参.cpp: 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include <iostr ...

  5. Linux网络编程基础(二)

    转自:http://blogold.chinaunix.net/u/4502/showart.php?id=13488 服务套和客户机的信息函数 1.字节转换函数 在网络上面有着许多类型的机器,这些机 ...

  6. C++编程基础二 03-const形参与实参

    1 // C++函数和类 03-const形参与实参.cpp: 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include < ...

  7. 负基础学python编程_【数据科学系统学习】Python # 编程基础[二]

    在上一篇中我们讲到了函数,如果你想在所编写的别的程序中重用一些函数的话,应该怎么办?正如你可能想象到的那样,答案是模块(Modules).我们这一篇就从模块说起. 模块 为了编写可维护的代码,我们把很 ...

  8. C++编程基础二 13-函数与string对象

    1 // C++函数和类 13-函数与string对象.cpp: 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include &l ...

  9. [NodeJS]Node异步编程基础

    零.前言 为什么要用Node? Node把非阻塞IO作为提高应用性能的方式.而在JS中,天生拥有着异步编程机制: 事件机制.同时JS中不存在多进程.这样当你执行相对较慢需要花费时间长的IO操作时并不会 ...

  10. c#创建画布_C#GDI+编程基础(一:Graphics画布类)

    GDI+存在的意义:将变成与具体硬件实现细节分开. GDI+步骤:获取画布,绘制图像.处理图像 命名空间: using System.Drawing;//提供对GDI+基本图形功能的访问 using ...

最新文章

  1. C# 代码注释规范文档
  2. 互联网协议 — TCP — 拥塞控制(网络质量保障)
  3. Hadoop Streaming二次排序
  4. 产品必备:注册登录完整解决方案 | 含原型下载
  5. UnisGuard防篡改产品了解
  6. C 桥接模式 - 开关和电器
  7. tohexstring方法_Java Float类toHexString()方法的示例
  8. 三类测量血压原理 - 智能手环测血压原理详解
  9. 阿里巴巴架构师,讲透2亿用户的钉钉系统架构实践
  10. 计算机二进制教案教程,计算机的二进制教案.doc
  11. HBase二级索引的设计
  12. linux怎么打开隐藏文件夹,如何在文件管理器中隐藏文件和文件夹
  13. resources下建包
  14. python变成exe1023无标题_GitHub - Qing1023/Python-100-Days: Python - 100天从新手到大师
  15. linux自动同步onedrive,Linux下同步onedrive
  16. Lyx使用对中文进行编译
  17. HTML筑基知识点四
  18. MSVC C/C++编译器选项 cl命令参数
  19. vue.js前端开发技术读书笔记二:vue数据绑定
  20. mov 与 lea 区别

热门文章

  1. DOS及BIOS中断调用实验
  2. 汪洋大海中的一块绿地
  3. python代码打错怎么删除_Python程序员最常犯的十个错误
  4. jmeter的如何设置headers
  5. 从0开始运行flutter helloworld笔记
  6. Oracle 备份与恢复学习笔记(8)
  7. 雅虎卖身不影响梅耶尔赚钱 她总薪酬2.2亿美元
  8. 用Markdown格式写一份前端简历
  9. Eclipse问题提示
  10. 三维动画制作流程之间的关系