nasm x86_64 中的 strcmp 寄存器
Strcmp in nasm x86_64, registers
我正在尝试在 asm 中实现我自己的 strcmp。这是 ft_strcmp.s 文件:
global ft_strcmp
section .text
ft_strcmp:
mov eax, [rdi]
sub eax, [rsi]
jne .exit
cmp byte [rdi], 0 ; if s1 end
je .exit
cmp byte [rsi], 0 ; if s2 end
je .exit
inc rdi
inc rsi
jmp ft_strcmp
.exit:
ret
第一个字母没问题:
char *s1 = "你好世界"
char *s2 = "Jdllo 世界"
结果为 1。 (0000 0001)
问题是当我尝试比较这些字符串时:
char *s1 = "Hello World"
char *s2 = "Hdllo 世界"
RAX 中的结果不是 1,而是 256。(0000 0001 0000 0000)
另一个例子:
char *s1 = "Hello World"
char *s2 = "Hcllo 世界"
RAX 中的结果不是 2,而是 512。(0000 0010 0000 0000)
如您所知,第三个不同字母的结果将是:
char *s1 = "Hello World"
char *s2 = "Heklo World"
RAX 中的结果不是 1,而是 65 536。(0000 0001 0000 0000 0000 0000)
我意识到RAX递增不对,但我找不到代码中的错误。
所以我请你帮我理解。
汇编与其他语言不同,所以很多时候您认为可能正在发生的事情实际上并非如此。您得到令人惊讶/不正确结果的原因是因为您减去两个数字。在获得调试器之前,您不会立即看到这一点(如果您想在汇编中学习/生存,则应该已经拥有)。让我们看看在调试器的帮助下发生了什么。
首先让我们设置一个小 main
和一些数据:
section .data
str1: db "Hello world",0
str2: db "Hdllo world",0
section .text
global main
ft_strcmp:
... ; your code here
main:
nop
mov rdi, str1
mov rsi, str2
call ft_strcmp
nop
当执行开始时,我们加载 rdi
和 rsi
字符串(这只是一个字节序列)。这里重要的是 rdi
和 rsi
实际上并不“包含”字符串/字节,而是指向它们,即 rdi
和 rsi
包含位置的地址我们的琴弦所在的地方。
接下来我们调用该函数,这就是问题开始发生的地方。我将重点关注这两个说明:
mov eax, [rdi] ;1
sub eax, [rsi] ;2
在指令 1
中,您正在将字符串本身移动到 eax
中。 [rdi]
表示得到rdi
有的地址的值。这就像取消引用指针。现在,eax
的大小为 32 位(4 个字节),因此它只能包含 4 个字节。假设你有一个 litte endian 系统,字节的顺序将是相反的,所以 eax
中的值将是:
eax = 0x6c6c6548
如果你仔细看,你会发现它是 str1
:
的 4 个字节
6c 6c 65 48
'l' 'l' 'e' 'h'
接下来从 rsi
中地址的值中减去这个数字,即:
0x6c6c6448
OR
0x6c 6c 64 48
'l' 'l' 'd' 'h'
如果减去这两个数字:
0x6c6c6548 - 0x6c6c6448 = 0x100
0x100
以 10 为基数是 256。
由于该值不为零,因此不会设置 ZF
(零标志),您将跳转到 .exit
。
希望你现在明白实际发生了什么。
我强烈建议获取调试器并使用它来调试此类问题。
我正在尝试在 asm 中实现我自己的 strcmp。这是 ft_strcmp.s 文件:
global ft_strcmp
section .text
ft_strcmp:
mov eax, [rdi]
sub eax, [rsi]
jne .exit
cmp byte [rdi], 0 ; if s1 end
je .exit
cmp byte [rsi], 0 ; if s2 end
je .exit
inc rdi
inc rsi
jmp ft_strcmp
.exit:
ret
第一个字母没问题: char *s1 = "你好世界" char *s2 = "Jdllo 世界" 结果为 1。 (0000 0001)
问题是当我尝试比较这些字符串时:
char *s1 = "Hello World"
char *s2 = "Hdllo 世界"
RAX 中的结果不是 1,而是 256。(0000 0001 0000 0000)
另一个例子:
char *s1 = "Hello World"
char *s2 = "Hcllo 世界"
RAX 中的结果不是 2,而是 512。(0000 0010 0000 0000)
如您所知,第三个不同字母的结果将是:
char *s1 = "Hello World"
char *s2 = "Heklo World"
RAX 中的结果不是 1,而是 65 536。(0000 0001 0000 0000 0000 0000)
我意识到RAX递增不对,但我找不到代码中的错误。 所以我请你帮我理解。
汇编与其他语言不同,所以很多时候您认为可能正在发生的事情实际上并非如此。您得到令人惊讶/不正确结果的原因是因为您减去两个数字。在获得调试器之前,您不会立即看到这一点(如果您想在汇编中学习/生存,则应该已经拥有)。让我们看看在调试器的帮助下发生了什么。
首先让我们设置一个小 main
和一些数据:
section .data
str1: db "Hello world",0
str2: db "Hdllo world",0
section .text
global main
ft_strcmp:
... ; your code here
main:
nop
mov rdi, str1
mov rsi, str2
call ft_strcmp
nop
当执行开始时,我们加载 rdi
和 rsi
字符串(这只是一个字节序列)。这里重要的是 rdi
和 rsi
实际上并不“包含”字符串/字节,而是指向它们,即 rdi
和 rsi
包含位置的地址我们的琴弦所在的地方。
接下来我们调用该函数,这就是问题开始发生的地方。我将重点关注这两个说明:
mov eax, [rdi] ;1
sub eax, [rsi] ;2
在指令 1
中,您正在将字符串本身移动到 eax
中。 [rdi]
表示得到rdi
有的地址的值。这就像取消引用指针。现在,eax
的大小为 32 位(4 个字节),因此它只能包含 4 个字节。假设你有一个 litte endian 系统,字节的顺序将是相反的,所以 eax
中的值将是:
eax = 0x6c6c6548
如果你仔细看,你会发现它是 str1
:
6c 6c 65 48
'l' 'l' 'e' 'h'
接下来从 rsi
中地址的值中减去这个数字,即:
0x6c6c6448
OR
0x6c 6c 64 48
'l' 'l' 'd' 'h'
如果减去这两个数字:
0x6c6c6548 - 0x6c6c6448 = 0x100
0x100
以 10 为基数是 256。
由于该值不为零,因此不会设置 ZF
(零标志),您将跳转到 .exit
。
希望你现在明白实际发生了什么。
我强烈建议获取调试器并使用它来调试此类问题。