© Jo Van Hoey 2019
J. Van HoeyBeginning x64 Assembly Programminghttps://doi.org/10.1007/978-1-4842-5076-1_10

10. The Stack

Jo Van Hoey1 
(1)
Hamme, Belgium
 

We have already discussed registers, the type of fast temporary storage that can be used to store values or addresses to be used during execution of instructions. There is also the slower storage, memory, where the processor can store values for a longer time. Then there is the stack, a contiguous array of memory locations.

Understanding the Stack

As discussed in Chapter 8, the stack segment starts in high memory, and when it grows, it grows in the downward direction, like an icicle grows downward when it grows larger. Items are placed on the stack with the push instruction and removed from the stack with the pop instruction . Every time you push, the stack grows; every time you pop, the stack shrinks. You can verify this stack behavior by monitoring rsp, the stack pointer, which points to the top (thus actually the bottom, because it grows downward) of the stack.

The stack can be used as temporary storage to save values in registers and call them back later or, more importantly, to transfer values to functions. Functions or procedures will be treated in detail later.

In the example code in Listing 10-1, we will use the stack to reverse a string.
; stack.asm
extern printf
section .data
      strng       db    "ABCDE",0
      strngLen    equ   $ - strng-1 ; stringlength without 0
      fmt1        db    "The original string: %s",10,0
      fmt2        db    "The reversed string: %s",10,0
section .bss
section .text
      global main
main:
push  rbp
mov   rbp,rsp
; Print the original string
    mov    rdi, fmt1
    mov    rsi, strng
    mov    rax, 0
    call   printf
;push the string char per char on the stack
     xor   rax, rax
     mov   rbx, strng ; address of strng in rbx
     mov   rcx, strngLen   ; length in rcx counter
     mov   r12, 0          ; use r12 as pointer
     pushLoop:
          mov   al, byte [rbx+r12] ; move char into rax
          push  rax        ;push rax on the stack
          inc   r12        ; increase char pointer with 1
          loop  pushLoop   ; continue loop
;pop the string char per char from the stack
;this will reverse the original string
     mov   rbx, strng ; address of strng in rbx
     mov   rcx, strngLen   ; length in rcx counter
     mov   r12, 0          ; use r12 as pointer
     popLoop:
          pop   rax        ; pop a char from the stack
          mov   byte [rbx+r12], al  ;move the char into strng
          inc   r12        ; increase char pointer with 1
          loop  popLoop               ; continue loop
          mov   byte [rbx+r12],0 ; terminate string with 0
; Print the reversed string
    mov    rdi, fmt2
    mov    rsi, strng
    mov    rax, 0
    call   printf
mov   rsp,rbp
pop   rbp
ret
Listing 10-1

stack.asm

Figure 10-1 shows the output.
../images/483996_1_En_10_Chapter/483996_1_En_10_Fig1_HTML.jpg
Figure 10-1

Reversing a string

First, note that to calculate the string length, we decreased the length of the string by 1, ignoring the terminating 0. Otherwise, the reversed string would start with a 0. Then the original string is displayed followed by a new line. We will use rax to push the characters, so let’s first initialize rax with zeros using xor. The address of the string goes into rbx, and we will use a loop instruction , so we set rcx to the string length. Then a loop is used to push character after character on the stack, starting with the first character. We move a character (byte) into al. Then we push rax onto the stack. Every time you use push, 8 bytes are moved to the stack. If we did not initialize rax before, it might well be that rax contains values in the upper bytes, and pushing these values to the stack may not be what we want. After that, the stack contains the pushed character plus the additional 0 bits in the bits above al.

When the loop is finished, the last character is at the “top” of the stack, which is in fact at the lowest address of the icicle because the stack grows in the downward direction. Another loop is started that pops character after character from the stack and stores them in memory in the original string, one after another. Note that we only want 1 byte, so we pop to rax and only use al.

Here is an overview of what is happening (see Figure 10-2): the original string is at the right, and the characters are pushed, sent one by one to the stack, where they are appended to the previous stack content. After that the characters are popped and sent back to the memory address of the string, and because of the “last in first out” working of the stack, the string is reversed.
../images/483996_1_En_10_Chapter/483996_1_En_10_Fig2_HTML.jpg
Figure 10-2

Schema of reversing a string

Somehow you have to keep track of what you pushed on the stack and in what order. For example, when you use the stack to temporarily store registers, be sure to pop the registers in the reverse correct order; otherwise, your program will be wrong or in the worst case will probably crash. That is, when you push the following sequence:
      push rax
      push rbx
      push rcx
then you have to pop as follows, according to the “last in first out” principle:
      pop rcx
      pop rbx
      pop rax

In addition to registers, you can push memory and immediate values. You can pop to a register or a memory location but not to an immediate value, which is quite evident.

That’s good to know, but we will not use this here. If you want to push and pop the flag register to the stack, you can use the instructions pushf and use popf.

Keeping Track of the Stack

So, keeping track of the stack is important, and our old friend DDD has some easy features to do that. First open your editor to the source and delete the debug line that SASM added; then save the file and quit. At the CLI, make the program and then type the following:
      ddd stack

Select Data ➤ Status Displays in the menu, and scroll down until you find “Backtrace of the stack” and enable it. Set a breakpoint at, for example, main: and then click Run in the floating panel. Now start debugging and step through the program with the Next button (you do not want to step line per line through the printf function). See how the stack is displayed and updated in the upper window. Do not worry about the initial stuff that is displayed. When you arrive at the instruction after the push instruction, you will see that characters are pushed onto the stack in ASCII decimal representation (41, 42, etc.). Watch how the stack decreases during the second loop. That is an easy way to see what is on the stack and in what order.

Figure 10-3 shows how it looks.
../images/483996_1_En_10_Chapter/483996_1_En_10_Fig3_HTML.jpg
Figure 10-3

The stack in DDD

As we said before, DDD is open source and outdated. There is no guarantee that it will continue working as expected in the future, but for now it is not very elegant, but it will do.

In all fairness, you could force SASM to show the stack also, but that requires more manual work. Here is how it works: remember that you can show memory variables during debugging in SASM, and the stack is just a list of memory locations, with rsp pointing to the lowest location. Thus, we have to convince SASM to show us what is at address rsp and at the memory locations above. Figure 10-4 shows an example memory window in SASM showing the stack.
../images/483996_1_En_10_Chapter/483996_1_En_10_Fig4_HTML.jpg
Figure 10-4

The stack in SASM

We referred to rsp as $rsp. We increase the stack address every time with 8 ($rsp + 8), because at every push, 8 bytes are sent to the stack. As Type, we specified Char, bytes, 8 bytes, and Address. We chose Characters because we are pushing a string and then it is easy to read for us, and we chose bytes, because we are interested in byte values (al contains 1 byte every time), so 8 bytes are pushed every time. And rsp contains an Address. Step through the program and see how the stack changes.

It works, but you have to detail every stack memory place manually, which can be a burden if you are using a large stack and/or have a lot of additional memory variables you want to keep track of.

Summary

In this chapter, you learned the following:
  • The stack starts at an address in high memory and grows to lower addresses.

  • Push decreases the stack pointer (rsp).

  • Pop increases the stack pointer (rsp).

  • Push and pop work in reverse order.

  • How to use DDD to examine the stack.

  • How to use SASM to examine the stack.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.163.175