2017-07-27 40 views
3

我通过拆卸win32的C++程序读,我看到不少:“AND AL,0xFF”的用途是什么?

AND AL,0xFF 

这是完全没有意义的,或者为什么会编译器生成这些?

这是一个较长的例子:

movsx eax, byte ptr [ebx] 
shl  eax, 18h 
movsx edx, byte ptr [ebx+1] 
shl  edx, 10h 
add  eax, edx 
movsx ecx, byte ptr [ebx+2] 
shl  ecx, 8 
add  eax, ecx 
movsx edx, byte ptr [ebx+3] 
add  eax, edx 
xor  edx, edx 
call sub_43B55C 
mov  ecx, eax 
mov  edx, eax 
sar  ecx, 10h 
and  al, 0FFh  # <---- 
sar  edx, 8 
and  cl, 0FFh  # <---- 
mov  [esi], cl 
and  dl, 0FFh  # <---- 
mov  [esi+1], dl 
mov  [esi+2], al 
add  ebx, 4 
add  esi, 3 
inc  ebp 
cmp  ebp, 6 
jl  short loc_43B5E4 

标志不能被这些操作之后检查以致不能成为目的。在AND之后,AL,CLDL中的值正被移至[ESI + n]

+2

不知道之前发生了什么,很难说。假设EAX是一个指针,它需要在一个16字节的边界上对齐。这将设置ZF为真,因此指令的要点是设置标志。或者,如果编译器设置适当,可能会使用'test al,0xff'。 –

+0

感谢您的评论。标志没有被检查。 ANDing似乎在将字节移到内存之前发生。我使用周围的代码更新了上下文。 – user35358

+3

似乎很奇怪。也许这条指令是用于填充或作为钩子的标记。 – fuz

回答

4

正如@fuz所建议的那样,这只是优化程序不认识到foo & 0xff是在原始函数中最可能使用的上下文中没有操作的错误。

我编译下面的代码片段与Borland C++ Builder中6设置项目的编译设置,以“释放”后:

unsigned char foobar(int foo) { return (foo >> 16) & 0xff; } 

这类似于操作中你颇有渊源提供的拆卸进行。我们有一个32位的值,我们想要移位一个给定的位数,然后把它变成一个字节值,基本上将原始值的16-23位作为一个字节返回。输入参数的类型为int,以便生成sar指令而不是shr:在原始代码中最有可能还使用了int

编制,并与objconv拆卸造成的obj(因为我无法弄清楚如何实现从C++ Builder的IDE内汇编清单)后,我得到这个:

@foobar$qi PROC NEAR 
; COMDEF @foobar$qi 
     push ebp          ; 0000 _ 55 
     mov  ebp, esp        ; 0001 _ 8B. EC 
     mov  eax, dword ptr [ebp+8H]     ; 0003 _ 8B. 45, 08 
     sar  eax, 16         ; 0006 _ C1. F8, 10 
     and  al, 0FFFFFFFFH       ; 0009 _ 24, FF 
     pop  ebp          ; 000B _ 5D 
     ret            ; 000C _ C3 
@foobar$qi ENDP 

正如你所看到的,多余的and仍然存在。反汇编中的32位立即数可以忽略不计,因为指令的编码清楚地表明实际码流中的立即数是8位:无论如何8位寄存器没有其他有效选项。

Microsoft Visual Studio C++ 6似乎有同样的问题,但在整个32位寄存器上运行(因此由于32位立即产生更多3个字节),清除高位 - 这是不必要的,参观了解函数的返回值被明确宣布为8位:

[email protected]@[email protected] PROC NEAR        ; foobar 
; 1 : unsigned char foobar(int foo) { return (foo >> 16) & 0xff; } 
    00000 55    push ebp 
    00001 8b ec   mov  ebp, esp 
    00003 8b 45 08   mov  eax, DWORD PTR _foo$[ebp] 
    00006 c1 f8 10   sar  eax, 16      ; 00000010H 
    00009 25 ff 00 00 00 and  eax, 255    ; 000000ffH 
    0000e 5d    pop  ebp 
    0000f c3    ret  0 
[email protected]@[email protected] ENDP         ; foobar 

同时,the oldest version of gcc available on godbolt正确编译成什么本质上只是一个转变,但由于调用约定的清单之间的自然差异这一点。