热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

编程基础(二)——汇编

目录一、概述二、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.

目录

 

一、概述

二、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 数据格式

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


charbbyte
shortwword
intllong word / doubleword
longqquad word
char *qquad 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栈指针
%ripPC
  
  

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的倍数

 

 


推荐阅读
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 《2017年3月全国计算机等级考试二级C语言上机题库完全版》由会员分享,可在线阅读,更多相关《2017年3月全国计算机等级考试二级C语言上机题库完全版( ... [详细]
  • c语言基础编写,c语言 基础
    本文目录一览:1、C语言如何编写?2、如何编写 ... [详细]
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼*madebyebhrz*#include#include#include#include#include#include#include ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了PHP常量的定义和使用方法,包括常量的命名规则、大小写敏感性、全局范围和标量数据的限制。同时还提到了应尽量避免定义resource常量,并给出了使用define()函数定义常量的示例。 ... [详细]
  • 本文介绍了在Java中检查字符串是否仅包含数字的方法,包括使用正则表达式的示例代码,并提供了测试案例进行验证。同时还解释了Java中的字符转义序列的使用。 ... [详细]
author-avatar
zealyw
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有