在linux上的程序集64中实现strcmp函数
我正在尝试实现assembly 64中的在linux上的程序集64中实现strcmp函数,c,linux,assembly,nasm,strcmp,C,Linux,Assembly,Nasm,Strcmp,我正在尝试实现assembly 64中的C函数,以下是我目前的工作代码: 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
C
函数,以下是我目前的工作代码:
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
当我试图用这个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);
}
这是从a
中减去o
的结果:ret='a'-'o'
是十进制ascii码97-111=-14
但当我用另一个main.c
尝试它时,我只是直接将字符串传递给strcmp()
和ft\u 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);
}
我在互联网上搜索了一下,找到了一些关于这种行为的解释:
但问题是我如何在我的汇编代码中实现这种行为,我的意思是有没有办法知道字符串是否直接传递给参数
我试着用lldb
调试一下,发现rdi
和rsi
的地址(分别获取第一个参数和第二个参数的寄存器)
在上述两种情况下是不同的
在第一种情况下,地址是这样写的:
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
但在第二种情况下,它们是这样写的:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
我不确定这是否会有帮助,但谁知道呢,请提前感谢
#编辑
既然我的问题标记为[重复],我将发布我的答案,它似乎完成了上述行为的工作,如下所示:
使用lldb
进行调试后,我注意到每当我向ft_strcmp()
传递文本字符串时,rdi
和rsi
的地址都是这样写的:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
每当我传递声明的变量而不是文字字符串时,地址就会变成这样:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
“至少这是我在使用linux X64操作系统的机器上得到的”,所以我考虑做一些转移技巧:
这就是0x00007fffffffde50
以二进制表示的方式:
11111111111111111111111111111111101111001010000
为了得到7
以便稍后在比较中使用它,我将它移位44位,在本例中,让我们将它存储在rax
寄存器中:
mov rax, 0x00007fffffffde50
rax >> 44 in assembly ==> shr rax, 44 ==> (rax = 111 ==> 7)
现在我将检查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
现在,当我使用上面的main.c
编译时,结果是相同的。strcmp()
只保证结果的符号。在第二种情况下,某些东西可能得到了优化。你不必在意大小的不同,所以最好不要在意
编译器有权进行优化
printf("strcmp = %d\n",strcmp("hella world", "hello world"));
到
这听起来像是一个错误。为什么你认为你需要这种精确的行为?天哪,你测试并调试了它!这太罕见了,我的咖啡杯掉了。请对此进行投票:)注意,字节减法后的
movsx-rax,r13b
并非适用于所有情况。在进行减法运算之前,需要对输入进行零扩展,以使有符号溢出成为不可能。出于同样的原因,jg
不仅检查SF,还检查SF和OF。如果调用了glibc函数,编译器不会内联strcmp
,也不会在编译时评估结果。是一个潜在的复制品。如果您想阻止gcc内联strcmp
,请使用gcc-fno builtin strcmp
。您正在根据它们的地址检测堆栈上的局部变量与静态存储。这是非常脆弱的,并不总是与编译器匹配。e、 g.您可以拥有不在堆栈上的非常量字符串!!例如charstr1[]=“foo”代码>在全局范围内。如果您想了解GCC版本中发生了什么,您应该看看它的asm。你所发明的与结果相匹配的方法是疯狂的,这是你在现实生活中永远不想做的事情,而且只是碰巧在这种情况下给出了正确的结果,而不是在一般情况下。