int default_memory_insert_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { int val; const unsigned char *bp; gdb_byte *readbuf; /* Determine appropriate breakpoint contents and size for this address. */ bp = gdbarch_breakpoint_from_pc (gdbarch, &bp_tgt->placed_address, &bp_tgt->placed_size); if (bp == NULL) error (_("Software breakpoints not implemented for this target.")); /* Save the memory contents in the shadow_contents buffer and then write the breakpoint instruction. */ bp_tgt->shadow_len = bp_tgt->placed_size; readbuf = alloca (bp_tgt->placed_size); val = target_read_memory (bp_tgt->placed_address, readbuf, bp_tgt->placed_size); if (val == 0) { memcpy (bp_tgt->shadow_contents, readbuf, bp_tgt->placed_size); val = target_write_raw_memory (bp_tgt->placed_address, bp, bp_tgt->placed_size); } return val; }
int default_memory_insert_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { int val; const unsigned char *bp; CORE_ADDR pc_addr; /* Determine appropriate breakpoint contents and size for this address. */ pc_addr = bp_tgt->requested_address ? bp_tgt->requested_address : bp_tgt->placed_address; bp = gdbarch_breakpoint_from_pc (gdbarch, &pc_addr, &bp_tgt->placed_size); if (bp == NULL) error (_("Software breakpoints not implemented for this target.")); /* Save the memory contents. */ bp_tgt->shadow_len = bp_tgt->placed_size; val = target_read_memory (bp_tgt->placed_address, bp_tgt->shadow_contents, bp_tgt->placed_size); /* Write the breakpoint. */ if (val == 0) val = target_write_memory (bp_tgt->placed_address, bp, bp_tgt->placed_size); return val; }
/* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint in much the same fashion as memory_remove_breakpoint in mem-break.c, but is careful not to write back the previous contents if the code in question has changed in between inserting the breakpoint and removing it. Here is the problem that we're trying to solve... Once upon a time, before introducing this function to remove breakpoints from the inferior, setting a breakpoint on a shared library function prior to running the program would not work properly. In order to understand the problem, it is first necessary to understand a little bit about dynamic linking on this platform. A call to a shared library function is accomplished via a bl (branch-and-link) instruction whose branch target is an entry in the procedure linkage table (PLT). The PLT in the object file is uninitialized. To gdb, prior to running the program, the entries in the PLT are all zeros. Once the program starts running, the shared libraries are loaded and the procedure linkage table is initialized, but the entries in the table are not (necessarily) resolved. Once a function is actually called, the code in the PLT is hit and the function is resolved. In order to better illustrate this, an example is in order; the following example is from the gdb testsuite. We start the program shmain. [kev@arroyo testsuite]$ ../gdb gdb.base/shmain [...] We place two breakpoints, one on shr1 and the other on main. (gdb) b shr1 Breakpoint 1 at 0x100409d4 (gdb) b main Breakpoint 2 at 0x100006a0: file gdb.base/shmain.c, line 44. Examine the instruction (and the immediatly following instruction) upon which the breakpoint was placed. Note that the PLT entry for shr1 contains zeros. (gdb) x/2i 0x100409d4 0x100409d4 <shr1>: .long 0x0 0x100409d8 <shr1+4>: .long 0x0 Now run 'til main. (gdb) r Starting program: gdb.base/shmain Breakpoint 1 at 0xffaf790: file gdb.base/shr1.c, line 19. Breakpoint 2, main () at gdb.base/shmain.c:44 44 g = 1; Examine the PLT again. Note that the loading of the shared library has initialized the PLT to code which loads a constant (which I think is an index into the GOT) into r11 and then branchs a short distance to the code which actually does the resolving. (gdb) x/2i 0x100409d4 0x100409d4 <shr1>: li r11,4 0x100409d8 <shr1+4>: b 0x10040984 <sg+4> (gdb) c Continuing. Breakpoint 1, shr1 (x=1) at gdb.base/shr1.c:19 19 l = 1; Now we've hit the breakpoint at shr1. (The breakpoint was reset from the PLT entry to the actual shr1 function after the shared library was loaded.) Note that the PLT entry has been resolved to contain a branch that takes us directly to shr1. (The real one, not the PLT entry.) (gdb) x/2i 0x100409d4 0x100409d4 <shr1>: b 0xffaf76c <shr1> 0x100409d8 <shr1+4>: b 0x10040984 <sg+4> The thing to note here is that the PLT entry for shr1 has been changed twice. Now the problem should be obvious. GDB places a breakpoint (a trap instruction) on the zero value of the PLT entry for shr1. Later on, after the shared library had been loaded and the PLT initialized, GDB gets a signal indicating this fact and attempts (as it always does when it stops) to remove all the breakpoints. The breakpoint removal was causing the former contents (a zero word) to be written back to the now initialized PLT entry thus destroying a portion of the initialization that had occurred only a short time ago. When execution continued, the zero word would be executed as an instruction an illegal instruction trap was generated instead. (0 is not a legal instruction.) The fix for this problem was fairly straightforward. The function memory_remove_breakpoint from mem-break.c was copied to this file, modified slightly, and renamed to ppc_linux_memory_remove_breakpoint. In tm-linux.h, MEMORY_REMOVE_BREAKPOINT is defined to call this new function. The differences between ppc_linux_memory_remove_breakpoint () and memory_remove_breakpoint () are minor. All that the former does that the latter does not is check to make sure that the breakpoint location actually contains a breakpoint (trap instruction) prior to attempting to write back the old contents. If it does contain a trap instruction, we allow the old contents to be written back. Otherwise, we silently do nothing. The big question is whether memory_remove_breakpoint () should be changed to have the same functionality. The downside is that more traffic is generated for remote targets since we'll have an extra fetch of a memory word each time a breakpoint is removed. For the time being, we'll leave this self-modifying-code-friendly version in ppc-linux-tdep.c, but it ought to be migrated somewhere else in the event that some other platform has similar needs with regard to removing breakpoints in some potentially self modifying code. */ static int ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { CORE_ADDR addr = bp_tgt->placed_address; const unsigned char *bp; int val; int bplen; gdb_byte old_contents[BREAKPOINT_MAX]; struct cleanup *cleanup; /* Determine appropriate breakpoint contents and size for this address. */ bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); if (bp == NULL) error (_("Software breakpoints not implemented for this target.")); /* Make sure we see the memory breakpoints. */ cleanup = make_show_memory_breakpoints_cleanup (1); val = target_read_memory (addr, old_contents, bplen); /* If our breakpoint is no longer at the address, this means that the program modified the code on us, so it is wrong to put back the old value. */ if (val == 0 && memcmp (bp, old_contents, bplen) == 0) val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen); do_cleanups (cleanup); return val; }
int memory_validate_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { CORE_ADDR addr = bp_tgt->placed_address; const gdb_byte *bp; int val; int bplen; gdb_byte cur_contents[BREAKPOINT_MAX]; /* Determine appropriate breakpoint contents and size for this address. */ bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); if (bp == NULL) return 0; /* Make sure we see the memory breakpoints. */ scoped_restore restore_memory = make_scoped_restore_show_memory_breakpoints (1); val = target_read_memory (addr, cur_contents, bplen); /* If our breakpoint is no longer at the address, this means that the program modified the code on us, so it is wrong to put back the old value. */ return (val == 0 && memcmp (bp, cur_contents, bplen) == 0); }
static int microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { CORE_ADDR addr = bp_tgt->reqstd_address; const gdb_byte *bp; int val; int bplen; gdb_byte old_contents[BREAKPOINT_MAX]; /* Determine appropriate breakpoint contents and size for this address. */ bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); if (bp == NULL) error (_("Software breakpoints not implemented for this target.")); val = target_read_memory (addr, old_contents, bplen); /* If our breakpoint is no longer at the address, this means that the program modified the code on us, so it is wrong to put back the old value. */ if (val == 0 && memcmp (bp, old_contents, bplen) == 0) val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen); return val; }
void default_skip_permanent_breakpoint (struct regcache *regcache) { struct gdbarch *gdbarch = get_regcache_arch (regcache); CORE_ADDR current_pc = regcache_read_pc (regcache); int bp_len; gdbarch_breakpoint_from_pc (gdbarch, ¤t_pc, &bp_len); current_pc += bp_len; regcache_write_pc (regcache, current_pc); }
CORE_ADDR displaced_step_at_entry_point (struct gdbarch *gdbarch) { CORE_ADDR addr; int bp_len; addr = entry_point_address (); /* Inferior calls also use the entry point as a breakpoint location. We don't want displaced stepping to interfere with those breakpoints, so leave space. */ gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len); addr += bp_len * 2; return addr; }
static CORE_ADDR amd64_dicos_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr, struct value **args, int nargs, struct type *value_type, CORE_ADDR *real_pc, CORE_ADDR *bp_addr, struct regcache *regcache) { int bplen; CORE_ADDR bppc = sp; gdbarch_breakpoint_from_pc (gdbarch, &bppc, &bplen); *bp_addr = sp - bplen; *real_pc = funaddr; return *bp_addr; }
void default_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *kindptr) { gdbarch_breakpoint_from_pc (gdbarch, pcptr, kindptr); }