作为一个学习练习,我一直在手写集会。我似乎无法弄清楚如何将地址的值加载到寄存器中。
从语义上讲,我想做以下事情:
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
lea (%ecx, %ecx), %edi # Pull the value at address into %edi
cmp $97, %edi # Compare to 'a'
je done
我在C:
中编写了更高级别的实现char buffer[1];
int main()
{
read(0, buffer, 1);
char a = buffer[0];
return (a == 'a') ? 1 : 0;
}
但使用gcc -S
进行编译会产生无法很好地移植到上面的实现中的程序集。
我认为lea
是我应该使用的正确指令,用于将%ecx
中存储的给定地址的值加载到%edi
,但在gdb
中进行检查时,执行此指令后,%edi
包含垃圾值。这种方法是否正确?
答案 0 :(得分:2)
而不是lea
指令,您需要的是:
movzbl (%ecx), %edi
即零扩展到edi
寄存器ecx
中包含的内存地址的字节。
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
movzbl (%ecx), %edi # Pull the value at address ecx into edi
cmp $97, %edi # Compare to 'a'
je done
您并不需要movz
指令:您不需要单独的加载操作,因为您可以直接比较ecx
指向的内存中的字节与cmp
:
cmpb $97, (%ecx)
您可能希望将要比较的字符(即'a'
)指定为$'a'
而不是$97
,以提高可读性:
cmpb $'a', (%ecx)
Avoiding conditional branches is usually a good idea。执行系统调用后,您可以立即使用以下代码使用cmov
来确定返回值,该值存储在eax
中,而不是执行条件跳转(即je
}指令):
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
cmovz %edx, %eax # conditionally move edx(=1) into eax
ret # eax is either 0 or 1 at this point
在系统调用之前, edx
设置为1
。因此,上述方法依赖于在整个系统调用中保留edx
的事实(即int 0x80
指令)。
更好的是,您可以在比较后sete
而不是al
使用cmov
:
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
sete %al # conditionally set al
ret # eax is either 0 or 1 at this point
如果al
标志由xor %eax, %eax
设置,则通过1
设置为零的注册表ZF
将设置为cmp
(即,如果ecx
指向的字节是'a'
)。使用这种方法,您不必关心系统调用是否保留edx
,因为结果不依赖edx
。