Hol*_*lon 2 c linux assembly nasm strcmp
我正在尝试实现strcmp,它是C
程序集 64 中的一个函数,这是迄今为止我的工作代码:
global ft_strcmp
section .text
ft_strcmp: ;rax ft_strcmp(rdi, rsi)
mov r12, 0
loop:
mov r13b, [rdi + r12]
cmp byte [rdi + r12], 0
jz exit
cmp byte [rsi + r12], 0
jz exit
cmp r13b, byte [rsi + r12]
jnz exit
inc r12
jmp loop
exit:
sub r13b, [rsi + r12]
movsx rax, r13b
ret
Run Code Online (Sandbox Code Playgroud)
当我尝试用这个编译它时main.c
:
#include <stdio.h>
#include <string.h>
int ft_strcmp(const char *str1, const char *str2);
int main()
{
const char str1[20] = "hella world";
const char str2[20] = "hello world";
printf("ft_strcmp = %d\n",ft_strcmp(str1, str2));
printf("strcmp = %d\n",strcmp(str1, str2));
return (0);
}
Run Code Online (Sandbox Code Playgroud)
结果如下图所示:
ft_strcmp = -14
strcmp = -14
Run Code Online (Sandbox Code Playgroud)
o
这是减去from的结果a
:ret = 'a' - 'o'
十进制 ascii 代码97 - 111 = -14
。
但是当我尝试使用另一个main.c
如下所示的方法时,我只是将字符串直接传递给两者strcmp()
,而ft_strcmp()
不是传递声明的变量:
#include <stdio.h>
#include <string.h>
int ft_strcmp(const char *str1, const char *str2);
int main()
{
printf("ft_strcmp = %d\n",ft_strcmp("hella world", "hello world"));
printf("strcmp = %d\n",strcmp("hella world", "hello world"));
return (0);
}
Run Code Online (Sandbox Code Playgroud)
结果变成:
ft_strcmp = -14
strcmp = -1
Run Code Online (Sandbox Code Playgroud)
我在这个广阔的互联网上进行了一些搜索,发现了一些关于此行为的解释:
但问题是我如何在汇编代码中实现这种行为,我的意思是有没有办法知道字符串是否直接传递给参数?
我试着调试了一下lldb
,发现上面两种情况下rdi
和的地址是不同的。rsi
(the registers that get the first parameter and the second parameter respectively)
在第一种情况下,地址是这样写的:
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
Run Code Online (Sandbox Code Playgroud)
但在第二种情况下,它们是这样写的:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
Run Code Online (Sandbox Code Playgroud)
我不确定这是否有帮助,但谁知道呢,提前致谢。
#编辑
好吧,既然我的问题标记为[重复],我将发布我的答案,它似乎可以完成上述行为,如下所示:
使用调试后,lldb
我注意到每当我将文字字符串传递给 时,和ft_strcmp()
的地址 都会写成这样:rdi
rsi
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
Run Code Online (Sandbox Code Playgroud)
每当我传递声明的变量而不是文字字符串时,地址就会变成这样:
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
Run Code Online (Sandbox Code Playgroud)
“至少这是我在 Linux X64 操作系统的机器上得到的”,所以我考虑做一些转换技巧:
这是0x00007fffffffde50
二进制表示的方式:
11111111111111111111111111111111101111001010000
Run Code Online (Sandbox Code Playgroud)
我会将其移位 44 位,以便7
稍后进行比较,rax
在本例中让我们将其存储在寄存器中:
mov rax, 0x00007fffffffde50
rax >> 44 in assembly ==> shr rax, 44 ==> (rax = 111 ==> 7)
Run Code Online (Sandbox Code Playgroud)
现在我将检查 和rdi
是否rsi
是文字字符串:
mov r8, rdi ; store the address of rdi in r8
shr r8, 44 ; right shift the address of r8 by 44 bits
cmp r8, rax ; compare if the results are the same or not
jl loop2 ; if r8 < rax then jump to loop2 for example 5 < 7
Run Code Online (Sandbox Code Playgroud)
这是我的最终代码,但我不确定这是否是一个好方法,这只是一个小技巧,它与我一起进行上述测试,不确定复杂的测试。(注意:它不适用于调用在全局范围内声明的变量,感谢Peter Cordes指出了这一点)
global ft_strcmp
section .text
ft_strcmp: ;rax ft_strcmp(rdi, rsi)
mov r12, 0
mov rax, 0x00007fffffffde50
shr rax, 44
mov r8, rdi
shr r8, 44
cmp r8, rax
jl loop2
loop1:
mov r13b, [rdi + r12]
cmp byte [rdi + r12], 0
jz exit1
cmp byte [rsi + r12], 0
jz exit1
cmp r13b, byte [rsi + r12]
jnz exit1
inc r12
jmp loop1
exit1:
sub r13b, [rsi + r12]
movsx rax, r13b
ret
loop2:
mov r13b, [rdi + r12]
cmp byte [rdi + r12], 0
jz exit2
cmp byte [rsi + r12], 0
jz exit2
cmp r13b, byte [rsi + r12]
jnz exit2
inc r12
jmp loop2
exit2:
cmp r13b, byte [rsi + r12]
jl ret_m
jg ret_p
ret_z:
mov rax, 0
ret
ret_p:
mov rax, 1
ret
ret_m:
mov rax, -1
ret
Run Code Online (Sandbox Code Playgroud)
现在,当我使用上述两者进行编译时,结果是相同的main.c
。
strcmp()
只保证结果的符号。在第二种情况下,某些东西可能得到了优化。你不需要关心大小的不同,所以最好不要关心。
编译器有权优化
printf("strcmp = %d\n",strcmp("hella world", "hello world"));
Run Code Online (Sandbox Code Playgroud)
到
printf("strcmp = %d\n",-1);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
729 次 |
最近记录: |