123

我试图了解一些装配。

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

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

我试图理解testl介于%eax和之间的那个点%eax?我认为这段代码的细节并不重要,我只是想用它本身来理解测试——这个值不总是正确的吗?

4

8 回答 8

95

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

于 2008-09-29T01:23:43.687 回答
95

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

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

于 2008-09-29T01:24:49.877 回答
35

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

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

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

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

cmp eax, 0
je  somewhere

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

于 2008-09-29T01:29:03.297 回答
26

test就像and,除了它只写 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照常只根据低8位设置

除了过时的 AF(辅助进位标志,由 ASCII/BCD 指令使用)。 TEST 将其保留为 undefined,但CMP 将其设置为“根据结果”。由于减零不能产生从第 4 位到第 5 位的进位,CMP 应始终清除 AF。


TEST 更小(不立即),有时更快(可以在比 CMP 更多的情况下在更多 CPU 上宏融合到比较和分支微指令中)。 这使得test将寄存器与零进行比较的首选习语。这是一个窥视孔优化,cmp reg,0无论语义如何,您都可以使用它。

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


AVX512F addkortestw k1, k2和 AVX512DQ/BW (Skylake-X 但不是 KNL) add ktestb/w/d/q k1, k2,它们在 AVX512 掩码寄存器 (k0..k7) 上运行,但仍然像test整数ORAND指令一样设置常规 FLAGS。(有点像 SSE4ptest或 SSE ucomiss:在 SIMD 域中输入并产生整数 FLAGS。)

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


jzvs.的使用je可能会令人困惑。

jz并且je实际上是相同的指令,即机器代码中的相同操作码。 它们做同样的事情,但对人类有不同的语义。反汇编程序(通常是编译器的 asm 输出)只会使用一个,因此语义区别会丢失。

cmpsub在它们的两个输入相等(即减法结果为0)时设置ZF。 je(jump if equal) 是语义相关的同义词。

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

于 2016-06-25T20:45:19.103 回答
5

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

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

在 asm 中,与 perl、TMTOWTDI 一样。

于 2008-09-29T03:40:00.550 回答
3

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

于 2008-09-29T01:25:07.787 回答
0

在某些程序中,它们可用于检查缓冲区溢出。在分配空间的最顶部放置一个 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
于 2016-12-06T21:19:36.093 回答
-4

我们可以看到jg,<strong>jle 如果testl %edx,%edx. jle .L3我们很容易发现jle是西装(SF^OF)|ZF,如果 %edx 为零,ZF=1,但如果 %edx 不为零且为-1,则在 testl 之后,OF=0,而SF =1,所以flag =true,实现跳转。对不起,我的英语很差

于 2017-03-14T07:35:26.293 回答