4

I have just started to teach myself x86 assembly on linux from these video tutorials. Early on it teaches you how to use the write sys-call to print a string that is stored in the data section. Is it possible to use the write syscall to print a string that is stored on the stack. Here is the code I wrote to try and do this which doesn't seem to work.

.data
abc: 
    .asciz "ABC"
.text
    .globl _start

_start:
    pushq %rbp
    movq %rsp, %rbp
    subq $32, %rsp
    leaq -32(%rbp), %rdi
    movb $65, (%rdi)        #move 'A' on to stack
    addq $1, %rdi           
    movb $66, (%rdi)        #move 'B' on to stack
    addq $1, %rdi
    movb $67, (%rdi)        #move 'C' on to stack
    addq $1, %rdi
    movb $0, (%rdi)         #Null terminate  

    movq $4, %rax           #4 is write syscall
    movq $1, %rbx           #1 for stdout
    movq %rsp, %rcx         #pointer to ABC string on stack
    movq $3, %rdx           #length of string
    int $0x80

    movq $1, %rax           #exit syscall
    xorq %rbx, %rbx
    int $0x80

This program just runs and exits without printing ABC, but if I pass the string stored in the data segment, ABC is printed. Am I doing something wrong or can you not do it this way. Any help apprecitated.

  • Except for the initial pushq, this code never appears to modify the stack pointer... Did you mean 'subq $32, %rsp', or am I missing something? – Nemo Jul 17 '11 at 13:40
  • @Nemo I did yeah, that's just a typo in my post. I do it correctly in my original code. Thanks –  Jul 17 '11 at 13:43

1 Answers1

4

Your syscall numbers seem WAY off.

From your use of movq and the "r" registers, I can guess you are trying on x86-64. Taking a look at /usr/include/asm/unistd_64.h, I can see the following:

#define __NR_write                              1
#define __NR_stat                               4
#define __NR_exit                               60

strace agrees with me:

$ strace ./abc
execve("./abc", ["./abc"], [/* 43 vars */]) = 0
stat("", NULL)                          = -1 EFAULT (Bad address)
write(-1698988341, NULL, 3 <unfinished ... exit status 0>

Note that the parameters are also way off. You are also using the wrong registers for the rest of the parameters. The calling convention on x86-64, AFAIK, uses the following registers for the parameters, in this order: rdi, rsi, rdx, r10, r8, r9.

Perhaps you are trying to do syscalls on x86-64 the way they are done on i386 and expecting it to be the same?

CesarB
  • 43,947
  • 7
  • 63
  • 86
  • Related question: http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64 – CesarB Jul 17 '11 at 14:19
  • `int $0x80` isn't the syscall gate on x86-64 either... – bdonlan Jul 17 '11 at 14:22
  • Another related question: http://stackoverflow.com/questions/1204026/syscall-from-inline-asm-in-x86-64-linux – CesarB Jul 17 '11 at 14:27
  • @bdonlan: Yet, for some reason, it worked here (perhaps because I have 32-bit compatibility enabled?). Feel free to edit my answer to add the correct way to call the kernel syscall gate (AFAIK `syscall`, but I did not check). – CesarB Jul 17 '11 at 14:30
  • 1
    Thank you. I never suspected it'd be a 64bit issue. I changed the syscall number and googled for the the correct registers for the parameters and it works now. It is strange though why the 32bit syscall procedure works with the string in the data segment. –  Jul 17 '11 at 14:39
  • @CesarB, yes, IIRC `int $0x80` is interpreted as a 32-bit syscall on x86-64. So if your addresses are in the lowest 4G of memory, it might work. Presumably the stack was outside of the low 4G of memory. – bdonlan Jul 17 '11 at 14:41
  • 1
    @bdonlan, this seems very logical. I ran my program in gdb and the stack address was a value greater than 2**32, where as the data segment naturally was a low address. Thanks for all your help everyone. :) –  Jul 17 '11 at 14:51
  • Strace decodes `int 0x80` from 64-bit code as if it had used `syscall`, so it's wrong. See the duplicate target. (The right answer of course is to use `syscall`, not `int 0x80`. That's essential. `int $0x80` invokes the 32-bit ABI regardless of what mode it's called from) – Peter Cordes Sep 25 '17 at 17:32