我试图在程序集x86中实现memset。这两个函数都是按字节和按字复制的,所以我得到了两个函数:kmemset
和kmemsetw
,我以这种方式暴露给我的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进行调试,但它似乎无法进入asm代码。如果有人能对代码发表评论,我会很高兴。 (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
答案 0 :(得分:3)
你的参数参考位置是4。如果你在函数序言中按ebp,然后更新epb,那么你的函数ebp之后的前两个(4字节)位置将包含ebp(SFP)和ret。您的代码使ret成为字符串副本的目标位置。在所有引用中添加4。
关于如何处理/引用参数可能也存在问题,但是从代码片段中不清楚这一点。
编辑:这不是您的段错误,但您还需要更改返回方式。 edi在rep存储期间更新,因此要返回指向内存区域开头的指针,请从临时存储它的ebx中获取它。
答案 1 :(得分:3)
您的程序中没有使用ebx
,为什么要保存?它不需要保存。 ecx
是一个易失性寄存器,您无需保存它。
正如gnometorule所提到的,你的proc中的参数都已关闭。
另一个重要因素是你最后没有恢复堆栈指针。当然你pop ebp
,但你在哪里mov esp, ebp
???你mov ebp, esp
开头,但从不反转它。
如果查看memset
,它将返回传递给proc的指针。所以,这是错误的:
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