15 ms·
How breakpoints are set
- dkopi 10y agoThe reason INT 3 is used is that it's the only interrupt that has a single byte opcode (0xCC). Other interrupts require two bytes: CD <interupt number>. This makes setting a breakpoint really easy, as all you have to do is replace a single byte (and restore a single byte) where you want to place your breakpoint. INT 3 being only one byte is also important when you're setting a breakpoint instead of a another single byte instruction - your newly set breakpoint won't override the consecutive instruction, which might be jumped to somewhere else in the code.
- colejohnson66 10y ago> The reason INT 3 is used is that it's the only interrupt that has a single byte opcode (0xCC). Other interrupts require two bytes: CD <interupt number>. It's kind of the other way around. The reason it has a single byte opcode is because Intel wanted INT3 to be for break points, so they designated 0xCC for it. In fact, 0xCD 0x03 works, but just isn't used.
- vardump 10y agoBecause x86 instructions can cross 4/8/16 byte alignment boundaries, you can't safely set a multi-byte breakpoint in all cases. CPU might execute (bytes [0xCD, x] -> int x) instruction before the parameter becomes visible and trigger some other exception, whatever happened to be at that address before.
- dkopi 10y agoI agree with the first part, it's kind of a self reinforcing decision. Intel wanted INT 3 to be for break points so they gave it a single byte instruction, and because INT 3 is a single byte instruction - it's the only one that makes sense for debug breakpoints. Lets say you have a lot of single byte opcodes: 40 INC EAX 43 INC EBX 41 INC ECX C3 RET And you want to set a breakpoint on INC EAX. If you replace "40" with "CD03" - you'll overwrite INC EBX as well. That can cause your program to crash if there are control flows that end up jumping to INC EBX without going through INC EAX first. That's the main reason why 0xCD, 0x03 isn't used.
- burfog 10y agoThere is also 0xf1, known as icebp or int1. One-byte opcodes won't save you when the code is jumping into the middle of instructions. The instruction you want to breakpoint might be in the middle of some other instruction that will run.
- jack9 10y agoI went 20 years without knowing how breakpoints work because they always just did (when they were available). Reading this, it's unsurprising how the tool works. That's the best kind of tool.
- dkopi 10y agoWhat's even better is that breakpoints haven't really changed over that period of time. They just work. While a lot of tech is rapidly moving and constantly changing - this is the type of fundamental knowledge that will probably prove valuable for the rest of your career.
- bbcbasic 10y agoIs this pretty much how all breakpoints work even in higher level languages with an IL/Bytecode etc.?
- ygra 10y ago(Note: All speculation) With an interpreter you can just probably ask the interpreter to stop at a certain instruction. For the purposes of your debugger the interpreter is the CPU in that respect. Except that you don't necessarily need to rewrite memory (although this probably exists too where the IL has a special breakpoint opcode). With a JIT compiler you could do the same as in the article, but it complicates things because the code you want to debug may not have been jitted yet, or, with some JIT compilers, may not be jitted at all, ever (e.g. a small method that runs exactly once). You could also do the same as above, with a breakpoint opcode, or asking the runtime to break at a particular statement. Both cases require the JIT to play along and do the right thing. For code that isn't jitted you'd have to fall back to the interpreter anyway, though, so in some cases JIT compilation is simply disabled in the debugger (e.g. Java, if I remember correctly), which has the unfortunate side effect that not only you lose optimizations, which is normal for debug code, but you also get a hefty performance hit because you're now running interpreted.
- pcwalton 10y agoUsually they either run debugged code in the interpreter or recompile the JIT code in a special instrumentation mode. (Not having to monkey patch the code at runtime—being able to do a "proper" recompilation—is one of the advantages of having a JIT!) See this explanation of how it works in SpiderMonkey, for instance: http://rfrn.org/~shu/2014/11/20/speeding-up-debugger.html http://rfrn.org/~shu/2014/11/20/speeding-up-debugger.html
- jtchang 10y agoHow would a program detect the use of a debugger? I know a lot of crackmes and other anti-piracy measures involve detecting the use of one but am not sure how they do it. Do they just look for running processes with a debugger signature like softice?
- colejohnson66 10y agoThe most basic one asks the kernel if there's a debugger attached. Others check if certain programs are running. There's more complex ones, but I can't remember them right now
- gst 10y agoLots of different approaches: To figure out if a process is ptraced the process can try to ptrace itself. An executable can only get ptraced once at a time, so the second call to ptrace will fail. Of course you can workaround that check by patching the code so that it doesn't call ptrace. Breakpoints are also easy to spot. Just look for 0xCC in the memory. But of course there are also multiple ways to work around those checks (such as using harder to detect hardware breakpoints instead of software breakpoints).
- rollulus 10y agoWhat I recall from the old days: it was indeed possible to detect Softice, through various ways, for instance simply trying to use its own API (through int3 as well having registers set to special magic values). Another trick to detect breakpoints as an anti-debugging measure was to compute a key over the block of memory that was to be protected. Any int3s in there would give a different output. A countermeasure was to use "memory breakpoints" which work via the x86 debug registers: https://en.wikipedia.org/wiki/X86_debug_register https://en.wikipedia.org/wiki/X86_debug_register
- mappu 10y agoAn ideal debugger can't be detected. In the specific case of x86 and int3 bps, you can abuse variable-length instruction encoding to jump into the middle of another instruction. Then code execution can differ depending on the presence of 0xcc.
- pcwalton 10y agoAlso worth noting: x86 supports four hardware breakpoints, which are sometimes more convenient, since they don't involve overwriting any instructions. (They're also more flexible because they can be used to trap on memory reads, etc.) https://en.wikipedia.org/wiki/X86_debug_register https://en.wikipedia.org/wiki/X86_debug_register
- wazari972 10y agoI thought that these HW 'breakpoint' registers were (only) used to implement watchpoint, that is, read/write operations on data. Can they be used to implement 'instruction breakpoints'?
- jasonzemos 10y agoOn register %dr7 there are 4-bit switches for each of the 4 breakpoints. The first two bits can trap on Instruction (0b00), Write (0b01), and ReadWrite (0b11). The second two bits specify the alignment of Byte (0b00), Short (0b01), Long (0b10), and Int (0b11).
- pcwalton 10y agoYou can set the "break on" bitfield to "execute" (0) to have the debug register function as a normal breakpoint.
- burfog 10y agoSure, but normally you wouldn't waste them on that. You might use them for instruction breakpoints in ROM, or when you suspect that the program checksums itself.
- EvanAnderson 10y agoI remember the first time I read about the hardware-based debugging functionality in the 80386 and being in awe. I'd programmed 6502 and 8086 assembler and was just blown-away by the treasure-trove of debugging functionality the '386 had available to it. It seemed like it was just short of an in-circuit emulator and so very, very cool.
- 10y ago
- gulpahum 10y agoNice explanation. However, it didn't explain how the debugger can stop again at the breakpoint after the last step? The interrupt command has been replaced with the original command, so the process won't stop again..
- hderms 10y agoCould presumably replace the following instructions with int 3 and basically swap it back without showing the interactive user anything
- ubershmekel 10y ago"the following instructions" may be a bit tricky. E.g. if the instruction jumps to itself.
- hderms 10y agoVery true, you can tell I'm not an assembly guy
- dimfeld 10y agoUsually the debugger just replaces the breakpoint instruction with the original byte, then resumes execution in a single-step mode that causes the CPU to just execute a single instruction before firing another interrupt. After that, the debugger sets the breakpoint again and resumes normal execution.
- to3m 10y agoWhen single-stepping, it's necessary to step only one thread, to ensure that other threads don't skip the (temporarily) disabled breakpoint. There's a paper here that discusses one solution to the problems this causes: http://www.bmrtech.com/uploadfile/image/whitepaper/mentorpaper_multcore_db.pdf http://www.bmrtech.com/uploadfile/image/whitepaper/mentorpap... (You can engineer a deadlock in gdb due to this, e.g., on x64, by stepping over a SYSCALL instruction that reads from a pipe that's about to be filled by another thread. But you're unlikely to experience this in practice, as system calls are wrapped by a glibc function, and you'll probably be stepping over that rather than the instruction directly.)
- tavish1 10y agoAny comments on how breakpoints work on external targets, running bare-metal, and you can't replace instructions? Ex. debugging an 8-bit AVR, say atmega1280 over JTAG. I am guessing it has to do with the JTAG doing a simple compare of the PC with the breakpoint address, just want confirmation and more details.
- cnvogel 10y agoThe program counter will not typically be compared via JTAG (this would be way too slow) but there will be dedicated registers inside the CPU that are compared against the program counter. For AVR specifically, have a look at e.g. https://github.com/raimue/avarice/blob/master/src/jtagbp.cc https://github.com/raimue/avarice/blob/master/src/jtagbp.cc (just random find on github, likely not the official repository). This is, by the way, also possible on a typical PC (as in "personal computer" not program counter), also x86/64 has debug registers. http://www.codeproject.com/Articles/28071/Toggle-hardware-data-read-execute-breakpoints-prog http://www.codeproject.com/Articles/28071/Toggle-hardware-da... ...and very likely also on ARM, m68k, MIPS, Sparc, PPC, ... I just didn't look it up.
- TickleSteve 10y agoyeah, processors such as the ARM cortex-M series have built in debug features such as ~8 breakpoint registers that trigger a change in processor state when they match a code or data access address. The breakpoint registers are accessed via JTAG/SWD using your j-link/FET/whatever. Quite often, when you're debugging embedded systems, you run out of "hardware" breakpoints and have to resort to software-style breakpoints described in the article.
- sigill 10y agoYes. And these software breakpoints are written into flash. This is why they're very slow - and why they wear down your flash. The situation is kinda OK when you don't often change breakpoints and your CPU has an instruction register writable by the debug probe via JTAG/SWD/etc. Upon stepping or continuing from a breakpoint, the debugger will write the actual instruction at the breakpoint into the instruction register and tell the CPU "run again, but don't load the instruction from memory as I have already loaded it into your instruction register.". Another option is to emulate the effects of the instruction in the debugger and write the results back into registers/RAM/I/O. This is not always possible. If you don't have the options above, your flash will wear down quickly, as stepping away or continuing from a breakpoint entails writing the actual instruction back, stepping, then writing the breakpoint again.
- pjmlp 10y agoNo mention of hardware breakpoints?
- vardump 10y agoIndeed, x86 hardware breakpoints are way more powerful and don't require changing instructions. Only problem there's just 4 of them, but it's usually enough. It's nice to be able to set a breakpoint on data access as well, it's often more useful than on instructions. https://en.wikipedia.org/wiki/X86_debug_register https://en.wikipedia.org/wiki/X86_debug_register
- stinos 10y agoYou can consider your debugger to be a program which forks() to create a child process and then calls execl() to load the process we want to debug That is one way to look at it, but I find it a bit too limiting (debuggers can attach to an existing process as well) and too confusing (requires knowing what fork does/is, same for execl - and are those even used when attaching to an existing process?) and because of the latter functions used obviously coming from a linux background (nothing wrong with that, on the contrary, but I can imagine windows people or beginners still having no clue whatsoever about a debugger after reading this - though it's likely not the target group).
- swiley 10y agoOn Linux, fork is a wrapper around clone, so it's still not particularly accurate.
- hornetblack 10y agoThere is also remote debugging. Via network or JTAG.
- Annatar 10y ago1. contemporary central processing units have large instruction and data caches. 2. ptrace() modifies the currently debugged program. 3. if the program currently being debugged is sufficiently small, it might end up in the processor's instruction cache. 4. instruction cache invalidation, at least on some processors like MC68000 family, causes crashes. 5. since ptrace() effectively performs the equivalent of self-modifying code, how is instruction cache invalidation avoided?
- Shivetya 10y agoSo when stepping through are debuggers just using breakpoints over and over? how is a step through different than setting a breakpoint if at all?
- apaprocki 10y agoFun tidbit.. AIX ptrace() doesn't support single stepping (PT_STEP). So the way that single step is implemented is by actually interpreting the instruction and if it is not a branch, then simply do the breakpoint swap mentioned in the post. If it is a branch, then decode the instruction (for all the various types of branch instructions!), actually compute all forms of the implicit/explicit branch target (register based, offset based, absolute addresses, etc) and do the instruction swap at all the possible branch targets. After one of the branches is taken, then put everything back. Oh, and care must be taken to not disturb atomics... https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/rs6000-aix-tdep.c;h=acd52bbd9548d6600a7246f8d984410bea72fc64;hb=HEAD#l670 https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=b...
- umanwizard 10y agoWhy would you need to put breakpoints in more than one place? Presumably when you're about to execute the instruction, you know the values of all the registers and memory addresses so you can just interpret where the jump goes. What am I missing?
- apaprocki 10y agoIf memory serves, there are certain branch conditions that can't be easily queried via ptrace to implement it that way. So instead, you enumerate the targets and break on all of them.
- pmalynin 10y agoOthers have mentioned hardware breakpoints but I also want to mention another type of breakpoint that can be used for both code and data and does not require program modification. And I can name at least one debugger that uses this method. The method is hooking Page Faults. Set the present flag of the page you want to debug to false and you'll be notified of any code that is executed there. But you can also be notified when data is accessed and by which instruction and address. On Windows this can be done with SEH and Linux has its own thing too.
- d23 10y agoWeird, I just happened to be reading about this topic last night. If you liked this, make sure to check out the articles by Eli Bendersky in the footer. He has a 3 part series on how debuggers work: http://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1 http://eli.thegreenplace.net/2011/01/23/how-debuggers-work-p... http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-p... http://eli.thegreenplace.net/2011/02/07/how-debuggers-work-part-3-debugging-information http://eli.thegreenplace.net/2011/02/07/how-debuggers-work-p...