23 May 2025

No Starch Press sent me a copy of Sy Brand's "Building A Debugger", which walks, step by step, through the process of building your own assembly-level, native-executable debugger. It geeks me out in ways I haven't geeked out since... well, since the last low-level geekery book (the ARM assembly book) they sent me.

For close to two decades now, I've been telling developers (experts to neophytes) that you need to know "one level below the level at which you work". Knowing how your Java class turns into JVM bytecode, or how that .NET assembly looks like on disk, is often the difference between knowledge and ignorance. How does the yield return keyword work in C#? How did Java do inner classes before adopting nested-access classes in JDK 11 (or 12 or 15 or whenever the hell nested-access was introduced)? Being able to crack open the compiled binary, look at the disassembled output, and have at least a rough idea of what's going on, is a huge skill that every developer must have.

In my personal quest to pursue that, I've deep-dived into bytecode virtual machines (both the CLR and the JVM, among others), language compilers and interpreters (too numerous to count), and even an operating systems class in college. (I had quite the time explaining why an International Relations major wanted to take this class, let me tell yoU!) But as much as I've been aware of how a debugger works, I've never tried to build one, particularly not one that's based on the Linux OS. I did dabble once with how to do it in Win32, but that went as far as "attach the process, walk the PE file, and... yeah, that's about all I want to do". I never actually got to a point where I could set a breakpoint, examine memory, any of that. It was a pretty shallow dipping-of-the-toe and that was about it.

Sy Brand has managed to not only tell me the water's fine, but snuck up behind me and pushed me in.

Physical

It's a typical No Starch Press book--the covers are pretty solid, the binding has that crease that allows for the book to be opened pretty widely without cracking, and the pages are heavy enough to feel good. It weighs in around 700 or so pages and 20+ chapters, and each chapter is pretty singly-focused, making each chapter a nice "chunk" to peruse during a short reading stint.

Contents

