Home > Enterprise >  Inappropriate behaviour while printing an ASCII character from a number taken from the stack in x86
Inappropriate behaviour while printing an ASCII character from a number taken from the stack in x86

Time:12-16

I am trying to understand how to properly work with stack in x86-64 assembly in Linux. I am pushing some numbers to the stack then I want to take the top number in the stack and print the appropriate ASCII value. But it prints different stuff every time. Please help me understand why the code below behaves that way.

.section .bss
.comm x, 4 

.section .text
.globl _start

_start:
    push $52
    push $65

    mov %rsp, (x)

    mov $1,                 %rax
    mov $1,                 %rdi
    mov $x,                 %rsi
    mov $1,                 %rdx
    syscall

    mov $60,  %rax             
    movb $0,  %dil
    syscall

Also, I would appreciate it if you gave me some sources where I can learn to work with the stack in assembly.

CodePudding user response:

The instruction mov %rsp, (x) writes the value of RSP (current stack pointer) to the location x in memory, which is in your .bss section. Then, you are doing mov $x, %rsi: this moves the address of x into RSI, so RSI will point to your .bss variable, which holds the value of the stack pointer. When you try and issue a write syscall reading from the memory pointed by RSI, your output will be the least significant byte of RSP that you saved there. Since the position of the stack can vary every single execution of your program, this is why you are getting a different output every time.

Also, you are reserving space in .bss with .comm x, 4, but you are moving RSP into it with mov %rsp, (x), which is an 8-byte move.

What you really want to do to print values from the stack to standard output is simply mov %rsp, %rsi. You don't need x at all:

.section .text
.globl _start

_start:
    push $65

    mov $1, %rax
    mov $1, %rdi
    mov %rsp, %rsi
    mov $1, %rdx
    syscall

    mov $60, %rax
    mov $0, %rdi
    syscall

The above code should output A and exit.

If you want to use an intermediate variable in the .bss, you will have to perform two moves to copy a value from the stack into it using an intermediate scratch register (since mov can only take one memory operand at a time):

.section .bss
.comm x, 4

.section .text
.globl _start

_start:
    push $65

    movb (%rsp), %al
    movb %al, (x)

    mov $1, %rax
    mov $1, %rdi
    mov $x, %rsi
    mov $1, %rdx
    syscall

    mov $60, %rax
    mov $0, %rdi
    syscall

Also, note that on Linux exit takes an int as parameter which is 4 bytes on x86, so movb $0, %dil won't suffice in general. In your case it's ok since you previously set RDI to 1 and RDI is preserved on syscall.

In general, you can use xor

  • Related