assembly 在linux上实现程序集64中的strcmp函数[重复]

wj8zmpe1  于 2022-11-13  发布在  Linux
关注(0)|答案(1)|浏览(86)

此问题在此处已有答案

Inconsistent strcmp() return value when passing strings as pointers or as literals(2个答案)
1年前关闭。
我正在尝试实现strcmp,这是汇编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 + 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);
}

下面的结果如下所示:

ft_strcmp = -14
strcmp = -14

其是从a减去o的结果: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);
}

结果变为:

ft_strcmp = -14
strcmp = -1

我在互联网上搜索了一下,找到了一些关于这种行为的解释:
Why does strcmp() in a template function return a different value?
Is this the only return value for strcmp() in C?
但问题是我如何在我的汇编代码中实现这个行为,我的意思是有没有办法知道字符串是否直接传递给参数?
我尝试使用lldb进行一些调试,发现在上述两种情况下,rdirsi(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

但在第二种情况下,它们是这样写的:

rdi = 0x0000555555556010  ; the address of the first string
rsi = 0x0000555555556004  ; the address of the second string

我不确定这是否会有帮助,但谁知道呢,并提前感谢。

#编辑

既然我的问题被标记为[duplicate],我将张贴我的答案,它似乎做上述行为的工作,它是如下:
在使用lldb进行调试之后,我注意到每当我将一个字符串传递给ft_strcmp()时,rdirsi的地址都是这样写的:

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

我将它移位44位,以便让7在以后的比较中使用它,在本例中,我们将它存储在rax寄存器中:

mov rax, 0x00007fffffffde50
rax >> 44  in assembly ==> shr  rax, 44 ==> (rax = 111 ==> 7)

现在我将检查rdirsi是否为文字字符串:

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

这是我的最终代码,但我不确定这是不是一个好方法,这只是一个小技巧,它与我的工作与上述测试,不确定复杂的测试.(注:它不能调用在全局作用域声明的变量,这要感谢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

现在当我使用上面的两个main.c进行编译时,结果是相同的。

cwdobuhd

cwdobuhd1#

strcmp()只保证结果的符号。在第二种情况下,某些东西可能得到了优化。你不需要关心大小的不同,所以最好不要这样做。
编译器将在其权限范围内优化

printf("strcmp = %d\n",strcmp("hella world", "hello world"));

printf("strcmp = %d\n",-1);

相关问题