【问题标题】:`testl` eax against eax?`testl` eax 对 eax?
【发布时间】:2010-09-13 21:56:30
【问题描述】:

我正在尝试理解一些程序集。

汇编如下,我感兴趣的是testl这一行:

000319df  8b4508        movl   0x08(%ebp), %eax  
000319e2  8b4004        movl   0x04(%eax), %eax  
000319e5  85c0          testl  %eax, %eax  
000319e7  7407          je     0x000319f0  

我想了解%eax%eax 之间的testl 点?我认为这段代码的细节并不重要,我只是想自己理解测试 - 值不总是正确的吗?

【问题讨论】:

    标签: assembly x86 instructions


    【解决方案1】:

    它测试eax 是否为0,或高于或低于0。在这种情况下,如果eax 为 0,则进行跳转。

    【讨论】:

    • 我进行了编辑,将这个流行的答案变成了一个更好的规范答案,即“这个 TEST 是什么,它与 CMP 有何不同”,这是隐含的。关于同义词 JE 和 JZ 的语义含义,请参阅我自己的答案进一步了解 cmets。请查看我的编辑,因为它非常重要,它仍然是您的答案。
    • @PeterCordes 我很欣赏你的意图,但我会恢复你的编辑。 1. 你的“声音”和我的很不一样,现在它读起来更像你的答案而不是我的答案。 2. 更成问题的是在testcmp 之间出现的标志完全相同的大胆断言。是的,我知道这是您对科迪的信任。但是,将其放在我的帖子中是另一回事;这不是我愿意支持的断言,只是因为我不知道它是否在所有情况下都相同。
    • @PeterCordes 如果我找到一些空闲时间,我确实想充实这个答案,使其更加规范。不过,我会像写它一样写它,而且我对如何写东西非常讲究。 :-) 例如,我会写 jejzcmptest,而不是 JE、JZ、CMP 或 TEST。我就是那样挑剔。
    • 我并没有试图提升自己的答案。我实际上忘记了我在进行编辑时自己回答了这个问题,后来才注意到。我只是在有人撞到它之后才看到它,开始的一个小编辑变得太多了。没有冒犯你想回滚它;这只是一个建议,它绝对像我的作品,而不是你的作品。我将把我写的一些内容放到我自己的答案中。
    • 哇,在编辑了我对这个问题的答案以包括我添加到你的答案之后,我意识到我几乎完全复制了我在六月写的大部分内容。哎呀!我用更多的理由更新了它,以支持我的主张,即test a,acmp $0,a 设置相同的标志;感谢您指出这是一个重要的主张。回复:测试与test:最近我开始使用像英特尔手册这样的全大写字母。但是当我谈论 AT&T 助记符与英特尔助记符时,我使用testb 风格来表示 AT&T。 IDK 如果这有助于提高可读性。
    【解决方案2】:

    test 的含义是将参数与在一起,并检查结果是否为零。所以这段代码测试 EAX 是否为零。 je 为零时会跳转。

    顺便说一句,这会生成比cmp eax, 0 更小的指令,这就是编译器通常会这样做的原因。

    【讨论】:

      【解决方案3】:

      如果 eax 为零,则执行条件跳转,否则将在 319e9 处继续执行

      【讨论】:

        【解决方案4】:

        测试指令在操作数之间进行逻辑与运算,但不将结果写回寄存器。仅更新标志。

        在您的示例中,测试 eax,如果 eax 为零,则 eax 将设置零标志,如果设置了最高位,则设置符号标志以及其他一些标志。

        如果设置了零标志,则跳转(je)指令跳转。

        您可以将代码翻译成更易读的代码,如下所示:

        cmp eax, 0
        je  somewhere
        

        这具有相同的功能,但需要一些字节更多的代码空间。这就是编译器发出测试而不是比较的原因。

        【讨论】:

        • 实际上,cmp 在那里可能不起作用。也就是说,它适用于呈现的特定情况,但 cmp 对标志的影响与 test 不同,因为它是一个内部子而不是 and。需要记住的一点。
        • 针对零的测试是完全有效的。
        • 但是后面看flags就不知道了。对标志的影响非常不同,因此这可能是一个问题,而且非常频繁。
        • 不,唯一由不同 /method/ 设置的标志是进位和溢出,两者都设置为 0。其他标志的 /values/ 会有所不同,因为 cmp 使用 sub 和测试使用和。
        • @CodyBrocious: test eax, eaxcmp eax, 0 都设置了所有标志,并将它们设置为相同的值。两条指令都“根据结果”设置所有标志。减去0 永远不会产生进位或溢出。您的论点对于 0 以外的任何立即数都是正确的,但对于 0 则不正确。
        【解决方案5】:

        这段代码的 sn-p 来自一个子程序,它被赋予了一个指向某个东西的指针,可能是某个结构或对象。第二行取消引用该指针,从该事物中获取一个值 - 可能本身是一个指针,也可能只是一个 int,存储为其第二个成员(偏移量 +4)。第 3 行和第 4 行测试该值是否为零(如果是指针则为 NULL),如果为零则跳过以下几个操作(未显示)。

        零测试有时被编码为与立即字面零值的比较,但编写此代码的编译器(或人类?)可能认为 testl 操作会运行得更快 - 考虑到所有现代 CPU 的东西,比如流水线和寄存器重命名。它来自同一个技巧,包含使用 XOR EAX,EAX 清除寄存器的想法(我在科罗拉多州的某人的车牌上看到!)而不是明显但可能较慢的 MOV EAX,#0(我使用较旧的符号)。

        在 asm 中,如 perl、TMTOWTDI。

        【讨论】:

          【解决方案6】:

          testand 类似,不同之处在于它只写入 FLAGS,而其两个输入均未修改。使用两个 不同 输入,这对于测试某些位是否全为零或是否至少设置了一个很有用。 (例如,test al, 3 如果 EAX 是 4 的倍数,则设置 ZF(因此其低 2 位均为零)。


          test eax,eax 设置所有标志的方式与cmp eax, 0 完全相同

          • CF 和 OF 清零(AND/TEST 总是这样做;减去零永远不会产生进位)
          • ZF、SF 和 PF 根据 EAX 中的值。 (a = a&a = a-0)。
            (PF照常is only set according to the low 8 bits

          除了过时的 AF(辅助进位标志,由 ASCII/BCD 指令使用)。 TEST leaves it undefined,但CMP sets it "according to the result"。由于减零不能产生从第 4 位到第 5 位的进位,因此 CMP 应始终清除 AF。


          TEST 更小(不是立即的),有时更快(在比 CMP 更多的情况下,可以在更多 CPU 上宏融合到比较和分支微指令中)。 That makes test the preferred idiom for comparing a register against zero。这是cmp reg,0 的窥视孔优化,无论语义如何,您都可以使用它。

          使用立即数为 0 的 CMP 的唯一常见原因是当您想与内存操作数进行比较时。例如,cmpb $0, (%esi) 检查隐式长度 C 样式字符串末尾的终止零字节。


          AVX512F 添加 kortestw k1, k2 和 AVX512DQ/BW(Skylake-X 但不是 KNL)添加 ktestb/w/d/q k1, k2,它们在 AVX512 掩码寄存器 (k0..k7) 上运行,但仍设置常规 FLAGS,例如test 的作用与整数 ORAND 指令的作用相同。 (有点像 SSE4 ptest 或 SSE ucomiss:在 SIMD 域中输入并生成整数 FLAGS。)

          kortestw k1,k1 是基于 AVX512 比较结果分支 /cmovcc / setcc 的惯用方式,替换 SSE/AVX2 (v)pmovmskb/ps/pd + testcmp


          jzje 的使用可能会造成混淆。

          jz and je are literally the same instruction,即机器码中的操作码相同。 它们做同样的事情,但对人类有不同的语义含义。反汇编程序(通常是编译器的 asm 输出)只会使用一个,因此语义区别会丢失。

          cmpsub 在两个输入相等(即减法结果为 0)时设置 ZF。 je(如果相等则跳转)是语义相关的同义词。

          test %eax,%eax / and %eax,%eax 在结果为零时再次设置 ZF,但没有“相等”测试。测试后的 ZF 不会告诉您两个操作数是否相等。所以jz(如果为零则跳转)是语义相关的同义词。

          【讨论】:

          • 我会考虑添加关于test 的基本信息是按位and 操作,对于刚学习汇编的人来说可能并不明显(并且懒惰/不知道每 60 秒检查一次指令参考指南; ) :) ).
          • @Ped7g:很公平,我想将所有内容都放在这个答案中并没有什么坏处,而不是将那部分留给其他答案。在我使用的时候添加了 AVX512 kortest*ktest*
          • 顺便说一句,这与my answer to another version of the same question 基本相同,但我在那里说了更多关于性能的内容,例如可能通过用相同的值重写寄存器来避免像 Nehalem 这样的旧 P6 系列 CPU 上的寄存器读取停顿。
          • @PeterCordes 这应该是公认的答案:详尽和技术性。与接受的职位不同,这可以消除一个人的好奇心和对知识的渴望。坚持下去,先生。
          • 需要注意的是PF设置为低8位的奇偶校验,本例为AL。
          【解决方案7】:

          在某些程序中,它们可用于检查缓冲区溢出。 在分配空间的最顶部放置一个 0。将数据输入堆栈后,它会在分配空间的最开头查找0,以确保分配的空间没有溢出。

          在exploit-exercises的stack0练习中使用它来检查它是否溢出,如果没有溢出并且那里有一个零,它会显示“再试一次”

          0x080483f4 <main+0>:    push   ebp
          0x080483f5 <main+1>:    mov    ebp,esp
          0x080483f7 <main+3>:    and    esp,0xfffffff0
          0x080483fa <main+6>:    sub    esp,0x60                     
          0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack
          0x08048405 <main+17>:   lea    eax,[esp+0x1c]
          0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
          0x0804840c <main+24>:   call   0x804830c <gets@plt>
          0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c] 
          0x08048415 <main+33>:   test   eax,eax                  ; checks if its zero
          0x08048417 <main+35>:   je     0x8048427 <main+51>
          0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500 
          0x08048420 <main+44>:   call   0x804832c <puts@plt>
          0x08048425 <main+49>:   jmp    0x8048433 <main+63>
          0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
          0x0804842e <main+58>:   call   0x804832c <puts@plt>
          0x08048433 <main+63>:   leave
          0x08048434 <main+64>:   ret
          

          【讨论】:

          • 我看不出这种检查寄存器是否为非零的特定情况会增加这个问答。尤其是当cmp DWORD PTR [esp+0x5c], 0 / jz 0x8048427 &lt;main+51&gt; 比单独的 MOV 加载然后测试更有效时。这几乎不是检查零的常见用例。
          【解决方案8】:

          我们可以看到jgjle 如果testl %edx,%edx. jle .L3我们很容易发现jle适合(SF^OF)|ZF,如果%edx为零,ZF=1,但如果%edx不为零且为-1,经过testl,OF =0,SF =1,所以标志=true,实现跳转 .sorry ,我的英语很差

          【讨论】:

            猜你喜欢
            • 2022-01-15
            • 2012-10-15
            • 2011-02-11
            • 2017-01-26
            • 2013-09-10
            • 1970-01-01
            • 1970-01-01
            • 2019-03-30
            相关资源
            最近更新 更多