当前位置: 首页 > 知识库问答 >
问题:

条件跳转是如何工作的,基于x86程序集中标志的值?

羊舌高峰
2023-03-14

我正在阅读Jeff Duntemann的汇编语言分步,我对一些条件跳转的工作原理感到困惑。我知道CMP用于使用减法比较两个值,然后丢弃结果以设置标志。

有没有办法确定哪些标志需要设置/取消设置?我了解JE和JNE的情况,它查看是否设置了ZF,但我不确定其他分支操作。

这是我被卡住的部分:

ClearLine:
    pushad                  ; Save all caller’s GP registers
    mov edx,15              ; We’re going to go 16 pokes, counting from 0
    .poke:  mov eax,1   
    sub edx,1
    jae .poke               ; Loop back if EDX >= 0
    popad                   
    ret                     `

如果EDX,为什么JAE会回环

共有1个答案

丁俊爽
2023-03-14

根据问题中的措辞,您的困惑似乎至少有一部分源于未正确区分比较指令和条件跳转指令。CMP首先设置标志,然后根据标志的状态进行条件跳转分支。有许多不同的指令设置标志(几乎所有算术和位指令都设置标志;有关详细信息,请参阅每个指令的文档),但这些指令都没有任何分支。为了基于标志进行分支,需要一条Jcc指令(其中cc是条件代码,指示它将检查的标志,例如ae,意思是“高于或等于”)。

我之所以指出这一点,是因为你说的是:

所以通过基本上说CMP-edx,1,我们不是说“如果第一个操作数(edx)大于或等于第二个操作数(1)”?

这可能只是描述实际发生的事情的捷径,但仍然——这是一个不正确的心理模型,不可避免地会导致混淆。CMP指令从不进行任何跳跃。它所做的只是设置标志。您是正确的,它设置标志与减法(SUB)完全一样,但标志不会做任何事情,直到您执行Jcc指令读取它们并相应地分支。

虽然您已经理解了它们,但我们将从JE和JZ开始,因为它们是最容易理解的条件。这些只需查看零标志(ZF),并根据其状态进行分支JE与JZ完全等价。只有两种不同的助记符,程序员可以根据他们认为可以使代码更清晰、更容易阅读的内容进行选择。例如,当你做CMP时,跟随JE通常是有意义的,因为从逻辑上讲,如果两个值相等,你就会跳转。从技术上讲,如果减法的结果是0,那么实际上是跳跃,因为CMP设置了像SUB这样的标志,所以这就是为什么它百分之百等同于写JZ,你不会经常看到程序员这样做。相反,当你做一些类似于测试reg,reg的事情时,你经常会看到后面跟着JZ,因为如果最后一个操作的结果为零,那么将其视为跳跃更具语义。在条件中添加“not”具有明显的效果。

您可以在这里找到一个非常有用的条件分支指令表。我仍然发现自己在定期查阅这张桌子或类似的东西。作为初学者,最有用的是对助记符的文字描述。作为一个更高级的程序员,最有用的是将助记符映射到所检查的实际标志。(实际的代码字节有时也很方便。)

如您所见,JAE表示“高于或等于时跳转”,这由进位标志的状态决定(CF)。如果未设置进位,则将执行分支。如果设置了进位,则执行将失败。正如表中所示,这对于无符号比较非常方便。为什么?因为这就是进位标志的用途。我刚刚写了一个很长的答案,解释这里的进位和溢出标志。它比您需要的详细一点,但仍然包含相关的位,例如这些标志的定义。

你还会在那张图中看到,JAE有多个助记符,就像我们在JE和JZ中看到的那样。替代助记符是JNB和JNC。第一个是JNB,这与JAE正好相反。如果一个值高于或等于另一个值,则它也不低于该值<代码>JNC只是对跳转所基于的标志的更文字化描述:进位标志。再说一次,从技术上讲,使用哪一个并不重要,但如果您仔细选择,它通常会使您的代码在语义上更加正确和可读。

有了这种概念上的理解,让我们更详细地看看您的代码:

    mov edx, 15
.poke:
    mov eax, 1
    sub edx, 1
    jae .poke

(我不喜欢你的格式,所以我稍微重写了一下。:-p)显然,这将EDX设置为15,然后进入循环。在循环内部,它从EDX中减去1,并设置标志。然后,下面的JAE指令查看标志的状态并分支回。当且仅当未设置进位标志(CF)时,戳(继续循环)。

另一种思考方式是,当且仅当EDX中的值高于或等于1时,循环才会继续。象征性地说,这只是:EDX

让我们浏览EDX的几个示例值,以确保我们理解逻辑。

第一次通过循环,当EDX为15时,SUB指令从15中减去1。结果当然是14。因此,零标志(ZF)设置为0(因为结果是非零)。进位标志(CF)设置为0,因为没有进位(没有无符号溢出)。溢出标志(OF)设置为0,因为没有有符号溢出。符号标志(SF)设置为0,因为结果是无符号的(其符号标志,即最高有效位,未设置,表示值为正)。根据CF的状态,JAE将分支回. poke并继续循环。从逻辑上讲,您将继续循环,因为EDX(15)中的值高于或等于1。

同样的事情持续了一段时间。我们让循环旋转,然后在EDX为1时中断循环。现在,SUB指令从1中减去1。结果为0。因此,ZF为1(结果为零),OF为0(未发生有符号溢出),CF为0(无进位,即无符号溢出),SF为0(结果为无符号)。那么,树枝会被拿走吗?是的,CF是0。从逻辑上讲,1大于或等于1(当然等于)。

下一次,EDX为0,因此将从0中减去1。结果为−1.ZF为0(结果为非零),OF为0(未发生有符号溢出),CF为1(发生进位,即无符号溢出),SF为1(结果为有符号)。这次不执行分支,因为CF是1。从逻辑上讲,这是有意义的,因为0不大于或等于1(请记住,这是一个无符号比较)。

这就是它总共循环16次的原因。它在EDX为15时循环,并在EDX为0时继续循环。这是因为您的条件测试位于循环的底部。也就是说,在C表示法中:

do
{
    ...
}
while (edx-- >= 1);

 类似资料:
  • 这与这个问题有关 想想看,在现代英特尔中央处理器上,证券交易委员会阶段是在微码中实现的,这意味着将有一个检查,通过该检查,一个烧毁的密钥被用来验证PEI ACM上的签名。如果它不匹配,那么它需要做一些事情,如果它匹配,它需要做其他事情。鉴于这是作为MSROM过程实现的,必须有一种分支方式,但鉴于MSROM指令没有RIP。 通常,当分支误预测为被采用时,则当指令失效时,ROB将检查异常代码,并因此将

  • 我相信一个好的答案将适合于一个大的SO答案,或者至少在几个答案中。 主要目标是给完全的初学者提供足够的信息,使他们能够自己使用手册,并且能够理解与分页相关的基本OS概念。 建议的准则: null x86虚拟化是如何工作的:body只要求提供源代码。

  • 如果我正在开发一个相当简单的基于Spring Boot控制台的应用程序,那么我不确定主执行代码的位置。我应该将其放置在public static void main(String[]args)方法中,还是让主应用程序类实现CommandLineRunner接口并将代码放置在run(String…args)方法中? 我将使用一个示例作为上下文。假设我有以下[基本]应用程序(编码为接口,Spring样

  • 收集了mit Jooq的multiset的第一次经验,我试图弄清楚如何根据multiset的一些标准来排序结果集。考虑一个带有Product表的datastructure,每个产品可以分配任意数量的存储。 使用以下查询获取产品时: 如何根据多集的条件对结果进行排序。 例如,把那些已经分配存储的放在第一位。 给出所需的结果,但对我来说,不清楚实际的排序比较在做什么。 谢谢你的帮助! 问候你,安德烈亚

  • 但实际结果似乎有所不同--TestArray包含FullArray的所有三个条目,而TestStr是'xxxx' 我在这里漏掉了什么?有什么变通办法吗?

  • 我正在阅读《实用恶意软件分析》一书,其中出现了以下示例代码: 作者接着说: 返回的COM对象将存储在堆栈中IDA Pro标记为ppv的变量中,如图所示。 我的问题是,这是为什么?既然我们做了一个mov eax,[esp 24h ppv],这难道不是将[esp 24h ppv]内部的数据移动到eax并覆盖返回值,而不是将返回值存储在变量中吗?我认为在Intel格式中,mov操作数1、操作数2总是将第