程序的机器级表示——Intel x86 汇编讲解

往期地址:

本期主题:

程序的汇编指令讲解


目录

  • [1. x86-64的历史由来](#1. x86-64的历史由来)
  • [2. x86-64的寄存器信息](#2. x86-64的寄存器信息)
  • [3. 操作数指示符](#3. 操作数指示符)
    • [1. 寻址方式](#1. 寻址方式)
    • [2. 数据传输指令](#2. 数据传输指令)
      • [example 实例](#example 实例)
    • [3. 压入和弹出栈数据](#3. 压入和弹出栈数据)

1. x86-64的历史由来

  1. Intel处理器系列俗称x86系列,刚开始是单芯片,16位微处理器,后面随着集成电路技术不断提高,现在都是64位的处理器,称为x86-64;
  2. 由于是从16位体系结构扩展成32位的,Intel用术语 字(words) 来表示16位数据类型,因此,32位被称为双字(double words),64位称为 四字(quad words)

下图是C语言类型在 x86-64位中的大小

C语言类型 Intel数据类型 汇编代码后缀 大小(字节)
char 字节 b 1
short w 2
int 双字 l 4
long 四字 q 8
char* 四字 q 8
float 单精度 s 4
double 双精度 l 8

大多数gcc生成的汇编代码都有一个字符的后缀,表明操作数的大小。例如数据传输指令mov有四种变种:

  1. movb(传送字节)
  2. movw(传输字)
  3. movl(传输双字)
  4. movq(传输四字)

2. x86-64的寄存器信息

x86-64的机器代码和C语言代码相差非常大,C语言程序员比较关注的如下:

  • 程序计数器(通常也被称为"PC",在x86-64中用%rip来表示),表示将要执行的下一条指令在内存中的地址;
  • 通用目的寄存器 ,一个x86-64的中央处理器包含一组16个存储64位值的通用目的寄存器;
  • 栈指针寄存器,%rsp用来指明运行时栈的结束位置;
  • 帧指针寄存器 ,%rbp所直接指向的数据是调用该函数之前的rbp值,即可以认为是被调用函数的返回值

寄存器如下图:

3. 操作数指示符

大多数指令有一个或多个操作数(operand),指示出执行一个操作中要使用的源数据值,放置位置以及目的位置。

1. 寻址方式

根据操作数的可能性,将寻址方式分为三种类型:

  1. 立即数,用来表示常数值,例如 $1234,就是代表常数1234;
  2. 寄存器 ,表示某个寄存器的内容,我们用符号 r a r_a ra 来表示任意寄存器,用符号 R r a r_a ra 来表示它的值;
  3. 内存引用,根据计算出来的地址访问某个内存位置;

寻址模式综合一下:

2. 数据传输指令

将数据从一个位置复制到另一个位置的指令。

MOV 指令的简单形式如下,代表着将数据从 S移动到D这个位置:

armasm 复制代码
MOV S,D 

MOV指令由四条指令组成:movb、movw、movl、movq,对应着我们前面说的汇编指令后缀名的差异,其实主要差别在于操作数的大小不同

MOV操作指令有几个特点:

  1. 源操作数指定的是一个数据,这个数据可以是 立即数,也可以存储在寄存器或者内存中;
  2. 目的操作数指定的是一个位置,要么是寄存器或者是一个地址;
  3. mov指令不能两个操作数都指向内存地址,如果想要实现将一个值从一个内存地址复制到另一个内存地址,需要使用两条指令------第一条指令将源值加载到寄存器中,第二条指令将寄存器值写入目的位置;

举例看下面的命令,实际上的意思就是将 0x4050这个数据,放在eax寄存器中

armasm 复制代码
movl $0x4050, %eax

example 实例

看一个指针的具体例子

源码:

c 复制代码
long exchange(long *xp, long y)
{
	long x = *xp;
	*xp = y;
	return x;
}

对应的反汇编代码,使用 objdump -d xxx 来进行反汇编:

对汇编代码进行解释:

armasm 复制代码
// 前面讲过了,%rdi存的是第一个参数,%rsi 存的是第二个参数
// 在例子中具体具体就是  %rdi存的是xp这个指针,%rsi存的是y
// (%rdi),取出xp指针的值,即*xp
mov (%rdi),%rax , 把*xp赋值给返回值x
mov %rsi,(%rdi) , 把y赋值给*xp

从上面的汇编代码中可以看出两点:

  1. C语言的指针其实就是地址。间接引用指针其实就是将该指针放在一个寄存器中,然后在内存引用中使用这个寄存器。
  2. 像x这样的局部变量通常保存在寄存器中,而不是内存中。由于访问寄存器比访问内存要快得多。

3. 压入和弹出栈数据

栈是一种数据结构,可以添加或者删除值,遵循后进先出的原则。通过push把数据压入栈中,通过pop操作删除数据。

栈向下增长,栈顶元素的地址是所有栈中元素地址最低的。%rsp保存着栈顶元素的地址。

指令 效果 描述
pushq S R%rsp <- R%rsp - 8; MR\[%rsp] <- S 将四字压入栈 1. 先是取出%rsp寄存器的值,并减8,再给回%rsp寄存器; 2. 将S的值给到新的%rsp寄存器的值所指向的内容中
popq D D <- MR\[%rsp] ; R%rsp <- R%rsp + 8 将四字弹出栈 1. 先是取出%rsp寄存器的值,给到8; 2. 将%rsp寄存器+8
相关推荐
han_hanker4 小时前
mybatis-plus 常用方法
java·tomcat·mybatis
长不胖的路人甲5 小时前
CAS讲解
java
我是唐青枫5 小时前
Java SLF4J 实战指南:从日志门面到 Logback、MDC 和链路追踪
java·开发语言·logback
XS0301065 小时前
JavaEE 核心知识
java·java-ee
微硬创新5 小时前
耐达讯自动化PROFINET转DeviceNet网关:医药包装生产线的合规与效率双保障
服务器·人工智能·物联网·网络协议·自动化·信息与通信
weixin_446729166 小时前
java实现发送邮件
java·开发语言
云计算磊哥@6 小时前
运维开发宝典055-大型网站nginx服务器管理全集1
服务器·nginx·运维开发
土星云SaturnCloud6 小时前
SAM2模型在土星云边缘计算设备上的部署实战(下):从原理到落地全解析
服务器·人工智能·ai·边缘计算
QCoding6 小时前
Java转AI第三课:Spring AI如何把100行HTTP代码缩减到10行?
java·人工智能
振宇i6 小时前
Java集成openfeign远程RPC调用教程
java·rpc·openfeign