" AND AL,0xFF"的目的是什么?

时间:2017-07-27 21:33:31

标签: assembly bitwise-operators eflags

我正在通过一个反汇编的win32 c ++程序阅读,我看到了不少:

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之后,ALCLDL中的值将移至[ESI + n]

1 个答案:

答案 0 :(得分:4)

正如@fuz建议的那样,这只是一个优化器的错误,它没有将foo & 0xff识别为在最初可能在原始函数中使用它的上下文中的无操作。

在将项目的编译设置设置为“Release”后,我使用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位:

?foobar@@YAEH@Z 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
?foobar@@YAEH@Z ENDP                                    ; foobar

与此同时,the oldest version of gcc available on godbolt正确地将其编译成基本上只是一个转变,除了由于调用约定导致的列表之间的自然差异。