我刚开始用汇编语言编程,所以我是一个初学者。
为了练习,我正在尝试在汇编中重写基本的libc(NASM Intel语法)。
但是我被困在 strcmp 函数上:
;; Compare two C-style NUL-terminated strings
;; Inputs : ESI = address of s1, EDI = address of s2
;; Outputs : EAX = return an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2
strcmp:
call strlen
mov ecx, eax ; ecx = length of the string in esi
repe cmpsb
sub esi, edi ; result = *esi - *edi
mov eax, esi
ret
对我来说,它应该像这样工作:
s1 db 'Hello World', 0
s2 db 'Hello Stack', 0
在repe cmpsb
指令之后,ESI
应该等于[s1 + 7]
,而EDI
应该等于[s2 + 7]
。
所以我只需要做EAX
='W'-'S'= 87-83 = 4
问题是,它不起作用。我认为问题在于执行此指令时:
sub esi, edi ; result = *esi - *edi
我不认为这意味着:减去EDI
和ESI
指向的字符。
有人知道我该怎么做吗?
答案 0 :(得分:3)
您的代码几乎正确。剩下三个问题:
strcmp
保留esi
和edi
的内容。稍后更改strcmp
然后忘记要求是很容易的,从而导致各种烦人的问题。*edi
和*esi
之间的差异,而不是返回edi
和esi
之间的差异。另外,随着cmpsb
向esi
和edi
前进一个,比较的最后一个字符位于edi[-1]
和esi[-1]
。strlen
返回NUL字节之前的字符数,但是您也需要比较NUL字节。否则,如果一个字符串是另一个字符串的前缀,您将最终发现两个字符串相等,因为您永远不会检查第二个字符串实际上在第一个字符串结束时就结束了。要解决第一个问题,建议您在调用esi
的前后保存并恢复edi
和strlen
。最简单的方法是将它们压入堆栈:
push esi ; save ESI and EDI
push edi
call strlen ; compute the string length
pop edi ; restore ESI and EDI
pop esi
第二个问题已解决,方法是从内存中加载要比较的字符,计算差异,然后将结果存储到eax
:
movzx eax, byte [esi-1] ; load byte from ESI[-1] and zero extend into EAX
movzx ecx, byte [edi-1] ; load byte from EDI[-1] and zero extend into ECX
sub eax, ecx ; compute the difference
这也通过立即使用正确的偏移量解决了第三个问题。请注意,这里需要movzx
而不是稍微简单的
mov al, [esi-1] ; load byte from ESI[-1] into AL
sub al, [edi-1] ; subtract EDI[-1] from AL
因为我们希望将减法的结果正确符号扩展到eax
。