IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> x86的编码格式 -> 正文阅读

[Python知识库]x86的编码格式

1 x86的编码格式

x86采取可变长度的方式进行编码,其主要原因应是8086的机器性能不高,导致编码越短越好。而8086的编码模式相较于x64较简单,且x64兼容x86编码,所以,以下阐述将由8086一直讲到x64

1.1 8086编码

8086采取以下编码格式

在这里插入图片描述

其中opcode是必须的,其他的是可选的

opcode

opcode的格式前6位表示操作,后2位分为d or s以及w

在这里插入图片描述

Mod R/M

在这里插入图片描述

Mod R/M表示寻址方式,下表表示寻址的类型

ModRM.mod寻址模式描述
00[base]内存寻址,提供[base]形式的memory寻址
01[base+disp8]内存寻址,提供[base+disp8]形式的memory寻址
10[base+disp16]内存寻址,提供[base+disp32]形式的memory寻址
11register两个寄存器之间赋值

首先以mod=11为例进行阐述
首先制定reg域的编码规则,此规则适用于任何mod方式

REGW=0W=1
000ALAX
001CLCX
010DLDX
011BLBX
100AHSP
101CHBP
110DHSI
111BHDI

假设指令为mov %sp %bp则指令序列为89 e5,根据上述的格式可知

89为opcode,e5ModRM,对89进行分析

1000,1001==>[1000,00]为opcode
         ==>[01]为DW,其中W表示16位的值

e5进行分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oxZJsKYA-1655714796572)(./images/regreg.png)].

下面以mod==00与mod=01为例进行解释

  • mod==00表示直接按照内存基址的方式进行访问

    R/M译码
    000[BX+SI]
    001[BX+DI]
    010[BP+SI]
    011[BP+DI]
    100[SI]
    101[DI]
    110
    111[BX]

SI为源变址寄存器

DI为目的编制寄存器

这两个寄存器在8086中有特殊用途,此处不涉及,不赘述

由于ModR/M中的可执行操作可分为

  1. Register -> Memory
  2. Memory -> Register

因此,不管是何种方式,只要是有内存相关的数据,一定是放在r/m域中,而单纯寄存器相关的放在Register

同理也可指mod=01及mod=10,其译码值只需要查表可知

1.2 x86指令编码

x86系列编码一直秉持着向下兼容的概念,同时又要支持新的寄存器及编码,其处理方式为在原有的8086的编码规则之上在指令头部加入prefix域,当译码器读到该域中的对应值时即可知当前为x86编码,x86编码的prefix值如下

编码值含义
66H改变指令期望的数据的默认大小,例如由16位变32位
67H改变指令期望的地址的默认大小,例如由16位变32位
2EHCS寄存器
3EHDS寄存器
26HES寄存器
64HFS寄存器
65HGS寄存器
26HSS寄存器
F3HREP,REPE,重复指令,其数量由ECX决定,当ECX=0时停止
F2H中止循环,当ZF标志位赋值
F0H保证指令将有专门的共享内存使用,直到指令完全结束
这保证了在x86多处理同时处理的情况下,不会在单条指令操做受到干扰

其他的部分与8086编码逻辑一致,改变的只有数据的长度,可通过查表进行编码

1.3 x64编码

x64比x86不仅通用寄存器多了8个,更重要的是数据及地址长度可以扩展到64位,因此,其编码也较x86更复制,然后,秉承着向下兼容的原则,其编码方式在原有的prefix的基础上增加了REX prefix语法,将在x86下的prefix更名为legacy prefix,这也意味着当指令被译码的时候,如果碰到x86的头,依然以x86的方式进行译码,而碰到REX prefix的头时,则以x64的方式进行译码。

在这里着重指出的是legacy prefix和REX prefix共用同块空间。

在这里插入图片描述

x64最大可以允许16字节的指令,但目前为止,最大的也就15个字节

代表x64特殊的前缀为

0100, WRXB

也就是以4打头,后面跟WRXB位的指令,其中

