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.
Setup
Letβs run one:
$ ./fun01
πΏπΏπΏπΏπΏπΏπΏπΏ no fun πΏπΏπΏπΏπΏπΏπΏπΏ
That wasnβt fun!
These programs are puzzles. Look at fundriver.cc
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 fun01.cc
)
Looks like this fun
function will return 0 if and only if the arguments
contain an exclamation point. Letβs test that:
$ ./fun01 !
πππππππππ½π½π½ππππππππ
FUN
πππππππππ½π½π½ππππππππ
$ ./fun01 'yay!'
πππππππππ½π½π½ππππππππ
FUN
πππππππππ½π½π½ππππππππ
$ ./fun01 'amazing!!!!!!!!!!!!!!!!!'
πππππππππ½π½π½ππππππππ
FUN
πππππππππ½π½π½ππππππππ
$ ./fun01 'amazing?'
πΏπΏπΏπΏπΏπΏπΏπΏ no fun πΏπΏπΏπΏπΏπΏπΏπΏ
It works.
GDB
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 s
es); 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 fundriver.cc:34
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
(demo)
But what if you forgot these breakpoints??? Well, thatβs a good case for .gdbinit
.
GDB usage
x
for examining memoryx/20i $pc
for examining instructions- autodisplay instructions:
display/20i $pc
-tui
info registers
s
vs.n
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
fun02
: Callsstrtol
. Hypothesis: string should be an integer! Adds 1 to the return fromstrtol
. Answer:-1
fun03
: Checks first character of string, then returns second. Answer: any single-character string. Introduces control flow.fun04
: Any two-character string with both characters same. More control flow.fun05
: Any nonzero-character string with all characters same. A loop.fun06
: Any string with length a multiple of 4. For loop.fun07
: Checks for primes. Work top-down: expects an integer. Test a bunch of integers at command line. Ask students to spot the pattern.fun08
: Reads three integers, returnsa + b - c
. Examples:1 1 2
,0 0 0
.fun09
: Expects a filename of an openable file. Pretty simple.fun10
: A power of 2 greater than 128. Bitwise arithmetic.fun11
: A six-or-more character string consisting of uppercase letters.fun12
: A five-or-more character palindrome.
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.