为什么strlen的实现工作?

时间:2017-03-07 18:22:32

标签: c assembly

为什么这段代码有效?

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

2 个答案:

答案 0 :(得分:3)

  

64位sysv调用约定将第一个参数放入rdi。所以调用者main已经为你做了那个负载。您可以检查其汇编代码并亲自查看。

(由Jester提供的答案)

答案 1 :(得分:1)

该问题的代码是32位版本的strlen,它只能在64b环境中部分工作,偶然发生在"#34; (因为无论如何,大多数SW都在现实中工作;))。

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

其余的64b环境效果不太好,代码将为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 '\0')
        ret