Contrary to a lot of books (including ones I've written!), Brand doesn't spend the first chapter explaining the concepts of what's going to be covered--this is a book about building debuggers, and the assumption is that you know what a debugger is, what its basic functionality is and should be, and at least the basics of how one interacts with a debugger to, you know, debug programs. Instead, the Introduction does a lot of that "here's what we're going to do", and Chapter 1 dives right into laying out the project structure using CMake to build sdb, the "Sy Brand Debugger".

Oh, did I forget to mention? Brand uses C++17 to build the debugger, which actually makes a great deal of sense for the topic at hand. The book isn't about C++17, though, and explanatory text about C++ itself is left to an "explain it when we get there" approach. As someone who's not used C++ "in anger" in about twenty years--meaning my last foray with C++ was somewhere around C++03 or so--I think I'm just about at the level assumed for the book.

Likewise, CMake is not explained or explored, other than to offer up a cursory description of it and how it works. I can appreciate that--again, this is a book about building a debugger, not a book about CMake. And for the most part, the Chapter One steps are pretty easy to follow, although in my own tdb/"Ted DeBugger" setup, I found that (a) the latest CMake is 4 where the book assumes 3 (which I don't think will be a problem down the road), and (b) some of the supporting cast, vpkg and libedit, have some OS dependencies that I had to puzzle through myself.

Oh, and by the way, this is a book about building an x64 debugger, not an x86 or ARM debugger, so if you're on a recent MacBookPro, you're a little stuck. The choice to make this an x64 book is pretty reasonable--after all, x86 is ancient history, but not everybody has transitioned to ARM yet--but at the same time, it will limit the audience of the book. I have a vague idea to try and build an ARM debugger using the principles laid out in the book, but that's a long ways off for me, personally. (But if I were an editor at No Starch Press, I'd be thinking very seriously about putting Randall Hyde and Sy Brand into a room together and not let them out until a "Building an ARM Debugger" manuscript was on its way.)

Once the project is set up--complete with tests, by the way, yay--Chapter 2 walks the reader through the very basics of "Compilation and Computer Architecture". This is probably the fastest pass I think I've ever seen on topics ranging from CPU endianness and major pieces (registers, program counter, etc) to executable file formats and memory layout. From pages 11 to 25--fourteen pages in total--is not a lot of room to explain all that. Heck, Linkers and Loaders took an entire book just to explain executable file formats, and that was several hundred pages in size. My point here is, if you're not familiar with some of the material in Chapter 2, you'll probably want (or need) to supplement what's here with external resources. Again, this is a book about building a debugger, and it stays pretty tightly focused to that. The author won't leave you entirely out in the cold if you don't know what a DWARF or ELF file is, but you have to do your homework, too.

Once we get past that, though, from Chapter 3 onwards, it's "pick a subtopic in debuggers, and let's put it into our debugger" territory. Chapter 3 is about attaching to a target process, Chapter 4 is about automated testing, 5 is registers, 6 is testing those registers, 7 gets us to breakpoints, and so on, all the way up through Chapter 22's "Advanced Topics", which touches on remote debugging, among other things. As Brand covers elements of each topic, code is written in steps, sometimes refactoring along the way to make the debugger more maintainable and extensible. All of the "sdb" code is available via the author's GitHub, by the way, so while you don't need to type it all in by hand, I've often found that if I juts wanted to use somebody else's debugger, I'd brew install gdb, not "Alexa, order the book 'Building a Debugger'". Typing it in and working through the error messages is a part of learning the subject.

Thoughts

This book is going to come with me on a few upcoming trips, but then probably go to a shelf and stay there a while--it's a tutorial, not a reference, and like many tutorials, once you "get it", the need to go back and do the tutorial again is drastically reduced. Quite honestly, this is true of some of my favorite books, one of which is the ancient Windows++ book by Paul DiLascia, wherein he builds a C++ GUI framework for the Windows OS. Man, I worshipped that book for the longest time. Still have it on a shelf somewhere. Will probably never read it again, except for nostalgia's sake, but it did its thing and did it well--I learned so much about how to build an important part of what we were using at the time to write software.

Frankly, I wish books like this were more popular, but I'm simultaneously glad they aren't, because knowing this material is often what helps set the "senior developer" apart from the "junior". If you know how something works under the hood, you find the concepts carry over into all sorts of different places. Knowing how an x64 debugger works makes using a debugger on any CPU platform a much easier exercise, and too few programmers actually know how to make use of a debugger at all, much less use it well. On top of that, there's often opportunities to build "little tools" to help gain visibility into one-off situations and scenarios. (For example, knowing how debuggers work make it much easier to understand how code coverage tools work, not to mention certain observability tools and platforms. Oh, and did you ever want to "intercept" a native call to be able to provide some pre-/post-processing? Yeah, knowing how debuggers work open all of those options up inside your brain.)

Is this book worth learning C++ if that's never been a language you mastered? Eh, maybe. Honestly I think any developer who wants to crack the upper echelon of the industry needs to know at least a little C/C++, and this is a good exercise by which to go from "I know how to read it" to "I have built an interesting tool with it". But you could, conceivably, use any system-level language, like Rust, Nim, Zig, or maybe even Odin, to build this, assuming you know enough of how those languages work to be able to mentally translate the C/C++ into the language of choice.

Overall, I think you should buy it, read it, then tweak what you've learned into something that's useful for your own purposes. And who knows? Maybe next time there's a weird Heisenbug that's driving the rest of your team natty, you might think, "Wait, maybe if I write a little tool that hooks certain API calls and takes a snapshot of certain variables, we can get a better sense of what's going on...." and build that tool and get that better sense and be the brilliant idea that finds the bug and get the kudos and the raise and the promotion and the cheering parade and.... Uh, right, sorry, got lost in my own daydream for a second there.

Did I say buy this book yet? Yeah, go buy this book.

Disclaimer: No Starch Press sent me a review copy of the book; no other compensation was provided.


Tags: book   assembly   x64   book review