将地址加载到寄存器中

时间:2018-01-29 06:56:42

标签: linux assembly x86

作为一个学习练习,我一直在手写集会。我似乎无法弄清楚如何将地址的值加载到寄存器中。

从语义上讲,我想做以下事情:

_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包含垃圾值。这种方法是否正确?

1 个答案:

答案 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