为什么 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 ecx
和 mov 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
为什么这段代码有效?
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 callermain
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 ecx
和 mov 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