This assignment will make you more familiar with how to build simple Unix programs, and debug them with GDB. You can do this assignment on any operating system that supports the Unix API (Linux Openlab machines, your laptop that runs Linux or Linux VM, and even MacOS, etc.). You don't need to set up xv6 for this assignment. Submit your programs and the shell through Gradescope (see instructions at the bottom of this page).
NOTE: YOU CANNOT PUBLICLY RELEASE SOLUTIONS TO THIS HOMEWORK. It's ok to show your work to your future employer as a private github/gitlab repo, however any public release is prohibited.
For Mac / OSX users, the support of 32 bit applications is deprecated in the latest version. So if you already updated your system to macOS Catalina or have updated XCode then we recommend that you do the homework on Openlab machines.
Part 1: Using GDB
Download the main.c, and look it over. This is a skeleton for a simple UNIX program.
To compile main.c, you need a C compiler, such as gcc. On Openlab machines, you can compile the skeleton with the following command:
This will produce an a.out file, which you can run:
Alternatively you can pass an additional option to gcc to give a more meaningful name to the compiled binary, like
Here gcc will compile your program as hello.
Debugging programs with GDB
On UNIX systems the main debugger is GDB (GNU debugger). To be able to comfortably debug your code compile it with the -g option which will instruct the compiler to generate debug symbols (variable names, source lines, etc.) for the program. For example, change the command you ran to build hello to
This will compile your hello program with debugging symbols (-g flag), as a 32bit x86 executable (-m32 flag), and for simplicity avoid generating position independent code (-fno-pic flag). Then you can start you program under control of gdb:
This starts gdb ready to execute your hello program. To get it running type the run command in the GDB command prompt (or just r for short):
Now the program runs and finished printing "Hello world".
GDB is a feature-rich debugger, and it will take you some time to learn all the features. Here are a few starting points: GDB tutorial, GDB intro and GDB Cheat Sheet.
Probably, the best resource for this homework is Operating Systems from 0 to 1. Chapter 6. Runtime Inspection and Debug (it is strongly recommended to read this chapter).
At a high level you need only two main things: 1) breakpoints and 2) ability to examine data. Breakpoints can be set with the "b" command inside gdb.
Breakpoints and single stepping
Just to make debugging a bit more realistic lets add another function to our simple program. Lets change it to compute a sum of numbers from 0 to n. You can do this by implementing the sum function, and calling it from main:
Running the program on its own is not that useful. Lets try setting a breakpoint on the "main" function to examine what the program is actually doing. Type break main
The debugger stopped at the beginning of the main function (line 26 of main.c). You can examine the source code of the program by typing list (or l for short).
Now you can execute the program line by line by typing next (or n for short), which executes the next line. By default typing next will skip over functions. Type step (or s for short) to step into a function. Try stepping into the sum function by running step.
We are now inside the sum function. Type l to list the source code, and then type n repeatedly to execute the function line by line. Note that we can also type n once, and then simply hit Enter asking GDB to execute the last command for us.
TUI: Graphical User Interface
The second most useful feature is the TUI mode that turns GDB into a real modern debugger. Here is a useful discussion about TUI.
You can switch into TUI by pressing Ctrl-X and then "1", or start gdb in TUI mode right away
You can also type tui enable in the gdb command prompt (this command doesn't work on Openlab, so you'll have to do Ctrl + X and then 1, but it normally works). Start the program from the begginging and single step it with n and s. The source code of the program will be scrolling in the TUI window in the top part of the screen.
Examining data
You can print values of variables with "print", e.g., print the values of i and sum
Conditional breakpoints
While debugging programs it's often useful to see what the program is doing right before it crashes. One way to do this is to step through, one at a time, every statement of the program, until we get to the point of execution where we want to examine the state of the program. This works, but sometimes you may want to just run until you reach a particular section of code based on a condition, and stop execution at that point so you can examine data at that point of execution.
For instance, in the sum function, you might want to examine the state of the program when the index i is equal to 50. You can single step until i increments and reaches the value 50, but this would be very tedious.
GDB allows you to set conditional breakpoints. To set a conditional breakpoint to break inside the loop of the sum function when the index i is equal to 50, we do the following: first, list the source code to get the exact source lines; second, set a breakpoint inside the main.c file at line 16 with break main.c:16; third, to make the breakpoint trigger only when i is equal to 50 (and not trigger for every iteration of the loop) we type condition 2 i==50.
Note that the 2 in the condition refers to the breakpoint number we were notified about when we initially set the breakpoint. We can also achieve the above in one command statement with the following:
We now continue execution of the program with the continue or c command.
When the breakpoint is hit we can check if the value of i is really 50:
Exploring crashes
Now, lets take a look at how you can use GDB to debug your crashing programs. First, lets generate a program that crashes. Add a global variable a[32] to your program (it's an array of 32 integers), and then add a function that makes an out of bounds array access.
If you invoke this function with n larger than 31 it will crash. Note that you might get lucky and it will not crash: not all out of bounds accesses cause a crash in C programs. To be sure, lets invoke it with n equal to 10,000
If you append the above lines to your main.c, compile, and run it, it will crash.
Now, to understand the crash you can run it under gdb:
You can use the backtrace (bt) command to look at the backtrace (a chain of function invocations leading to the crash):
Here, the GDB tells you that crash_array got a segmentation fault at line 18 in main.c. You see that there are two stack frames available (0 for main and 1 for crash_array). You can use the frame
We know that line 18 is the crashing line. We can print the values of the local variable i
It is equal to 35824. This should give you enough information for why you crashed.
Now fix the crash_array function to prevent the program from crashing.
What to submit
The fixed main.c program.
Part 2: A Simple UNIX Program
cat pogram (cat238p)
Use the main.c template as a starting point for a simple cat program that you should implement. First copy the main.c into main-cat238p.c (you will need to use main.c for other parts of future homeworks, so lets keep it around).
Our cat program displays the contents of a single file on the standard output. It takes either one or no arguments. If one argument is provided (the name of the file), then the program simply displays the contents on standard output. If no argument is given the program simply shows the content of the standard input on the standard output.
Here is an example invocation which displays the contents of a file main.c, with the name of the file provided as an argument (assuming you call your executable cat238p):
Or it should also work like this, where standard input has been redirected to the file:
You should use read() and write() system calls to read the input and write the output. Since cat238p takes command line arguments you should change the definition of the main() function to allow passing of command line arguements like:
If you have never worked with command line arguments in C here is a link that might be useful: Arguments to main. You can also take a look at a couple of user-level programs that take command line arguments from the xv6 source tree: rm.c, ls.c, wc.c.
Note: You might find it useful to look at the manual page for read(), write(), and other system calls. For example, type
and read about the read system call. Here the manual says that you should include
in your program to be able to use it, and the system call can be called as a function with the following signature:
The manual describes the meaning of the arguments for the system call, return value, and possible return codes. Finally, it lists several related system calls that might be helpful.
Note that when the manual lists a function like open(2), it means that it's described in the 2nd section of the manual and to get to that specific section you have to invoke man with an additional argument like this:
It's a good idea to read the man entry on man itself, i.e.,
Some useful commands are -k to search the manual for the string matching a query:
Note, that here there are multiple entries for the open() system call and default invocation, man open, will return an entry for the openvt command, and not file open command.
Important Note: You are not supposed to just call exec and replace the program with the already implemented cat that comes with your OS. Instead, you are expected to use the open, read, write, and close system calls.
What to submit
Submit main-cat238p.c which is your own implementation of cat
Submit your work
Please name the C files main.c for part 1, and main-cat238p.c for part 2. Place each part of the assignment into folders with name part1, part2, then pack them into a zip archive. Please note that part1 and part2 must be in the root of the zip archive, not inside yet another folder.
Submit your solution (the zip archive) through Gradescope in the assignment called HW1 - GDB and simlpe UNIX programs. You can resubmit as many times as you wish. If you have any problems with the structure the autograder will tell you.
The structure of the zip file must be the following: