Breakpoint Handling
In general, a breakpoint is a user-designated location in the program where the user wants to regain control if program execution ever reaches that location.
There are two main ways to implement breakpoints; either as hardware breakpoints or as software breakpoints.
Hardware breakpoints are sometimes available as a built-in debugging features with some chips. Typically these work by having dedicated register into which the breakpoint address may be stored. If the PC (shorthand for program counter) ever matches a value in a breakpoint registers, the CPU raises an exception and reports it to GDB.
Another possibility is when an emulator is in use; many emulators include circuitry that watches the address lines coming out from the processor, and force it to stop if the address matches a breakpoint’s address.
A third possibility is that the target already has the ability to do breakpoints somehow; for instance, a ROM monitor may do its own software breakpoints. So although these are not literally “hardware breakpoints”, from GDB’s point of view they work the same; GDB need not do anything more than set the breakpoint and wait for something to happen.
Since they depend on hardware resources, hardware breakpoints may be limited in number; when the user asks for more, GDB will start trying to set software breakpoints. (On some architectures, notably the 32-bit x86 platforms, GDB cannot always know whether there’s enough hardware resources to insert all the hardware breakpoints and watchpoints. On those platforms, GDB prints an error message only when the program being debugged is continued.)
Software breakpoints require GDB to do somewhat more work. The basic theory is that GDB will replace a program instruction with a trap, illegal divide, or some other instruction that will cause an exception, and then when it’s encountered, GDB will take the exception and stop the program. When the user says to continue, GDB will restore the original instruction, single-step, re-insert the trap, and continue on. Note that this is only true in all-stop mode, where all threads are explicitely stopped by GDB whenever any of them hits a breakpoint. In non-stop mode, removing the trap even for a short time would open a window of time for other threads to miss the breakpoint. This is covered in the chapter about single-stepping.
Since it literally overwrites the program being tested, the program area must be writable, so this technique won’t work on programs in ROM. It can also distort the behavior of programs that examine themselves, although such a situation would be highly unusual.
Also, the software breakpoint instruction should be the smallest size of instruction, so it doesn’t overwrite an instruction that might be a jump target, and cause disaster when the program jumps into the middle of the breakpoint instruction. (Strictly speaking, the breakpoint must be no larger than the smallest interval between instructions that may be jump targets; perhaps there is an architecture where only even-numbered instructions may jumped to.) Note that it’s possible for an instruction set not to have any instructions usable for a software breakpoint, although in practice only the ARC has failed to define such an instruction.
Basic breakpoint object handling is in breakpoint.c. However, much of the interesting breakpoint action is in infrun.c.
target_remove_breakpoint (bp_tgt)
target_insert_breakpoint (bp_tgt)
Insert or remove a software breakpoint at address bp_tgt->placed_address. Returns zero for success, non-zero for failure. On input, bp_tgt contains the address of the breakpoint, and is otherwise initialized to zero. The fields of the struct bp_target_info pointed to by bp_tgt are updated to contain other information about the breakpoint on output. The field placed_address may be updated if the breakpoint was placed at a related address; the field shadow_contents contains the real contents of the bytes where the breakpoint has been inserted, if reading memory would return the breakpoint instead of the underlying memory; the field shadow_len is the length of memory cached in shadow_contents, if any; and the field placed_size is optionally set and used by the target, if it could differ from shadow_len.
For example, the remote target ‘Z0’ packet does not require shadowing memory, so shadow_len is left at zero. However, the length reported by gdbarch_breakpoint_from_pc is cached in placed_size, so that a matching ‘z0’ packet can be used to remove the breakpoint.
target_remove_hw_breakpoint (bp_tgt)
target_insert_hw_breakpoint (bp_tgt)
Insert or remove a hardware-assisted breakpoint at address bp_tgt->placed_address. Returns zero for success, non-zero for failure. See target_insert_breakpoint for a description of the struct bp_target_info pointed to by bp_tgt; the shadow_contents and shadow_len members are not used for hardware breakpoints, but placed_size may be.