"AND AL,0xFF" 的目的是什么?
What's the purpose of "AND AL,0xFF"?
我正在阅读一个反汇编的 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
之后,AL
、CL
和 DL
中的值被移动到 [ESI + n]
。
正如@fuz 所建议的,这只是优化器的错误,没有将 foo & 0xff
识别为最有可能在原始函数中使用的上下文中的空操作。
在将项目的编译设置设置为 "Release" 后,我使用 Borland C++ Builder 6 编译了以下代码片段:
unsigned char foobar(int foo) { return (foo >> 16) & 0xff; }
这与您提供的反汇编中执行的操作非常相似。我们有一个 32 位的值,我们希望将其移位给定的位数,然后将其转换为字节值,本质上是 returning 原始值的第 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 个字节),清除高位- 这是不必要的,看看函数的 return 值是如何明确声明为 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 正确地将其编译成本质上只是一个转变的内容,除了由于调用约定而导致列表之间的自然差异。
我正在阅读一个反汇编的 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
之后,AL
、CL
和 DL
中的值被移动到 [ESI + n]
。
正如@fuz 所建议的,这只是优化器的错误,没有将 foo & 0xff
识别为最有可能在原始函数中使用的上下文中的空操作。
在将项目的编译设置设置为 "Release" 后,我使用 Borland C++ Builder 6 编译了以下代码片段:
unsigned char foobar(int foo) { return (foo >> 16) & 0xff; }
这与您提供的反汇编中执行的操作非常相似。我们有一个 32 位的值,我们希望将其移位给定的位数,然后将其转换为字节值,本质上是 returning 原始值的第 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 个字节),清除高位- 这是不必要的,看看函数的 return 值是如何明确声明为 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 正确地将其编译成本质上只是一个转变的内容,除了由于调用约定而导致列表之间的自然差异。