Search code examples
assemblyx86-64

Problem copying value from register to variable x86-64


I am trying to store a command line argument passed to my simple assembly language program in a variable for later use.

global _start
    
section .text

_start:
    pop rax       ; argc
    cmp rax,1     ; no command line arguments supplied
    jz  begin
get_cmdln_args: 
    pop rax       ; argv
    pop rax       ; arg[1]
    cmp byte [rax], '-'; test string first char for - character
    jz  parse_option  ;
    pop rax       ; arg[2]
    pop rax       ; arg[3]
parse_option:
    cmp byte [rax +1], 'p'
    jz portarg
portarg:
    pop rax
    mov [port], rax
portargl:   
    cmp byte [rax], $0
    jz get_cmdln_args
    inc rax
    jnz portargl
begin:  
    mov rax, 1        ; write(
    mov rdi, 1        ;   STDOUT_FILENO,
    mov rsi, msg      ;   "Hello, world!\n",
    mov rdx, msglen   ;   sizeof("Hello, world!\n")
    syscall           ; );

    mov rax, 60       ; exit(
    mov rdi, 0        ;   EXIT_SUCCESS
    syscall           ; );

section .bss
  port: resb 8
    
section .rodata
  msg: db "Hello, I am from the Isle of Mann!", 10
  msglen: equ $ - msg

The problem comes with the portarg label.

In GDB I can see that the value of RAX is "8080" which is what is specified on the command line such that

gdb --args hello -port 8080

launches the debugger and I can see the command line arguments on the stack. After I pop the stack I can see the command line argument in RAX.

After the mov instruction is executed I can still see the "8080" in RAX but what is stored in my variable looks sus.

The address of the variable is 0x403024

x/s 0x403024

returns

"\222\342\377\377\377\177"

What is going on here?

How can I store this as an integer at this address? I have seen examples where you do this

sub RAX, '0'

to remove the trailing 0 and then it is treated as an integer. If I try it here I get an empty string.


Solution

  • The misunderstanding is that you try to store the command line argument as an integer in your memory. Nevertheless it is represented by a string if you use 'pop rax'. The solution is fairly easy: you need to manually convert the string to integer. Make sure to save the calculated value as a quadword. The essential routine could look like this:

    str_to_int:
        ; Convert a null-terminated string in rdi to an integer in rax
        xor rax, rax          ; Clear rax to store the result
        xor rcx, rcx          ; Clear rcx to use as a counter
    
    convert_loop:
        movzx rbx, byte [rdi + rcx]  ; Load the next character
        test rbx, rbx                 ; Check if it's the null terminator
        jz done_conversion            ; If it is, we are done
    
        sub rbx, '0'                  ; Convert ASCII to integer
        imul rax, rax, 10            ; Multiply current result by 10
        add rax, rbx                 ; Add the new digit
        inc rcx                      ; Move to the next character
        jmp convert_loop
    
    done_conversion:
        ret