《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理

《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理

  • 总结
  • 9.1 操作符 offset
    • 问题 9.1
  • 9.2 jmp 指令
  • 9.3 依据位移进行转移的 jmp 指令
    • jmp short 标号
      • 程序 9.1
      • 程序 9.2
        • 图 9.2 程序 9.2 的机器码
    • jmp near ptr 标号
  • 9.4 转移的目的地址在指令中的 jmp 指令
    • 如何选择 jmp short、jmp near、jmp far
  • 9.5 转移地址在寄存器中的 jmp 指令
  • 9.6 转移地址在内存中的jmp 指令
    • jmp word ptr 内存单元地址(段内转移)
    • jmp dword ptr 内存单元地址(段间转移)
    • 检测点 9.1
  • 9.7 jcxz 指令
    • 1. 循环控制
    • 2. 条件分支
    • 检测点 9.2
  • 9.8 loop 指令
    • 检测点 9.3
  • 9.9 根据位移进行转移的意义
  • 9.10 编译器对转移位移超界的检测
  • 实验 8 分析一个奇怪的程序
  • 实验 9 根据材料编程

总结

本章主要介绍了转移指令的原理,这些指令允许程序在执行过程中改变控制流程,实现程序的分支循环跳转。掌握这些概念有助于深入理解计算机程序的执行机制和内存管理。

9.1 操作符 offset

问题 9.1

在这里插入图片描述
分析:

  1. ss0 都在 CS 段,偏移量也拿到分别放在了 sidi 中。
  2. ax 通用寄存器大小1个字(2字节)够存放这条语句 mov ax, bx
  3. 先把 s 标号处的第一句复制到 ax,再从 ax 复制到 s0
assume cs:codesg codesg segments:	mov ax, bx			; mov ax,bx 的机器码占两个字节mov si, offset smov di, offset s0mov ax, cs:[si]		mov cs:[di], axs0: nop					; nop 的机器码占一个字节nop
codesg ends
end s

在这里插入图片描述

9.2 jmp 指令

jmp无条件转移指令,可以只修改 IP,也可以同时修改 CS 和 IP
jmp 有两种跳转思路:

  1. 偏移量跳。向前或向后N个字节。(相对跳转)
  2. 目录地址跳。跳到指定位置。(绝对跳转)

通过只修改 偏移地址 IP 还是同时修改 段地址和偏移地址 CS:IP 可以区分段内段间转移。

