为什么 rax 和 rdi 在这种情况下工作相同? [英] Why does rax and rdi work the same in this situation?

查看:64
本文介绍了为什么 rax 和 rdi 在这种情况下工作相同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经制作了这个代码:

全局字符串;int strlen(const char *string);力量:异或 rcx, rcx重试:cmp 字节 [rdi + rcx], 0结果包括 rcxjmp重试结果:mov rax, rcx回复

这就是我测试它的方式:

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

这是一个有效的 strlen,这里没问题,但我注意到我可以在没有它的情况下为 rax 切换重试块第一行中的 rdi改变任何东西,我不知道这是否是正常行为.我应该保留哪些值?

解决方案

只是运气不好.

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

 推送 rbpmov rbp, rsp子 rsp, 32mov DWORD PTR [rbp-20], edi ;argcmov QWORD PTR [rbp-32], rsi ;argvmov rax, QWORD PTR [rbp-32] ;argvmov 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调用 strlen ;strlen(bob)mov esi, eaxmov edi,偏移平面:.LC0移动轴,0调用 printf移动轴,0离开回复

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

printf("%i\n", strlen("bob"));mov edi,偏移平面:.LC1call strlen ;这里没有RAXmov esi, eaxmov edi,偏移平面:.LC0移动轴,0调用 printf

指定如何将参数传递给函数的文档是您的操作系统 ABI,在此答案中阅读更多内容.><小时>

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

I have made this code :

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

And this is how I test it :

#include <stdio.h>

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

This is a working strlen, no problem here but I've noticed that I can switch the rdi in the first line of the retry block for a rax without it changing anything, I don't know if this is normal behavior. which of those values should I keep ?

解决方案

It's just bad luck.

GCC 8, without optimisations, uses rax as an intermediary location to move argv[1] to bob and to move the latter into the first parameter of 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

This is just bad luck, it's not a documented behaviour, in fact it fails if you use a string literal:

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

The document specifying how to parameters are passed to function is your OS ABI, read more in this answer.


GCC generates "dumb" code that uses the registers a lot when the optimisations are disabled, this eases the debugging (both of the GCC engine and the program compiled) and essentially mimics a beginners: first the variable is read from memory and put in the first free register (one problem solved), then it is copied in the right register (another one gone) and finally the call is made.
GCC just picked up the first free register, in this simple program there is no registers pressure and rax is always picked up.

这篇关于为什么 rax 和 rdi 在这种情况下工作相同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