diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/arch-utils.c | 6 | ||||
-rw-r--r-- | gdb/gdbarch-gen.h | 13 | ||||
-rw-r--r-- | gdb/gdbarch.c | 22 | ||||
-rw-r--r-- | gdb/gdbarch_components.py | 17 | ||||
-rw-r--r-- | gdb/rs6000-tdep.c | 19 | ||||
-rw-r--r-- | gdb/symtab.c | 13 |
6 files changed, 90 insertions, 0 deletions
diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 997a292..1de5981 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1105,6 +1105,12 @@ default_dwarf2_omit_typedef_p (struct type *target_type, const char *producer, return false; } +static CORE_ADDR +default_update_call_site_pc (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + return pc; +} + /* Non-zero if we want to trace architecture code. */ #ifndef GDBARCH_DEBUG diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 56b0a91..7f83bf4 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -476,6 +476,19 @@ typedef bool (gdbarch_dwarf2_omit_typedef_p_ftype) (struct type *target_type, co extern bool gdbarch_dwarf2_omit_typedef_p (struct gdbarch *gdbarch, struct type *target_type, const char *producer, const char *name); extern void set_gdbarch_dwarf2_omit_typedef_p (struct gdbarch *gdbarch, gdbarch_dwarf2_omit_typedef_p_ftype *dwarf2_omit_typedef_p); +/* Update PC when trying to find a call site. This is useful on + architectures where the call site PC, as reported in the DWARF, can be + incorrect for some reason. + + The passed-in PC will be an address in the inferior. GDB will have + already failed to find a call site at this PC. This function may + simply return its parameter if it thinks that should be the correct + address. */ + +typedef CORE_ADDR (gdbarch_update_call_site_pc_ftype) (struct gdbarch *gdbarch, CORE_ADDR pc); +extern CORE_ADDR gdbarch_update_call_site_pc (struct gdbarch *gdbarch, CORE_ADDR pc); +extern void set_gdbarch_update_call_site_pc (struct gdbarch *gdbarch, gdbarch_update_call_site_pc_ftype *update_call_site_pc); + /* Return true if the return value of function is stored in the first hidden parameter. In theory, this feature should be language-dependent, specified by language and its ABI, such as C++. Unfortunately, compiler may diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 9406458..30199a0 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -115,6 +115,7 @@ struct gdbarch gdbarch_return_value_as_value_ftype *return_value_as_value = default_gdbarch_return_value; gdbarch_get_return_buf_addr_ftype *get_return_buf_addr = default_get_return_buf_addr; gdbarch_dwarf2_omit_typedef_p_ftype *dwarf2_omit_typedef_p = default_dwarf2_omit_typedef_p; + gdbarch_update_call_site_pc_ftype *update_call_site_pc = default_update_call_site_pc; gdbarch_return_in_first_hidden_param_p_ftype *return_in_first_hidden_param_p = default_return_in_first_hidden_param_p; gdbarch_skip_prologue_ftype *skip_prologue = nullptr; gdbarch_skip_main_prologue_ftype *skip_main_prologue = nullptr; @@ -372,6 +373,7 @@ verify_gdbarch (struct gdbarch *gdbarch) log.puts ("\n\treturn_value_as_value"); /* Skip verify of get_return_buf_addr, invalid_p == 0 */ /* Skip verify of dwarf2_omit_typedef_p, invalid_p == 0 */ + /* Skip verify of update_call_site_pc, invalid_p == 0 */ /* Skip verify of return_in_first_hidden_param_p, invalid_p == 0 */ if (gdbarch->skip_prologue == 0) log.puts ("\n\tskip_prologue"); @@ -794,6 +796,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: dwarf2_omit_typedef_p = <%s>\n", host_address_to_string (gdbarch->dwarf2_omit_typedef_p)); gdb_printf (file, + "gdbarch_dump: update_call_site_pc = <%s>\n", + host_address_to_string (gdbarch->update_call_site_pc)); + gdb_printf (file, "gdbarch_dump: return_in_first_hidden_param_p = <%s>\n", host_address_to_string (gdbarch->return_in_first_hidden_param_p)); gdb_printf (file, @@ -2639,6 +2644,23 @@ set_gdbarch_dwarf2_omit_typedef_p (struct gdbarch *gdbarch, gdbarch->dwarf2_omit_typedef_p = dwarf2_omit_typedef_p; } +CORE_ADDR +gdbarch_update_call_site_pc (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->update_call_site_pc != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_update_call_site_pc called\n"); + return gdbarch->update_call_site_pc (gdbarch, pc); +} + +void +set_gdbarch_update_call_site_pc (struct gdbarch *gdbarch, + gdbarch_update_call_site_pc_ftype update_call_site_pc) +{ + gdbarch->update_call_site_pc = update_call_site_pc; +} + int gdbarch_return_in_first_hidden_param_p (struct gdbarch *gdbarch, struct type *type) { diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index 62cd430..07010b1 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -929,6 +929,23 @@ Return 0 by default""", Method( comment=""" +Update PC when trying to find a call site. This is useful on +architectures where the call site PC, as reported in the DWARF, can be +incorrect for some reason. + +The passed-in PC will be an address in the inferior. GDB will have +already failed to find a call site at this PC. This function may +simply return its parameter if it thinks that should be the correct +address.""", + type="CORE_ADDR", + name="update_call_site_pc", + params=[("CORE_ADDR", "pc")], + predefault="default_update_call_site_pc", + invalid=False, +) + +Method( + comment=""" Return true if the return value of function is stored in the first hidden parameter. In theory, this feature should be language-dependent, specified by language and its ABI, such as C++. Unfortunately, compiler may diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index afeea02..ee18b49 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -7471,6 +7471,24 @@ rs6000_program_breakpoint_here_p (gdbarch *gdbarch, CORE_ADDR address) return false; } +/* Implement the update_call_site_pc arch hook. */ + +static CORE_ADDR +ppc64_update_call_site_pc (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + /* Some versions of GCC emit: + + . bl function + . nop + . ... + + but emit DWARF where the DW_AT_call_return_pc points to + instruction after the 'nop'. Note that while the compiler emits + a 'nop', the linker might put some other instruction there -- so + we just unconditionally check the next instruction. */ + return pc + 4; +} + /* Initialize the current architecture based on INFO. If possible, re-use an architecture from ARCHES, which is a list of architectures already created during this debugging session. @@ -8257,6 +8275,7 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_return_value (gdbarch, ppc64_sysv_abi_return_value); set_gdbarch_get_return_buf_addr (gdbarch, ppc64_sysv_get_return_buf_addr); + set_gdbarch_update_call_site_pc (gdbarch, ppc64_update_call_site_pc); } else set_gdbarch_return_value (gdbarch, ppc_sysv_abi_return_value); diff --git a/gdb/symtab.c b/gdb/symtab.c index 9e97986..6b6905d 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -349,6 +349,19 @@ compunit_symtab::find_call_site (CORE_ADDR pc) const struct call_site call_site_local (unrelocated_pc, nullptr, nullptr); void **slot = htab_find_slot (m_call_site_htab, &call_site_local, NO_INSERT); + if (slot != nullptr) + return (call_site *) *slot; + + /* See if the arch knows another PC we should try. On some + platforms, GCC emits a DWARF call site that is offset from the + actual return location. */ + struct gdbarch *arch = objfile ()->arch (); + CORE_ADDR new_pc = gdbarch_update_call_site_pc (arch, pc); + if (pc == new_pc) + return nullptr; + + call_site new_call_site_local (new_pc - delta, nullptr, nullptr); + slot = htab_find_slot (m_call_site_htab, &new_call_site_local, NO_INSERT); if (slot == nullptr) return nullptr; |