assembly 为什么AL、BL和CL寄存器的行为很奇怪?

piztneat  于 10个月前  发布在  其他
关注(0)|答案(1)|浏览(93)
section .data
    format db '%d', 0x0a, 0

section .text
    global ft_strlen

ft_strlen:
    push ebp
    mov ebp, esp

    mov ecx, [ebp + 8]
    mov eax, 0
    
    loop:
    mov cl, [ecx + eax]
    inc eax
    cmp cl, 0
    jne loop

    mov esp, ebp 
    pop ebp
    ret

字符串
当我使用CL寄存器时,程序正常工作,但当我使用AL时,它给我一个无限循环,当我使用BL时,它给我一个SEGV。
实际上,这很奇怪,因为其中三个是8位寄存器。

9lowa7mx

9lowa7mx1#

当我使用cl寄存器时,程序工作正常,但当我使用al时,它给了我一个无限循环
这两种选择都是错误的,但它们的效果大多是巧合!
mov cl, [ecx + eax]中,ECX是字符串的基址,EAX是字符串的偏移量。您不应该将字符串中的字节加载到CL中,因为CL是较大的ECX寄存器的最低8位,同样,您不应该将字节加载到AL中,因为AL是较大的EAX寄存器的最低8位。
当我使用bl时,它会给我一个SEGV。
只有在保留了它预先存在的值(在堆栈上)之后,使用BL才成为一个选项。这是因为EBX在标准调用约定中是“调用保留”的。调用您的函数的编译器生成的代码将假定EBX在其call之后仍然具有相同的值。您的选项是在某处保存/恢复EBX,或者不修改它或它的任何部分。
如果你使用调试器来查看segfault发生的位置,在这种情况下,你应该在函数返回后的某个地方看到它发生。这通常是你以某种方式违反了调用约定的标志,踩到了调用者的脚趾。另外两个bug,AL或CL,可能会在你自己的函数中产生崩溃,你可以看到错误的地址。
实际上,这很奇怪,因为其中三个是8位寄存器。
x86架构没有单独的8位寄存器。我们所认为的8位寄存器总是更大寄存器的一部分。有关详细信息,请参阅 * How do AX, AH, AL map onto EAX? *,这同样适用于AL,BL,CL和DL。

解决方案:使用另一个类似EDX的call-clobbered寄存器

ft_strlen:
  mov   ecx, [esp+4]          ; Base
  xor   eax, eax              ; Offset
.loop:
  movzx edx, byte [ecx + eax]
  inc   eax
  test  dl, dl
  jnz   .loop
  dec   eax                   ; Don't include zero-terminator
  ret

字符串
你的代码在返回的计数中包含了零终止符。通常我们不会这样做。
如果你想知道为一个副本分配多少空间,这将保存你在返回值中添加1的麻烦。但是给予这个函数一个不同的名字,以避免与不包括终止符的C标准库strlen函数混淆。并且一定要在注解中记录你的函数与做类似事情的标准函数的任何不同之处,如果这是无意的,只需更改代码,使其成为C的strlen的实现。

相关问题