使用 AND、OR、SHR 和 SHL 指令以及数组将循环从 x86 汇编转换为 C 语言
Converting a loop from x86 assembly to C language with AND, OR, SHR and SHL instructions and an array
我不明白是什么问题,因为结果是正确的,但其中有问题,我不明白。
1.This 是我必须转换为 C:
的 x86 代码
%include "io.inc"
SECTION .data
mask DD 0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555
SECTION .text
GLOBAL CMAIN
CMAIN:
GET_UDEC 4, EAX
MOV EBX, mask
ADD EBX, 16
MOV ECX, 1
.L:
MOV ESI, DWORD [EBX]
MOV EDI, ESI
NOT EDI
MOV EDX, EAX
AND EAX, ESI
AND EDX, EDI
SHL EAX, CL
SHR EDX, CL
OR EAX, EDX
SHL ECX, 1
SUB EBX, 4
CMP EBX, mask - 4
JNE .L
PRINT_UDEC 4, EAX
NEWLINE
XOR EAX, EAX
RET
2.My 转换后的 C 代码,当我输入 0 时它输出正确的答案但是我的代码中有一些错误我不明白是什么:
#include "stdio.h"
int main(void)
{
int mask [5] = {0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555};
int eax;
int esi;
int ebx;
int edi;
int edx;
char cl = 0;
scanf("%d",&eax);
ebx = mask[4];
ebx = ebx + 16;
int ecx = 1;
L:
esi = ebx;
edi = esi;
edi = !edi;
edx = eax;
eax = eax && esi;
edx = edx && edi;
eax = eax << cl;
edx = edx >> cl ;
eax = eax || edx;
ecx = ecx << 1;
ebx = ebx - 4;
if(ebx == mask[1]) //mask - 4
{
goto L;
}
printf("%d",eax);
return 0;
}
if(ebx != mask[1]) //mask - 4
{
goto L;
}
//JNE 隐含一个 !=
汇编 AND 是 C 按位 &
,不是逻辑 &&
。 (与 OR 相同)。所以你想要 eax &= esi
.
(使用 &=
"compound assignment" 使 C 甚至 看起来 像 x86 风格的 2 操作数 asm 所以我建议这样做。)
NOT 也是按位翻转所有位,而不是布尔化为 0/1。在 C 中是 edi = ~edi;
阅读 x86 指令手册,例如 https://www.felixcloutier.com/x86/not, and for C operators like ~
and !
to check that they are / aren't what you want. https://en.cppreference.com/w/c/language/expressions https://en.cppreference.com/w/c/language/operator_arithmetic
您应该在调试器中单步执行您的 C 和 asm,以便您注意到 第一个 分歧,并知道要修复哪个指令/C 语句。不要只 运行 整个事情,只看一个数字的结果!调试器对 asm 非常有用;不要浪费你的时间。
CL是ECX的低字节,不是单独的C变量。你可以在 C 中使用 uint32_t
和 uint8_t
之间的联合, 或者只使用 eax <<= ecx&31;
因为你没有任何东西可以单独写 CL ECX。 (x86 移位掩盖了它们的计数;该 C 语句可以编译为 shl eax, cl
。https://www.felixcloutier.com/x86/sal:sar:shl:shr)。 ECX的低5位也是CL的低5位。
SHR
是逻辑右移,而不是算术右移,因此至少对于 >>
,您需要使用 unsigned
而不是 int
。但真的只是用它来做所有事情。
您对 EBX 的处理完全错误;这是一个指针。
MOV EBX, mask
ADD EBX, 16
这就像unsigned int *ebx = mask+4;
双字的大小为 4 个字节,但 C 指针数学按类型大小缩放,因此 +1
是一个完整元素,而不是 1 个字节。所以 16 个字节是 4 个双字 = 4 unsigned int
个元素。
MOV ESI, DWORD [EBX]
这是一个使用 EBX 作为地址的负载。如果您在调试器中单步执行 asm,这应该很容易看出:它不仅仅是复制值。
CMP EBX, mask - 4
JNE .L
这是NASM语法;它与数组开始前双字的 地址 进行比较。它实际上是相当正常的 do{}while 循环的底部。 ()
do { // .L
...
} while(ebx != &mask[-1]); // cmp/jne
它从 mask
数组的末尾开始循环,当指针越过末尾时停止。
等价地,比较可以是ebx !-= mask - 1
。我用一元 &
(地址)写了它,取消了 []
以明确它是数组之前的一个元素的地址。
请注意,它在 上跳跃而不是 相等;你的 if()goto
倒退了,只在平等的基础上跳跃。这是一个循环。
unsigned mask[]
应该是 static
因为它在 section .data
中,而不是在堆栈中。而不是 const
,因为它再次位于 .data
而不是 .rodata
(Linux) 或 .rdata
(Windows))
这个不影响逻辑,只影响反编译的细节。
可能还有其他bug;我没有尝试检查所有内容。
我不明白是什么问题,因为结果是正确的,但其中有问题,我不明白。
1.This 是我必须转换为 C:
的 x86 代码%include "io.inc"
SECTION .data
mask DD 0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555
SECTION .text
GLOBAL CMAIN
CMAIN:
GET_UDEC 4, EAX
MOV EBX, mask
ADD EBX, 16
MOV ECX, 1
.L:
MOV ESI, DWORD [EBX]
MOV EDI, ESI
NOT EDI
MOV EDX, EAX
AND EAX, ESI
AND EDX, EDI
SHL EAX, CL
SHR EDX, CL
OR EAX, EDX
SHL ECX, 1
SUB EBX, 4
CMP EBX, mask - 4
JNE .L
PRINT_UDEC 4, EAX
NEWLINE
XOR EAX, EAX
RET
2.My 转换后的 C 代码,当我输入 0 时它输出正确的答案但是我的代码中有一些错误我不明白是什么:
#include "stdio.h"
int main(void)
{
int mask [5] = {0xffff, 0xff00ff, 0xf0f0f0f, 0x33333333, 0x55555555};
int eax;
int esi;
int ebx;
int edi;
int edx;
char cl = 0;
scanf("%d",&eax);
ebx = mask[4];
ebx = ebx + 16;
int ecx = 1;
L:
esi = ebx;
edi = esi;
edi = !edi;
edx = eax;
eax = eax && esi;
edx = edx && edi;
eax = eax << cl;
edx = edx >> cl ;
eax = eax || edx;
ecx = ecx << 1;
ebx = ebx - 4;
if(ebx == mask[1]) //mask - 4
{
goto L;
}
printf("%d",eax);
return 0;
}
if(ebx != mask[1]) //mask - 4
{
goto L;
}
//JNE 隐含一个 !=
汇编 AND 是 C 按位 &
,不是逻辑 &&
。 (与 OR 相同)。所以你想要 eax &= esi
.
(使用 &=
"compound assignment" 使 C 甚至 看起来 像 x86 风格的 2 操作数 asm 所以我建议这样做。)
NOT 也是按位翻转所有位,而不是布尔化为 0/1。在 C 中是 edi = ~edi;
阅读 x86 指令手册,例如 https://www.felixcloutier.com/x86/not, and for C operators like ~
and !
to check that they are / aren't what you want. https://en.cppreference.com/w/c/language/expressions https://en.cppreference.com/w/c/language/operator_arithmetic
您应该在调试器中单步执行您的 C 和 asm,以便您注意到 第一个 分歧,并知道要修复哪个指令/C 语句。不要只 运行 整个事情,只看一个数字的结果!调试器对 asm 非常有用;不要浪费你的时间。
CL是ECX的低字节,不是单独的C变量。你可以在 C 中使用 uint32_t
和 uint8_t
之间的联合, 或者只使用 eax <<= ecx&31;
因为你没有任何东西可以单独写 CL ECX。 (x86 移位掩盖了它们的计数;该 C 语句可以编译为 shl eax, cl
。https://www.felixcloutier.com/x86/sal:sar:shl:shr)。 ECX的低5位也是CL的低5位。
SHR
是逻辑右移,而不是算术右移,因此至少对于 >>
,您需要使用 unsigned
而不是 int
。但真的只是用它来做所有事情。
您对 EBX 的处理完全错误;这是一个指针。
MOV EBX, mask
ADD EBX, 16
这就像unsigned int *ebx = mask+4;
双字的大小为 4 个字节,但 C 指针数学按类型大小缩放,因此 +1
是一个完整元素,而不是 1 个字节。所以 16 个字节是 4 个双字 = 4 unsigned int
个元素。
MOV ESI, DWORD [EBX]
这是一个使用 EBX 作为地址的负载。如果您在调试器中单步执行 asm,这应该很容易看出:它不仅仅是复制值。
CMP EBX, mask - 4
JNE .L
这是NASM语法;它与数组开始前双字的 地址 进行比较。它实际上是相当正常的 do{}while 循环的底部。 (
do { // .L
...
} while(ebx != &mask[-1]); // cmp/jne
它从 mask
数组的末尾开始循环,当指针越过末尾时停止。
等价地,比较可以是ebx !-= mask - 1
。我用一元 &
(地址)写了它,取消了 []
以明确它是数组之前的一个元素的地址。
请注意,它在 上跳跃而不是 相等;你的 if()goto
倒退了,只在平等的基础上跳跃。这是一个循环。
unsigned mask[]
应该是 static
因为它在 section .data
中,而不是在堆栈中。而不是 const
,因为它再次位于 .data
而不是 .rodata
(Linux) 或 .rdata
(Windows))
这个不影响逻辑,只影响反编译的细节。
可能还有其他bug;我没有尝试检查所有内容。