diff options
author | Thiago Jung Bauermann <bauerman@br.ibm.com> | 2011-03-31 14:32:49 +0000 |
---|---|---|
committer | Thiago Jung Bauermann <bauerman@br.ibm.com> | 2011-03-31 14:32:49 +0000 |
commit | f13101077fe972666625fa9d12952a9465f26f94 (patch) | |
tree | 129f11de0e72b593cc70b71882326ec23cb3857d /gdb | |
parent | bbda34159e3b58cdd3f9b5ab715df213ed53b16b (diff) | |
download | gdb-f13101077fe972666625fa9d12952a9465f26f94.zip gdb-f13101077fe972666625fa9d12952a9465f26f94.tar.gz gdb-f13101077fe972666625fa9d12952a9465f26f94.tar.bz2 |
2011-03-31 Thiago Jung Bauermann <bauerman@br.ibm.com>
Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Implement support for PowerPC BookE ranged breakpoints.
gdb/
* NEWS: Mention support for ranged breakpoints on embedded PowerPC.
* breakpoint.h (struct bp_target_info) <length>: New member
variable.
(struct breakpoint_ops) <breakpoint_hit>: Take struct bp_location
instead of struct breakpoint as argument, and also add ASPACE
and BP_ADDR arguments. Update all callers.
(struct breakpoint_ops) <print_one_detail>: New method.
(struct breakpoint) <addr_string_range_end>: New member variable.
* breakpoint.c (breakpoint_location_address_match): Add function
prototype.
(insert_bp_location): Set bl->target_info.length.
(breakpoint_here_p): Call breakpoint_location_address_match.
(moribund_breakpoint_here_p): Likewise.
(regular_breakpoint_inserted_here_p): Likewise.
(breakpoint_thread_match): Likewise.
(bpstat_stop_status): Likewise.
(bpstat_check_location): Move call to
breakpoint_ops.breakpoint_hit to the top.
(print_one_breakpoint_location): Call
breakpoint_ops.print_one_detail if available.
(breakpoint_address_match_range): New function.
(breakpoint_location_address_match): Likewise.
(breakpoint_locations_match): Compare the length field of the
locations too.
(hw_breakpoint_used_count): Count resources used by all locations
in a breakpoint, and use breakpoint_ops.resources_needed if
available.
(breakpoint_hit_ranged_breakpoint): New function.
(resources_needed_ranged_breakpoint): Likewise.
(print_it_ranged_breakpoint): Likewise.
(print_one_ranged_breakpoint): Likewise.
(print_one_detail_ranged_breakpoint): Likewise.
(print_mention_ranged_breakpoint): Likewise.
(print_recreate_ranged_breakpoint): Likewise.
(ranged_breakpoint_ops): New structure.
(find_breakpoint_range_end): New function.
(break_range_command): Likewise.
(delete_breakpoint): Free addr_string_range_end.
(update_breakpoint_locations): Add SALS_END argument. Update
all callers. Calculate breakpoint length if a non-zero SALS_END
is given. Call breakpoint_locations_match instead of
breakpoint_address_match.
(reset_breakpoint): Find SaL of the end of the range if B is a
ranged breakpoint.
(_initialize_breakpoint): Register break-range command.
* defs.h (print_core_address): Add function prototype.
* ppc-linux-nat.c (ppc_linux_ranged_break_num_registers): New
function.
(ppc_linux_insert_hw_breakpoint): Support ranged breakpoints.
(ppc_linux_remove_hw_breakpoint): Likewise.
(_initialize_ppc_linux_nat): Initialize
to_ranged_break_num_registers.
* target.c (update_current_target): Add comment about
to_ranged_break_num_registers.
(target_ranged_break_num_registers): New function.
* target.h (struct target_ops) <to_ranged_break_num_registers>:
New method.
(target_ranged_break_num_registers): Add function prototype.
* ui-out.c (ui_out_field_core_addr): Move address-printing logic to ...
* utils.c (print_core_address): ... here.
gdb/doc/
* gdb.texinfo (PowerPC Embedded): Document ranged breakpoints.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 65 | ||||
-rw-r--r-- | gdb/NEWS | 6 | ||||
-rw-r--r-- | gdb/ada-lang.c | 3 | ||||
-rw-r--r-- | gdb/breakpoint.c | 479 | ||||
-rw-r--r-- | gdb/breakpoint.h | 32 | ||||
-rw-r--r-- | gdb/defs.h | 6 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 18 | ||||
-rw-r--r-- | gdb/elfread.c | 5 | ||||
-rw-r--r-- | gdb/ppc-linux-nat.c | 46 | ||||
-rw-r--r-- | gdb/target.c | 16 | ||||
-rw-r--r-- | gdb/target.h | 6 | ||||
-rw-r--r-- | gdb/ui-out.c | 19 | ||||
-rw-r--r-- | gdb/utils.c | 19 |
14 files changed, 661 insertions, 67 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 3f49dcc..4da68a0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,68 @@ +2011-03-31 Thiago Jung Bauermann <bauerman@br.ibm.com> + Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com> + + Implement support for PowerPC BookE ranged breakpoints. + * NEWS: Mention support for ranged breakpoints on embedded PowerPC. + * breakpoint.h (struct bp_target_info) <length>: New member + variable. + (struct breakpoint_ops) <breakpoint_hit>: Take struct bp_location + instead of struct breakpoint as argument, and also add ASPACE + and BP_ADDR arguments. Update all callers. + (struct breakpoint_ops) <print_one_detail>: New method. + (struct breakpoint) <addr_string_range_end>: New member variable. + * breakpoint.c (breakpoint_location_address_match): Add function + prototype. + (insert_bp_location): Set bl->target_info.length. + (breakpoint_here_p): Call breakpoint_location_address_match. + (moribund_breakpoint_here_p): Likewise. + (regular_breakpoint_inserted_here_p): Likewise. + (breakpoint_thread_match): Likewise. + (bpstat_stop_status): Likewise. + (bpstat_check_location): Move call to + breakpoint_ops.breakpoint_hit to the top. + (print_one_breakpoint_location): Call + breakpoint_ops.print_one_detail if available. + (breakpoint_address_match_range): New function. + (breakpoint_location_address_match): Likewise. + (breakpoint_locations_match): Compare the length field of the + locations too. + (hw_breakpoint_used_count): Count resources used by all locations + in a breakpoint, and use breakpoint_ops.resources_needed if + available. + (breakpoint_hit_ranged_breakpoint): New function. + (resources_needed_ranged_breakpoint): Likewise. + (print_it_ranged_breakpoint): Likewise. + (print_one_ranged_breakpoint): Likewise. + (print_one_detail_ranged_breakpoint): Likewise. + (print_mention_ranged_breakpoint): Likewise. + (print_recreate_ranged_breakpoint): Likewise. + (ranged_breakpoint_ops): New structure. + (find_breakpoint_range_end): New function. + (break_range_command): Likewise. + (delete_breakpoint): Free addr_string_range_end. + (update_breakpoint_locations): Add SALS_END argument. Update + all callers. Calculate breakpoint length if a non-zero SALS_END + is given. Call breakpoint_locations_match instead of + breakpoint_address_match. + (reset_breakpoint): Find SaL of the end of the range if B is a + ranged breakpoint. + (_initialize_breakpoint): Register break-range command. + * defs.h (print_core_address): Add function prototype. + * ppc-linux-nat.c (ppc_linux_ranged_break_num_registers): New + function. + (ppc_linux_insert_hw_breakpoint): Support ranged breakpoints. + (ppc_linux_remove_hw_breakpoint): Likewise. + (_initialize_ppc_linux_nat): Initialize + to_ranged_break_num_registers. + * target.c (update_current_target): Add comment about + to_ranged_break_num_registers. + (target_ranged_break_num_registers): New function. + * target.h (struct target_ops) <to_ranged_break_num_registers>: + New method. + (target_ranged_break_num_registers): Add function prototype. + * ui-out.c (ui_out_field_core_addr): Move address-printing logic to ... + * utils.c (print_core_address): ... here. + 2011-03-31 Ulrich Weigand <uweigand@de.ibm.com> * breakpoint.c (addr_string_to_sals): Avoid uninitialized @@ -156,6 +156,12 @@ libthread_db library with the "set libthread-db-search-path" command. See the user manual for more details on this command. +* When natively debugging programs on PowerPC BookE processors running + a Linux kernel version 2.6.34 or later, GDB supports ranged breakpoints, + which stop execution of the inferior whenever it executes an instruction + at any address within the specified range. See the "PowerPC Embedded" + section in the user manual for more details. + * New features in the GDB remote stub, GDBserver ** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x), diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 5728ac8..2063f3d 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -10925,6 +10925,7 @@ static struct breakpoint_ops catch_exception_breakpoint_ops = NULL, /* resources_needed */ print_it_catch_exception, print_one_catch_exception, + NULL, /* print_one_detail */ print_mention_catch_exception, print_recreate_catch_exception }; @@ -10964,6 +10965,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = { NULL, /* resources_needed */ print_it_catch_exception_unhandled, print_one_catch_exception_unhandled, + NULL, /* print_one_detail */ print_mention_catch_exception_unhandled, print_recreate_catch_exception_unhandled }; @@ -11001,6 +11003,7 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = { NULL, /* resources_needed */ print_it_catch_assert, print_one_catch_assert, + NULL, /* print_one_detail */ print_mention_catch_assert, print_recreate_catch_assert }; diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index ed7c1c1..863198f 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -131,6 +131,10 @@ static int breakpoint_address_match (struct address_space *aspace1, static int watchpoint_locations_match (struct bp_location *loc1, struct bp_location *loc2); +static int breakpoint_location_address_match (struct bp_location *bl, + struct address_space *aspace, + CORE_ADDR addr); + static void breakpoints_info (char *, int); static void watchpoints_info (char *, int); @@ -1537,6 +1541,7 @@ insert_bp_location (struct bp_location *bl, memset (&bl->target_info, 0, sizeof (bl->target_info)); bl->target_info.placed_address = bl->address; bl->target_info.placed_address_space = bl->pspace->aspace; + bl->target_info.length = bl->length; if (bl->loc_type == bp_loc_software_breakpoint || bl->loc_type == bp_loc_hardware_breakpoint) @@ -2799,11 +2804,10 @@ breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc) && bl->loc_type != bp_loc_hardware_breakpoint) continue; - /* ALL_BP_LOCATIONS bp_location has bl->OWNER always non-NULL. */ + /* ALL_BP_LOCATIONS bp_location has BL->OWNER always non-NULL. */ if ((breakpoint_enabled (bl->owner) || bl->owner->enable_state == bp_permanent) - && breakpoint_address_match (bl->pspace->aspace, bl->address, - aspace, pc)) + && breakpoint_location_address_match (bl, aspace, pc)) { if (overlay_debugging && section_is_overlay (bl->section) @@ -2828,8 +2832,7 @@ moribund_breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc) int ix; for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix) - if (breakpoint_address_match (loc->pspace->aspace, loc->address, - aspace, pc)) + if (breakpoint_location_address_match (loc, aspace, pc)) return 1; return 0; @@ -2853,8 +2856,7 @@ regular_breakpoint_inserted_here_p (struct address_space *aspace, continue; if (bl->inserted - && breakpoint_address_match (bl->pspace->aspace, bl->address, - aspace, pc)) + && breakpoint_location_address_match (bl, aspace, pc)) { if (overlay_debugging && section_is_overlay (bl->section) @@ -2971,8 +2973,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc, && bl->owner->enable_state != bp_permanent) continue; - if (!breakpoint_address_match (bl->pspace->aspace, bl->address, - aspace, pc)) + if (!breakpoint_location_address_match (bl, aspace, pc)) continue; if (bl->owner->thread != -1) @@ -3860,6 +3861,9 @@ bpstat_check_location (const struct bp_location *bl, /* BL is from existing struct breakpoint. */ gdb_assert (b != NULL); + if (b->ops && b->ops->breakpoint_hit) + return b->ops->breakpoint_hit (bl, aspace, bp_addr); + /* By definition, the inferior does not report stops at tracepoints. */ if (is_tracepoint (b)) @@ -3888,7 +3892,7 @@ bpstat_check_location (const struct bp_location *bl, if (is_hardware_watchpoint (b) && b->watchpoint_triggered == watch_triggered_no) return 0; - + if (b->type == bp_hardware_breakpoint) { if (bl->address != bp_addr) @@ -3899,13 +3903,6 @@ bpstat_check_location (const struct bp_location *bl, return 0; } - if (b->type == bp_catchpoint) - { - gdb_assert (b->ops != NULL && b->ops->breakpoint_hit != NULL); - if (!b->ops->breakpoint_hit (b)) - return 0; - } - return 1; } @@ -4265,8 +4262,7 @@ bpstat_stop_status (struct address_space *aspace, for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix) { - if (breakpoint_address_match (loc->pspace->aspace, loc->address, - aspace, bp_addr)) + if (breakpoint_location_address_match (loc, aspace, bp_addr)) { bs = bpstat_alloc (loc, &bs_link); /* For hits of moribund locations, we should just proceed. */ @@ -4952,9 +4948,12 @@ print_one_breakpoint_location (struct breakpoint *b, ui_out_field_int (uiout, "task", b->task); } } - + ui_out_text (uiout, "\n"); - + + if (!part_of_multiple && b->ops && b->ops->print_one_detail) + b->ops->print_one_detail (b, uiout); + if (!part_of_multiple && b->static_trace_marker_id) { gdb_assert (b->type == bp_static_tracepoint); @@ -5537,6 +5536,39 @@ breakpoint_address_match (struct address_space *aspace1, CORE_ADDR addr1, && addr1 == addr2); } +/* Returns true if {ASPACE2,ADDR2} falls within the range determined by + {ASPACE1,ADDR1,LEN1}. In most targets, this can only be true if ASPACE1 + matches ASPACE2. On targets that have global breakpoints, the address + space doesn't really matter. */ + +static int +breakpoint_address_match_range (struct address_space *aspace1, CORE_ADDR addr1, + int len1, struct address_space *aspace2, + CORE_ADDR addr2) +{ + return ((gdbarch_has_global_breakpoints (target_gdbarch) + || aspace1 == aspace2) + && addr2 >= addr1 && addr2 < addr1 + len1); +} + +/* Returns true if {ASPACE,ADDR} matches the breakpoint BL. BL may be + a ranged breakpoint. In most targets, a match happens only if ASPACE + matches the breakpoint's address space. On targets that have global + breakpoints, the address space doesn't really matter. */ + +static int +breakpoint_location_address_match (struct bp_location *bl, + struct address_space *aspace, + CORE_ADDR addr) +{ + return (breakpoint_address_match (bl->pspace->aspace, bl->address, + aspace, addr) + || (bl->length + && breakpoint_address_match_range (bl->pspace->aspace, + bl->address, bl->length, + aspace, addr))); +} + /* Assuming LOC1 and LOC2's types' have meaningful target addresses (breakpoint_address_is_meaningful), returns true if LOC1 and LOC2 represent the same location. */ @@ -5559,8 +5591,10 @@ breakpoint_locations_match (struct bp_location *loc1, else if (hw_point1) return watchpoint_locations_match (loc1, loc2); else - return breakpoint_address_match (loc1->pspace->aspace, loc1->address, - loc2->pspace->aspace, loc2->address); + /* We compare bp_location.length in order to cover ranged breakpoints. */ + return (breakpoint_address_match (loc1->pspace->aspace, loc1->address, + loc2->pspace->aspace, loc2->address) + && loc1->length == loc2->length); } static void @@ -6201,9 +6235,10 @@ remove_catch_fork (struct bp_location *bl) catchpoints. */ static int -breakpoint_hit_catch_fork (struct breakpoint *b) +breakpoint_hit_catch_fork (const struct bp_location *bl, + struct address_space *aspace, CORE_ADDR bp_addr) { - return inferior_has_forked (inferior_ptid, &b->forked_inferior_pid); + return inferior_has_forked (inferior_ptid, &bl->owner->forked_inferior_pid); } /* Implement the "print_it" breakpoint_ops method for fork @@ -6272,6 +6307,7 @@ static struct breakpoint_ops catch_fork_breakpoint_ops = NULL, /* resources_needed */ print_it_catch_fork, print_one_catch_fork, + NULL, /* print_one_detail */ print_mention_catch_fork, print_recreate_catch_fork }; @@ -6298,9 +6334,10 @@ remove_catch_vfork (struct bp_location *bl) catchpoints. */ static int -breakpoint_hit_catch_vfork (struct breakpoint *b) +breakpoint_hit_catch_vfork (const struct bp_location *bl, + struct address_space *aspace, CORE_ADDR bp_addr) { - return inferior_has_vforked (inferior_ptid, &b->forked_inferior_pid); + return inferior_has_vforked (inferior_ptid, &bl->owner->forked_inferior_pid); } /* Implement the "print_it" breakpoint_ops method for vfork @@ -6368,6 +6405,7 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops = NULL, /* resources_needed */ print_it_catch_vfork, print_one_catch_vfork, + NULL, /* print_one_detail */ print_mention_catch_vfork, print_recreate_catch_vfork }; @@ -6457,12 +6495,14 @@ remove_catch_syscall (struct bp_location *bl) catchpoints. */ static int -breakpoint_hit_catch_syscall (struct breakpoint *b) +breakpoint_hit_catch_syscall (const struct bp_location *bl, + struct address_space *aspace, CORE_ADDR bp_addr) { /* We must check if we are catching specific syscalls in this breakpoint. If we are, then we must guarantee that the called syscall is the same syscall we are catching. */ int syscall_number = 0; + const struct breakpoint *b = bl->owner; if (!inferior_has_called_syscall (inferior_ptid, &syscall_number)) return 0; @@ -6531,7 +6571,7 @@ print_it_catch_syscall (struct breakpoint *b) static void print_one_catch_syscall (struct breakpoint *b, - struct bp_location **last_loc) + struct bp_location **last_loc) { struct value_print_options opts; @@ -6652,6 +6692,7 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops = NULL, /* resources_needed */ print_it_catch_syscall, print_one_catch_syscall, + NULL, /* print_one_detail */ print_mention_catch_syscall, print_recreate_catch_syscall }; @@ -6747,9 +6788,10 @@ remove_catch_exec (struct bp_location *bl) } static int -breakpoint_hit_catch_exec (struct breakpoint *b) +breakpoint_hit_catch_exec (const struct bp_location *bl, + struct address_space *aspace, CORE_ADDR bp_addr) { - return inferior_has_execd (inferior_ptid, &b->exec_pathname); + return inferior_has_execd (inferior_ptid, &bl->owner->exec_pathname); } static enum print_stop_action @@ -6806,6 +6848,7 @@ static struct breakpoint_ops catch_exec_breakpoint_ops = NULL, /* resources_needed */ print_it_catch_exec, print_one_catch_exec, + NULL, /* print_one_detail */ print_mention_catch_exec, print_recreate_catch_exec }; @@ -6829,13 +6872,22 @@ create_syscall_event_catchpoint (int tempflag, VEC(int) *filter, static int hw_breakpoint_used_count (void) { - struct breakpoint *b; int i = 0; + struct breakpoint *b; + struct bp_location *bl; ALL_BREAKPOINTS (b) { if (b->type == bp_hardware_breakpoint && breakpoint_enabled (b)) - i++; + for (bl = b->loc; bl; bl = bl->next) + { + /* Special types of hardware breakpoints may use more than + one register. */ + if (b->ops && b->ops->resources_needed) + i += b->ops->resources_needed (bl); + else + i++; + } } return i; @@ -8303,6 +8355,309 @@ stopat_command (char *arg, int from_tty) break_command_1 (arg, 0, from_tty); } +/* Implement the "breakpoint_hit" breakpoint_ops method for + ranged breakpoints. */ + +static int +breakpoint_hit_ranged_breakpoint (const struct bp_location *bl, + struct address_space *aspace, + CORE_ADDR bp_addr) +{ + return breakpoint_address_match_range (bl->pspace->aspace, bl->address, + bl->length, aspace, bp_addr); +} + +/* Implement the "resources_needed" breakpoint_ops method for + ranged breakpoints. */ + +static int +resources_needed_ranged_breakpoint (const struct bp_location *bl) +{ + return target_ranged_break_num_registers (); +} + +/* Implement the "print_it" breakpoint_ops method for + ranged breakpoints. */ + +static enum print_stop_action +print_it_ranged_breakpoint (struct breakpoint *b) +{ + struct bp_location *bl = b->loc; + + gdb_assert (b->type == bp_hardware_breakpoint); + + /* Ranged breakpoints have only one location. */ + gdb_assert (bl && bl->next == NULL); + + annotate_breakpoint (b->number); + if (b->disposition == disp_del) + ui_out_text (uiout, "\nTemporary ranged breakpoint "); + else + ui_out_text (uiout, "\nRanged breakpoint "); + if (ui_out_is_mi_like_p (uiout)) + { + ui_out_field_string (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); + ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition)); + } + ui_out_field_int (uiout, "bkptno", b->number); + ui_out_text (uiout, ", "); + + return PRINT_SRC_AND_LOC; +} + +/* Implement the "print_one" breakpoint_ops method for + ranged breakpoints. */ + +static void +print_one_ranged_breakpoint (struct breakpoint *b, + struct bp_location **last_loc) +{ + struct bp_location *bl = b->loc; + struct value_print_options opts; + + /* Ranged breakpoints have only one location. */ + gdb_assert (bl && bl->next == NULL); + + get_user_print_options (&opts); + + if (opts.addressprint) + /* We don't print the address range here, it will be printed later + by print_one_detail_ranged_breakpoint. */ + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + print_breakpoint_location (b, bl); + *last_loc = bl; +} + +/* Implement the "print_one_detail" breakpoint_ops method for + ranged breakpoints. */ + +static void +print_one_detail_ranged_breakpoint (const struct breakpoint *b, + struct ui_out *uiout) +{ + CORE_ADDR address_start, address_end; + struct bp_location *bl = b->loc; + struct ui_stream *stb = ui_out_stream_new (uiout); + struct cleanup *cleanup = make_cleanup_ui_out_stream_delete (stb); + + gdb_assert (bl); + + address_start = bl->address; + address_end = address_start + bl->length - 1; + + ui_out_text (uiout, "\taddress range: "); + fprintf_unfiltered (stb->stream, "[%s, %s]", + print_core_address (bl->gdbarch, address_start), + print_core_address (bl->gdbarch, address_end)); + ui_out_field_stream (uiout, "addr", stb); + ui_out_text (uiout, "\n"); + + do_cleanups (cleanup); +} + +/* Implement the "print_mention" breakpoint_ops method for + ranged breakpoints. */ + +static void +print_mention_ranged_breakpoint (struct breakpoint *b) +{ + struct bp_location *bl = b->loc; + + gdb_assert (bl); + gdb_assert (b->type == bp_hardware_breakpoint); + + if (ui_out_is_mi_like_p (uiout)) + return; + + printf_filtered (_("Hardware assisted ranged breakpoint %d from %s to %s."), + b->number, paddress (bl->gdbarch, bl->address), + paddress (bl->gdbarch, bl->address + bl->length - 1)); +} + +/* Implement the "print_recreate" breakpoint_ops method for + ranged breakpoints. */ + +static void +print_recreate_ranged_breakpoint (struct breakpoint *b, struct ui_file *fp) +{ + fprintf_unfiltered (fp, "break-range %s, %s", b->addr_string, + b->addr_string_range_end); +} + +/* The breakpoint_ops structure to be used in ranged breakpoints. */ + +static struct breakpoint_ops ranged_breakpoint_ops = +{ + NULL, /* insert */ + NULL, /* remove */ + breakpoint_hit_ranged_breakpoint, + resources_needed_ranged_breakpoint, + print_it_ranged_breakpoint, + print_one_ranged_breakpoint, + print_one_detail_ranged_breakpoint, + print_mention_ranged_breakpoint, + print_recreate_ranged_breakpoint +}; + +/* Find the address where the end of the breakpoint range should be + placed, given the SAL of the end of the range. This is so that if + the user provides a line number, the end of the range is set to the + last instruction of the given line. */ + +static CORE_ADDR +find_breakpoint_range_end (struct symtab_and_line sal) +{ + CORE_ADDR end; + + /* If the user provided a PC value, use it. Otherwise, + find the address of the end of the given location. */ + if (sal.explicit_pc) + end = sal.pc; + else + { + int ret; + CORE_ADDR start; + + ret = find_line_pc_range (sal, &start, &end); + if (!ret) + error (_("Could not find location of the end of the range.")); + + /* find_line_pc_range returns the start of the next line. */ + end--; + } + + return end; +} + +/* Implement the "break-range" CLI command. */ + +static void +break_range_command (char *arg, int from_tty) +{ + char *arg_start, *addr_string_start, *addr_string_end; + struct linespec_result canonical_start, canonical_end; + int bp_count, can_use_bp, length; + CORE_ADDR end; + struct breakpoint *b; + struct symtab_and_line sal_start, sal_end; + struct symtabs_and_lines sals_start, sals_end; + struct cleanup *cleanup_bkpt; + + /* We don't support software ranged breakpoints. */ + if (target_ranged_break_num_registers () < 0) + error (_("This target does not support hardware ranged breakpoints.")); + + bp_count = hw_breakpoint_used_count (); + bp_count += target_ranged_break_num_registers (); + can_use_bp = target_can_use_hardware_watchpoint (bp_hardware_breakpoint, + bp_count, 0); + if (can_use_bp < 0) + error (_("Hardware breakpoints used exceeds limit.")); + + if (arg == NULL || arg[0] == '\0') + error(_("No address range specified.")); + + sals_start.sals = NULL; + sals_start.nelts = 0; + init_linespec_result (&canonical_start); + + while (*arg == ' ' || *arg == '\t') + arg++; + + parse_breakpoint_sals (&arg, &sals_start, &canonical_start, NULL); + + sal_start = sals_start.sals[0]; + addr_string_start = canonical_start.canonical[0]; + cleanup_bkpt = make_cleanup (xfree, addr_string_start); + xfree (sals_start.sals); + xfree (canonical_start.canonical); + + if (arg[0] != ',') + error (_("Too few arguments.")); + else if (sals_start.nelts == 0) + error (_("Could not find location of the beginning of the range.")); + else if (sals_start.nelts != 1) + error (_("Cannot create a ranged breakpoint with multiple locations.")); + + resolve_sal_pc (&sal_start); + + arg++; /* Skip the comma. */ + while (*arg == ' ' || *arg == '\t') + arg++; + + /* Parse the end location. */ + + sals_end.sals = NULL; + sals_end.nelts = 0; + init_linespec_result (&canonical_end); + arg_start = arg; + + /* We call decode_line_n1 directly here instead of using + parse_breakpoint_sals because we need to specify the start location's + symtab and line as the default symtab and line for the end of the + range. This makes it possible to have ranges like "foo.c:27, +14", + where +14 means 14 lines from the start location. */ + sals_end = decode_line_1 (&arg, 1, sal_start.symtab, sal_start.line, + &canonical_end, NULL); + + /* canonical_end can be NULL if it was of the form "*0xdeadbeef". */ + if (canonical_end.canonical == NULL) + canonical_end.canonical = xcalloc (1, sizeof (char **)); + /* Add the string if not present. */ + if (arg_start != arg && canonical_end.canonical[0] == NULL) + canonical_end.canonical[0] = savestring (arg_start, arg - arg_start); + + sal_end = sals_end.sals[0]; + addr_string_end = canonical_end.canonical[0]; + make_cleanup (xfree, addr_string_end); + xfree (sals_end.sals); + xfree (canonical_end.canonical); + + if (sals_end.nelts == 0) + error (_("Could not find location of the end of the range.")); + else if (sals_end.nelts != 1) + error (_("Cannot create a ranged breakpoint with multiple locations.")); + + resolve_sal_pc (&sal_end); + + end = find_breakpoint_range_end (sal_end); + if (sal_start.pc > end) + error (_("Invalid address range, end preceeds start.")); + + length = end - sal_start.pc + 1; + if (length < 0) + /* Length overflowed. */ + error (_("Address range too large.")); + else if (length == 1) + { + /* This range is simple enough to be handled by + the `hbreak' command. */ + hbreak_command (addr_string_start, 1); + + do_cleanups (cleanup_bkpt); + + return; + } + + /* Now set up the breakpoint. */ + b = set_raw_breakpoint (get_current_arch (), sal_start, + bp_hardware_breakpoint); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->disposition = disp_donttouch; + b->addr_string = addr_string_start; + b->addr_string_range_end = addr_string_end; + b->ops = &ranged_breakpoint_ops; + b->loc->length = length; + + discard_cleanups (cleanup_bkpt); + + mention (b); + update_global_location_list (1); +} + /* Return non-zero if EXP is verified as constant. Returned zero means EXP is variable. Also the constant detection may fail for some constant expressions and in such case still falsely return @@ -8455,6 +8810,7 @@ static struct breakpoint_ops watchpoint_breakpoint_ops = resources_needed_watchpoint, NULL, /* print_it */ NULL, /* print_one */ + NULL, /* print_one_detail */ NULL, /* print_mention */ NULL /* print_recreate */ }; @@ -9233,6 +9589,7 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = { NULL, /* resources_needed */ print_exception_catchpoint, print_one_exception_catchpoint, + NULL, /* print_one_detail */ print_mention_exception_catchpoint, print_recreate_exception_catchpoint }; @@ -10179,6 +10536,7 @@ delete_breakpoint (struct breakpoint *bpt) xfree (bpt->cond_string); xfree (bpt->cond_exp); xfree (bpt->addr_string); + xfree (bpt->addr_string_range_end); xfree (bpt->exp); xfree (bpt->exp_string); xfree (bpt->exp_string_reparse); @@ -10473,14 +10831,21 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal) return sal; } +/* Create new breakpoint locations for B (a hardware or software breakpoint) + based on SALS and SALS_END. If SALS_END.NELTS is not zero, then B is + a ranged breakpoint. */ + void update_breakpoint_locations (struct breakpoint *b, - struct symtabs_and_lines sals) + struct symtabs_and_lines sals, + struct symtabs_and_lines sals_end) { int i; - char *s; struct bp_location *existing_locations = b->loc; + /* Ranged breakpoints have only one start location and one end location. */ + gdb_assert (sals_end.nelts == 0 || (sals.nelts == 1 && sals_end.nelts == 1)); + /* If there's no new locations, and all existing locations are pending, don't do anything. This optimizes the common case where all locations are in the same shared library, that was unloaded. @@ -10501,6 +10866,7 @@ update_breakpoint_locations (struct breakpoint *b, old symtab. */ if (b->cond_string != NULL) { + char *s; struct gdb_exception e; s = b->cond_string; @@ -10527,6 +10893,13 @@ update_breakpoint_locations (struct breakpoint *b, if (b->line_number == 0) b->line_number = sals.sals[i].line; + + if (sals_end.nelts) + { + CORE_ADDR end = find_breakpoint_range_end (sals_end.sals[0]); + + new_loc->length = end - sals.sals[0].pc + 1; + } } /* Update locations of permanent breakpoints. */ @@ -10552,8 +10925,7 @@ update_breakpoint_locations (struct breakpoint *b, if (have_ambiguous_names) { for (; l; l = l->next) - if (breakpoint_address_match (e->pspace->aspace, e->address, - l->pspace->aspace, l->address)) + if (breakpoint_locations_match (e, l)) { l->enabled = 0; break; @@ -10673,8 +11045,9 @@ static void re_set_breakpoint (struct breakpoint *b) { int found; - struct symtabs_and_lines sals; + struct symtabs_and_lines sals, sals_end; struct symtabs_and_lines expanded = {0}; + struct symtabs_and_lines expanded_end = {0}; struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); input_radix = b->input_radix; @@ -10689,7 +11062,17 @@ re_set_breakpoint (struct breakpoint *b) expanded = expand_line_sal_maybe (sals.sals[0]); } - update_breakpoint_locations (b, expanded); + if (b->addr_string_range_end) + { + sals_end = addr_string_to_sals (b, b->addr_string_range_end, &found); + if (found) + { + make_cleanup (xfree, sals_end.sals); + expanded_end = expand_line_sal_maybe (sals_end.sals[0]); + } + } + + update_breakpoint_locations (b, expanded, expanded_end); do_cleanups (cleanups); } @@ -12733,7 +13116,23 @@ inferior in all-stop mode, gdb behaves as if always-inserted mode is off."), &show_always_inserted_mode, &breakpoint_set_cmdlist, &breakpoint_show_cmdlist); - + + add_com ("break-range", class_breakpoint, break_range_command, _("\ +Set a breakpoint for an address range.\n\ +break-range START-LOCATION, END-LOCATION\n\ +where START-LOCATION and END-LOCATION can be one of the following:\n\ + LINENUM, for that line in the current file,\n\ + FILE:LINENUM, for that line in that file,\n\ + +OFFSET, for that number of lines after the current line\n\ + or the start of the range\n\ + FUNCTION, for the first line in that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ + *ADDRESS, for the instruction at that address.\n\ +\n\ +The breakpoint will stop execution of the inferior whenever it executes\n\ +an instruction at any address within the [START-LOCATION, END-LOCATION]\n\ +range (including START-LOCATION and END-LOCATION).")); + automatic_hardware_breakpoints = 1; observer_attach_about_to_proceed (breakpoint_about_to_proceed); diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 2cb56b7..18a7ce7 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -231,6 +231,10 @@ struct bp_target_info is used to determine the type of breakpoint to insert. */ CORE_ADDR placed_address; + /* If this is a ranged breakpoint, then this field contains the + length of the range that will be watched for execution. */ + int length; + /* If the breakpoint lives in memory and reading that memory would give back the breakpoint, instead of the original contents, then the original contents are cached here. Only SHADOW_LEN bytes of @@ -339,7 +343,8 @@ struct bp_location CORE_ADDR address; /* For hardware watchpoints, the size of the memory region being - watched. */ + watched. For hardware ranged breakpoints, the size of the + breakpoint range. */ int length; /* Type of hardware watchpoint. */ @@ -397,7 +402,8 @@ struct breakpoint_ops /* Return non-zero if the debugger should tell the user that this breakpoint was hit. */ - int (*breakpoint_hit) (struct breakpoint *); + int (*breakpoint_hit) (const struct bp_location *, struct address_space *, + CORE_ADDR); /* Tell how many hardware resources (debug registers) are needed for this breakpoint. If this function is not provided, then @@ -412,6 +418,20 @@ struct breakpoint_ops breakpoints". */ void (*print_one) (struct breakpoint *, struct bp_location **); + /* Display extra information about this breakpoint, below the normal + breakpoint description in "info breakpoints". + + In the example below, the "address range" line was printed + by print_one_detail_ranged_breakpoint. + + (gdb) info breakpoints + Num Type Disp Enb Address What + 2 hw breakpoint keep y in main at test-watch.c:70 + address range: [0x10000458, 0x100004c7] + + */ + void (*print_one_detail) (const struct breakpoint *, struct ui_out *); + /* Display information about this breakpoint after setting it (roughly speaking; this is called from "mention"). */ void (*print_mention) (struct breakpoint *); @@ -502,6 +522,11 @@ struct breakpoint /* String we used to set the breakpoint (malloc'd). */ char *addr_string; + + /* For a ranged breakpoint, the string we used to find + the end of the range (malloc'd). */ + char *addr_string_range_end; + /* Architecture we used to set the breakpoint. */ struct gdbarch *gdbarch; /* Language we used to set the breakpoint. */ @@ -904,7 +929,8 @@ extern int breakpoint_thread_match (struct address_space *, extern void until_break_command (char *, int, int); extern void update_breakpoint_locations (struct breakpoint *b, - struct symtabs_and_lines sals); + struct symtabs_and_lines sals, + struct symtabs_and_lines sals_end); extern void breakpoint_re_set (void); @@ -532,6 +532,12 @@ extern const char *host_address_to_string (const void *addr); This is usually formatted similar to 0x%lx. */ extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr); +/* Return a string representation in hexadecimal notation of ADDRESS, + which is suitable for printing. */ + +extern const char *print_core_address (struct gdbarch *gdbarch, + CORE_ADDR address); + /* %d for LONGEST */ extern char *plongest (LONGEST l); /* %u for ULONGEST */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 08d6c8c..424f5c2 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2011-03-31 Thiago Jung Bauermann <bauerman@br.ibm.com> + Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com> + + Implement support for PowerPC BookE ranged breakpoints. + * gdb.texinfo (PowerPC Embedded): Document ranged breakpoints. + 2011-03-28 Tom Tromey <tromey@redhat.com> * gdb.texinfo (Set Tracepoints): Fix typo. @@ -9,7 +15,7 @@ 2011-03-18 Phil Muldoon <pmuldoon@redhat.com> - PR python/12149 + PR python/12149 * gdb.texinfo (Basic Python): Update gdb.write and flush text. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a4e976f..e72a305 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -18742,9 +18742,27 @@ region using one of the following commands (@pxref{Expressions}): (@value{GDBP}) watch @{char[@var{length}]@} @var{address} @end smallexample +@cindex ranged breakpoint +PowerPC embedded processors support hardware accelerated +@dfn{ranged breakpoints}. A ranged breakpoint stops execution of +the inferior whenever it executes an instruction at any address within +the range it specifies. To set a ranged breakpoint in @value{GDBN}, +use the @code{break-range} command. + @value{GDBN} provides the following PowerPC-specific commands: @table @code +@kindex break-range +@item break-range @var{start-location}, @var{end-location} +Set a breakpoint for an address range. +@var{start-location} and @var{end-location} can specify a function name, +a line number, an offset of lines from the current line or from the start +location, or an address of an instruction (see @ref{Specify Location}, +for a list of all the possible ways to specify a @var{location}.) +The breakpoint will stop execution of the inferior whenever it +executes an instruction at any address within the specified range, +(including @var{start-location} and @var{end-location}.) + @kindex set powerpc @item set powerpc soft-float @itemx show powerpc soft-float diff --git a/gdb/elfread.c b/gdb/elfread.c index 68bed7e..b9cfa13 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -1011,7 +1011,7 @@ elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b) struct value *value; CORE_ADDR resolved_address, resolved_pc; struct symtab_and_line sal; - struct symtabs_and_lines sals; + struct symtabs_and_lines sals, sals_end; gdb_assert (b->type == bp_gnu_ifunc_resolver_return); @@ -1050,9 +1050,10 @@ elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b) sal = find_pc_line (resolved_pc, 0); sals.nelts = 1; sals.sals = &sal; + sals_end.nelts = 0; b->type = bp_breakpoint; - update_breakpoint_locations (b, sals); + update_breakpoint_locations (b, sals, sals_end); } struct build_id diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index 049cde8..f0c7f61 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -1637,6 +1637,19 @@ booke_remove_point (struct ppc_hw_breakpoint *b, int tid) hw_breaks[i].hw_break = NULL; } +/* Return the number of registers needed for a ranged breakpoint. */ + +static int +ppc_linux_ranged_break_num_registers (struct target_ops *target) +{ + return ((have_ptrace_booke_interface () + && booke_debug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)? + 2 : -1); +} + +/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for + success, 1 if hardware breakpoints are not supported or -1 for failure. */ + static int ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) @@ -1650,12 +1663,24 @@ ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch, p.version = PPC_DEBUG_CURRENT_VERSION; p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE; - p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; p.addr = (uint64_t) bp_tgt->placed_address; - p.addr2 = 0; p.condition_value = 0; + if (bp_tgt->length) + { + p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; + + /* The breakpoint will trigger if the address of the instruction is + within the defined range, as follows: p.addr <= address < p.addr2. */ + p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length; + } + else + { + p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; + p.addr2 = 0; + } + ALL_LWPS (lp, ptid) booke_insert_point (&p, TIDGET (ptid)); @@ -1675,12 +1700,24 @@ ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch, p.version = PPC_DEBUG_CURRENT_VERSION; p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE; - p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; p.addr = (uint64_t) bp_tgt->placed_address; - p.addr2 = 0; p.condition_value = 0; + if (bp_tgt->length) + { + p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; + + /* The breakpoint will trigger if the address of the instruction is within + the defined range, as follows: p.addr <= address < p.addr2. */ + p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length; + } + else + { + p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; + p.addr2 = 0; + } + ALL_LWPS (lp, ptid) booke_remove_point (&p, TIDGET (ptid)); @@ -2392,6 +2429,7 @@ _initialize_ppc_linux_nat (void) t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range; t->to_can_accel_watchpoint_condition = ppc_linux_can_accel_watchpoint_condition; + t->to_ranged_break_num_registers = ppc_linux_ranged_break_num_registers; t->to_read_description = ppc_linux_read_description; t->to_auxv_parse = ppc_linux_auxv_parse; diff --git a/gdb/target.c b/gdb/target.c index 45259fd..a032052 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -594,6 +594,7 @@ update_current_target (void) INHERIT (to_can_use_hw_breakpoint, t); INHERIT (to_insert_hw_breakpoint, t); INHERIT (to_remove_hw_breakpoint, t); + /* Do not inherit to_ranged_break_num_registers. */ INHERIT (to_insert_watchpoint, t); INHERIT (to_remove_watchpoint, t); INHERIT (to_stopped_data_address, t); @@ -3491,6 +3492,21 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size) tcomplain (); } +/* The documentation for this function is in its prototype declaration + in target.h. */ + +int +target_ranged_break_num_registers (void) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + if (t->to_ranged_break_num_registers != NULL) + return t->to_ranged_break_num_registers (t); + + return -1; +} + static void debug_to_prepare_to_store (struct regcache *regcache) { diff --git a/gdb/target.h b/gdb/target.h index 237d1aa..f0b2e43 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -450,6 +450,7 @@ struct target_ops int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *); int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *); int (*to_can_use_hw_breakpoint) (int, int, int); + int (*to_ranged_break_num_registers) (struct target_ops *); int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *); int (*to_remove_hw_breakpoint) (struct gdbarch *, struct bp_target_info *); @@ -1354,6 +1355,11 @@ extern char *target_thread_name (struct thread_info *); #define target_remove_hw_breakpoint(gdbarch, bp_tgt) \ (*current_target.to_remove_hw_breakpoint) (gdbarch, bp_tgt) +/* Return number of debug registers needed for a ranged breakpoint, + or -1 if ranged breakpoints are not supported. */ + +extern int target_ranged_break_num_registers (void); + /* Return non-zero if target knows the data address which triggered this target_stopped_by_watchpoint, in such case place it to *ADDR_P. Only the INFERIOR_PTID task is being queried. */ diff --git a/gdb/ui-out.c b/gdb/ui-out.c index 2cd1a54..59aaa24 100644 --- a/gdb/ui-out.c +++ b/gdb/ui-out.c @@ -492,23 +492,8 @@ ui_out_field_core_addr (struct ui_out *uiout, struct gdbarch *gdbarch, CORE_ADDR address) { - /* Maximum size string returned by hex_string_custom is 50 chars. - This buffer must be bigger than that, for safety. */ - char addstr[64]; - int addr_bit = gdbarch_addr_bit (gdbarch); - - if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT)) - address &= ((CORE_ADDR) 1 << addr_bit) - 1; - - /* FIXME: cagney/2002-05-03: Need local_address_string() function - that returns the language localized string formatted to a width - based on gdbarch_addr_bit. */ - if (addr_bit <= 32) - strcpy (addstr, hex_string_custom (address, 8)); - else - strcpy (addstr, hex_string_custom (address, 16)); - - ui_out_field_string (uiout, fldname, addstr); + ui_out_field_string (uiout, fldname, + print_core_address (gdbarch, address)); } void diff --git a/gdb/utils.c b/gdb/utils.c index e688d19..496c9dc 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -3224,6 +3224,25 @@ paddress (struct gdbarch *gdbarch, CORE_ADDR addr) return hex_string (addr); } +/* This function is described in "defs.h". */ + +const char * +print_core_address (struct gdbarch *gdbarch, CORE_ADDR address) +{ + int addr_bit = gdbarch_addr_bit (gdbarch); + + if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT)) + address &= ((CORE_ADDR) 1 << addr_bit) - 1; + + /* FIXME: cagney/2002-05-03: Need local_address_string() function + that returns the language localized string formatted to a width + based on gdbarch_addr_bit. */ + if (addr_bit <= 32) + return hex_string_custom (address, 8); + else + return hex_string_custom (address, 16); +} + static char * decimal2str (char *sign, ULONGEST addr, int width) { |