中文命令说明修改的
寄存器
例子(假设有标号叫 label
段内短转移jmp short 标号根据相对偏移量在当前段内进行跳转。
IP修改范围 -128 到 +127 (8位)。
IPjmp short label
段内近转移jmp near ptr 标号根据相对偏移量在当前段内进行跳转。
IP修改范围 -32768 到 +32767(16位)。
IPjmp near ptr label
段间转移
(远转移)
jmp far ptr 标号同时修改CSIP标号段地址偏移地址
以实现跨段跳转。
CS:IPjmp far ptr label
寄存器
间接转移
jmp 寄存器指定寄存器的值加载到IP。实现修改偏移地址
(段地址CS不变)
IPjmp dx
段内
间接转移
jmp word ptr [内存]将指定内存中的数据l读取到IP
读取长度一个 word(字,16位)
IPjmp word ptr [bx+si]
段间
间接转移
jmp dword ptr [内存]将指定内存中的数据l读取到CS:IP
读取长度一个 dword(双字,32位)
IP=低16位, CS=高16位
CS:IPjmp dword ptr ds:[0]
———————————————————————————————

9.3 依据位移进行转移的 jmp 指令

使用jmp short 还是 jmp near 取决于想要跳转到的目标相对于当前指令执行位置的距离(字节)
如果该距离在 -128 ~ 127(字节)范围内,使用 jmp short
如果该距离在 -32768 ~ 32767(字节)范围内,使用 jmp near

jmp short 标号

程序 9.1

此例中因为偏移量为 03-128 ~ 127 范围内,所以使用短转移

assume cs:codesg
codesg segmentstart:	mov ax,0		; 1. 设置 ax 为 0jmp short s		; 2. 跳到标号 s 处add ax,1		; 3. 被跳过s:	inc ax				; 4. ax 自增 1
codesg ends				; 5. 最终执行结果 ax  为 1
end start

在这里插入图片描述
如上图查看 jmp short s 反编译的机器码对应 EB03
这里 EBJMP 指令的操作码,对应 jmp short
03就是要跳过的字节数,也就是add ax,1的机器码83C001的长度。
执行后 IP + 3 字节,跳过了 add ax,1 指向 inc ax
在这里插入图片描述
单步执行,看结果 AX=0001

程序 9.2

我们把程序 9.1 改写一下,变成下面这样:

assume cs:codesg
codesg segmentstart: mov ax,0mov bx,0jmp short sadd ax,1s:	inc ax
codesg ends
end start
图 9.2 程序 9.2 的机器码

在这里插入图片描述
分析 jmp short 标号 前先回忆一下 CPU 执行指令的过程:(详见:2.10 CS和IP)

读取指令
重复
CS:IP 所指内存
指令缓冲器
IP指向下一条指令
执行当前指令

按照这个步骤,我们参照 【图 9.2】 看一下,程序 9.2 中 jmp short s 指令的读取和执行过程:

  1. (CS)=0BBDH,(IP)=0006HCS:IP 指向 EB 03( jmp short s的机器码,长度2字节);
  2. 读取指令码 EB 03 进入指令缓冲器;
  3. (IP) = (IP)+所读取指令的长度 = (IP)+2 = 0008HCS:IP 指向 add ax,1;
  4. CPU 执行指令缓冲器中的指令 EB 03;
  5. 指令 EB 03 执行后(IP+=3),(IP)=000BH,CS:IP 指向 inc ax

jmp near ptr 标号

jmp near 的偏移范围比 jmp short 更大,
如跳转的目标地址在当前代码段内,且偏移量可以使用2字节表示,则应该使用 jmp near

assume cs:codesg
codesg segmentstart:	mov ax,0		; 1. 设置 ax 为 0jmp near ptr s	; 2. 跳到标号 s 处add ax,1		; 3. 被跳过s:	inc ax				; 4. ax 自增 1
codesg ends				; 5. 最终执行结果 ax  为 1
end start

这段代码只是修改了 jmp near ptr s 一句。我们看下面截图:
虽然偏移量(操作数)还是 3 但此时占用了2字节
在这里插入图片描述

9.4 转移的目的地址在指令中的 jmp 指令

jmp far 它直接 修改CSIP实现跨段跳转

ssume cs:codesg
codesg segmentstart:	mov ax,0mov bx,0jmp far ptr sdb 256 dup (0) 		; 被跳过s:	add ax,1			; ax 中的值 +1inc ax				; ax 自增 1
codesg ends
end start

查看反汇编效果 jmp far
操作码:EA
操作数:偏移地址=010B 段地址=076C (在内存中存储,低位在前,高位在后)
在这里插入图片描述

在这里插入图片描述
单步执行,看结果 AX=0002

如何选择 jmp short、jmp near、jmp far

从设计的角度考虑,区分 jmp shortjmp nearjmp far 的主要原因是为了节省字节码空间。
根据上面的例子我们看到了:

指令机器码长度
jmp short2字节
jmp near3字节
jmp far5字节

在早期8086处理器的设计中,内存和存储器资源相对有限,因此指令大小对于程序大小和效率至关重要。
在现代处理器中,尽管内存不再是主要瓶颈,但这样的设计传统仍然保留下来,并且在某些嵌入式系统或对空间极度敏感的应用场景中仍然具有重要意义。

在实际编程中,现代汇编器会根据目标地址与当前指令之间的距离自动选择合适的跳转指令类型。程序员只需要指定目标地址,而无需关心具体的跳转指令类型。

jmp my_label
my_label:; 业务代码略

当汇编器处理这段代码时,它会计算 jmp 指令与目标标签 my_label 之间的距离。

  1. 如果距离在 jmp short 指令的偏移量范围内(1 个字节),汇编器会生成 jmp short 指令。
  2. 如果距离超过 jmp short 指令的范围,但仍在 jmp near 指令的偏移量范围内(2 个字节),汇编器会生成 jmp near 指令。
  3. 如果距离超过 jmp near 指令的范围,汇编器会生成 jmp far 指令。

9.5 转移地址在寄存器中的 jmp 指令

jmp 指令将程序的控制流跳转到 BX 寄存器中存储的地址。

assume cs:codesg
codesg segmentstart:	mov bx,s	; 把标号 s 的地址存到 bxmov ax,0jmp bx		; 跳转到 bx 寄存器中存储的地址add ax,1	; 这句被跳过s:	inc ax		; ax 自增 1
codesg ends
end start

在这里插入图片描述
在这里插入图片描述
单步执行,看结果 AX=0001

9.6 转移地址在内存中的jmp 指令

jmp word ptr 内存单元地址(段内转移)

修改 IP 段内转移

assume cs:code, ds:data
data segmentt dw offset s 		; 定义一个内存变量 t 保存标号 s 的偏移地址。
data endscode segmentstart: mov ax, data		; 数据段偏移量存入 axmov ds, ax			; 设置数据段 ds 地址jmp word ptr [t]	; 段内跳转,到内存变量 t 保存的址mov ax, 0			; 这句被跳过s: mov ax, 1			; 最终结果 ax 为 1
code ends
end start

在这里插入图片描述

jmp dword ptr 内存单元地址(段间转移)

同时修改 CS:IP 段间转移

assume cs:code, ds:data
data segmentt dd 12345678h
data endscode segmentstart: mov ax, data		; 数据段偏移量存入 axmov ds, ax			; 设置数据段 ds 地址jmp dword ptr [t]	; 段内跳转,到内存变量 t 保存的址
code ends
end start

在这里插入图片描述
这里就确认一下 jmp 后 CSIP都正确的修改了。

检测点 9.1

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.1

9.7 jcxz 指令

指令格式:jcxz 标号
jcxz(Jump if CX is Zero)指令在x86汇编语言中主要用于条件跳转,当CX寄存器的值为0时执行跳转。否则啥也不做,继续执行下一句。(所有的有条件转移指令都是短转移,对 IP 的修改范围都为: -128 ~ 127
它的常见用途包括:循环控制、条件分支

1. 循环控制

在编写循环结构时,jcxz可以用来检测循环计数器是否已经减至0,从而决定是否结束循环。
如下示例代码,循环累加 AX

assume cs:code
code segmentstart:	mov ax,0		; 初始化 ax 为 0mov cx,3		; 初始化循环次数 3 到 CX寄存器loop_start:; 循环体开始inc ax			; ax 自增; 循环体结束dec cx          ; 每次循环后递减CXjcxz loop_end   ; 如果 CX为0,则跳转到 loop_endjmp loop_start	; 否则跳转到 loop_start 处继续下一次循环loop_end:mov ax,4c00Hint 21H
code ends
end start

等价于JS的 do{...}while(ax != 0)

var ax = 0, cx = 3;
do { console.log(++ax);
} while (--cx);
#include <stdio.h>int main() {int ax = 0, cx = 3;do {printf("Current value: %d\n", ++ax);} while (--cx); // cx 不等于 0 就继续return 0;
}

2. 条件分支

在某些算法或数据处理中,CX可能被当作条件判断的标志位。当CX代表某种条件成立与否时,可以用jcxz来进入相应的处理分支。

assume cs:code
code segmentstart:mov cx,0			; 将标志变量加载到 CX 寄存器中jcxz branch_B   	; 如果 CX 为 0,则跳转到 分支Bbranch_A:mov ax,6666Hjmp branch_end		; 跳转到结束branch_B:mov ax,3333Hbranch_end:mov ax,4c00Hint 21H
code ends
end start

分别演示 cx01 的效果。
在这里插入图片描述 在这里插入图片描述
总之,jcxz是一个用于简化基于CX寄存器值进行条件跳转的指令,它在需要根据CX是否为0来进行决策的场景中非常有用。不过,现代编程中往往使用高级循环结构和条件判断语句,但在底层编程和特定优化场合,jcxz仍有其独特的应用价值

检测点 9.2

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.2

9.8 loop 指令

loop指令是一种条件循环指令,主要用于实现计数型循环。它依赖于CX寄存器作为循环计数器。

  1. 在执行loop指令之前,需要先将循环次数(通常是递减的次数)加载到CX寄存器中。
  2. 每次执行loop 标号时,CPU会自动将CX寄存器中的值减1
  3. 然后检查CX寄存器是否为0
    3.1. 如果CX != 0,则跳转到标号指定的地址处继续执行循环体内的代码;
    3.2. 否则CX == 0,程序流程将继续向下执行,即跳出循环。

所有的循环指令都是短转移,对 IP 的修改范围都为:-128 ~ 127
偏移量的计算方式为:标号地址 - loop 下一句的地址
以补码形式保存,在编译时获得。

loop 标号 的功能相当于:

(cx)--;
if((cx) != 0){jmp short 标号;
}

检测点 9.3

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.3

9.9 根据位移进行转移的意义

jmp short 标号jmp near ptr 标号jcxz 标号loop 标号 这几种汇编指令,都是相对跳转。
在对应的机器码中不包含转移的目的地址,而是包含到目的地址的偏移量(字节)。
这种设计,方便了程序段在内存中的浮动装配,如:
在这里插入图片描述
这段代码中,loop s 用的相对跳转,记录的是 -4(字节) 而不是 s 的地址,这样无论这段程序加载到内存中任何位置,都能正常跳到 s
但如果使用 s 的目标地址来跳转,比如 s 的地址是2000:0000那这个地址就被限制死了。
这段程序只能加载到内存这个位置才能正常跑。如果这段内存被其它程序占用,就没法玩了。

FC说明
11111100补码
11111011反码 = 补码 - 1
10000100原码 = 反码按位取反(最左侧的符号位不动)
-4结果为 -4

9.10 编译器对转移位移超界的检测

根据位移进行相对跳转的指令,转移范围都是有限制的,
编译器会在编译时检测越界问题,如果触发会报·编译错误
所以我们只要知道偏移量是如何计算即可。实际操作中编译器在编译时会自动算好。

实验 8 分析一个奇怪的程序

《汇编语言》- 读书笔记 - 实验8 分析一个奇怪的程序

实验 9 根据材料编程

《汇编语言》- 读书笔记 - 实验9 根据材料编程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/470792.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux platform tree下的单总线驱动程序设计(DHT11)

目录 概述 1 认识DHT11 1.1 DHT11特性 1.2 DHT11数据格式 1.3 DHT11与MCU通信 1.4 DHT11信号解析 1.4.1 起始信号 1.4.2 解析信号0 1.4.3 解析信号1 2 驱动开发 2.1 硬件接口 2.2 更新设备树 2.2.1 添加驱动节点 2.2.2 编译.dts 2.2.3 更新板卡中的.dtb 2.3 驱…

问题:内存时序参数 CASLatency 是() #学习方法#微信#微信

问题&#xff1a;内存时序参数 CASLatency 是&#xff08;&#xff09; A&#xff0e;行地址控制器延迟时间 B&#xff0e;列地址至行地址延迟时间 C&#xff0e;列地址控制器预充电时间 D&#xff0e;列动态时间 参考答案如图所示

84 CTF夺旗-PHP弱类型异或取反序列化RCE

目录 案例1&#xff1a;PHP-相关总结知识点-后期复现案例2&#xff1a;PHP-弱类型对比绕过测试-常考点案例3&#xff1a;PHP-正则preg_match绕过-常考点案例4&#xff1a;PHP-命令执行RCE变异绕过-常考点案例5&#xff1a;PHP-反序列化考题分析构造复现-常考点涉及资源&#xf…

倒模UV树脂胶制作舞台监听耳返入耳式耳机壳可行吗?

使用倒模UV树脂胶制作舞台监听耳返入耳式耳机壳是一种可行的方法&#xff0c;能够为专业或业余的音乐制作人、DJ和舞台表演者提供定制的、高品质的监听耳返体验。 以下是一些关键步骤和注意事项&#xff1a; 耳模制作&#xff1a;首先&#xff0c;为使用者制作一个精确的耳模…

操作 Docker 存储卷的常用指令汇总

1. 什么是存储卷&#xff1f; 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。使得可以在宿主机和容器内共享数据库内容&#xff0c;让容器直接访问宿主机中的内容&#xff0c;也可以宿主机向容器写入内容&#xff0c;容…

TMGM官网平台开户运作流程如下:

TMGM官网平台开户运作流程如下&#xff1a; 首先&#xff0c;投资者需要注册并登录TMGM官网平台。在平台上&#xff0c;投资者可以选择适合自己的交易账户类型&#xff0c;包括标准账户、高级账户等。 然后&#xff0c;投资者需要进行身份验证和资金入账操作。TMGM会要求投资…

问题:从完整的问题解决过程来看,( )是首要环节。A.理解问题 B.提出假设C.发现问题 D.检验假设 #学习方法#学习方法

问题&#xff1a;从完整的问题解决过程来看&#xff0c;&#xff08; &#xff09;是首要环节。A&#xff0e;理解问题 B&#xff0e;提出假设C&#xff0e;发现问题 D&#xff0e;检验假设 A.理解问题 B.提出假设 C&#xff0e;发现问题 参考答案如图所示

leetcode刷题之或操作使用场景

文章目录 概要题目问题分析小结 概要 今天晚上上床前刷了一个leetcode的题目&#xff0c;是leetcode的2103题&#xff0c;因为是个简单题&#xff0c;我只是想复习一下hash表的用法。结果反而让我看到了或操作的使用场景。 题目 总计有 n 个环&#xff0c;环的颜色可以是红、…

黑马Java——异常、File、综合案例

目录 一、异常 1、异常的分类 1.1、Error 1.2、Exception 1.3、小结 2、编译时异常和运行时异常 2.1、编译时异常 2.2、运行时异常 2.3、为什么异常要分成编译时异常和运行时异常&#xff1f; 2.4、小结&#xff08;运行时异常和编译时异常的区别&#xff09; 3、异…

c语言(指针进阶)

指针 一.什么是字符指针二.使用指针数组模拟二维数组三.函数指针 一.什么是字符指针 字符指针&#xff1a;指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。即将字符串的首地址赋值给字符指针&#xff0c;可让字符指针…

力扣---通配符匹配

题目描述&#xff1a; 给你一个输入字符串 (s) 和一个字符模式 (p) &#xff0c;请你实现一个支持 ? 和 * 匹配规则的通配符匹配&#xff1a; ? 可以匹配任何单个字符。 * 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 判定匹配成功的充要条件是&#xff…

GoJS可视化JavaScript库讲解

1.简介 GoJS是一个可视化JavaScript库&#xff0c;用于浏览器中创建交互图形&#xff0c;&#xff08;比如流程图&#xff0c;树图&#xff0c;关系图&#xff0c;力导图等等&#xff09;。允许您为用户构建各种图表&#xff0c;从简单的流程图、组织图到图表、SCADA和BPMN图表…