Monday, December 20, 2010

PowerPC Assembly Tutorial on AIX: Chapter 5, Saving non-volatile registers

We will now return to the problem we left at – why was the exit value 97 instead of 5? We now know that volatile registers don't retain their values across function calls, and when we called getchar(), the contents of r3 got overwritten. How do we overcome this? We can store the value of r3 in a non-volatile register before calling getchar. By convention, while using non-volatile registers, we use them in the order r31, r30 ... r13. Also, as we use each non-volatile register, we need to save them in the stack, and restore them when we return from the function. Hence the above program should be modified as follow:

  1. .set r0, 0
  2. .set r1, 1
  3. .set r3, 3
  4. .set r31, 31
  5.  
  6. .extern            .getchar          # Tell the assembler that .getchar is an external symbol
  7.  
  8. .csect
  9. .globl  .main
  10. .main:
  11.          # Function prolog begins
  12.          mflr     r0                     # Get the link register in r0
  13.          stw      r31, -4(r1)       # Save caller's r31
  14.          stw      r0, 8(r1)          # Save the link register
  15.          stwu    r1, -64(r1)       # Store the stack pointer, and update. Create a frame of 64 bytes.
  16.          # Function prolog ends
  17.  
  18.          li          r3, 5
  19.          mr        r31, r3 # Copy contents of r3 into r31 for retention
  20.          bl         .getchar
  21.          ori        r0, r0, 0      # No-op, required by compiler/loader after a branch to an external function
  22.          mr        r3, r31 # Copy contents of r31 back to r3
  23.  
  24.          # Function epilog begins
  25.          addi    r1, r1, 64         # Restore the stack pointer
  26.          lwz      r31, -4(r1)       # Restore caller's r31
  27.          lwz      r0, 8(r1)          # Read the saved link register
  28.          mtlr      r0
  29.          # Function epilog ends

  30.          blr
In line 13, we save the caller's (in this case __start's) r31 in the stack frame. In line 19, we copy the contents of r3 into r31, so that when getchar returns, although r3 would have been overwritten, we would still retain the value in r31. After getchar returns, we copy the contents of r31 to r3. We restore the caller's r31 in line 26 before returning from this function.

4 comments:

Lee Killough said...

This code is not 100% safe:

stw r31, -4(r1)
stw r0, 8(r1) # Save the link register
stwu r1, -64(r1) # Store the stack

Why? Because if an interrupt occurs between the stw r31, -4(r1) and the stwu r1, -64(r1), the stack below r1 will be clobbered and the saved value of r31 will be lost.

It needs to be written:

stw r0, 8(r1) # Save the link register
stwu r1, -64(r1) # Store the stack
stw r31, 60(r1) # Save caller's r31

so that the stack register is decremented before r31 gets stored.

Lee Killough said...

Similarly, this code:

addi r1, r1, 64 # Restore the stack pointer
lwz r31, -4(r1) # Restore caller's r31

should be written:

lwz r31, 60(r1) # Restore caller's r31
addi r1, r1, 64 # Restore the stack pointer

Lee Killough said...

I should amend this slightly; apparently some PowerPC stack frame conventions have what is called a "Red Zone" which protects the data below the stack pointer even in the event of an interrupt.

However, other PPC stack frame conventions do not have this guarantee of a protected Red Zone. So I would generally refrain from assuming anything below the current stack pointer.

Rajbir Bhattacharjee said...

Thanks for stopping by and taking the time to comment on this. This is much appreciated.

In this blog, I have considered mainly userland programs running under AIX, and have used the following convention as a reference:
href=http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.aixassem/doc/alangref/linkage_convent.htm

> Why? Because if an interrupt occurs between the
> stw r31, -4(r1) and the stwu r1, -64(r1),
> the stack below r1 will be
> clobbered and the saved value of r31 will be lost.
That is not true, for a couple of reasons:
It is guaranteed by the OS that userspace applications will never face any issues because of interrupt. In this blog, I was mostly focused on user-space applications running under AIX.
Interrupts use their own stack, and don't use the applications current stack at all. The CSA of each processor points to a pinned unused memory area which is used to save all the registers of the current context.

> apparently some PowerPC stack frame conventions
> have what is called a "Red Zone"
Thats a good note to have. In this program, I have mostly concentrated on programs running under AIX. Just a reminder to everybody that this blog is very specific.

Again, thanks for stopping by, and contributing to this. My attempt is to present the general concepts on how to write assembly programs, but I would not like to have something *wrong* in my blog. Feel free to mail me at rajbir.bhattacharjee at gmail.com