diff options
Diffstat (limited to 'gdb/dwarf2loc.c')
-rw-r--r-- | gdb/dwarf2loc.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index 1661d9f..fef0c6b 100644 --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -422,6 +422,87 @@ func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr) return sym; } +/* Define VEC (CORE_ADDR) functions. */ +DEF_VEC_I (CORE_ADDR); + +/* Verify function with entry point exact address ADDR can never call itself + via its tail calls (incl. transitively). Throw NO_ENTRY_VALUE_ERROR if it + can call itself via tail calls. + + If a funtion can tail call itself its entry value based parameters are + unreliable. There is no verification whether the value of some/all + parameters is unchanged through the self tail call, we expect if there is + a self tail call all the parameters can be modified. */ + +static void +func_verify_no_selftailcall (struct gdbarch *gdbarch, CORE_ADDR verify_addr) +{ + struct obstack addr_obstack; + struct cleanup *old_chain; + CORE_ADDR addr; + + /* Track here CORE_ADDRs which were already visited. */ + htab_t addr_hash; + + /* The verification is completely unordered. Track here function addresses + which still need to be iterated. */ + VEC (CORE_ADDR) *todo = NULL; + + obstack_init (&addr_obstack); + old_chain = make_cleanup_obstack_free (&addr_obstack); + addr_hash = htab_create_alloc_ex (64, core_addr_hash, core_addr_eq, NULL, + &addr_obstack, hashtab_obstack_allocate, + NULL); + make_cleanup_htab_delete (addr_hash); + + make_cleanup (VEC_cleanup (CORE_ADDR), &todo); + + VEC_safe_push (CORE_ADDR, todo, verify_addr); + while (!VEC_empty (CORE_ADDR, todo)) + { + struct symbol *func_sym; + struct call_site *call_site; + + addr = VEC_pop (CORE_ADDR, todo); + + func_sym = func_addr_to_tail_call_list (gdbarch, addr); + + for (call_site = TYPE_TAIL_CALL_LIST (SYMBOL_TYPE (func_sym)); + call_site; call_site = call_site->tail_call_next) + { + CORE_ADDR target_addr; + void **slot; + + /* CALLER_FRAME with registers is not available for tail-call jumped + frames. */ + target_addr = call_site_to_target_addr (gdbarch, call_site, NULL); + + if (target_addr == verify_addr) + { + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol_by_pc (verify_addr); + throw_error (NO_ENTRY_VALUE_ERROR, + _("DW_OP_GNU_entry_value resolving has found " + "function \"%s\" at %s can call itself via tail " + "calls"), + msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym), + paddress (gdbarch, verify_addr)); + } + + slot = htab_find_slot (addr_hash, &target_addr, INSERT); + if (*slot == NULL) + { + *slot = obstack_copy (&addr_obstack, &target_addr, + sizeof (target_addr)); + VEC_safe_push (CORE_ADDR, todo, target_addr); + } + } + } + + do_cleanups (old_chain); +} + /* Print user readable form of CALL_SITE->PC to gdb_stdlog. Used only for ENTRY_VALUES_DEBUG. */ @@ -780,6 +861,10 @@ dwarf_expr_reg_to_entry_parameter (struct frame_info *frame, int dwarf_reg, paddress (gdbarch, func_addr)); } + /* No entry value based parameters would be reliable if this function can + call itself via tail calls. */ + func_verify_no_selftailcall (gdbarch, func_addr); + for (iparams = 0; iparams < call_site->parameter_count; iparams++) { parameter = &call_site->parameter[iparams]; |