我已经编写了此代码:
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;
}
这是一个正常的工作,在这里没问题,但是我注意到我可以将重试块的第一行中的rdi
切换为rax
,而无需进行任何更改,我不会不知道这是否是正常行为。
我应该保留哪些值?
答案 0 :(得分:4)
真是倒霉。
GCC 8, without optimisations,使用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
这真是倒霉,这不是有据可查的行为,实际上是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
指定如何将参数传递给函数的文档是您的OS ABI read more in this answer。
GCC生成“哑”代码,在禁用优化时会大量使用寄存器,这简化了调试工作(包括GCC引擎和已编译的程序),并从本质上模仿了初学者:首先从内存中读取变量,然后放入第一个空闲寄存器(已解决一个问题),然后将其复制到正确的寄存器中(另一个已消失),最后进行调用。
GCC刚刚拾取了第一个空闲寄存器,在这个简单的程序中没有寄存器压力,并且rax
总是被拾取。