CS202 - GDB Tutorial / exercise

Objectives

  • Get familiar with working with GDB

Introduction

I decided that you will get a better feel for working with GDB if you just do it rather than watching me plow through examples. So, we are going to do something a warm-up run to prepare you to tackle the bomb lab. As such, we will be working with executables that do not have included debugging data.

As with our other work, this must be done on a Linux machine. I am handing you Linux executable files so they flat out will not run on other platforms.

To help you out, here is a quick reference to GDB

We will also start using a different interface to GDB called the "TUI" (terminal user interface). This provides us with some subwindows so we can see the registers and source while we debug.

First look

Download the file.

Open terminal, move to a directory you want to work in ad then enter the following (the $ is the prompt -- don't include that):

$ wget http://www.cs.middlebury.edu/~candrews/classes/cs202-f18/code/mystery.tar

This will download our test file (mystery.tar) to your current working directory.

Unpack the tar file with tar xvf mystery.tar. This will give you an executable file called mystery.

Start GDB with the first file: gdb mystery -tui.

The screen should be split in two, and the top will say something about "No Source Available". Type C-x 2 (this is the control key and x followed by a 2). This will add a third pane. There are a couple of different modes, so keep typing C-x 2 until the top pane says "Register Values Unavailable" (it will populate when we start the program), the middle shows you assembly, and the bottom is the GDB command line. This is our default assembly debugging view.

You will probably see that the central pane (the assembly code) is highlighted. You can use your arrow keys to move around in the code a bit. To move to the other panes, you can type C-x o. It will cycle through the available panes if you keep typing it.

Question 1: Recalling what you know about the arguments to main, look at the first couple of lines. How many arguments is this program expecting (including the name of the program)?

If you follow the branch to main+62, you will see that this starts setting up for a call to printf on main+75. Let's take a look at what is going on there.

Set a breakpoint at main (type b main).

Type run to start the program. You should see the registers populate, and the assembly pane will show that we stopped at the start of main.

We did not enter an argument, so we should fail the comparison and jump. We will walk forward in the code one line at a time. Use nexti (or ni). Do this five times and you should be one line main+70.

You can see that the program just finished loading two arguments. Let's take a look at the contents of %rsi. We can see that on line main+62 it copied the contents of memory at %rsi into %rsi. Recalling that this program just started, %rsi is still the second argument to main -- i.e., argv, an array of strings (or a pointer to a pointer). Indeed, if you look in the register, you will see that it is a big number, so it is probably still an address.

We just did the equivalent of argv = *argv, or argv = argv[0]. So, %rsi should now be holding the address of the first string in argv.

We can check this. To see what it points at, we can examine memory (shortcut x).

Type x /s $rsi. The /s is a format string (see the guide for more details). In this case, it says "read memory as a null terminated string" (this is the meaning of the s). Also note that we have to type "$" instead of "%" for the register name. Since examine is a command for looking at memory, it will use the value stored in %rsi as an address (i.e., treat this as de-referencing a pointer). When you do this, it should print out the name of the executable, which is what we expect to find in argv[0].

Question 2: On line main+65, we can see that an immediate value is being loaded into %edi. We know %edi will be the first argument to printf. We also know that the first argument to printf is the format string. Use examine to reveal the format string pointed to by %edi. Copy the response into the answer field for question 2 (make sure to include everything from the address to the final quote).

Type continue or c. This should print out the usage message and then exit.

Examining the functionality

We learned from the usage message that this expects a number. If you look at the path in the program we didn't take, you can see the call to strol shortly after the jump. This would seem to confirm that it expects a number.

We can pass arguments to the program with the run command.

Type run 13.

Notice that our breakpoint is still active, so we stop when we reach the first line.

Looking ahead in the code, we can see that call to strol is followed by a line putting the return value into %edi and then calling something called calculate. So, the program seems to parse the value into a number and then calls calculate with it (this also reveals that there is no error checking to make sure the input was a valid number...).

calculate seems to be where the action is, so we can turn our attention there.

Type b calculate to put a breakpoint at the function, and then type continue. You should now be at the start of the function.

Step forward until you are on line calculate+14.

Question 3: What values are in %eax, %edx, and %edi?

Looking at the jump, it should be obvious this is a loop. Step forward through another iteration of the loop (so you are back at line calculate+14 again).

Question 4: What values are in %eax, %edx, and %edi?

Watching the registers, step forward until you exit the loop and reach line `calculate+16.

Question 5: What value is in %eax?

Question 6: Bearing in mind that we initially passed in a 13, and thinking about what you saw as you stepped through the loop, what does this function do?

Go ahead and type c to let the program print out the answer and finish.

Last Updated: 11/5/2018, 5:12:52 PM