This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[patch 02/12] entryval: Basic parameter values recovery
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Mon, 18 Jul 2011 22:15:28 +0200
- Subject: [patch 02/12] entryval: Basic parameter values recovery
Hi,
this patch reads in the DWARF info for entry values and recovers the current
values from it.
the testcase:
#0 d (i=<optimized out>) at ./gdb.arch/amd64-entry-value.cc:33^M
#1 0x00000000004003ba in main () at ./gdb.arch/amd64-entry-value.cc:39^M
->
#0 d (i=31) at ./gdb.arch/amd64-entry-value.cc:33^M
#1 0x00000000004003da in main () at ./gdb.arch/amd64-entry-value.cc:39^M
It uses entry values internally, user cannot yet access them directly.
NOT_FOUND_ERROR is probably wrong, I can change it to some new *_ERROR
probably, I do not have a reproducer but NOT_FOUND_ERROR overloading can
probably regress something.
Thanks,
Jan
gdb/
2011-07-18 Jan Kratochvil <jan.kratochvil@redhat.com>
Implement basic support for DW_TAG_GNU_call_site.
* block.c: Include gdbtypes.h.
(call_site_for_pc): New function.
* block.h (call_site_for_pc): New declaration.
* defs.h: Include hashtab.h.
(make_cleanup_htab_delete, core_addr_hash, core_addr_eq): New
declarations.
* dwarf2-frame.c (no_push_dwarf_reg_entry_value): New function.
(dwarf2_frame_ctx_funcs): Install it.
* dwarf2expr.c (dwarf_block_to_dwarf_reg)
(dwarf_block_to_dwarf_reg_deref, dwarf_block_to_sp_offset): New
functions.
(execute_stack_op) <DW_OP_GNU_entry_value>: Implement it.
* dwarf2expr.h (struct dwarf_expr_context_funcs): New field
push_dwarf_reg_entry_value.
(dwarf_block_to_dwarf_reg, dwarf_block_to_sp_offset)
(dwarf_block_to_dwarf_reg_deref): New declarations.
* dwarf2loc.c (dwarf_expr_ctx_funcs): New forward declaration.
(call_site_to_target_addr, dwarf_expr_dwarf_reg_entry_value)
(dwarf_entry_parameter_to_block, dwarf_expr_push_dwarf_reg_entry_value):
New functions.
(dwarf_expr_ctx_funcs): Install dwarf_expr_push_dwarf_reg_entry_value.
(dwarf2_evaluate_loc_desc_full): Do old_chain in advance in the case of
EX. Handle NOT_FOUND_ERROR.
(needs_dwarf_reg_entry_value): New function.
(needs_frame_ctx_funcs): Install it.
* dwarf2read.c (struct dwarf2_cu): New field call_site_htab.
(read_call_site_scope): New forward declaration.
(process_full_comp_unit): Copy call_site_htab.
(process_die): Support DW_TAG_GNU_call_site.
(dlbaton_obstack_copy, read_call_site_scope): New functions.
(dwarf2_get_pc_bounds): Support NULL HIGHPC.
(dwarf_tag_name): Support DW_TAG_GNU_call_site.
* gdb-gdb.py (StructMainTypePrettyPrinter): Support
FIELD_LOC_KIND_DWARF_BLOCK.
* gdbtypes.h (enum field_loc_kind): New entry
FIELD_LOC_KIND_DWARF_BLOCK.
(struct main_type): New loc entry dwarf_block.
(struct call_site, FIELD_DWARF_BLOCK, SET_FIELD_DWARF_BLOCK)
(TYPE_FIELD_DWARF_BLOCK): New.
* symtab.h (struct symtab): New field call_site_htab.
* utils.c (do_htab_delete_cleanup, make_cleanup_htab_delete)
(core_addr_hash, core_addr_eq): New functions.
gdb/testsuite/
2011-07-18 Jan Kratochvil <jan.kratochvil@redhat.com>
Implement basic support for DW_TAG_GNU_call_site.
* gdb.arch/Makefile.in (EXECUTABLES): Add amd64-entry-value.
* gdb.arch/amd64-entry-value.cc: New file.
* gdb.arch/amd64-entry-value.exp: New file.
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -25,6 +25,7 @@
#include "gdb_obstack.h"
#include "cp-support.h"
#include "addrmap.h"
+#include "gdbtypes.h"
/* This is used by struct block to store namespace-related info for
C++ files, namely using declarations and the current namespace in
@@ -160,6 +161,29 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct obj_section *section,
return 0;
}
+/* Return call_site for specified PC. PC must match exactly, it must be the
+ next instruction after call (or after tail call jump). Return NULL if it
+ cannot be found. */
+
+struct call_site *
+call_site_for_pc (CORE_ADDR pc)
+{
+ struct symtab *symtab;
+ void **slot;
+
+ /* -1 as tail call PC can be already after the compilation unit range. */
+ symtab = find_pc_symtab (pc - 1);
+
+ if (symtab == NULL || symtab->call_site_htab == NULL)
+ return NULL;
+
+ slot = htab_find_slot (symtab->call_site_htab, &pc, NO_INSERT);
+ if (slot == NULL)
+ return NULL;
+
+ return *slot;
+}
+
/* Return the blockvector immediately containing the innermost lexical block
containing the specified pc value, or 0 if there is none.
Backward compatibility, no section. */
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -142,6 +142,8 @@ extern struct blockvector *blockvector_for_pc_sect (CORE_ADDR,
struct block **,
struct symtab *);
+extern struct call_site *call_site_for_pc (CORE_ADDR pc);
+
extern struct block *block_for_pc (CORE_ADDR);
extern struct block *block_for_pc_sect (CORE_ADDR, struct obj_section *);
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -89,6 +89,7 @@
#include <stdarg.h> /* For va_list. */
#include "libiberty.h"
+#include "hashtab.h"
/* Rather than duplicate all the logic in BFD for figuring out what
types to use (which can be pretty complicated), symply define them
@@ -370,6 +371,8 @@ extern struct cleanup *make_final_cleanup (make_cleanup_ftype *, void *);
extern struct cleanup *make_my_cleanup (struct cleanup **,
make_cleanup_ftype *, void *);
+extern struct cleanup *make_cleanup_htab_delete (htab_t htab);
+
extern struct cleanup *make_my_cleanup2 (struct cleanup **,
make_cleanup_ftype *, void *,
void (*free_arg) (void *));
@@ -540,6 +543,11 @@ extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr);
extern const char *print_core_address (struct gdbarch *gdbarch,
CORE_ADDR address);
+/* Callback hash_f and eq_f for htab_create_alloc or htab_create_alloc_ex. */
+
+extern hashval_t core_addr_hash (const void *ap);
+extern int core_addr_eq (const void *ap, const void *bp);
+
/* %d for LONGEST */
extern char *plongest (LONGEST l);
/* %u for ULONGEST */
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -361,6 +361,16 @@ no_base_type (struct dwarf_expr_context *ctx, size_t die)
error (_("Support for typed DWARF is not supported in CFI"));
}
+/* Helper function for execute_stack_op. */
+
+static void
+no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg,
+ CORE_ADDR fb_offset, CORE_ADDR deref_size)
+{
+ internal_error (__FILE__, __LINE__,
+ _("Support for DW_OP_GNU_entry_value is unimplemented"));
+}
+
/* Execute the required actions for both the DW_CFA_restore and
DW_CFA_restore_extended instructions. */
static void
@@ -402,7 +412,8 @@ static const struct dwarf_expr_context_funcs dwarf2_frame_ctx_funcs =
no_get_frame_pc,
no_get_tls_address,
no_dwarf_call,
- no_base_type
+ no_base_type,
+ no_push_dwarf_reg_entry_value
};
static CORE_ADDR
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -481,6 +481,127 @@ dwarf_get_base_type (struct dwarf_expr_context *ctx, ULONGEST die, int size)
return result;
}
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_reg* return the
+ DWARF register number. Otherwise return -1. */
+
+int
+dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end)
+{
+ ULONGEST dwarf_reg;
+
+ if (buf_end <= buf)
+ return -1;
+ if (*buf >= DW_OP_reg0 && *buf <= DW_OP_reg31)
+ {
+ if (buf_end - buf != 1)
+ return -1;
+ return *buf - DW_OP_reg0;
+ }
+
+ if (*buf != DW_OP_regx)
+ return -1;
+ buf++;
+ buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+ if (buf != buf_end || (int) dwarf_reg != dwarf_reg)
+ return -1;
+ return dwarf_reg;
+}
+
+/* If <BUF..BUF_END] contains DW_FORM_block* with just DW_OP_breg*(0) and
+ DW_OP_deref* return the DWARF register number. Otherwise return -1.
+ DEREF_SIZE_RETURN contains -1 for DW_OP_deref; otherwise it contains the
+ size from DW_OP_deref_size. */
+
+int
+dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end,
+ CORE_ADDR *deref_size_return)
+{
+ ULONGEST dwarf_reg;
+ LONGEST offset;
+
+ if (buf_end <= buf)
+ return -1;
+ if (*buf >= DW_OP_breg0 && *buf <= DW_OP_breg31)
+ {
+ dwarf_reg = *buf - DW_OP_breg0;
+ buf++;
+ }
+ else if (*buf == DW_OP_bregx)
+ {
+ buf++;
+ buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+ if ((int) dwarf_reg != dwarf_reg)
+ return -1;
+ }
+ else
+ return -1;
+
+ buf = read_sleb128 (buf, buf_end, &offset);
+ if (offset != 0)
+ return -1;
+
+ if (buf >= buf_end)
+ return -1;
+
+ if (*buf == DW_OP_deref)
+ {
+ buf++;
+ *deref_size_return = -1;
+ }
+ else if (*buf == DW_OP_deref_size)
+ {
+ buf++;
+ if (buf >= buf_end)
+ return -1;
+ *deref_size_return = *buf++;
+ }
+ else
+ return -1;
+
+ if (buf != buf_end)
+ return -1;
+
+ return dwarf_reg;
+}
+
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_bregSP(X) fill
+ in SP_OFFSET_RETURN with the X offset and return 1. Otherwise return 0.
+ The matched SP register number depends on GDBARCH. */
+
+int
+dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
+ const gdb_byte *buf_end, CORE_ADDR *sp_offset_return)
+{
+ ULONGEST dwarf_reg;
+ LONGEST sp_offset;
+
+ if (buf_end <= buf)
+ return 0;
+ if (*buf >= DW_OP_breg0 && *buf <= DW_OP_breg31)
+ {
+ dwarf_reg = *buf - DW_OP_breg0;
+ buf++;
+ }
+ else
+ {
+ if (*buf != DW_OP_bregx)
+ return 0;
+ buf++;
+ buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+ }
+
+ if (gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg)
+ != gdbarch_sp_regnum (gdbarch))
+ return 0;
+
+ buf = read_sleb128 (buf, buf_end, &sp_offset);
+ *sp_offset_return = sp_offset;
+ if (buf != buf_end || sp_offset != (LONGEST) *sp_offset_return)
+ return 0;
+
+ return 1;
+}
+
/* The engine for the expression evaluator. Using the context in CTX,
evaluate the expression between OP_PTR and OP_END. */
@@ -1187,11 +1308,41 @@ execute_stack_op (struct dwarf_expr_context *ctx,
goto no_push;
case DW_OP_GNU_entry_value:
- /* This operation is not yet supported by GDB. */
- ctx->location = DWARF_VALUE_OPTIMIZED_OUT;
- ctx->stack_len = 0;
- ctx->num_pieces = 0;
- goto abort_expression;
+ {
+ ULONGEST len;
+ int dwarf_reg;
+ CORE_ADDR deref_size;
+
+ op_ptr = read_uleb128 (op_ptr, op_end, &len);
+ if (op_ptr + len > op_end)
+ error (_("DW_OP_GNU_entry_value: too few bytes available."));
+
+ dwarf_reg = dwarf_block_to_dwarf_reg (op_ptr, op_ptr + len);
+ if (dwarf_reg != -1)
+ {
+ op_ptr += len;
+ ctx->funcs->push_dwarf_reg_entry_value (ctx, dwarf_reg,
+ 0 /* unused */, -1);
+ goto no_push;
+ }
+
+ dwarf_reg = dwarf_block_to_dwarf_reg_deref (op_ptr, op_ptr + len,
+ &deref_size);
+ if (dwarf_reg != -1)
+ {
+ if (deref_size == -1)
+ deref_size = ctx->addr_size;
+ op_ptr += len;
+ ctx->funcs->push_dwarf_reg_entry_value (ctx, dwarf_reg,
+ 0 /* unused */,
+ deref_size);
+ goto no_push;
+ }
+
+ error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is "
+ "supported only for single DW_OP_reg* "
+ "or for DW_OP_breg*(0)+DW_OP_deref*"));
+ }
case DW_OP_GNU_const_type:
{
--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -62,6 +62,17 @@ struct dwarf_expr_context_funcs
meaningful to substitute a stub type of the correct size. */
struct type *(*get_base_type) (struct dwarf_expr_context *ctx, size_t die);
+ /* Push on DWARF stack an entry evaluated for DW_TAG_GNU_call_site's
+ DWARF_REG/FB_OFFSET at the caller of specified BATON. If DWARF register
+ number DWARF_REG specifying the push_dwarf_reg_entry_value parameter is
+ not -1 FB_OFFSET is ignored. Otherwise FB_OFFSET specifies stack
+ parameter offset against caller's stack pointer (which equals the callee's
+ frame base). If DEREF_SIZE is not -1 then use
+ DW_AT_GNU_call_site_data_value instead of DW_AT_GNU_call_site_value. */
+ void (*push_dwarf_reg_entry_value) (struct dwarf_expr_context *ctx,
+ int dwarf_reg, CORE_ADDR fb_offset,
+ CORE_ADDR deref_size);
+
#if 0
/* Not yet implemented. */
@@ -253,4 +264,14 @@ const char *dwarf_stack_op_name (unsigned int);
void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
const char *);
+int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end);
+
+int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
+ const gdb_byte *buf_end,
+ CORE_ADDR *sp_offset_return);
+
+int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf,
+ const gdb_byte *buf_end,
+ CORE_ADDR *deref_size_return);
+
#endif /* dwarf2expr.h */
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -47,6 +47,8 @@ extern int dwarf2_always_disassemble;
static void dwarf_expr_frame_base_1 (struct symbol *framefunc, CORE_ADDR pc,
const gdb_byte **start, size_t *length);
+static const struct dwarf_expr_context_funcs dwarf_expr_ctx_funcs;
+
static struct value *dwarf2_evaluate_loc_desc_full (struct type *type,
struct frame_info *frame,
const gdb_byte *data,
@@ -296,6 +298,232 @@ dwarf_expr_get_base_type (struct dwarf_expr_context *ctx, size_t die_offset)
return dwarf2_get_die_type (die_offset, debaton->per_cu);
}
+/* Find DW_TAG_GNU_call_site's DW_AT_GNU_call_site_target address.
+ CALLER_FRAME (for registers) can be NULL if it is not known. This function
+ always returns valid address or it throws NOT_FOUND_ERROR. */
+
+static CORE_ADDR
+call_site_to_target_addr (struct call_site *call_site,
+ struct frame_info *caller_frame)
+{
+ switch (FIELD_LOC_KIND (call_site->target))
+ {
+ case FIELD_LOC_KIND_DWARF_BLOCK:
+ {
+ struct dwarf2_locexpr_baton *dwarf_block;
+ struct value *val;
+ struct type *caller_core_addr_type;
+ struct gdbarch *caller_arch;
+
+ dwarf_block = FIELD_DWARF_BLOCK (call_site->target);
+ if (dwarf_block == NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("DW_AT_GNU_call_site_target is not specified"));
+ if (caller_frame == NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("DW_AT_GNU_call_site_target DWARF block resolving "
+ "requires known frame which is currently not "
+ "available"));
+ caller_arch = get_frame_arch (caller_frame);
+ caller_core_addr_type = builtin_type (caller_arch)->builtin_func_ptr;
+ val = dwarf2_evaluate_loc_desc (caller_core_addr_type, caller_frame,
+ dwarf_block->data, dwarf_block->size,
+ dwarf_block->per_cu);
+ /* DW_AT_GNU_call_site_target is a DWARF expression, not a DWARF
+ location. */
+ if (VALUE_LVAL (val) == lval_memory)
+ return value_address (val);
+ else
+ return value_as_address (val);
+ }
+
+ case FIELD_LOC_KIND_PHYSNAME:
+ {
+ const char *physname;
+ struct minimal_symbol *msym;
+
+ physname = FIELD_STATIC_PHYSNAME (call_site->target);
+ msym = lookup_minimal_symbol_text (physname, NULL);
+ if (msym == NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("Cannot find function \"%s\" for a call site target"),
+ physname);
+ return SYMBOL_VALUE_ADDRESS (msym);
+ }
+
+ case FIELD_LOC_KIND_PHYSADDR:
+ return FIELD_STATIC_PHYSADDR (call_site->target);
+
+ default:
+ internal_error (__FILE__, __LINE__, _("invalid call site target kind"));
+ }
+}
+
+/* Fetch call_site_parameter from caller matching the parameters. FRAME is for
+ callee. See DWARF_REG and FB_OFFSET description at struct
+ dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
+
+ Function always returns non-NULL, it throws NOT_FOUND_ERROR otherwise. */
+
+static struct call_site_parameter *
+dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg,
+ CORE_ADDR fb_offset)
+{
+ CORE_ADDR func_addr = get_frame_func (frame);
+ CORE_ADDR caller_pc;
+ struct gdbarch *caller_gdbarch = frame_unwind_arch (frame);
+ struct frame_info *caller_frame = get_prev_frame (frame);
+ struct call_site *call_site;
+ int iparams;
+ struct value *val;
+ struct dwarf2_locexpr_baton *dwarf_block;
+ struct call_site_parameter *parameter;
+ CORE_ADDR target_addr;
+
+ if (caller_frame == NULL)
+ {
+ struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (func_addr);
+
+ throw_error (NOT_FOUND_ERROR, _("DW_OP_GNU_entry_value resolving "
+ "requires caller of %s (%s)"),
+ paddress (get_frame_arch (frame), func_addr),
+ msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+ }
+ caller_pc = get_frame_pc (caller_frame);
+
+ call_site = call_site_for_pc (caller_pc);
+ if (call_site == NULL)
+ {
+ struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc);
+
+ /* DW_TAG_gnu_call_site will be missing just if GCC could not determine
+ the call target. So do not complain more than NOT_FOUND_ERROR. */
+ throw_error (NOT_FOUND_ERROR,
+ _("DW_OP_GNU_entry_value resolving cannot find "
+ "DW_TAG_GNU_call_site %s in %s"),
+ paddress (caller_gdbarch, caller_pc),
+ msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+ }
+
+ target_addr = call_site_to_target_addr (call_site, caller_frame);
+ if (target_addr != func_addr)
+ {
+ struct minimal_symbol *target_msym, *func_msym;
+
+ target_msym = lookup_minimal_symbol_by_pc (target_addr);
+ func_msym = lookup_minimal_symbol_by_pc (func_addr);
+ throw_error (NOT_FOUND_ERROR,
+ _("DW_OP_GNU_entry_value resolving expects callee %s at %s "
+ "but the called frame is for %s at %s"),
+ (target_msym == NULL ? "???"
+ : SYMBOL_PRINT_NAME (target_msym)),
+ paddress (caller_gdbarch, target_addr),
+ func_msym == NULL ? "???" : SYMBOL_PRINT_NAME (func_msym),
+ paddress (caller_gdbarch, func_addr));
+ }
+
+ for (iparams = 0; iparams < call_site->parameter_count; iparams++)
+ {
+ parameter = &call_site->parameter[iparams];
+ if (parameter->dwarf_reg == -1 && dwarf_reg == -1)
+ {
+ if (parameter->fb_offset == fb_offset)
+ break;
+ }
+ else if (parameter->dwarf_reg == dwarf_reg)
+ break;
+ }
+ if (iparams == call_site->parameter_count)
+ {
+ struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc);
+
+ /* DW_TAG_GNU_call_site_parameter will be missing just if GCC could not
+ determine its value. So do not complain more than NOT_FOUND_ERROR. */
+ throw_error (NOT_FOUND_ERROR, _("Cannot find DWARF reg%d/fbreg(%s) at "
+ "DW_TAG_GNU_call_site %s at %s"),
+ dwarf_reg, paddress (caller_gdbarch, fb_offset),
+ paddress (caller_gdbarch, caller_pc),
+ msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+ }
+
+ return parameter;
+}
+
+/* Return dwarf2_locexpr_baton for PARAMETER matching DEREF_SIZE. If
+ DEREF_SIZE is -1, return the normal DW_AT_GNU_call_site_value block.
+ Otherwise return the DW_AT_GNU_call_site_data_value (dereferenced) block.
+
+ Function always returns non-NULL, it throws NOT_FOUND_ERROR if DEREF_SIZE
+ was not -1 and the DW_AT_GNU_call_site_data_value block is not defined by
+ PARAMETER. */
+
+static struct dwarf2_locexpr_baton *
+dwarf_entry_parameter_to_block (struct call_site_parameter *parameter,
+ CORE_ADDR deref_size)
+{
+
+ if (deref_size == -1)
+ {
+ gdb_assert (parameter->call_site_value != NULL);
+ return parameter->call_site_value;
+ }
+
+ /* DEREF_SIZE size is not verified here. */
+
+ if (parameter->call_site_data_value == NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("Cannot resolve DW_AT_GNU_call_site_data_value"));
+
+ return parameter->call_site_data_value;
+}
+
+/* Execute DWARF_BLOCK for caller of the CTX's frame. CTX must be of
+ dwarf_expr_ctx_funcs kind. See DWARF_REG, FB_OFFSET and DEREF_SIZE
+ description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
+
+ The CTX caller can be from a different CU - per_cu_dwarf_call is simpler as
+ it does not support cross-CU DWARF executions. */
+
+static void
+dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx,
+ int dwarf_reg, CORE_ADDR fb_offset,
+ CORE_ADDR deref_size)
+{
+ struct dwarf_expr_baton *debaton;
+ struct frame_info *frame, *caller_frame;
+ struct dwarf2_locexpr_baton *dwarf_block;
+ struct dwarf_expr_baton baton_local;
+ struct dwarf_expr_context saved_ctx;
+ struct call_site_parameter *parameter;
+
+ gdb_assert (ctx->funcs == &dwarf_expr_ctx_funcs);
+ debaton = ctx->baton;
+ frame = debaton->frame;
+ caller_frame = get_prev_frame (frame);
+
+ parameter = dwarf_expr_dwarf_reg_entry_value (frame, dwarf_reg, fb_offset);
+ dwarf_block = dwarf_entry_parameter_to_block (parameter, deref_size);
+
+ baton_local.frame = caller_frame;
+ baton_local.per_cu = dwarf_block->per_cu;
+
+ saved_ctx.gdbarch = ctx->gdbarch;
+ saved_ctx.addr_size = ctx->addr_size;
+ saved_ctx.offset = ctx->offset;
+ saved_ctx.baton = ctx->baton;
+ ctx->gdbarch = get_objfile_arch (dwarf2_per_cu_objfile (baton_local.per_cu));
+ ctx->addr_size = dwarf2_per_cu_addr_size (baton_local.per_cu);
+ ctx->offset = dwarf2_per_cu_text_offset (baton_local.per_cu);
+ ctx->baton = &baton_local;
+
+ dwarf_expr_eval (ctx, dwarf_block->data, dwarf_block->size);
+
+ ctx->gdbarch = saved_ctx.gdbarch;
+ ctx->addr_size = saved_ctx.addr_size;
+ ctx->offset = saved_ctx.offset;
+ ctx->baton = saved_ctx.baton;
+}
+
struct piece_closure
{
/* Reference count. */
@@ -1083,7 +1311,8 @@ static const struct dwarf_expr_context_funcs dwarf_expr_ctx_funcs =
dwarf_expr_frame_pc,
dwarf_expr_tls_address,
dwarf_expr_dwarf_call,
- dwarf_expr_get_base_type
+ dwarf_expr_get_base_type,
+ dwarf_expr_push_dwarf_reg_entry_value
};
/* Evaluate a location description, starting at DATA and with length
@@ -1129,13 +1358,21 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
}
if (ex.reason < 0)
{
+ do_cleanups (old_chain);
+
if (ex.error == NOT_AVAILABLE_ERROR)
{
- do_cleanups (old_chain);
retval = allocate_value (type);
mark_value_bytes_unavailable (retval, 0, TYPE_LENGTH (type));
return retval;
}
+ else if (ex.error == NOT_FOUND_ERROR)
+ {
+ if (info_verbose)
+ exception_print (gdb_stdout, ex);
+
+ return allocate_optimized_out_value (type);
+ }
else
throw_exception (ex);
}
@@ -1351,6 +1588,17 @@ needs_frame_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset)
ctx->funcs->get_frame_pc, ctx->baton);
}
+/* DW_OP_GNU_entry_value accesses require a caller, therefore a frame. */
+
+static void
+needs_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg,
+ CORE_ADDR fb_offset, CORE_ADDR deref_size)
+{
+ struct needs_frame_baton *nf_baton = ctx->baton;
+
+ nf_baton->needs_frame = 1;
+}
+
/* Virtual method table for dwarf2_loc_desc_needs_frame below. */
static const struct dwarf_expr_context_funcs needs_frame_ctx_funcs =
@@ -1362,7 +1610,8 @@ static const struct dwarf_expr_context_funcs needs_frame_ctx_funcs =
needs_frame_frame_cfa, /* get_frame_pc */
needs_frame_tls_address,
needs_frame_dwarf_call,
- NULL /* get_base_type */
+ NULL, /* get_base_type */
+ needs_dwarf_reg_entry_value
};
/* Return non-zero iff the location expression at DATA (length SIZE)
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -400,6 +400,9 @@ struct dwarf2_cu
after all type information has been read. */
VEC (delayed_method_info) *method_list;
+ /* To be copied to symtab->call_site_htab. */
+ htab_t call_site_htab;
+
/* Mark used when releasing cached dies. */
unsigned int mark : 1;
@@ -1068,6 +1071,8 @@ static void read_func_scope (struct die_info *, struct dwarf2_cu *);
static void read_lexical_block_scope (struct die_info *, struct dwarf2_cu *);
+static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
+
static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
struct dwarf2_cu *, struct partial_symtab *);
@@ -4757,6 +4762,8 @@ process_full_comp_unit (struct dwarf2_per_cu_data *per_cu)
if (gcc_4_minor >= 5)
symtab->epilogue_unwind_valid = 1;
+
+ symtab->call_site_htab = cu->call_site_htab;
}
if (dwarf2_per_objfile->using_index)
@@ -4795,6 +4802,9 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
case DW_TAG_catch_block:
read_lexical_block_scope (die, cu);
break;
+ case DW_TAG_GNU_call_site:
+ read_call_site_scope (die, cu);
+ break;
case DW_TAG_class_type:
case DW_TAG_interface_type:
case DW_TAG_structure_type:
@@ -6074,6 +6084,215 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu)
using_directives = new->using_directives;
}
+/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack),
+ including a copy of the BLK DWARF code. */
+
+static struct dwarf2_locexpr_baton *
+dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu)
+{
+ struct objfile *objfile = cu->objfile;
+ struct dwarf2_locexpr_baton *dlbaton;
+
+ dlbaton = obstack_alloc (&objfile->objfile_obstack, sizeof (*dlbaton));
+ dlbaton->data = obstack_copy (&objfile->objfile_obstack, blk->data,
+ blk->size);
+ dlbaton->size = blk->size;
+ dlbaton->per_cu = cu->per_cu;
+
+ return dlbaton;
+}
+
+/* Read in DW_TAG_GNU_call_site and insert it to CU->call_site_htab. */
+
+static void
+read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu)
+{
+ struct objfile *objfile = cu->objfile;
+ struct gdbarch *gdbarch = get_objfile_arch (objfile);
+ CORE_ADDR pc, baseaddr;
+ struct attribute *attr;
+ struct call_site *call_site, call_site_local;
+ void **slot;
+ int nparams;
+ struct die_info *child_die;
+
+ baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
+
+ attr = dwarf2_attr (die, DW_AT_low_pc, cu);
+ if (!attr)
+ {
+ complaint (&symfile_complaints,
+ _("missing DW_AT_low_pc for DW_TAG_GNU_call_site "
+ "DIE 0x%x [in module %s]"),
+ die->offset, cu->objfile->name);
+ return;
+ }
+ pc = DW_ADDR (attr) + baseaddr;
+
+ if (cu->call_site_htab == NULL)
+ cu->call_site_htab = htab_create_alloc_ex (16, core_addr_hash, core_addr_eq,
+ NULL, &objfile->objfile_obstack,
+ hashtab_obstack_allocate, NULL);
+ call_site_local.pc = pc;
+ slot = htab_find_slot (cu->call_site_htab, &call_site_local, INSERT);
+ if (*slot != NULL)
+ {
+ complaint (&symfile_complaints,
+ _("Duplicate PC %s for DW_TAG_GNU_call_site "
+ "DIE 0x%x [in module %s]"),
+ paddress (gdbarch, pc), die->offset, cu->objfile->name);
+ return;
+ }
+
+ /* Count parameters at the caller. */
+
+ nparams = 0;
+ for (child_die = die->child; child_die && child_die->tag;
+ child_die = sibling_die (child_die))
+ {
+ if (child_die->tag != DW_TAG_GNU_call_site_parameter)
+ {
+ complaint (&symfile_complaints,
+ _("Tag %d is not DW_TAG_GNU_call_site_parameter in "
+ "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+ child_die->tag, child_die->offset, cu->objfile->name);
+ continue;
+ }
+
+ nparams++;
+ }
+
+ call_site = obstack_alloc (&objfile->objfile_obstack,
+ (sizeof (*call_site)
+ + (sizeof (*call_site->parameter)
+ * (nparams - 1))));
+ *slot = call_site;
+ memset (call_site, 0, sizeof (*call_site) - sizeof (*call_site->parameter));
+ call_site->pc = pc;
+
+ attr = dwarf2_attr (die, DW_AT_GNU_call_site_target, cu);
+ if (attr == NULL)
+ attr = dwarf2_attr (die, DW_AT_abstract_origin, cu);
+ SET_FIELD_DWARF_BLOCK (call_site->target, NULL);
+ if (!attr || (attr_form_is_block (attr) && DW_BLOCK (attr)->size == 0))
+ /* Keep NULL DWARF_BLOCK. */;
+ else if (attr_form_is_block (attr))
+ SET_FIELD_DWARF_BLOCK (call_site->target,
+ dlbaton_obstack_copy (DW_BLOCK (attr), cu));
+ else if (is_ref_attr (attr))
+ {
+ struct objfile *objfile = cu->objfile;
+ struct dwarf2_cu *target_cu = cu;
+ struct die_info *target_die;
+
+ target_die = follow_die_ref_or_sig (die, attr, &target_cu);
+ gdb_assert (target_cu->objfile == objfile);
+ if (die_is_declaration (target_die, target_cu))
+ {
+ const char *target_physname;
+
+ target_physname = dwarf2_physname (NULL, target_die, target_cu);
+ if (target_physname == NULL)
+ complaint (&symfile_complaints,
+ _("DW_AT_GNU_call_site_target target DIE has invalid "
+ "physname, for referencing DIE 0x%x [in module %s]"),
+ die->offset, cu->objfile->name);
+ else
+ SET_FIELD_PHYSNAME (call_site->target, (char *) target_physname);
+ }
+ else
+ {
+ CORE_ADDR lowpc;
+
+ /* DW_AT_entry_pc should be preferred. */
+ if (!dwarf2_get_pc_bounds (target_die, &lowpc, NULL, target_cu, NULL))
+ complaint (&symfile_complaints,
+ _("DW_AT_GNU_call_site_target target DIE has invalid "
+ "low pc, for referencing DIE 0x%x [in module %s]"),
+ die->offset, cu->objfile->name);
+ else
+ SET_FIELD_PHYSADDR (call_site->target, lowpc + baseaddr);
+ }
+ }
+ else
+ complaint (&symfile_complaints,
+ _("DW_TAG_GNU_call_site DW_AT_GNU_call_site_target is neither "
+ "block nor reference, for DIE 0x%x [in module %s]"),
+ die->offset, cu->objfile->name);
+
+ for (child_die = die->child;
+ child_die && child_die->tag;
+ child_die = sibling_die (child_die))
+ {
+ struct dwarf2_locexpr_baton *dlbaton;
+ struct call_site_parameter *parameter;
+
+ if (child_die->tag != DW_TAG_GNU_call_site_parameter)
+ {
+ /* Already printed the complaint above. */
+ continue;
+ }
+
+ gdb_assert (call_site->parameter_count < nparams);
+ parameter = &call_site->parameter[call_site->parameter_count];
+
+ /* DW_AT_location specifies the register number. Value of the data
+ assumed for the register is contained in DW_AT_GNU_call_site_value. */
+
+ attr = dwarf2_attr (child_die, DW_AT_location, cu);
+ if (!attr || !attr_form_is_block (attr))
+ {
+ complaint (&symfile_complaints,
+ _("No DW_FORM_block* DW_AT_location for "
+ "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+ child_die->offset, cu->objfile->name);
+ continue;
+ }
+ parameter->dwarf_reg = dwarf_block_to_dwarf_reg (DW_BLOCK (attr)->data,
+ &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size]);
+ if (parameter->dwarf_reg == -1
+ && !dwarf_block_to_sp_offset (gdbarch, DW_BLOCK (attr)->data,
+ &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size],
+ ¶meter->fb_offset))
+ {
+ complaint (&symfile_complaints,
+ _("Only single DW_OP_reg is supported for DW_FORM_block* "
+ "DW_AT_location for DW_TAG_GNU_call_site "
+ "child DIE 0x%x [in module %s]"),
+ child_die->offset, cu->objfile->name);
+ continue;
+ }
+
+ attr = dwarf2_attr (child_die, DW_AT_GNU_call_site_value, cu);
+ if (!attr_form_is_block (attr))
+ {
+ complaint (&symfile_complaints,
+ _("No DW_FORM_block* DW_AT_GNU_call_site_value for "
+ "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+ child_die->offset, cu->objfile->name);
+ continue;
+ }
+ parameter->call_site_value = dlbaton_obstack_copy (DW_BLOCK (attr), cu);
+
+ /* Parameters are not pre-cleared by memset above. */
+ parameter->call_site_data_value = NULL;
+ call_site->parameter_count++;
+
+ attr = dwarf2_attr (child_die, DW_AT_GNU_call_site_data_value, cu);
+ if (attr)
+ {
+ if (!attr_form_is_block (attr))
+ complaint (&symfile_complaints,
+ _("No DW_FORM_block* DW_AT_GNU_call_site_data_value for "
+ "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+ child_die->offset, cu->objfile->name);
+ else
+ parameter->call_site_data_value
+ = dlbaton_obstack_copy (DW_BLOCK (attr), cu);
+ }
+ }
+}
+
/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
Return 1 if the attributes are present and valid, otherwise, return 0.
If RANGES_PST is not NULL we should setup `objfile->psymtabs_addrmap'. */
@@ -6273,7 +6492,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
return 0;
*lowpc = low;
- *highpc = high;
+ if (highpc)
+ *highpc = high;
return ret;
}
@@ -12624,6 +12844,8 @@ dwarf_tag_name (unsigned tag)
return "DW_TAG_PGI_kanji_type";
case DW_TAG_PGI_interface_block:
return "DW_TAG_PGI_interface_block";
+ case DW_TAG_GNU_call_site:
+ return "DW_TAG_GNU_call_site";
default:
return "DW_TAG_<unknown>";
}
--- a/gdb/gdb-gdb.py
+++ b/gdb/gdb-gdb.py
@@ -158,6 +158,8 @@ class StructMainTypePrettyPrinter:
return 'physaddr = 0x%x' % loc_val['physaddr']
elif loc_kind == "FIELD_LOC_KIND_PHYSNAME":
return 'physname = %s' % loc_val['physname']
+ elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
+ return 'dwarf_block = %s' % loc_val['dwarf_block']
else:
return 'loc = ??? (unsupported loc_kind value)'
def struct_field_img(self, fieldno):
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -348,7 +348,8 @@ enum field_loc_kind
{
FIELD_LOC_KIND_BITPOS, /* bitpos */
FIELD_LOC_KIND_PHYSADDR, /* physaddr */
- FIELD_LOC_KIND_PHYSNAME /* physname */
+ FIELD_LOC_KIND_PHYSNAME, /* physname */
+ FIELD_LOC_KIND_DWARF_BLOCK /* dwarf_block */
};
/* A discriminant to determine which field in the main_type.type_specific
@@ -510,6 +511,12 @@ struct main_type
CORE_ADDR physaddr;
const char *physname;
+
+ /* The field location can be computed by evaluating the following DWARF
+ block. Its DATA is allocated on objfile_obstack - no CU load is
+ needed to access it. */
+
+ struct dwarf2_locexpr_baton *dwarf_block;
}
loc;
@@ -897,6 +904,53 @@ struct func_type
unsigned calling_convention;
};
+/* A place where some function gets called from, represented by
+ DW_TAG_GNU_call_site. It can be looked up from symtab->call_site_htab. */
+
+struct call_site
+ {
+ /* Address of the first instruction after this call. It must be the first
+ field as we overload core_addr_hash and core_addr_eq for it. */
+ CORE_ADDR pc;
+
+ /* Describe DW_AT_GNU_call_site_target. Missing attribute uses
+ FIELD_LOC_KIND_DWARF_BLOCK with FIELD_DWARF_BLOCK == NULL. */
+ struct
+ {
+ union field_location loc;
+
+ /* Discriminant for union field_location. */
+ ENUM_BITFIELD(field_loc_kind) loc_kind : 2;
+ }
+ target;
+
+ /* Size of the PARAMETER array. */
+ unsigned parameter_count;
+
+ /* Describe DW_TAG_GNU_call_site's DW_TAG_formal_parameter. */
+ struct call_site_parameter
+ {
+ /* DW_TAG_formal_parameter's DW_AT_location's DW_OP_regX as DWARF
+ register number, for register passed parameters. If -1 then use
+ fb_offset. */
+ int dwarf_reg;
+
+ /* Offset from the callee's frame base, for stack passed parameters.
+ This equals offset from the caller's stack pointer. Valid only if
+ DWARF_REGNUM is -1. */
+ CORE_ADDR fb_offset;
+
+ /* DW_TAG_formal_parameter's DW_AT_GNU_call_site_value. It is never
+ NULL. */
+ struct dwarf2_locexpr_baton *call_site_value;
+
+ /* DW_TAG_formal_parameter's DW_AT_GNU_call_site_data_value. It may be
+ NULL if not provided by DWARF. */
+ struct dwarf2_locexpr_baton *call_site_data_value;
+ }
+ parameter[1];
+ };
+
/* The default value of TYPE_CPLUS_SPECIFIC(T) points to the
this shared static structure. */
@@ -1019,6 +1073,7 @@ extern void allocate_gnat_aux_type (struct type *);
#define FIELD_BITPOS(thisfld) ((thisfld).loc.bitpos)
#define FIELD_STATIC_PHYSNAME(thisfld) ((thisfld).loc.physname)
#define FIELD_STATIC_PHYSADDR(thisfld) ((thisfld).loc.physaddr)
+#define FIELD_DWARF_BLOCK(thisfld) ((thisfld).loc.dwarf_block)
#define SET_FIELD_BITPOS(thisfld, bitpos) \
(FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_BITPOS, \
FIELD_BITPOS (thisfld) = (bitpos))
@@ -1028,6 +1083,9 @@ extern void allocate_gnat_aux_type (struct type *);
#define SET_FIELD_PHYSADDR(thisfld, addr) \
(FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_PHYSADDR, \
FIELD_STATIC_PHYSADDR (thisfld) = (addr))
+#define SET_FIELD_DWARF_BLOCK(thisfld, addr) \
+ (FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_DWARF_BLOCK, \
+ FIELD_DWARF_BLOCK (thisfld) = (addr))
#define FIELD_ARTIFICIAL(thisfld) ((thisfld).artificial)
#define FIELD_BITSIZE(thisfld) ((thisfld).bitsize)
@@ -1038,6 +1096,7 @@ extern void allocate_gnat_aux_type (struct type *);
#define TYPE_FIELD_BITPOS(thistype, n) FIELD_BITPOS (TYPE_FIELD (thistype, n))
#define TYPE_FIELD_STATIC_PHYSNAME(thistype, n) FIELD_STATIC_PHYSNAME (TYPE_FIELD (thistype, n))
#define TYPE_FIELD_STATIC_PHYSADDR(thistype, n) FIELD_STATIC_PHYSADDR (TYPE_FIELD (thistype, n))
+#define TYPE_FIELD_DWARF_BLOCK(thistype, n) FIELD_DWARF_BLOCK (TYPE_FIELD (thistype, n))
#define TYPE_FIELD_ARTIFICIAL(thistype, n) FIELD_ARTIFICIAL(TYPE_FIELD(thistype,n))
#define TYPE_FIELD_BITSIZE(thistype, n) FIELD_BITSIZE(TYPE_FIELD(thistype,n))
#define TYPE_FIELD_PACKED(thistype, n) (FIELD_BITSIZE(TYPE_FIELD(thistype,n))!=0)
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -831,6 +831,9 @@ struct symtab
struct objfile *objfile;
+ /* struct call_site entries for this compilation unit or NULL. */
+
+ htab_t call_site_htab;
};
#define BLOCKVECTOR(symtab) (symtab)->blockvector
--- a/gdb/testsuite/gdb.arch/Makefile.in
+++ b/gdb/testsuite/gdb.arch/Makefile.in
@@ -1,8 +1,8 @@
VPATH = @srcdir@
srcdir = @srcdir@
-EXECUTABLES = altivec-abi altivec-regs amd64-byte amd64-disp-step \
- amd64-dword amd64-i386-address amd64-word i386-bp_permanent \
+EXECUTABLES = altivec-abi altivec-regs amd64-byte amd64-disp-step amd64-dword \
+ amd64-entry-value amd64-i386-address amd64-word i386-bp_permanent \
i386-permbkpt i386-avx i386-signal i386-sse
all info install-info dvi install uninstall installcheck check:
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc
@@ -0,0 +1,41 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2011 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+static volatile int v;
+
+static void __attribute__((noinline, noclone))
+e (int i)
+{
+ v = 0;
+}
+
+static void __attribute__((noinline, noclone))
+d (int i)
+{
+ i++;
+ e (i);
+ e (v);
+asm ("breakhere:");
+ e (v);
+}
+
+int
+main ()
+{
+ d (30);
+ return 0;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp
@@ -0,0 +1,49 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set testfile amd64-entry-value
+set srcfile ${testfile}.s
+set opts {}
+
+if [info exists COMPILE] {
+ # make check RUNTESTFLAGS="gdb.arch/amd64-entry-value.exp COMPILE=1"
+ set srcfile ${testfile}.cc
+ lappend opts debug optimize=-O2
+} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+ verbose "Skipping amd64-entry-value."
+ return
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} $opts] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_breakpoint "breakhere"
+
+
+# Test @entry values for register passed parameters.
+
+gdb_continue_to_breakpoint "entry: breakhere"
+
+# (gdb) bt full
+# #0 d (i=31) at gdb.arch/amd64-entry-value.cc:33
+# #1 0x00000000004003da in main () at gdb.arch/amd64-entry-value.cc:56
+gdb_test "bt full" "^bt full\r\n#0 +d *\\(i=31\\) \[^\r\n\]*\r\nNo locals\\.\r\n#1 +0x\[0-9a-f\]+ in main .*" \
+ "entry: bt full"
+gdb_test "p i" " = 31" "entry: p i"
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -403,6 +403,24 @@ make_cleanup_unpush_target (struct target_ops *ops)
return make_my_cleanup (&cleanup_chain, do_unpush_target, ops);
}
+/* Helper for make_cleanup_htab_delete compile time checking the types. */
+
+static void
+do_htab_delete_cleanup (void *htab_voidp)
+{
+ htab_t htab = htab_voidp;
+
+ htab_delete (htab);
+}
+
+/* Return a new cleanup that deletes HTAB. */
+
+struct cleanup *
+make_cleanup_htab_delete (htab_t htab)
+{
+ return make_cleanup (do_htab_delete_cleanup, htab);
+}
+
struct restore_ui_file_closure
{
struct ui_file **variable;
@@ -3051,6 +3069,27 @@ print_core_address (struct gdbarch *gdbarch, CORE_ADDR address)
return hex_string_custom (address, 16);
}
+/* Callback hash_f for htab_create_alloc or htab_create_alloc_ex. */
+
+hashval_t
+core_addr_hash (const void *ap)
+{
+ const CORE_ADDR *addrp = ap;
+
+ return *addrp;
+}
+
+/* Callback eq_f for htab_create_alloc or htab_create_alloc_ex. */
+
+int
+core_addr_eq (const void *ap, const void *bp)
+{
+ const CORE_ADDR *addr_ap = ap;
+ const CORE_ADDR *addr_bp = bp;
+
+ return *addr_ap == *addr_bp;
+}
+
static char *
decimal2str (char *sign, ULONGEST addr, int width)
{