Create a week06 subdirectory in your weeklylab subdirectory and copy over some files:
cd cs31/weeklylab pwd mkdir week06 ls cd week06 pwd cp ~kwebb/public/cs31/week06/* . ls
Load effective address: leal S,D # D<--&S, where D must be a register, and S is a Memory operand. It's often used to implement C's address of (&) operator.
leal looks like a mov instruction, but it 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). For example, suppose you write C code that looks like:
int *values = malloc(15 * sizeof(int)); if (values == NULL) { //Error handling } int *index12 = &values[12];
When this example is converted to assembly code by the compiler, values (the memory block's base address) will be assigned to a register. Suppose it's put into %eax. Let's say the compiler wants to preserve %eax as the base address, but it also wants to store index12, the address of a bucket from the middle of the memory block, in %ecx. One way that the compiler might compute &values[12] is to use a leal:
leal 48(%eax), %ecx # compute an address equal to the value in eax + 48 and store the result in ecx
The key thing about interpreting the leal instruction is that it violates our rule of putting () around a register. For just this instruction, the () does not perform a dereference. Instead, the instruction is computing an address using the memory address hardware (e.g., with a displacement + register) and storing the result of that address computation.
leal appears a lot in compiler generated code. The compiler sometimes abuses leal to perform basic arithmetic, since it's another way to perform an add or subtract.
So... if it's just performing basic add/subtract arithmetic, why use leal then? The answer is that it cuts down on the number of instructions you need. In the example above, there's no other simple way to express, "add 48 to eax and store the result in ecx". Here's alternative, but it's twice as many instructions!
# Alternative movl $48, %ecx # Overwrite ecx by setting it to the constant value 48 addl %eax, %ecx # Add eax and ecx, store the result in ecx
First, let's open up simplefuncs.c in a text editor.
We are going to look again 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.
If you open up the Makefile you can see the rules for building .s, .o and executable files from simplefuncs.c. We will be compiling the 32-bit version of instructions, so we will use the -m32 flag to gcc:
gcc -m32 -S simplefuncs.c # just runs the assembler to create a .s text file gcc -m32 -c simplefuncs.c # compiles to a relocatable object binary file (.o) gcc -m32 -o simplefuncs simplefuncs.o # creates a 32-bit executable file
strings simplefuncs
objdump -d simplefuncs
First, let's open up simplefuncs.c in an editor. Then, let's try some things out in gdb:
gdb simplefuncs (gdb) break main (gdb) break func1 (gdb) runIn gdb you can disassemble code using the disass command:
(gdb) disass mainYou can set a break point at a specific instruction:
(gdb) break *0x565555cb # set breakpoint at specified addressAnd 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 (gdb) cont # continue to next break pointNow we are at the call to func1, let's step into this function using si (we also have a breakpoint at this function, let's see when it is hit):
(gdb) si (gdb) disass (gdb) ni (gdb) where (gdb) disass (gdb) contYou can print out the values of individual registers like this:
(gdb) print $eaxOr the memory contents at a given address, providing either the absolute numeric address or its value stored in registers:
(gdb) p *(int *)($ebp + 8) (gdb) x $ebp + 8 (gdb) x/d $ebp + 8 # x/d display as decimal valueYou can also view all register values:
(gdb) info registersYou can also use the display command to automatically display values each time a breakpoint is reached:
(gdb) display $eax (gdb) display $edxYou can use the examine command (x) to display the contents of a memory location either an address of via a register value (x is shorthand for examine, and p is shorthand for the print command):
x $esp-0x8 # see what p and x display for the same value p $esp-0x8 p *(int *)($ebp-0x8) # here is how to get what x gives you using print x $esp + 0x1c # here is examining the contents at a memory location x 0xffffd2fc # specifying the address in two different ways
ddd simplefuncsThe 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 register values as the program runs (choose Status->Registers to open the register window).
ddd a.out (gdb) break main (gdb) run 6 # run with the command line argument 6 (gdb) disass main # disassemble the main function (gdb) break sum # set a break point at the beginning of a function (gdb) cont # continue execution of the program (gdb) break *0x0804851a # set a break point at memory address 0x0804851a (gdb) ni # execute the next instruction (gdb) si # step into a function call (step instruction) (gdb) info registers # list the register contents (gdb) p $eax # print the value stored in register %eax (gdb) p *(int *)($ebp+8) # print out value of an int at addr (%ebp+8) (gdb) x/d $ebp+8 # examine the contents of memory at the given # address (/d: prints the value as an int) # display type in x is sticky: subsequent x commands # will display values in decimal until another type # is specified (e.g. x/x $ebp+8 # in hex)