W:表示wide,此处如果是1,则表示操作的对象是64位

R:表示Register,用于扩展原有的8个通用寄存器到16个寄存器,根据8086编码规则可知,在Mod R/M域中,reg域的取值范围位[0,7],如果加上R的[0,1],则整个的取值范围就可以扩展至[0000B, 1111B]共16个元素

X: 表示Extention,用于扩展SIB的Index域(后面会对SIB进行分析)

B:用于扩展Mod R/M中的r/m域,使得通用寄存器范围可以从8个扩展到16个,且访存也从8个扩展到16个

其搭配如下图所示

在这里插入图片描述

例1

现在以mov %rsp %rbp为例进行阐述

首先这是寄存器之间的传值,因此可知mod==11,另外,寄存器使用的是64位的,所以,W=1,通过反汇编上述的汇编语句可知

48 89 e5             	mov    %rsp,%rbp

拆解上述的语句

在这里插入图片描述

查寄存器编码表

_.Reg  Register
----------------
0.000   RAX
0.001   RCX
0.010   RDX
0.011   RBX
0.100   RSP
0.101   RBP
0.110   RSI
0.111   RDI
1.000   R8
1.001   R9
1.010   R10
1.011   R11
1.100   R12
1.101   R13
1.110   R14
1.111   R15

可知R.100所代表的寄存器是RSP, B.101所代表的寄存器是RBP

符合前面8086的编码规则,只不过加了一个前缀

例2

现在做一个访存的例子mov %rsp 10(%rbp)

通过gcc和objdump后获得以下的输出

48 89 65 0a          	mov    %rsp,0xa(%rbp)

在这里插入图片描述

可知R.100所代表的寄存器是RSP, B.101所代表的寄存器是RBP,由于没有对地址进行[base + index*scale]进行计算,因此SIB为0,偏移为10。

例3

访存mov %rsp (%rbp,%rax),由objdump出来的指令如下

48 89 64 05 00       	mov    %rsp,0x0(%rbp,%rax,1)

此处可以看到,目的值为M[%rbp + %rax *1 + 0],分析指令值

在这里插入图片描述

首先知道reg=R.100表示的是RSP

目的值需要通过查表得出计算方法,再根据指令值计算得出

现在给出对应的mod表,查表得出对应的操作

在这里插入图片描述

reg=B.100为[SIB+disp8]

很明显,disp8=0,SIB的值为05H

根据SIB的定义

在这里插入图片描述

其中SS表示的是scale,其取值范围为1.2.4.8

Encoded value
(binary)
scale factor
001
012
104
118

Index表示的是索引寄存器的编号

base表示的是基地址寄存器的编号

以本例而言,其SIB的值为05H,也就是说

在这里插入图片描述

SS为0,index为0号寄存器(rax),base为5号寄存器(rbp),所以,计算得出的有效地址为

effective_address = 1 * rax + rbp + 00

符合汇编语言的要求

总结一下

  • 寄存器间传值,Mod为11,且SIB,Disp及Imm都不需要

  • 当进行访存时,Mod值的取值范围为{00,01,10},SIB为可选项

    • 如果只是单纯的在基址上进行常数偏移,则SIB不需要

    • 如果进行可变寻址,则SIB需要,且SIB中的S取值范围为

      Encoded value
      (binary)
      scale factor
      001
      012
      104
      118

补全Mod R/M编码表

在这里插入图片描述
在这里插入图片描述

其他常用指令

