I’ve spend the last month or two fiddling with my own custom debugger, learning to parse DWARF. DWARF is a very flexible format, made to support a wide variety of languages out of the box, but it pays for that flexibility with complexity. Dealing with DWARF is a pain.
DWARF usually contains 6 sections:
all of which you need in some way to be able to provide full-featured debugging support.
.debug_line contains an executable bytecode which generates the map between lines in files and memory addresses that
you can use to set breakpoints
.debug_frame, together with
.eh_frame contain executable bytecode you can run to help unwind the stack, used for building a stack trace
provide enough information to rebuild a loose AST-like representation of the code,
so you can read variables in a live program, resolve type information, and determine where a function’s stack frame setup and teardown begins and ends.