首页 > 解决方案 > 减去两个字符

问题描述

我刚开始用汇编编程,所以我是初学者。

为了练习,我正在尝试在程序集中重写一个基本的 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]EDIto [s2 + 7]

所以我只需要做EAX= 'W' - 'S' = 87 - 83 = 4

问题是,它不起作用。我认为问题在于当我执行这条指令时:

sub esi, edi ; result = *esi - *edi

我不认为这意味着:减去and指向的字符EDIESI

有谁知道我该怎么做?

标签: assemblyx86nasmstrcmp

解决方案


您的代码几乎是正确的。剩下三个问题:

  • 您不应该假设strcmp保留 and 的内容,esi除非edi您明确指定它这样做。之后很容易更改strcmp然后忘记需求,从而导致各种烦人的问题。
  • 不是返回和之间的差,而是返回*edi和之间的*esi差。此外,作为前进和加一,最后比较的字符位于和处。ediesicmpsbesiediedi[-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.


推荐阅读