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