CSE 351 - Spring 2010 - Section 3

Compiling with GCC: Memory Referencing Example from Lecture

#include <stdio.h>

void fun(int i) {
  double d[1] = {3.14};
   long a[2];
   a[i] = 1073741824;
   printf("%.24f\n", d[0]);
}

int main(void) {
   fun(0);
   fun(1);
   fun(2);
   fun(3);
   fun(4);
   fun(5);
   fun(6);
   fun(7);
   fun(8);
   fun(9);
   return 0;
}

If you save this file as float.c, you can compile it with gcc as follows:

gcc -o float float.c

GCC stands for the GNU Compiler Collection. It is an open source compiler available for virtually every platform in existence, and it is the most popular (and free!) way to compile C programs in the world.

This will produce an executable binary file called float, which you can verify with the ls (list directory) command. The "$" below stands for the command prompt in your operating system's terminal/shell.

$ ls -l float

You'll see the following directory listing. The letter "x" means you have the ability to execute the file.

-rwxr-xr-x 1 buy-ppham staff 12604 Apr 16 13:31 float

Normally, gcc sets the execute permissions on the programs it produces automatically. After all, why would you compile a program if you can't run it? However, occasionally the permissions will get messed up if, for example, you extract a file created on Linux onto a Windows computer, and then copy it back to Linux. You can fix the execute permission as follows (on Linux), where program is the name of your program file.

$ chmod +x program

If you run the float program, you'll see the results we described in lecture.

3.140000000000000124344979
3.140000000000000124344979
3.139999866485595703125000
2.000000610351562624344979
3.140000000000000124344979
3.140000000000000124344979
3.140000000000000124344979
3.140000000000000124344979
Segmentation fault

Printing Out Binary Float Representations

You can use the following code to print out the binary representation of any float (single or double representations.) Use the same compilation commands with gcc as above.

#include <stdio.h>

union ufloat {
  float f;
  unsigned u;
};

int main(void) {
  union ufloat u;
  u.f = 3.75;
  printf("%x\n", u.u);
  return 0;
}

You can change the value "3.75" to whatever float you wish to inspect. When you run the program above, you get the following output in hex:

40700000

This translates to the following binary number:

0100 0000 0111 0000 0000 0000 0000 0000

Separating this into sign, exponent, and fractional parts:

0 10000000 11100000000000000000000

The sign bit is zero, as we would expect for a positive number.

The exponent is a 1 shifted 7 positions to the left, or 2 to the 7th power, which is 128. However, for an 8-bit exponent field, we have a bias of 2**(8-1) - 1, or 127. So the actual exponent is 1.

The fractional part begins with three ones, and there is an implied 1 before that.

1.111

Shifting by the exponent, 2**1, gives us:

11.11

This is the binary representation of 3.75, which you can verify by visual inspection.

GNU Debugger Tutorial

Here are some common tasks that you will need to do in gdb in order to complete Lab 3. First, follow the instructions in the lab handout to extract the lab file into your home directory.

First, start gdb on the program you wish to debug. Again, the '$' below just stands for the command prompt in your operating system's terminal/shell. Substitute your actual bomb program name for bombx.

$ gdb bombx

You'll see the following startup preamble if gdb was able to load the debug information in your executable program correctly.

GNU gdb (GDB) Fedora (6.8.50.20090302-40.fc11)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
(gdb)

list

You can run the list command to show the source code that corresponds to the original program.

(gdb) list

By default, list will display the ten lines previous to the current execution point in the program. In this case, we haven't started our program yet, so we are at the beginning, in the main function, which is the program entry point.

