为什么用REPNE SCASB实现strlen工作?

问题描述 投票:1回答:2

为什么此代码起作用?

[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 main:

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
c assembly x86-64 calling-convention strlen
2个回答
2
投票

问题中的代码是strlen的32位版本,它仅在64b环境中部分起作用,有点“偶然”(因为大多数SW实际上都在起作用;))。]]

64b环境的偶然影响是(在64b linux操作系统使用的System V ABI中,其他64b平台可能遵循不同的调用约定,从而使此无效!),函数调用中的第一个参数通过[ C0]寄存器,并且rdi在64b模式下使用scasb,因此这很自然地适合在一起(正如Jester的回答所述)。

其余的64b环境效果不太好,对于4 + G长字符串,代码将返回错误的值(我知道,在实际使用中极不可能发生这种情况,但是可以通过提供这种长字符串的综合测试来尝试)。 >

固定的64b版本(也是例程的结尾,利用rax = 0在单个指令中同时执行es:rdineg ecx:]

mov eax,ecx

64位sysv调用约定将第一个参数放入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 '\0') ret 。因此,呼叫者rdi已经为您完成了加载。您可以检查其汇编代码并亲自查看。

main提供的答案)


3
投票

64位sysv调用约定将第一个参数放入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 '\0') ret 。因此,呼叫者rdi已经为您完成了加载。您可以检查其汇编代码并亲自查看。

© www.soinside.com 2019 - 2024. All rights reserved.