CS31 Weekly Lab Week 4

Week 4 lab topics:

  1. "Boolean" values and expressions in C
  2. Command line arguments
  3. Pointers and "pass by reference"
  4. Assembly code, objdump, gdb and ddd disassemble, ni, si,

Start up a terminal and ssh into your CS machine:
  ssh -Y you@your_machine.cs.swarthmore.edu
Once remotely connected to cs, you can start other xterms on our system by:
   $ xterm &
Then 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
C boolean expressions
C does not have a boolean type, but defines "true" and "false" values as: if an expression evaluates to zero it is considered false, if an expression evaluates to any non-zero value, it is considered true. Some examples of "false" expressions in C (expressions that evaluate to 0) are: any signed or unsigned expression that evaluates to 0; any bit-wise operation involving two or more values whose result is 0; the ascii character value '\0'; and a NULL pointer. Here are some example C code snippets that test for logical "true" and "false" conditions:
// C relational operators: ==, !=, <, <=, >, >=
// C logical operators: &&, ||, !  
// relational operators have higher precidence than logical, but always 
// just use parens around complicated expressions involving both

if(x == 0) {  // if x is false  (x is equal to 0)
 ...
}

if (x != 0) {  // if x is true  (x is not equal to 0) 
 ...
}

// often times C programmers write expression the test for false like this:
if(!x) {   // if the logical not of x is true,  true when x == 0 (or when x is false)

}

ptr = malloc(...  
if(!ptr) {  // malloc returned NULL 
 ...
}

if(x+y) {  // this will be true whenever x+y is not == 0 
 ...
} else if (x > y) {  // this will be true whenever x is greater than y
 ...
} else {  // this will be executed whenever x+y is not == O and x <= y
 ...
}


C command line arguments and atoi
Let's look at an example C program that takes command line arguments. Open commandlineargs.c in an editor:
vim commandlineargs.c
The first thing to note is the change in main's definition:
int main(int argc, char *argv[]) { ...
The first parameter to main, argc, is the number of command line arguments. For example, if the user enters:
./a.out 10 11 200
argc will be 4 (a.out counts as one of the command line arguments, and 10, 11, and 200 as three more).

Each command line argument is passed to main in the second parameter, argv, as a string (argv is an array of strings):

        -----
argv[0]:| *-|-----> "./a.out"
        -----
argv[1]:| *-|-----> "10"
        -----
argv[2]:| *-|-----> "11"
        -----
argv[3]:| *-|-----> "200"
        -----
C has functions that can convert strings of numeric characters to their int, float, ..., values. See the man page for atoi (man atoi).
int x = atoi(argv[1]);  // x gets the int value 10

Let't try compiling and running this program with different command line arguments and see what happens.

C pointer variables and "pass by reference"
A pointer variable is declared as follows:
type_name *var_name;

char *str;     // str's type is (char *)
int  *array;   // array's type is (int *)
A pointer stores the memory address of a storage location of the specified type (e.g. array "points to" and int storage location in memory).

Pointers can be used to pass argument values by reference (pass by reference means that the argument's value can be changed by the function, pass by value means that it cannot).

Let's look at a program that does C style pass by reference:

vim passbyreference.c
Try running it and see if you understand what is happening and why.

Draw the call stack for the first call to this function:

See my Pointers in C reference for more information about pointers in C and C-style pass by reference.
Really everything in C is passed by value; C-style pass-by-reference is just passing the value of an address (a pointer) to a function as opposed to passing the value of an int or float or ...

gdb disassemble and instruction stepping
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 to gcc:

gcc -m32 -s simpleops.c   # just runs the assembler to create a .s text file
gcc -m32 -c simpleops.c   # compiles to a relocatable object binary file (.o) 
gcc -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
Now let's try some things out in gdb:
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 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.