You may have noticed that debuggers (e.g.,
gdb) do a
remarkably good job of understanding source-code-level information
entered from the keyboard.
They also understand which assembly language instructions
correspond to a given line of source code, so they can
“single-step” through your code one line at a time.
This is possible because the compiler (when invoked
with appropriate command-line switches) includes symbol table
information in its assembly language output.
In the current assignment you will leverage this information to
implement a web-based source and assembly cross-referencing tool.
Gcc (and likewise
llvm and other compilers) is
capable of producing symbol
table information in a variety of formats. One of the most
comprehensive of these—and the one used by default on
Linux—is known as DWARF. It captures details
about variables, functions, typedefs, etc., including the locations in
the source code (file and the line number), at which they are
declared. To embed DWARF information in your object file, compile
-g3 command-line switch in
This instructs the compiler to include detailed debugging information,
including macro definitions, in the symbol table.
After compilation, DWARF information can be extracted from an executable
or object file in any of several ways.
Arguably the easiest is to use the
llvm-dwarfdump tool, which
you can find in
/usr/bin/ on the
Your task in this assignment is to write a Ruby program—call
disassem—that uses the output of
llvm-dwarfdump --debug-line and
(also found in
construct a web page that contains
side-by-side source and assembly language versions of a given
Naively, one might expect that each line of source code would be turned into a sequence of instructions in the assembly code, and that one could list these sequences next to the corresponding statements. This doesn’t work in practice, however: even at low levels of optimization, compilers scramble their output code in surprisingly complicated ways. While the compiler can generally say which set of instructions serve to implement a given line of source code, those instructions may not be contiguous with one another in the assembly code. Moreover, some source lines (e.g., declarations, comments, or delimiters) may not correspond to any target code instructions, and some instructions may be considered to contribute to more than one line of source.
To help a debugger set breakpoints, single-step through code, or print
the source code context of the current program counter, DWARF
debug-line table that captures the mapping
between source and assembly. Your
disassem tool will
use this table to make its side-by-side code listing interactive in the
As an example, we are providing an example of the output your tool should produce:
csug machines using
gcc -g3 -O1 -o ascii ascii.c.
objdump -d ascii.
llvm-dwarfdump --debug-line name and
objdump -d name, generate a disassembly web
page, and place it in
disassem should orchestrate all this work
itself—the user should not need to perform any separate
steps. Note also that the name(s) of the source file(s) from
which an executable was created should be gleaned from the
llvm-dwarfdump output; this information will not be
provided on the command line, nor should you assume that it can be
inferred from the name of the executable.
Pointers to on-line DWARF documentation can be found below.
Please don’t be intimidated by the complexity of the
What you need for this assignment is relatively simple.
It’s basically the first two columns of the table at the end of
llvm-dwarfdump --debug-line output.
Lines in the table are sorted by assembly code address (column 1).
Each line indicates that the instruction at the given address
corresponds to (helps to implement) the source code on the line given in
Some instructions help to implement more than one line of source code:
these have more than one line in the table (always consecutive).
If you compare the instruction addresses to the ones found in the output
objdump -d, you will see that some instructions are
omitted from the table.
This is a space-saving convention: an omitted instruction corresponds to
the same (single) source line as the previous instruction.
This means you can (and probably should!) insert the omitted instructions
into the table (in sorted order), copying the line number from the line
above. If you think about the result for a bit, you’ll
realize it defines a two-way mapping: you can infer not only which
source line(s) correspond to a given assembly language instruction, but
also which assembly language instructions (if any) correspond to a given
line of source. For the
ascii program, this information
has been encoded into the
of the source and assembler lines, respectively.
While Python is almost certainly the most familiar scripting language to students in the class, Ruby is heavily used for server-side web scripting and has significant advantages over Python for programs of nontrivial length. (FWIW, it also has handier syntax for “regular” expressions and—if you want them—much better support for functional programming idioms.)
Be aware that behavioral details of
objdump vary across
platforms, so your code is unlikely to port easily from elsewhere.
You will almost certainly want to work on this assignment on the
To get a sense of whether you’re interpreting the
debug-line table correctly, you may want to look at the
objdump -Sd. It generates a
best-effort attempt at interleaved source and assembly.
You might also try the
disassemble /m command
For reasons explained above, the correspondences indicated by these tools
won’t always be precise, but if your interpretation of the
correspondence is dramatically different there may be a problem on your
Note that you are required to build your pages without
using these extra mechanisms: you are required to glean
the source-to-assembly correspondence from the
At higher levels of optimization, the correspondence between
source code and the assembly generated by a compiler becomes
increasingly murky, and the information in the
becomes an increasingly rough approximation.
You will probably want to start with
programs that have been compiled with
‑O0. We reserve the right to test
(and grade!) your code on arbitrary programs, however, including those
that have been compiled with
Finally, as you generate HTML output, be aware that
> characters in your C or
assembly code will confuse the browser; you will need to convert these
to appropriate character entities. You may also want to make
occasional use of nonblocking spaces (
Examples can be found in the provided sample output.
As in previous assignments, you may work alone or in teams of two.
If you choose to work in pairs, one possible division of labor is for one
partner to write the code that inspects the
and the other to use this information to create the HTML files.
If you do this, be very careful to agree on the information you need,
and read each other’s code to look for errors.
Be sure to follow all the rules on the Grading page. As with all assignments,
use the turn-in script:
~cs254/bin/TURN_IN. Put your write-up in a
README.pdf file in the directory in
which you run the script.
Be sure to include your name(s)—both of them, if you’re
working as a team.
Also be sure to describe any
features of your code that the TAs might not immediately notice.
To illustrate the functionality of your code, you may want to include
test data, contained in subdirectories.
There’s a pretty good tutorial
introduction at this site.
(The tutorial includes some history and some encoding information that
you won’t actually need, but even those parts are interesting.)
The official DWARF 5 Standard
is also available, but it’s almost 500 pages long, and much
more detailed than you need.
man page (also available from the command line with the
man command) to see what information is available,
especially if you want to try some of the extra credit options.
You are also welcome to look over the
objdump man page, but
you’re only allowed to use its
Before the end of the day on Wednesday, November 30, each student should complete the T5 trivia assignment found on Blackboard.