From 939c61faffc518af588365f5d39a69da78211578 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 26 Nov 2008 05:27:48 +0000 Subject: [PATCH] Fix automatic restoration of breakpoints memory for ia64. * ia64-tdep.c: New #if check on BREAKPOINT_MAX vs. BUNDLE_LEN. (ia64_memory_insert_breakpoint): New comment part for SHADOW_CONTENTS content. Remove variable instr. New variable cleanup. Force automatic breakpoints restoration. PLACED_SIZE and SHADOW_LEN are now set larger, to BUNDLE_LEN - 2. Variable `bundle' type update. Return error if even just final target_write_memory has failed. (ia64_memory_remove_breakpoint): Rename variables bundle to bundle_mem and instr to instr_saved. New variables bundle_saved and instr_breakpoint. Comment new reasons why we need to disable automatic restoration of breakpoints. Assert PLACED_SIZE and SHADOW_LEN. New check of the original memory content. Return error if even just final target_write_memory has failed. (ia64_breakpoint_from_pc): Implement the emulation of permanent breakpoints compatible with current bp_loc_is_permanent. (template_encoding_table): Make it `const'. * breakpoint.c (bp_loc_is_permanent): Support unsupported software breakpoints. New variables `cleanup' and `retval'. * monitor.c (monitor_insert_breakpoint): Remove unused variable `bp'. --- gdb/ChangeLog | 22 ++++++ gdb/breakpoint.c | 17 +++- gdb/ia64-tdep.c | 197 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 196 insertions(+), 40 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4467e97145c..e80021e7520 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,25 @@ +2008-11-25 Jan Kratochvil + + Fix automatic restoration of breakpoints memory for ia64. + * ia64-tdep.c: New #if check on BREAKPOINT_MAX vs. BUNDLE_LEN. + (ia64_memory_insert_breakpoint): New comment part for SHADOW_CONTENTS + content. Remove variable instr. New variable cleanup. Force + automatic breakpoints restoration. PLACED_SIZE and SHADOW_LEN are now + set larger, to BUNDLE_LEN - 2. Variable `bundle' type update. Return + error if even just final target_write_memory has failed. + (ia64_memory_remove_breakpoint): Rename variables bundle to bundle_mem + and instr to instr_saved. New variables bundle_saved and + instr_breakpoint. Comment new reasons why we need to disable automatic + restoration of breakpoints. Assert PLACED_SIZE and SHADOW_LEN. New + check of the original memory content. Return error if even just final + target_write_memory has failed. + (ia64_breakpoint_from_pc): Implement the emulation of permanent + breakpoints compatible with current bp_loc_is_permanent. + (template_encoding_table): Make it `const'. + * breakpoint.c (bp_loc_is_permanent): Support unsupported software + breakpoints. New variables `cleanup' and `retval'. + * monitor.c (monitor_insert_breakpoint): Remove unused variable `bp'. + 2008-11-24 Tom Tromey * ada-lang.c (value_from_contents_and_address): Move... diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 07a9619532a..617a5389634 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -5010,19 +5010,32 @@ bp_loc_is_permanent (struct bp_location *loc) CORE_ADDR addr; const gdb_byte *brk; gdb_byte *target_mem; + struct cleanup *cleanup; + int retval = 0; gdb_assert (loc != NULL); addr = loc->address; brk = gdbarch_breakpoint_from_pc (current_gdbarch, &addr, &len); + /* Software breakpoints unsupported? */ + if (brk == NULL) + return 0; + target_mem = alloca (len); + /* Enable the automatic memory restoration from breakpoints while + we read the memory. Otherwise we could say about our temporary + breakpoints they are permanent. */ + cleanup = make_show_memory_breakpoints_cleanup (0); + if (target_read_memory (loc->address, target_mem, len) == 0 && memcmp (target_mem, brk, len) == 0) - return 1; + retval = 1; - return 0; + do_cleanups (cleanup); + + return retval; } diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c index aeec16939ce..5acd8b1e431 100644 --- a/gdb/ia64-tdep.c +++ b/gdb/ia64-tdep.c @@ -110,6 +110,12 @@ typedef enum instruction_type #define BUNDLE_LEN 16 +/* See the saved memory layout comment for ia64_memory_insert_breakpoint. */ + +#if BREAKPOINT_MAX < BUNDLE_LEN - 2 +# error "BREAKPOINT_MAX < BUNDLE_LEN - 2" +#endif + static gdbarch_init_ftype ia64_gdbarch_init; static gdbarch_register_name_ftype ia64_register_name; @@ -442,7 +448,7 @@ replace_slotN_contents (char *bundle, long long instr, int slotnum) replace_bit_field (bundle, instr, 5+41*slotnum, 41); } -static enum instruction_type template_encoding_table[32][3] = +static const enum instruction_type template_encoding_table[32][3] = { { M, I, I }, /* 00 */ { M, I, I }, /* 01 */ @@ -545,7 +551,45 @@ fetch_instruction (CORE_ADDR addr, instruction_type *it, long long *instr) simulators. So I changed the pattern slightly to do "break.i 0x080001" instead. But that didn't work either (I later found out that this pattern was used by the simulator that I was using.) So I ended up - using the pattern seen below. */ + using the pattern seen below. + + SHADOW_CONTENTS has byte-based addressing (PLACED_ADDRESS and SHADOW_LEN) + while we need bit-based addressing as the instructions length is 41 bits and + we must not modify/corrupt the adjacent slots in the same bundle. + Fortunately we may store larger memory incl. the adjacent bits with the + original memory content (not the possibly already stored breakpoints there). + We need to be careful in ia64_memory_remove_breakpoint to always restore + only the specific bits of this instruction ignoring any adjacent stored + bits. + + We use the original addressing with the low nibble in the range <0..2> which + gets incorrectly interpreted by generic non-ia64 breakpoint_restore_shadows + as the direct byte offset of SHADOW_CONTENTS. We store whole BUNDLE_LEN + bytes just without these two possibly skipped bytes to not to exceed to the + next bundle. + + If we would like to store the whole bundle to SHADOW_CONTENTS we would have + to store already the base address (`address & ~0x0f') into PLACED_ADDRESS. + In such case there is no other place where to store + SLOTNUM (`adress & 0x0f', value in the range <0..2>). We need to know + SLOTNUM in ia64_memory_remove_breakpoint. + + ia64 16-byte bundle layout: + | 5 bits | slot 0 with 41 bits | slot 1 with 41 bits | slot 2 with 41 bits | + + The current addressing used by the code below: + original PC placed_address placed_size required covered + == bp_tgt->shadow_len reqd \subset covered + 0xABCDE0 0xABCDE0 0xE <0x0...0x5> <0x0..0xD> + 0xABCDE1 0xABCDE1 0xE <0x5...0xA> <0x1..0xE> + 0xABCDE2 0xABCDE2 0xE <0xA...0xF> <0x2..0xF> + + `objdump -d' and some other tools show a bit unjustified offsets: + original PC byte where starts the instruction objdump offset + 0xABCDE0 0xABCDE0 0xABCDE0 + 0xABCDE1 0xABCDE5 0xABCDE6 + 0xABCDE2 0xABCDEA 0xABCDEC + */ #define IA64_BREAKPOINT 0x00003333300LL @@ -554,34 +598,55 @@ ia64_memory_insert_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { CORE_ADDR addr = bp_tgt->placed_address; - char bundle[BUNDLE_LEN]; + gdb_byte bundle[BUNDLE_LEN]; int slotnum = (int) (addr & 0x0f) / SLOT_MULTIPLIER; - long long instr; + long long instr_breakpoint; int val; int template; + struct cleanup *cleanup; if (slotnum > 2) error (_("Can't insert breakpoint for slot numbers greater than 2.")); addr &= ~0x0f; + /* Disable the automatic memory restoration from breakpoints while + we read our instruction bundle. Otherwise, the general restoration + mechanism kicks in and we would possibly remove parts of the adjacent + placed breakpoints. It is due to our SHADOW_CONTENTS overlapping the real + breakpoint instruction bits region. */ + cleanup = make_show_memory_breakpoints_cleanup (1); val = target_read_memory (addr, bundle, BUNDLE_LEN); - /* Check for L type instruction in 2nd slot, if present then - bump up the slot number to the 3rd slot */ + /* Check for L type instruction in slot 1, if present then bump up the slot + number to the slot 2. */ template = extract_bit_field (bundle, 0, 5); - if (slotnum == 1 && template_encoding_table[template][1] == L) - { - slotnum = 2; - } - - instr = slotN_contents (bundle, slotnum); - memcpy (bp_tgt->shadow_contents, &instr, sizeof (instr)); - bp_tgt->placed_size = bp_tgt->shadow_len = sizeof (instr); + if (slotnum == 1 && template_encoding_table[template][slotnum] == L) + slotnum = 2; + + /* Slot number 2 may skip at most 2 bytes at the beginning. */ + bp_tgt->placed_size = bp_tgt->shadow_len = BUNDLE_LEN - 2; + + /* Store the whole bundle, except for the initial skipped bytes by the slot + number interpreted as bytes offset in PLACED_ADDRESS. */ + memcpy (bp_tgt->shadow_contents, bundle + slotnum, bp_tgt->shadow_len); + + /* Breakpoints already present in the code will get deteacted and not get + reinserted by bp_loc_is_permanent. Multiple breakpoints at the same + location cannot induce the internal error as they are optimized into + a single instance by update_global_location_list. */ + instr_breakpoint = slotN_contents (bundle, slotnum); + if (instr_breakpoint == IA64_BREAKPOINT) + internal_error (__FILE__, __LINE__, + _("Address %s already contains a breakpoint."), + paddr_nz (bp_tgt->placed_address)); replace_slotN_contents (bundle, IA64_BREAKPOINT, slotnum); + if (val == 0) - target_write_memory (addr, bundle, BUNDLE_LEN); + val = target_write_memory (addr + slotnum, bundle + slotnum, + bp_tgt->shadow_len); + do_cleanups (cleanup); return val; } @@ -590,9 +655,9 @@ ia64_memory_remove_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { CORE_ADDR addr = bp_tgt->placed_address; - char bundle[BUNDLE_LEN]; + gdb_byte bundle_mem[BUNDLE_LEN], bundle_saved[BUNDLE_LEN]; int slotnum = (addr & 0x0f) / SLOT_MULTIPLIER; - long long instr; + long long instr_breakpoint, instr_saved; int val; int template; struct cleanup *cleanup; @@ -601,40 +666,96 @@ ia64_memory_remove_breakpoint (struct gdbarch *gdbarch, /* Disable the automatic memory restoration from breakpoints while we read our instruction bundle. Otherwise, the general restoration - mechanism kicks in and ends up corrupting our bundle, because it - is not aware of the concept of instruction bundles. */ + mechanism kicks in and we would possibly remove parts of the adjacent + placed breakpoints. It is due to our SHADOW_CONTENTS overlapping the real + breakpoint instruction bits region. */ cleanup = make_show_memory_breakpoints_cleanup (1); - val = target_read_memory (addr, bundle, BUNDLE_LEN); + val = target_read_memory (addr, bundle_mem, BUNDLE_LEN); - /* Check for L type instruction in 2nd slot, if present then - bump up the slot number to the 3rd slot */ - template = extract_bit_field (bundle, 0, 5); - if (slotnum == 1 && template_encoding_table[template][1] == L) + /* Check for L type instruction in slot 1, if present then bump up the slot + number to the slot 2. */ + template = extract_bit_field (bundle_mem, 0, 5); + if (slotnum == 1 && template_encoding_table[template][slotnum] == L) + slotnum = 2; + + gdb_assert (bp_tgt->placed_size == BUNDLE_LEN - 2); + gdb_assert (bp_tgt->placed_size == bp_tgt->shadow_len); + + instr_breakpoint = slotN_contents (bundle_mem, slotnum); + if (instr_breakpoint != IA64_BREAKPOINT) { - slotnum = 2; + warning (_("Cannot remove breakpoint at address %s, " + "no break instruction at such address."), + paddr_nz (bp_tgt->placed_address)); + return -1; } - memcpy (&instr, bp_tgt->shadow_contents, sizeof instr); - replace_slotN_contents (bundle, instr, slotnum); + /* Extract the original saved instruction from SLOTNUM normalizing its + bit-shift for INSTR_SAVED. */ + memcpy (bundle_saved, bundle_mem, BUNDLE_LEN); + memcpy (bundle_saved + slotnum, bp_tgt->shadow_contents, bp_tgt->shadow_len); + instr_saved = slotN_contents (bundle_saved, slotnum); + + /* In BUNDLE_MEM be careful to modify only the bits belonging to SLOTNUM and + never any other possibly also stored in SHADOW_CONTENTS. */ + replace_slotN_contents (bundle_mem, instr_saved, slotnum); if (val == 0) - target_write_memory (addr, bundle, BUNDLE_LEN); + val = target_write_memory (addr, bundle_mem, BUNDLE_LEN); do_cleanups (cleanup); return val; } -/* We don't really want to use this, but remote.c needs to call it in order - to figure out if Z-packets are supported or not. Oh, well. */ -const unsigned char * +/* As gdbarch_breakpoint_from_pc ranges have byte granularity and ia64 + instruction slots ranges are bit-granular (41 bits) we have to provide an + extended range as described for ia64_memory_insert_breakpoint. We also take + care of preserving the `break' instruction 21-bit (or 62-bit) parameter to + make a match for permanent breakpoints. */ + +static const gdb_byte * ia64_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr) { - static unsigned char breakpoint[] = - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - *lenptr = sizeof (breakpoint); -#if 0 - *pcptr &= ~0x0f; -#endif - return breakpoint; + CORE_ADDR addr = *pcptr; + static gdb_byte bundle[BUNDLE_LEN]; + int slotnum = (int) (*pcptr & 0x0f) / SLOT_MULTIPLIER; + long long instr_fetched; + int val; + int template; + struct cleanup *cleanup; + + if (slotnum > 2) + error (_("Can't insert breakpoint for slot numbers greater than 2.")); + + addr &= ~0x0f; + + /* Enable the automatic memory restoration from breakpoints while + we read our instruction bundle to match bp_loc_is_permanent. */ + cleanup = make_show_memory_breakpoints_cleanup (0); + val = target_read_memory (addr, bundle, BUNDLE_LEN); + do_cleanups (cleanup); + + /* The memory might be unreachable. This can happen, for instance, + when the user inserts a breakpoint at an invalid address. */ + if (val != 0) + return NULL; + + /* Check for L type instruction in slot 1, if present then bump up the slot + number to the slot 2. */ + template = extract_bit_field (bundle, 0, 5); + if (slotnum == 1 && template_encoding_table[template][slotnum] == L) + slotnum = 2; + + /* A break instruction has its all its opcode bits cleared except for + the parameter value. For L+X slot pair we are at the X slot (slot 2) so + we should not touch the L slot - the upper 41 bits of the parameter. */ + instr_fetched = slotN_contents (bundle, slotnum); + instr_fetched &= 0x1003ffffc0; + replace_slotN_contents (bundle, instr_fetched, slotnum); + + *lenptr = BUNDLE_LEN - 2; + + /* SLOTNUM is possibly already locally modified - use caller's *PCPTR. */ + return bundle + (*pcptr & 0x0f); } static CORE_ADDR -- 2.30.2