Strcmp in nasm x86_64, registers

Viewed 1381

I am trying to implement my own strcmp in asm. Here is the ft_strcmp.s file:

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

For the first letter it's OK: char *s1 = "Hello World" char *s2 = "Jdllo World" The result is 1. (0000 0001)

The problem is that when I am trying to compare these strings:

char *s1 = "Hello World"

char *s2 = "Hdllo World"

The result in RAX is NOT 1, but 256. (0000 0001 0000 0000)

Another example:

char *s1 = "Hello World"

char *s2 = "Hcllo World"

The result in RAX is NOT 2, but 512. (0000 0010 0000 0000)

As you have already understood, the result for third different letters will be:

char *s1 = "Hello World"

char *s2 = "Heklo World"

The result in RAX is NOT 1, but 65 536. (0000 0001 0000 0000 0000 0000)

I realize that RAX is incrementing not right, but I can't find the mistake in the code. So I ask you to help me understand.

1 Answers

Assembly is different than other languages so often times what you think may be happening isn't actually what happens. The reason you are getting surprising / incorrect results is because you are subtracting two numbers. You will not immediately see this until you get a debugger(which you should already have if you want to learn / survive in assembly). Let's see what is happening with the help of a debugger. First let's setup a small main and some data:

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

When execution starts, we load up rdi and rsi with strings (which is just a sequence of bytes). Important thing here is that rdi and rsi don't actually "contain" the strings / bytes but point to them i.e., rdi and rsi contain the address of the location where our strings live.

Next we call the function and here is where the problem starts happening. I am going to focus on these two instructions:

    mov eax, [rdi] ;1
    sub eax, [rsi] ;2

At instruction 1, you are moving the string itself into eax. [rdi] means get the value at the address which rdi has. It's like dereferencing a pointer. Now, eax is 32 bits (4 bytes) in size, so it can only contain 4 bytes. And assuming you have a litte endian system, the order of the bytes will be in reverse, so the value in eax will be:

eax = 0x6c6c6548

if you look at it closely, you will see that it's 4 bytes from str1:

6c  6c   65  48
'l' 'l' 'e'  'h'

Next you subtract this number from the value at address in rsi which is:

0x6c6c6448
OR
0x6c 6c  64  48
'l' 'l' 'd'  'h'

If you subtract these two numbers:

0x6c6c6548 - 0x6c6c6448 = 0x100

0x100 in base 10 is 256.

Since the value is non zero, ZF (zero flag) will not be set and you will jump to .exit.

Hope you now understand what is actually happening.

I highly recommend getting a debugger and using it to debug such issues.

Related