在汇编x86中实现memset

3

我正在尝试在x86汇编中实现memset。我会用字节和字来进行复制,因此我将得到两个函数:kmemsetkmemsetw,我会以以下方式向我的C代码公开这两个函数:

extern uint8_t* kmemset(uint8_t* dest, uint8_t val, uint8_t size);  
extern uint16_t* kmemsetw(uint16_t* dest, uint16_t val, uint8_t size);  

问题在于当我测试时,出现了分段错误。我尝试用gdb调试,但似乎无法进入汇编代码。如果有人能对代码进行评论,我会很高兴。(kmemset非常相似,所以我没有包含它)。

.intel_syntax noprefix  
.section .text  
.align 16  
.global kmemsetw  
.type kmemsetw, @function  

kmemsetw:  
    push ebp  
    mov ebp, esp  
    push edi  
    push ecx  
    push ebx  
    xor eax, eax  
    mov ebx, [ebp+4]  
    mov ax, [ebp+8]  
    mov ecx, [ebp+12]  
    mov edi, ebx  
    rep stosw  
    mov eax, edi  
    pop ebx  
    pop ecx  
    pop edi  
    pop ebp  
    ret  

长度参数是以字节还是单词为单位接收的? - Leeor
顺便提一下,还有 stosd(存储 dword 大小的数据)。 - 500 - Internal Server Error
谢谢你的提示。另外,长度参数之前是以字节(uint8_t)传递的,但现在已更改为双字(uint32_t)。 - William
2个回答

4
您在过程中没有使用ebx,为什么要保存它?它不需要被保存。ecx是一个易失性寄存器,您不需要保存它。
正如gnometorule所提到的,您过程中的参数有误。
另一个重要问题是您在最后没有恢复堆栈指针。虽然您pop ebp,但您从未反转mov ebp, esp
如果您查看memset,它返回传递给过程的指针。因此,这是错误的:mov eax, edi应该是:mov eax, [ebp + 8]rep stos?会增加edi中的指针,因此如果您返回edi,则返回的指针是错误的。
但是为什么要为这个微小的过程设置堆栈帧?只需使用esp,因为我们需要将edi保存到堆栈中,所以esp中的参数将偏移与如果我们设置了堆栈帧相同。
kmemset:      
    push    edi             ; proc uses edi, so save it.

    mov     ecx, [esp + 16] ; size_t num
    mov     al, [esp + 12]  ; int value 
    mov     edi, [esp + 8]  ; void * ptr
    rep     stosb 

    mov     eax, [esp + 8]  ; return pointer
    pop     edi             ; restore edi
    ret                     ; let caller adjust stack

使用 stosw 会有一些不同。

SomeProc:
    push    ebp
    mov     ebp, esp
    push    edi

    ; params are at:
     ;~ ebp + 8
     ;~ ebp + 12
     ;~ ebp + 16
     etc...
    ; ...
    ; ...
    ; ...

    pop     edi
    ; the following 2 lines
    ; can be replaced with
    ; leave
    mov     esp, ebp
    pop     ebp
    ret

非常感谢。我的代码中确实有两个主要错误,一个是缺少返回地址的4字节偏移量,另一个是重置esp寄存器。还有感谢您提供的函数模板,让事情更加清晰。(同时也感谢gnome提供的第一个提示和nrz的编辑) - William
如果你突然从[di]开始倒退,可能也需要进行CLD操作! - carveone

3
你的参数引用位置偏移了4个字节。如果在函数序言中推入ebp,然后更新ebp,则函数ebp之后的前两个(4字节)位置将包含ebp(SFP)和ret。你的代码将ret作为字符串复制的目标位置。将所有引用加上4。可能还存在关于如何传递/引用参数的问题,但从代码片段中无法确定。
编辑:这不是你的segfault,但你还需要更改返回方式。rep存储期间edi会被更新,因此要返回指向内存区域开头的指针,请从你临时存储的ebx中取出它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接