CS31 Weekly Lab Week 4

IA32 assembly, gdb disassemble
Create a week04 subdirectory in your weeklylab subdirectory and copy over some files:
    cd cs31/weeklylab		
    pwd
    mkdir week04
    ls
    cd week04
    pwd
    cp ~newhall/public/cs31/week04/* .
    ls
    Makefile simpleops.c

Week 4 lab goals:

  1. Learn the complete Memory Addressing modes in IA32 and the leal instruction
  2. Some practice with IA32 assembly code, gcc, objdump
  3. Learn gdb (and ddd) disassemble, ni, si,

Complete Memory Addressing and leal
So far we have looked at special forms of Memory addressing in IA32.

The General Memory Addressing form is the following:
  D(Rb,Ri,S)   # Mem[Reg[Rb]+S*Reg[Ri]+ D]

 D,Rb,Ri,S are used together to compute a memory address
      Rb:     Base register             
      Ri:     Index register
      S:      Scale: often 1, 2, 4, or 8     Q: why 1, 2, 4 or 8?
      D:      Constant "displacement" 
     D(Rb,Ri,S)  # Mem[Reg[Rb]+S*Reg[Ri]+ D]   
For example, an instruction could specify a memory operand as:
addl 8(%ecx, %eax, 4), %edx  # R[%edx] <-- M[R[%ecx] + 4*R[%eax] + 8]
There are a lot of Special Cases of this general form (not all 4 values need to be present):
 D(Rb,Ri,S)  # Mem[Reg[Rb]+S*Reg[Ri]+ D]   The General Form
  (Rb,Ri,S)  # Mem[Reg[Rb]+S*Reg[Ri]]
   (Rb,Ri)   # Mem[Reg[Rb]+Reg[Ri]]
  D(Rb,Ri)   # Mem[Reg[Rb]+Reg[Ri]+D]
   (,Ri,S)   # Mem[S*Reg[Ri]]
  D(,Ri,S)   # Mem[S*Reg[Ri] + D]
  (Rb)       # Mem[Reg[Rb]]        we have already seen this form: (%eax)
  D(Rb)      # Mem[Reg[Rb]+D]      we have already seen this form: -8(%ebp)
  D          # Mem[D]              we have already seen this form: $10
Let's try out some examples of computing address (fill in the table):
Assume:
  %edx stores 0xf000
  %ecx stores 0x0100

Address Expression        Address Computation          Address
--------------------------------------------------------------
   0x8(%edx)
   (%edx, %ecx)
   (%edx, %ecx, 4)
   0x80(,%edx, 2)

Introduction to the leal instruction

Load effective address: leal S,D # D<--&S, where D must be a register, and S is a Memory operand.

leal looks like a mov instr, but does not access Memory. Instead, it takes advantage of the addressing circuitry and uses it to do arithmetic (as opposed to generating multiple arithmetic instructions to do arithmetic).

(ex) if edx holds the value of x:
 leal (%eax),%ecx  # R[%ecx]<--&(M[R[%eax]])       
 # this moves the value stored in %eax to %ecx
The key is that the address of (M[ at address x ]) is x, so this is moving the value stored in %eax to %ecx; there is no memory access in this instruction's execution.

Examples:

Assume:   %eax: x    %edx: y

leal (%eax), %ecx               # R[%ecx] <-- x
leal 6(%eax), %ecx              # R[%ecx] <-- x+6
leal 7(%edx, %edx, 4), %ecx     # R[%ecx] <-- 5y + 7 (y + 4y+ 7)
leal 10(%eax, %edx, 5), %ecx    # R[%ecx] <-- x + 5y + 10: 
leal appears a lot in compiler generated code. You will figure how it works as you see it more and more.

gcc to generate IA32 assembly
Let's try out gcc to build IA32 assembly files and .o files and look at the results.

Open up simpleops.c in vim.

We are going to look at how to use gcc to create an assembly version of this file, and how to create a object .o file, and how to examine its contents. We are also going to look at how to used gdb to see the assembly code and to step through the execution of individual instructions.

If you open up the Makefile you can see the rules for building .s, .o and executable files from simpleops.c. We will be compiling the 32-bit version of instructions, so we will use the -m32 flag, and we are using version 4.4 of gcc (version 4.4 generates easier to read IA32 code):

gcc-4.4 -m32 -S simpleops.c   # just runs the assembler to create a .s text file
gcc-4.4 -m32 -c simpleops.s   # compiles to a relocatable object binary file (.o) 
gcc-4.4 -m32 -o simpleops simpleops.o  # creates a 32-bit executable file
To see the machine code and assembly code mappings in the .o file:
objdump -d simpleops.o
You can compare this to the assembly file:
cat simpleops.s
gdb disassemble and instruction stepping
Next, let's try disassembling code using gdb. With gdb we can execute individual IA32 instructions, examine register values, and disassemble functions. Do a 'make clean' then a 'make' to rebuild an IA32 version of the simpleops executable file.
gdb simipleops
(gdb) break main
(gdb) run
In gdb you can disassemble code using the disass command:
(gdb) disass main
You can set a break point at a specific instruction:
(gdb) break *0x080483c1   # set breakpoint at specified address 
(gdb) cont
(gdb) disass 
And you can step or next at the instruction level using ni or si (si steps into function calls, ni skips over them):
(gdb) ni	  # execute the next instruction then gdb gets control again 
(gdb) ni
(gdb) ni
(gdb) ni
(gdb) ni
(gdb) disass
You can print out the values of individual registers like this:
(gdb) print $eax
You can also view all register values:
(gdb) info registers
You can also use the display command to automatically display values each time a breakpoint is reached:
(gdb) display $eax
(gdb) display $edx
We are going to try running this in ddd instead of gdb, because ddd has a nicer interface for viewing assembly, registers, and stepping through program execution:
ddd simpleops
The gdb prompt is in the bottom window. There are also menu options and buttons for gdb commands, but I find using the gdb prompt at the bottom easier to use.

You can view the assembly code by selecting the View->Machine Code Window menu option. You will want to resize this part to make it larger.

You can view the register values as the program runs (choose Status->Registers to open the register window).

See my GDB Guide for more information about using gdb and ddd. (see the "using gdb to debug assembly code and examine memory and register values" section).

Also see figure 3.30 on p.255 of the textbook.