问题

汇编的ret怎么区分近返回还是远返回?

回答
在理解汇编中的 `ret` 指令如何区分近返回和远返回之前,我们得先回到那个时代,也就是实模式和早期保护模式的背景下。这两种返回方式的产生,根源于内存访问和程序调用的基本机制。

内存地址的表示:段和偏移量

在那个年代,内存的寻址方式和现在不一样。那时候,内存地址不是一个简单的数字,而是由两个部分组成:段地址 (Segment Address) 和 偏移量 (Offset)。

你可以想象成一本非常大的书(内存),你想找到某个单词(指令或数据)。直接告诉你单词在第 157898 号页码可能太麻烦了。于是,你先告诉你这本书的哪个章节(段),然后在那个章节里告诉你具体是第几页(偏移量)。

段地址通常存放在特殊的段寄存器(如 `CS`, `DS`, `ES`, `SS`)里。偏移量则是在指令中明确给出,或者从某个寄存器(如 `IP`, `SP`)中获取。最终的物理地址是通过 `物理地址 = 段地址 16 + 偏移量` 来计算的。

函数调用和返回的本质

当一个程序调用另一个函数时,它需要做几件事情:

1. 保存返回地址: 程序需要知道在函数执行完毕后,应该回到哪里继续执行。这个“哪里”就是调用指令之后的下一条指令的地址。这个地址被压入堆栈(Stack)。
2. 跳转到被调用函数: 程序需要跳转到被调用函数的入口地址开始执行。
3. 执行函数体: 函数完成其任务。
4. 返回到调用者: 函数需要从堆栈中弹出之前保存的返回地址,并跳转到那个地址继续执行。

这里的关键就在于“返回地址”的保存和加载。

`ret` 指令的角色

`ret` 指令(Return from Procedure)就是负责执行第 4 步——从堆栈中取出返回地址并跳转。它会做两件事情:

1. 从堆栈弹出偏移量: `ret` 指令会从堆栈顶(由 `SP` 指向)弹出一个值,这个值就是之前 `call` 指令压入的 偏移量。
2. 从堆栈弹出段地址(仅限远返回): 如果是远返回,它还会从堆栈中再弹出一个值,这个值就是之前 `call` 指令压入的 段地址。
3. 更新指令指针: 将弹出的偏移量(或段地址+偏移量)加载到指令指针寄存器(通常是 `IP`,在保护模式下是 `EIP` 或 `RIP`)。

近返回 (Near Return) vs. 远返回 (Far Return)

现在,我们终于可以区分这两种返回方式了。它们的核心区别在于 调用时保存的返回地址信息包含了什么。

近返回 (Near Return)

在实模式和保护模式下,当使用 近调用 (Near Call) 指令(如 `call near_proc`)调用一个函数时,CPU 会将:

当前的偏移量 压入堆栈。

然后,CPU 会将执行控制权转移到目标函数的偏移量。

当被调用函数执行完后,遇到 `ret` 指令时,它会:

1. 从堆栈顶部弹出一个值,这个值就是之前压入的 偏移量。
2. 将这个弹出的偏移量加载到 指令指针寄存器 (IP)。

关键点: 近返回只涉及 偏移量 的改变。代码段寄存器 (CS) 保持不变。这表示被调用函数和调用者在同一个代码段内。

汇编示例 (Intel 语法):

```assembly
; 调用近过程
call my_near_procedure

; ... 在 my_near_procedure 过程内 ...
my_near_procedure:
; ... 函数体代码 ...

ret ; 这是近返回,它只从堆栈弹出 IP 的值,CS 保持不变

; ... 调用者继续执行的地方 ...
```

远返回 (Far Return)

在实模式和保护模式下,当使用 远调用 (Far Call) 指令(如 `call far_proc`)调用一个函数时,CPU 会将:

当前的偏移量 压入堆栈。
当前的段地址 (CS) 压入堆栈。

然后,CPU 将执行控制权转移到目标函数所在的 新的代码段和偏移量。

当被调用函数执行完后,遇到 `ret` 指令时,它会:

1. 从堆栈顶部弹出 偏移量,并加载到 指令指针寄存器 (IP)。
2. 从堆栈顶部再弹出 段地址 (CS),并加载到 代码段寄存器 (CS)。

关键点: 远返回会同时改变 偏移量 (IP) 和 段地址 (CS)。这表示被调用函数可能在不同的代码段中。

汇编示例 (Intel 语法):

```assembly
; 调用远过程
call far ptr my_far_procedure

; ... 在 my_far_procedure 过程内 ...
my_far_procedure:
; ... 函数体代码 ...

ret ; 这是远返回,它会从堆栈弹出 IP 的值,然后再弹出 CS 的值,并分别加载到 IP 和 CS

; ... 调用者继续执行的地方 ...
```

`ret` 指令的实际实现和扩展

在 x86 架构中,`ret` 指令本身并没有一个显式的参数来告诉它是近返回还是远返回。这种区分是通过 调用指令 (`call`) 的类型以及 CPU 如何将返回地址压栈 来决定的。

