我正在使用NASM
在x86-64汇编代码中实现memset
。当我尝试将值从rsi
移动到dl
时,我会得到一个segfault。你能帮我理解为什么吗?
以下是我目前所拥有的:
global my_memset
my_memset:
push rbp
mov rbp, rsp
xor rax, rax
xor rcx, rcx
mov dl, byte [rsi] ;segfault
mov rax, rdi
cmp rdx, 0
jz end
while:
inc rcx
mov byte [rdi], dl
inc rdi
cmp rcx, rdx
jne while
end:
mov rsp, rbp
pop rbp
ret
我用一个非常简单的main
函数来练习这个:
void *my_memset(void *data, int value, int size);
int main()
{
char buffer[100];
my_memset(buffer, 'm', 100);
printf("%sn", buffer);
}
请注意,我尝试了一个在汇编代码中实现main
的替代测试,结果似乎运行良好。为什么会这样?(如果你想让我也发布那个代码,请告诉我。)
esi
保存第二个参数,该参数是用于填充内存区域的值。(所以在你的测试中,rsi持有"m",或者更确切地说是它的ASCII代码)
在本说明中:
mov dl, byte [rsi]
您尝试将其用作内存地址,因此您访问的内存位置的地址是ASCII代码'm'。这会导致segfault。
您想要的只是直接使用rsi
,例如:
mov rdx, rsi
此外,您当前的代码使用rdx
做两件事——计数和要填充的字节。这不起作用——它只有一个寄存器,而且只保存一个值。(注意,dl
的意思是"rdx
的最低字节"-它不是一个单独的寄存器)
参数为rdi, rsi, rdx, rcx, r8, r9
。您正在将rsi
指向的字节(char
)复制到dl
,而不是寄存器的值。您可以将整个寄存器移动到rdx
:
mov rdx, rsi
但是您已经使用rdx
作为计数限制寄存器,因此我建议您将值移动到rax
并存储al
。
不过,在这种情况下,为了获得最短的代码,并且肯定比您的代码更具性能,可以使用字符串指令rep stosb
,将您的函数转换为
my_memset:
mov rax, rsi ; move the byte to al
mov rcx, rdx ; move the counter to rcx
rep stosb ; repeat store byte from al to [rdi]
; and increase rdi (if direction flag cleared)
; decrease rcx and if rcx is zero, break out
ret
(请注意,此函数假定方向标志已被清除,它应该被清除)。