由于是常用指令,往往是单独针对寄存器或者内存进行操作,因此不需要上述的模型,而是直接给出操作码的方式,经常会出现只需要1个字节即可完成所需的操作。

  • push

    push %rsp为例,根据objdump打印出来的结果

    54                   	push   %rsp
    

    查询AMD 64手册

    push reg64对应的操作码50+rq,而rsp的寄存器编号为4

  • pop

    5c                   	pop    %rsp
    

    查询AMD 64手册

    pop reg64对应的操作码58+rq

  • call

    以下列C程序

    int bar()
    {
    	return 100;
    }
    
    int foo(){
    	int i = bar();
    	return i;
    }
    int main(){
    
    	int i = foo();
    	return i;
    }
    

    首先用gcc -S来查看生成的汇编代码

    .file	"main.c"
    	.text
    	.globl	bar
    	.type	bar, @function
    bar:
    .LFB0:
    	.cfi_startproc
    	endbr64
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	movl	$100, %eax
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	bar, .-bar
    	.globl	foo
    	.type	foo, @function
    foo:
    .LFB1:
    	.cfi_startproc
    	endbr64
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	subq	$16, %rsp
    	movl	$0, %eax
    	call	bar
    	movl	%eax, -4(%rbp)
    	movl	-4(%rbp), %eax
    	leave
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE1:
    	.size	foo, .-foo
    	.globl	main
    	.type	main, @function
    main:
    .LFB2:
    	.cfi_startproc
    	endbr64
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	subq	$16, %rsp
    	movl	$0, %eax
    	call	foo
    	movl	%eax, -4(%rbp)
    	movl	-4(%rbp), %eax
    	leave
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE2:
    	.size	main, .-main
    	.ident	"GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
    	.section	.note.GNU-stack,"",@progbits
    	.section	.note.gnu.property,"a"
    	.align 8
    	.long	 1f - 0f
    	.long	 4f - 1f
    	.long	 5
    0:
    	.string	 "GNU"
    1:
    	.align 8
    	.long	 0xc0000002
    	.long	 3f - 2f
    2:
    	.long	 0x3
    3:
    	.align 8
    4:
    

    可以看到call bar

    然后再用objdump反汇编出指令看看有什么区别

    main.o:     file format elf64-x86-64
    
    
    Disassembly of section .text:
    
    0000000000000000 <bar>:
       0:	f3 0f 1e fa          	endbr64 
       4:	55                   	push   %rbp
       5:	48 89 e5             	mov    %rsp,%rbp
       8:	b8 64 00 00 00       	mov    $0x64,%eax
       d:	5d                   	pop    %rbp
       e:	c3                   	retq   
    
    000000000000000f <foo>:
       f:	f3 0f 1e fa          	endbr64 
      13:	55                   	push   %rbp
      14:	48 89 e5             	mov    %rsp,%rbp
      17:	48 83 ec 10          	sub    $0x10,%rsp
      1b:	b8 00 00 00 00       	mov    $0x0,%eax
      20:	e8 00 00 00 00       	callq  25 <foo+0x16>
      25:	89 45 fc             	mov    %eax,-0x4(%rbp)
      28:	8b 45 fc             	mov    -0x4(%rbp),%eax
      2b:	c9                   	leaveq 
      2c:	c3                   	retq   
    
    000000000000002d <main>:
      2d:	f3 0f 1e fa          	endbr64 
      31:	55                   	push   %rbp
      32:	48 89 e5             	mov    %rsp,%rbp
      35:	48 83 ec 10          	sub    $0x10,%rsp
      39:	b8 00 00 00 00       	mov    $0x0,%eax
      3e:	e8 00 00 00 00       	callq  43 <main+0x16>
      43:	89 45 fc             	mov    %eax,-0x4(%rbp)
      46:	8b 45 fc             	mov    -0x4(%rbp),%eax
      49:	c9                   	leaveq 
      4a:	c3                   	retq
    

    其中call bar的指令为

    e8 00 00 00 00       	callq  25 <foo+0x16>
    

    查询AMD 64手册可知

    CALL re/16off       E8 iw
    

    call指令的操作码为E8,E8直接读PC值,这样就跳到bar函数

    后面所跟的值是当调用结束之后下一条指令相对当前指令的偏移量(这里没有Mod和SIB,因为不需要通过寄存器计算访存的位置),而这个偏移量计算公式为

    displacement = destination address - address of next instruction
    

    其中displacement采用的是大端的方式,应转换为小端表达,下面的例子更加明显

    #include <stdio.h>
    int main(void) {
      printf("Hello, world!\n");
      printf("Hello, world!\n");
      return 0;
    }
    

    objdump出来的结果

       /helloworld.c:3
         804842c:       83 ec 0c                sub    $0xc,%esp
         804842f:       68 f0 84 04 08          push   $0x80484f0
         8048434:       e8 b7 fe ff ff          call   80482f0 
         8048439:       83 c4 10                add    $0x10,%esp
        /helloworld.c:4
         804843c:       83 ec 0c                sub    $0xc,%esp
         804843f:       68 f0 84 04 08          push   $0x80484f0
         8048444:       e8 a7 fe ff ff          call   80482f0 
         8048449:       83 c4 10                add    $0x10,%esp
    

    其中

    8048434:       e8 b7 fe ff ff          call   80482f0
    

    The opcode for this instruction is E8, followed by the relative offset that is computed by the following equation: destination address - address of next instruction.

    In this case, the relative offset of the first call is 80482f0 - 8048439 = FFFFFEB7, and the relative offset of the second call is 80482f0 - 8048449 = FFFFFEA7.

  • ret

    查询AMD 64测试可知

    C3
    

    实际执行如图