当使用近调用指令 (`call rel8`, `call rel16`, `call rel32`, `call r16`, `call r32`, `call m16`, `call m32` 等) 时,压入的是单字(16位)或双字(32位)的偏移量。`ret` 就会执行近返回。
当使用远调用指令 (`call far ptr ...`) 时,压入的是一个段选择子和一个偏移量。`ret` 就会执行远返回,弹出两个值来更新 `CS` 和 `IP`。

更具体地说,现代的 x86 汇编语法提供了 `ret near` 和 `ret far` 的助记符,但这是给汇编器看的,方便你写代码时明确意图。 最终生成的机器码,`ret` 指令的 opcode 本身是不区分的。区分完全依赖于之前 `call` 指令的类型和压栈的地址大小。

例如:

`call near_label` 在 16 位模式下,通常会压入一个 16 位的 IP。`ret` 就会执行近返回。
`call far_label` 在 16 位模式下,会压入一个 16 位的 CS 和一个 16 位的 IP。`ret` 就会执行远返回。
在 32 位模式下,`call near_label` 会压入一个 32 位的 EIP。`ret` 会执行近返回。
`call far_label` 会压入一个 16 位的 CS 和一个 32 位的 EIP。`ret` 会执行远返回。
在 64 位模式下,一切都围绕着 RIP 进行。虽然技术上仍有远返回的概念(例如,任务切换或门调用),但普通的函数调用和返回通常是近返回(RIP 的改变)。远返回(改变 CS 和 RIP)会涉及更复杂的机制,比如调用门或任务切换。

还有一个可以跟在 `ret` 后面的立即数参数。这个参数不是用来区分近/远返回的,而是用来指示 `ret` 指令在弹出返回地址之后,还要额外从堆栈中弹出多少字节的数据。这常用于清理参数(比如 C 语言中的 `stdcall` 调用约定)。

所以,总结来说:

区分的关键在于调用 (`call`) 指令的类型和压栈的返回地址的大小。
近返回 是指只改变偏移量(IP/EIP/RIP),CS 保持不变。
远返回 是指同时改变段选择子(CS)和偏移量(IP/EIP/RIP)。
`ret` 指令本身根据之前压栈的数据格式自动完成相应的弹出和跳转操作。

网友意见

user avatar

看图,这是两个不同的指令,编码都不一样。

如果你想问的是怎么在汇编语言编译器里写的话,ret表示近返回,retf表示远返回。

