Live data from Hacker News

Writing a debugger from scratch: Breakpoints

timdbg.com

11–20 of 56 posts

Re: Writing a debugger from scratch: Breakpoints

#11
post #9
post #6

Earlier quoted context omitted.

You use software breakpoints. Basically you overwrite the instruction you want to break at with a breakpoint instruction (e.g. int 3 on x86). This will cause the process to trap and the OS will then let the debugger process know about out somehow, e.g. via the SIGTRAP signal on Unix. The debugger then replaces the int 3 opcode (which is a single byte conveniently) with the first byte of the original instruction so th…

If you revert the int 3 to the original instruction’s byte, when do you put it back? The breakpoint could still be active. In a trivial example, the breaking instruction could be a jump to itself, which you’d expect to immediately break into the debugger again. I thought the debugger had to emulate the instruction instead, but it’s not like I’ve ever implemented one…

Emulation is an option, rotating hardware debug registers is another option, detecting self-jumps is another option.

I really only implemented a debugger for the esp8266 and it was just good enough for me and my team to get our job done so it didn't handle many edge cases like that

Re: Writing a debugger from scratch: Breakpoints

#12
post #9
post #6

Earlier quoted context omitted.

You use software breakpoints. Basically you overwrite the instruction you want to break at with a breakpoint instruction (e.g. int 3 on x86). This will cause the process to trap and the OS will then let the debugger process know about out somehow, e.g. via the SIGTRAP signal on Unix. The debugger then replaces the int 3 opcode (which is a single byte conveniently) with the first byte of the original instruction so th…

If you revert the int 3 to the original instruction’s byte, when do you put it back? The breakpoint could still be active. In a trivial example, the breaking instruction could be a jump to itself, which you’d expect to immediately break into the debugger again. I thought the debugger had to emulate the instruction instead, but it’s not like I’ve ever implemented one…

I believe when you resume the debugger, you can tell the process/thread to single-step over one instruction. So it's something like this:

1. Overwrite instruction with int 3.

2. When you hit the breakpoint, restore the original instruction.

3. Single-step over the original instruction by changing the thread's EFlags (Intel).

4. Restore the breakpoint with int 3.

5. Resume normally.

Re: Writing a debugger from scratch: Breakpoints

#15

Great article, thanks - one question I couldn't see answered there is, what do you do when you want to set more than four breakpoints at once?

Others have already mentioned software breakpoints where the instruction is replaced, another option is to run the code in an emulator that supports a virtually unlimited set of breakpoints. For example, using QEMU with its GDB stub.

Re: Writing a debugger from scratch: Breakpoints

#16
This is a great series!

I noticed that the author was using https://github.com/hydro-project/rust-sitter as a parser. Which is based on https://tree-sitter.github.io/tree-sitter/. I've been hearing about Tree-sitter a lot recently, so I dug into it.

Tree-sitter is a tool for generating fast, incremental parsers. In particular, the algorithm is suited towards writing "language servers" for IDEs, which re-parse code incrementally as the user works. These kinds of incremental parsers have historically been a huge problem. It looks like Tree-sitter is an enormous practical advance in this area.

And discovering that there's a way to use Tree-sitter from Rust is fantastic. From the post:

    #[rust_sitter::language]
    pub enum EvalExpr {
        Number(
            #[rust_sitter::leaf(
                pattern = r"(\d+|0x[0-9a-fA-F]+)",
                transform = parse_int
            )]
            u64
        ),
        Symbol(
            #[rust_sitter::leaf(
                pattern = r"(([a-zA-Z0-9_@#.]+!)?[a-zA-Z0-9_@#.]+)",
                transform = parse_sym
            )]
            String
        ),
        // ...
Getting easy access to fast, incremental parsing is a huge win. And Tree-Sitter has support for being used from a huge list of languages, not just Rust.

Re: Writing a debugger from scratch: Breakpoints

#17
post #2

Really nice read. Does anyone know any other good articles or videos about how to write a debugger?

I wrote this: https://ja.nsommer.dk/articles/x86-debugger-for-windows-and-...

It's a debugger for Windows (and Wine), like the one in the article, written in C. It uses software breakpoints (infinite breakpoints)

Post reply on HN