试试看?

我在试着理解一些集合。

装配情况如下,我对 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点?我认为这段代码的细节并不重要,我只是试图理解测试本身-它的值不总是正确的吗?

89819 次浏览

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

test的意思是将参数“ AND”在一起,并检查结果是否为零。因此,这段代码测试 EAX 是否为零。如果0,je将跳跃。

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

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

测试指令在操作数之间执行逻辑 AND 操作,但不将结果写回寄存器。只更新了标志。

在您的示例中,test eax 将设置0标志(如果 eax 为零)、符号标志(如果设置了最高位)和其他一些标志。

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

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

cmp eax, 0
je  somewhere

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

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

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

就像 perl,TMTOWTDI。

test 类似于 and,只不过它只写 FLAGS,而不修改它的两个输入。对于两个 与众不同输入,它有助于测试一些比特是否全部为零,或者是否至少设置了一个比特。(例如,如果 EAX 是4的倍数,则 test al, 3设置 ZF (因此它的低2位都归零)。


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

  • CF 和 OF 清除(AND/TEST 总是这样做; 减去零永远不会产生进位)
  • 根据 EAX 值计算 ZF、 SF 和 PF。(a = a&a = a-0)。
    (PF 一如往常 只根据低8位设置)

除了过时的 AF (由 ASCII/BCD 指令使用的辅助进位标志)。但是 CMP“根据结果”设置它。由于减去零不能产生从第4位到第5位的进位,CMP 应该总是清除 AF。


TEST 更小(不立即) ,有时更快(在比 CMP 更多的情况下,可以将宏融合到更多 CPU 上的比较和分支运行中)。这使得 test成为比较寄存器和零的首选成语.这是一个 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) ,但仍然像 test那样设置常规 FLAGS,就像整数 ORAND指令所做的那样。(类似于 SSE4ptest或 SSEucomiss: SIMD 域中的输入并导致整数 FLAGS。)

kortestw k1,k1是基于 AVX512比较结果进行分支/covcc/setcc 的惯用方法,取代了 SSE/AVX2 (v)pmovmskb/ps/pd + testcmp


使用 jzje可能会造成混淆。

jzje实际上是相同的指令 ,即机器码中的相同操作码。它们做同样的事情,但对人类有不同的语义.反汇编程序(通常是从编译器输出的)将永远只使用一个,因此语义区别就消失了。

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

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

在一些程序中,它们可以用来检查缓冲区溢出。 在分配的空间的最顶部放置一个0。在将数据输入到堆栈中之后,它将在所分配的空间的开始处查找0,以确保所分配的空间不会溢出。

它被用于堆栈的功能练习-练习检查它是否溢出,如果没有,并有一个零在那里,它会显示“再试一次”

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

我们可以看到 JG,< strong > jle 如果 testl %edx,%edx. jle .L3很容易找到适合 (SF^OF)|ZFJle,如果% edx 为0,ZF = 1,但是如果% edx 不是0,而是 -1,在 testl 之后,OF = 0,SF = 1,所以标志 = true,实现了跳转 。对不起,我的英语很差