This is not the current version of the class.

Section 2

In this section we’re going to have fun.

Update your section repository, cd s02, and make. This will built a number of fun programs.


Let’s run one:

$ ./fun01
😿😿😿😿😿😿😿😿 no fun 😿😿😿😿😿😿😿😿

That wasn’t fun!

These programs are puzzles. Look at and you’ll see the ground rules. The driver’s main function first creates a single C string that contains all program arguments, separated by spaces. It then calls the fun function, passing in that string. The fun function returns an integer; if fun(str) returns 0, then the driver has fun, and if it returns anything else, no fun is had (the function boo() is called, which prints the no fun message).

We want to have fun, how can we have fun? Well might as well look to see what the function is doing! (Open

Looks like this fun function will return 0 if and only if the arguments contain an exclamation point. Let’s test that:

$ ./fun01 !
$ ./fun01 'yay!'
$ ./fun01 'amazing!!!!!!!!!!!!!!!!!'
$ ./fun01 'amazing?'
😿😿😿😿😿😿😿😿 no fun 😿😿😿😿😿😿😿😿

It works.


Now let’s say that the idea of not having fun is so painful to you that you will do literally anything to avoid it.

Is there any way that you could, for example, prevent the boo() function from running? That you could stop the program if it reached boo()?

This calls for a debugger breakpoint. A debugger is a program that manages the execution of another program. It lets you run a program, stop it, and examine variables, registers, and the contents of memory. Among the most powerful debugger features is the ability to stop a program if it ever reaches an instruction. This is called β€œsetting a breakpoint”: the breakpoint marks a location that, when reached, β€œbreaks” the program and returns control to the debugger.

How would you stop the program from executing boo?

$ gdb fun01
(gdb) b boo

Now if we run the program with non-fun arguments

(gdb) r

we will stop before printing β€œno fun”!

If you’re not careful, though, it’s possible to accidentally step through and print the message. You can do this one step at a time (demo r, followed by several ses); or you can do it by continuing the program by accident (demo r followed by c).

What if you wanted to make this kind of accident wicked unlikely? Well, you could set more breakpoints!

(gdb) b foo
(gdb) r
Breakpoint 1, main (argc=1, argv=0x7fffffffdf88) at
34          boo();
(gdb) x/20i $pc
=> 0x400c47 <main(int, char**)+375>:  lea    0x344(%rip),%rsi        # 0x400f92
   0x400c4e <main(int, char**)+382>:  lea    0x20156b(%rip),%rdi        # 0x6021c0 <_ZSt4cerr@@GLIBCXX_3.4>
   0x400c55 <main(int, char**)+389>:  callq  0x400a70 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x400c5a <main(int, char**)+394>:  mov    $0x1,%edi
   0x400c5f <main(int, char**)+399>:  callq  0x400a90 <exit@plt>

We’ve stopped at the first instruction in the boo function (which has actually been inlined into main, but never mind). But can also set more breakpoints! For example, at the second instruction and the third:

(gdb) b *0x400c4e
(gdb) b *0x400c55


But what if you forgot these breakpoints??? Well, that’s a good case for .gdbinit.

GDB usage

More fun

Now let’s work through a couple more funs. We’ll try to understand the operation of the funs using GDB and assembly, though for the first 6 funs, the C++ is there if you get stuck.

ASSEMBLY IS HARD. And trying to understand assembly from first principles, without running it, is really hard! As with many aspects of systems, you will have more luck with an approach motivated by experimental science. Try and guess at an input that will work, using cues from the assembly. Develop a hypothesis and test it. For the bomb, you don’t need to fully understand the assembly, you just need to find an input that passes each phase. (That said, you will often end up understanding the assemblyβ€”but only after completing the phase with the help of experiments.)

It is also often effective to alternate between working top down, starting from the entry to a function, and bottom up, starting at the return statement. Working from the bottom up, you can eliminate error paths and trace through how the desired result is calculated. Working from the top down, you can develop hypothesis about how the input should look. As long as you have breakpoints set, you can experiment with a free and easy heart. (And if the bomb goes off, who really cares?)

Fun cheatsheet

Other stuff

The section cheatsheet has a bunch of material on assembly that can be used for reference or presentation if the above doesn’t work for you.