在这里插入图片描述

  • add,sub,mul

    三种计算指令则涉及了寄存器及访存操作,其编码规则可参考mov,opcode查询手册,下面以add指令为主进行讲解

       0:	48 01 c3             	add    %rax,%rbx
       3:	48 03 04 25 0a 00 00 	add    0xa,%rax
       a:	00 
       b:	48 01 03             	add    %rax,(%rbx)
       e:	48 01 04 18          	add    %rax,(%rax,%rbx,1)
      12:	48 01 44 58 0a       	add    %rax,0xa(%rax,%rbx,2)
    
    • add %rax, %rbx

      48 01 c3             	add    %rax,%rbx
      

      48是REX prefix, 01是opcode, c3是mod r/m,即

在这里插入图片描述

R.000是rax

B.011是rbx
  • add 0xa,%rax

    48 03 04 25 0a 00 00 	add    0xa,%rax
    

    编码如图

在这里插入图片描述

  • add %rax,(%rbx)

    48 01 03             	add    %rax,(%rbx)
    

在这里插入图片描述

  • div

    div是一条比较特殊的指令,该指令的被除数放在RAX寄存器中,除数指定为寄存器或者内存地址(这个实验没做出来)

    以下面的例子说明

    48 f7 f0 	divq   %rax
    

    查询AMD64 手册可知除法的opcode是F7/6
    在这里插入图片描述

    第二个字节中的高4位全部为1,低4位为寄存器编号

1.4 Intel MMX技术

MMX技术是Intel公司为增强 CPU 在音像、图形和通信应用方面而采取的技术,MMX技术是继Intel386?处理器(将体系结构扩展至32位)之后对Intel体系结构的最重要的加强。这些技术的指令能够加速处理有关图形、影像、声音等的应用,MMX 加强了在多媒体处理功能的不足,它可以利用其内建的多媒体指令来模拟3D绘图的处理、 MPEG的压缩/解压缩,立体声的音效等,只要是软件支持MMX CPU,即可以取代这些硬件的接口而达到多媒体的功效。

MMX技术是在CPU中加入了特地为视频信号(Video Signal),音频信号(Audio Signal)以及图像处理 (Graphical Manipulation)而设计的一套基本的、通用的整数指令、单指令、多数据(SIMD)技术,可简便地应用于各种多媒体及通信应用程序。

1.5 SSE Instructions

SSE instructions are an extension of the SIMD execution model introduced with the MMX technology. SSE instructions are divided into four subgroups:

大部分涉及到128位内存变量操作的,内存变量首地址必须要对齐16字节,也就是内存地址低4位为0,否则会引起CPU异常,导致指令执行失败,此错误编译器不检查.

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-06-26 16:52:04  更:2022-06-26 16:53:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 16:02:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码