2
votes

I am using elf64 compilation and trying to take a parameter and write it out to the console.

I am calling the function as ./test wooop

After stepping through with gdb there seems to be no problem, everything is set up ok:

rax: 0x4 rbx: 0x1 rcx: pointing to string, x/6cb $rcx gives 'w' 'o' 'o' 'o' 'p' 0x0 rdx: 0x5 <---correctly determining length

after the int 80h rax contains -14 and nothing is printed to the console. If I define a string in .data, it just works. gdb shows the value of $rcx in the same way.

Any ideas? here is my full source

    %define LF      0Ah
    %define stdout      1
    %define sys_exit    1
    %define sys_write   4


    global _start

    section .data

    usagemsg: db "test {string}",LF,0

    testmsg: db "wooop",0

    section .text

    _start:

    pop rcx     ;this is argc
    cmp rcx, 2      ;one argument
    jne usage
    pop rcx
    pop rcx               ; argument now in rcx
    test    rcx,rcx
    jz usage

    ;mov rcx, testmsg    ;<-----uncomment this to print ok!

    call print
    jmp exit


    usage:
    mov rcx, usagemsg
    call print
    jmp exit


    calclen:

    push rdi
    mov rdi, rcx
    push rcx
    xor rcx,rcx
    not rcx
    xor al,al
    cld
    repne scasb
    not rcx  
    lea rdx, [rcx-1]
    pop rcx
    pop rdi
    ret

    print:

    push rax
    push rbx
    push rdx

    call calclen

    mov rax, sys_write
    mov rbx, stdout
    int 80h
    pop rdx
    pop rbx
    pop rax
    ret

    exit:
    mov rax, sys_exit
    mov rbx, 0
    int 80h

Thanks

EDIT: After changing how I make my syscalls as below it works fine. Thanks all for your help!

sys_write is now 1
sys_exit is now 60
stdout now goes in rdi, not rbx
the string to write is now set in rsi, not rcx
int 80h is replaced by syscall

2

2 Answers

5
votes

I'm still running 32-bit hardware, so this is a wild asmed guess! As you probably know, 64-bit system call numbers are completely different, and "syscall" is used instead of int 80h. However int 80h and 32-bit system call numbers can still be used, with 64-bit registers truncated to 32-bit. Your tests indicate that this works with addresses in .data, but with a "stack address", it returns -14 (-EFAULT - bad address). The only thing I can think of is that truncating rcx to ecx results in a "bad address" if it's on the stack. I don't know where the stack is in 64-bit code. Does this make sense?

I'd try it with "proper" 64-bit system call numbers and registers and "syscall", and see if that helps.

Best, Frank

2
votes

As you said, you're using ELF64 as the target of the compilation. This is, unfortunately, your first mistake. Using the "old" system call interface on Linux, e.g. int 80h is possible only when running 32-bit tasks. Obviously, you could simply assemble your source as ELF32, but then you're going to lose all the advantages if running tasks in 64-bit mode, namely the extra registers and 64-bit operations.

In order to make system calls in 64-bit tasks, the "new" system call interface must be used. The system call itself is done with the syscall instruction. The kernel destroys registers rcx and r11. The number of the system is specified in the register rax, while the arguments of the call are passed in rdi, rsi, rdx, r10, r8 and r9. Keep in mind that the numbers of the syscalls are different than the ones in 32-bit mode. You can find them in unistd_64.h, which is usually in /usr/include/asm or wherever your distribution stores it.