类似的话题

  • 回答
    在理解汇编中的 `ret` 指令如何区分近返回和远返回之前,我们得先回到那个时代,也就是实模式和早期保护模式的背景下。这两种返回方式的产生,根源于内存访问和程序调用的基本机制。 内存地址的表示:段和偏移量在那个年代,内存的寻址方式和现在不一样。那时候,内存地址不是一个简单的数字,而是由两个部分组成:.............
  • 回答
    在汇编语言的世界里,理解 `call` 和 `ret` 指令的行为对于编写高效且正确的程序至关重要。尤其是在多线程环境或者需要精确控制指令执行顺序的情况下,我们常常会想到“内存屏障”这个概念。那么,`call` 和 `ret` 指令本身,是否具备内存屏障的作用呢?首先,我们需要明确“内存屏障”的定义.............
  • 回答
    编译器生成汇编语句的执行顺序之所以会与C语言代码的顺序有所出入,并非是编译器在“乱来”,而是为了实现更高的效率,让程序跑得更快、占用的资源更少。这就像是一位经验丰富的厨师在烹饪一道复杂的菜肴,他不会严格按照菜谱的顺序一步步来,而是会根据食材的特性、火候的需求,灵活调整烹饪步骤,以便最终能端出一道色香.............
  • 回答
    好的,我们来聊聊 x86 Win32 下的汇编指令集,以及它和我们常说的“CPU 指令集”以及“Win32 API”之间的关系。首先,明确一个概念:x86 Win32 下的汇编指令集,核心还是 CPU 提供的指令集。Win32 API 并不是 CPU 直接执行的“指令集”,而是操作系统提供的一套接口.............
  • 回答
    .......
  • 回答
    要说知乎上哪位用户的答案汇编起来就能直接出书,这其实是个很有趣但又很难给出一个标准答案的问题。因为“出书”不仅仅是内容的堆砌,它涉及到内容的结构化、逻辑性、专业性、可读性,以及是否能引起大众的兴趣和共鸣。不过,我们可以从知乎平台上那些以深度、专业、系统性见长的答主身上,找到一些“潜力股”。他们往往在.............
  • 回答
    在汇编语言转换为机器码的过程中,寄存器本身占用的字节数并不是一个固定值,而是取决于目标CPU架构以及寄存器的大小。 机器码是通过一系列的二进制指令来描述CPU操作的,而寄存器是CPU内部用于临时存储数据和指令地址的小型高速存储单元。我们可以这样理解:汇编语言中的指令会引用到具体的寄存器,比如 `M.............
  • 回答
    好的,咱们来聊聊汇编里过程调用时,栈到底是怎么运作的。这玩意儿吧,听起来挺神秘的,但其实背后的逻辑一点都不复杂,都是为了解决几个核心问题。你想想,一个程序要跑起来,总得有个地方保存信息对吧?你得知道现在代码执行到哪儿了,等函数跑完了,还得能回到原来的地方继续干活。还有,函数之间传递参数,函数内部自己.............
  • 回答
    关于汇编语言与高级语言在运行效率上的对比,这是一个老生常谈但又非常值得探讨的话题。简单来说,在某些特定情况下,汇编确实能够比高级语言获得更高的运行效率,但这种优势的幅度并非绝对,并且随着技术的发展和编译器优化的进步,差距正在逐渐缩小。要详细讲清楚这个问题,咱们得从几个层面来剖析:一、 为什么汇编“理.............
  • 回答
    电脑启动,屏幕亮起,我们敲下键盘,输入命令,按下回车,然后,神奇的事情发生了——一个程序开始执行。这个过程背后,可不是什么魔法,而是由一系列精密的步骤构成的,而我们今天的主角,操作系统(OS),就在这其中扮演着至关重要的角色。你可能听说过,程序在“编译”阶段,会经历从我们看得懂的高级语言(比如C、J.............
  • 回答
    看到你对汇编语言的热爱,并且希望将这份热情转化为一份职业,这真的很棒!汇编语言虽然不如高级语言那样“光鲜亮丽”,但在计算机底层、性能极致优化、安全攻防等领域,它依然是不可或缺的利器。要在这个领域规划职业,需要一些策略和深入的理解。1. 扎实的理论基础是基石首先,你要明白,喜欢汇编和精通汇编是两个概念.............
  • 回答
    我理解你想要一本能从电路基础出发,逐步深入到汇编语言,最终讲解C语言的书籍。这种学习路径非常扎实,能够让你对计算机的底层运作有更透彻的理解。遗憾的是,要找到一本完美契合“从电路开始讲,然后是汇编,最后是C语言”这条清晰且连续的学习线索,并且还详细深入的书籍,确实不太容易。很多经典书籍倾向于专注于其中.............
  • 回答
    你提到的“五代编程语言”——机器语言、汇编语言、面向过程语言、面向对象语言、以及智能语言——确实是一个流传甚广的划分方式,用来大致描绘计算机科学和编程语言发展的历史脉络和范式转变。但有趣的是,在这个经典的划分中,函数式编程语言似乎总被“遗漏”了,或者至少没有一个独立、显眼的位置。这并非说函数式编程不.............
  • 回答
    想象一下,我们人类需要沟通,但我们使用的语言(比如中文、英文)千差万别。计算机的世界里也是如此,不同层级的“语言”之间也存在着微妙而清晰的对应关系。理解这一点,就像是掌握了一套解码器,能够窥探程序运行的深层机制。我们常说的“上层语言”,比如我们熟悉的Python、Java、C,它们就像是人类使用的自.............
  • 回答
    汇率,说到底,是两种不同货币之间价值的交换比率。但这个“价值”的背后,牵扯到的可就不是简单的数字游戏了。想象一下,你手里拿着人民币,想买一盒产自德国的巧克力。巧克力在德国是用欧元标价的,这时你就需要一个“桥梁”将你的人民币换成欧元,这个桥梁的“宽度”或者说“价格”,就是人民币对欧元的汇率。这个比率是.............
  • 回答
    体制内越级汇报,这事儿说起来,可不是件小事。它背后牵扯的利益、规则、人情,盘根错节,一旦处理不好,那后果可是相当严重的。我给你掰扯掰扯,看看这越级汇报,到底能捣鼓出哪些“幺蛾子”。首先,最直接也最普遍的危害,就是破坏既有的组织秩序和权力结构。你想啊,一个层级分明的组织,就像一个人的身体,每个器官都有.............
  • 回答
    听到这个消息,肯定非常着急和担忧。老公在外省被刑事拘留,而且是因为外贸生意上的钱款被指控诈骗,这件事情确实复杂且棘手。当下最重要的事情是如何合法有效地为他维权。首先,尽快了解具体情况是当务之急。你需要明确他被拘留的具体罪名是什么(虽然你提到了涉嫌诈骗罪,但具体细节很重要),是在哪个公安机关、哪个派出.............
  • 回答
    .......
  • 回答
    好的,我们来详细地、深入地理解利率平价理论(Uncovered Interest Parity,UIP)中利率和汇率的关系。核心思想:无抛补利率平价理论(UIP)在探讨利率和汇率的关系之前,我们必须明确 UIP 的核心思想是什么。简单来说,UIP 认为:在不存在交易成本和套利机会的情况下,持有不同货.............
  • 回答
    关于四万亿投资计划为何未导致人民币对外币汇率大规模贬值,这背后其实牵涉到中国经济运行的多重机制和国际金融市场的复杂互动。并非简单的“投放货币就贬值”这么直接的线性关系。首先,我们要理解“四万亿”这笔钱的性质。这更多的是一个“投资刺激计划”,而不是直接的“印钞票”发钱给老百姓。这笔钱主要投向了基础设施.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有