35 int main(int argc, char *argv[])
36 {
37 char *input;
38
39 /* Note to self: remember to port this bomb to Windows and put a
40 * fantastic GUI on it. */
41
42 /* When run with no arguments, the bomb reads its input lines
43 * from standard input. */
44 if (argc == 1) {

start

Let's start running the program, since we can't really inspect its state before we do that.

(gdb) start
Temporary breakpoint 1 at 0x8048995: file bomb.c, line 44.
Starting program: /homes/iws/ppham/bomb7/bomb

Temporary breakpoint 1, main (argc=1, argv=0xbffff654) at bomb.c:44
44 if (argc == 1) {

As you can see, gdb sets an automatic (temporary) breakpoint at the first executable line in the main function. A breakpoint interrupts normal program execution, freezing it in time, so you can inspect the internal state of your program, dissecting it on your laboratory table (don't worry, programs can't feel pain, and you put it all back together before anyone notices!)

step

Let's step through the program line by line.

(gdb) step

This will take us to the next executable line.

45 infile = stdin;

Let's step a few more times until we get to the line that asks for input. You'll note that printf commands produce output interleaved with the gdb prompt.

72 input = read_line();

When you get to the line above, you'll need to enter in a string at the blank line following it in order to gdb to continue running the program.

(gdb) step
blah
73 phase_1(input);

print

One of the most useful gdb command is print, which lets you print out different expressions, especially source code variables. This doesn't work with registers and assembly instructions, but you can get around that using x and info registers commands below.

In normal C programs thought, print is pretty useful. Below, while we are in the main function, we can print the arguments that main was called with (strings from the command-line).

(gdb) print argc
$4 = 1
(gdb) print argv
$5 = (char **) 0xbffff644
(gdb) print argv[0]
$6 = 0xbffff78f "/homes/iws/ppham/bomb7/bomb"

argc is the first argument to main, and it tells us how many command-line arguments the user gave us. In this case, there was only 1.

argv is the second argument to main, and if we try to print it directly, it tells us it is of type char**, that is, an array of strings (pointer to a pointer to a char). Since we know there is exactly one string in this array (because argc was 1), we can index the first element with argv[0]. This gives us the full path and name of the bomb executable which we used to call gdb in the first place, as expected.

break

Let's set a breakpoint at the beginning of the phase_1 program, since it seems likely this is where we defuse the bomb's first phase.

(gdb) break phase_1
Breakpoint 2 at 0x8048e2a

Now let's use the continue command to keep running until we run into another breakpoint, in this case, the one we just set at the beginning of the phase_1 function.

(gdb) continue
Continuing.

Breakpoint 2, 0x08048e2a in phase_1 ()

where

At any point while debugging a program, you can check where you are by using the where command. It will show all the nested function calls that you executed to get where you are now, starting with the main function at the bottom of the stack, and the current function at the top of the stack.

(gdb) where
#0 0x08048e2a in phase_1 ()
#1 0x08048a35 in main (argc=1, argv=0xbffff654) at bomb.c:73

Sometimes the source code isn't available for a function, or you would like to inspect the assembly instructions directly. You can do this by using the disas command.

(gdb) disas
0x08048e24 : push %ebp
0x08048e25 : mov %esp,%ebp
0x08048e27 : sub $0x18,%esp
0x08048e2a : movl $0x80497c4,0x4(%esp)
0x08048e32 : mov 0x8(%ebp),%eax
0x08048e35 : mov %eax,(%esp)
0x08048e38 : call 0x8048e6b <strings_not_equal>
0x08048e3d : test %eax,%eax
0x08048e3f : je 0x8048e46 <phase_1+34>
0x08048e41 : call 0x8048f45 <explode_bomb>
0x08048e46 : leave
0x08048e47 : ret
End of assembler dump.

help

You can also specify the name of a function to disassemble. As with any of the commands above, you can use the help command to get more details about usage and options.

(gdb) help disas
Disassemble a specified section of memory.
Default is the function surrounding the pc of the selected frame.
With a /m modifier, source lines are included (if available).
With a single argument, the function surrounding that address is dumped.
Two arguments are taken as a range of memory to dump.

Typing help by itself will show you a list of more general topics you can use to navigate down to a specific command, if you don't know exactly what you are looking for.

stepi

To step through individual lines of assembly code, without returning to the next line of C code, you must use the stepi command instead of step.

(gdb) stepi
0x08048e32 in phase_1 ()
(gdb) stepi
0x08048e35 in phase_1 ()
(gdb) stepi
0x08048e38 in phase_1 ()

Command History

Note that you can press the up and down arrow keys to go backwards and forwards through your command history. This may save you typing if you want to reuse or slightly modify a previous command.

info registers

You can print out the current value of the processor's registers by using the following command.

(gdb) info registers
eax 0x804a860 134522976
ecx 0x6 6
edx 0x1 1
ebx 0x4d7a5ff4 1299865588
esp 0xbffff550 0xbffff550
ebp 0xbffff568 0xbffff568
esi 0xbffff644 -1073744316
edi 0x0 0
eip 0x8048e2a 0x8048e2a
eflags 0x200286 [ PF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

x

You can inspect locations in memory using the x command, and optionally specify what type to expect, and how many, at the given address. For example, we can use the address stored in the %eax register from the previous command, and tell it to expect one string (1s):

(gdb) x/1s 0x804a860
0x804a860 : "blah"

Run help x to see other datatypes you can use besides strings.