首页 > 解决方案 > 为什么在这种情况下 rax 和 rdi 的工作方式相同?

问题描述

我做了这个代码:

global  strlen
    ; int strlen(const char *string);
strlen:
    xor     rcx, rcx

retry:
    cmp byte    [rdi + rcx], 0
    je      result
    inc     rcx
    jmp     retry

result:
    mov     rax, rcx
    ret

这就是我测试它的方式:

#include <stdio.h>

int main(int argc, char **argv)
{
    char* bob = argv[1];
    printf("%i\n", strlen(bob));
    return 0;
}

这是一个有效的 strlen,在这里没问题,但我注意到我可以rdi将重试块的第一行中的 a 切换为 arax而不会改变任何内容,我不知道这是否是正常行为。我应该保留哪些价值观?

标签: assemblyx86-64undefined-behaviorcalling-convention

解决方案


只是运气不好。

GCC 8,没有优化rax用作中间位置移动argv[1]bob并将后者移动到的第一个参数strlen

  push rbp
  mov rbp, rsp
  sub rsp, 32

  mov DWORD PTR [rbp-20], edi             ;argc
  mov QWORD PTR [rbp-32], rsi             ;argv

  mov rax, QWORD PTR [rbp-32]             ;argv
  mov rax, QWORD PTR [rax+8]              ;argv[1]
  mov QWORD PTR [rbp-8], rax              ;bob = argv[1]

  mov rax, QWORD PTR [rbp-8]
  mov rdi, rax
  call strlen                             ;strlen(bob)

  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf

  mov eax, 0
  leave
  ret

这只是运气不好,它不是记录在案的行为,实际上,如果您使用字符串文字,它会失败

printf("%i\n", strlen("bob"));

  mov edi, OFFSET FLAT:.LC1
  call strlen                     ;No RAX here

  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf

指定如何将参数传递给函数的文档是您的 OS ABI,请在此答案中阅读更多内容


当优化被禁用时,GCC 会生成大量使用寄存器的“哑”代码,这简化了调试(GCC 引擎和编译的程序)并且基本上模仿了初学者:首先从内存中读取变量并将其放入第一个空闲寄存器(解决了一个问题),然后将其复制到正确的寄存器中(另一个消失了),最后调用。
GCC 刚刚拾取了第一个空闲寄存器,在这个简单的程序中没有寄存器压力并且rax总是被拾取。


推荐阅读