为什么这段代码有效?
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
答案 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 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 '\0')
ret