Issue
I am trying to implement strcmp which is a C
function in assembly 64, here is my working code so far :
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
when I try to compile it with this 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);
}
the following result is as shown below :
ft_strcmp = -14
strcmp = -14
which is the result of the subtraction of o
from a
: ret = 'a' - 'o'
which is in decimal ascii code 97 - 111 = -14
.
but when I try it with another main.c
as below, I just passed strings directly to both strcmp()
and ft_strcmp()
rather than passing declared variables:
#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);
}
the results becomes:
ft_strcmp = -14
strcmp = -1
I searched a little bit in this wide Internet and I found some explanations about this behavior:
Why does strcmp() in a template function return a different value?
Is this the only return value for strcmp() in C?
but the question is how I implement this behavior in my assembly code, I mean is there a way to know if the string is passed directly to the parameters, or not?
I tried to debug a little bit with lldb
and I found the addresses of rdi
and rsi
(the registers that get the first parameter and the second parameter respectively)
are different in the above two cases.
in the first case the addresses are written like this :
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
but int the second case they are written like this:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
I am not sure if this will help or not, but who knows, and thanks in advance.
#Edit
Well since my question marked as [duplicate], I will post my answer that it seems to do the job of the above behavior, and it is as follows:
after debugging using lldb
I noticed whenever I pass a literal string to ft_strcmp()
, the address of rdi
and rsi
are written like this:
rdi = 0x0000555555556010 ; the address of the first string
rsi = 0x0000555555556004 ; the address of the second string
and whenever I pass declared variables instead of literal strings, the addresses become like this:
rdi = 0x00007fffffffde50 ; the address of the first string
rsi = 0x00007fffffffde70 ; the address of the second string
"at least this is what I got on my machine with linux X64 operating system", so I thought about doing some shifting tricks:
this is how 0x00007fffffffde50
is represented in binary:
11111111111111111111111111111111101111001010000
I will shift it with 44 bits in order to get that 7
to use it in comparison later, let's store it in rax
register in this example:
mov rax, 0x00007fffffffde50
rax >> 44 in assembly ==> shr rax, 44 ==> (rax = 111 ==> 7)
and now I will check if rdi
and rsi
are literal strings or not :
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
and here is my final code, but I am not sure if this is a good way or not, it is just a small trick that it works with me with the above tests, not sure about complicated test. (NOTE: It will not work with calling variables that declared at the global scope, thanks to Peter Cordes for spotting out that)
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
and now the results are the same when I compile with the both main.c
above.
Solution
strcmp()
only guarantees the sign of the result. Something probably got optimized in the second case. You don't need to care that the magnitude is different, so it would be best if you didn't.
The compiler would be within its rights to optimize
printf("strcmp = %d\n",strcmp("hella world", "hello world"));
to
printf("strcmp = %d\n",-1);
Answered By - Joshua Answer Checked By - Marilyn (WPSolving Volunteer)