为什么 strlen 的 REPNE SCASB 实现有效?

Why does this REPNE SCASB implementation of strlen work?

为什么这段代码有效?

http://www.int80h.org/strlen/ 表示字符串地址必须在 EDI 寄存器中才能使 scasb 工作,但是这个汇编函数似乎没有这样做。

mystrlen 的汇编代码:

global  mystrlen
mystrlen:
        sub             ecx, ecx
        not             ecx
        sub             al, al
        cld
        repne scasb
        neg             ecx
        dec             ecx
        dec             ecx
        mov             eax, ecx
        ret

C 主线:

int mystrlen(const char *);
int main()
{
    return (mystrlen("1234"));
}

编译:

nasm -f elf64 test.asm
gcc -c main.c
gcc main.o test.o

输出:

./a.out
echo $?
4

The 64 bit sysv calling convention places the first argument into rdi. So the caller main already did that load for you. You can examine its assembly code and see for yourself.

(答案由Jester提供)

问题中的代码是 strlen 的 32 位版本,它只能在 64b 环境中部分工作,有点像 "by accident"(因为大多数软件在现实中都可以工作,无论如何 ;))。

64b环境的一个意外影响是(在64b linux OS使用的System V ABI中,其他64b平台可能遵循不同的调用约定,使这个无效!),函数调用中的第一个参数是通过 rdi 寄存器传递的,而 scasb 在 64b 模式下使用 es:rdi,所以这很自然地结合在一起(正如 Jester 的回答所说)。

其余64b环境效果不太好,该代码会return 4+G长字符串的错误值(我知道,在实际使用中极不可能发生,但可以通过综合测试尝试提供这么长的字符串)。

修复了 64b 版本(例程结束时利用 rax=0 在单条指令中执行 neg ecxmov eax,ecx):

global  mystrlen
mystrlen:
        xor       ecx,ecx    ; rcx = 0
        dec       rcx        ; rcx = -1 (0xFFFFFFFFFFFFFFFF)
        ; rcx = maximum length to scan
        xor       eax,eax    ; rax = 0 (al = 0 value to scan for)
        repne scasb          ; scan the memory for AL
        sub       rax,rcx    ; rax = 0 - rcx_leftover = scanned bytes + 1
        sub       rax,2      ; fix that into "string length" (-1 for '[=10=]')
        ret