This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[patch] Support DW_OP_call2 and DW_OP_call4 (PR 10640)
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Sun, 20 Sep 2009 14:36:47 +0200
- Subject: [patch] Support DW_OP_call2 and DW_OP_call4 (PR 10640)
Hi,
as GCC discusses its use in PR41343 the patch implements it for GDB.
Tried first to follow the #if0-ed prototype get_subr but it was inconvenient
at least because for LOC_STATIC we would need to evaluation-time construct new
artificial DWARF block.
It has some new overhead due to symbol_hash for all symbol DIEs. Did not
measure it but I am not aware much how it could be avoided as GDB does not
parse the DWARF blocks while reading them in.
No regressions on {x86_64,x86_64-m32,i686}-fedora11-linux-gnu.
Thanks,
Jan
gdb/
2009-09-20 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix PR 10640.
* dwarf2-frame.c (no_get_tls_address): New comment.
(no_dwarf_call): New function.
(execute_stack_op): Set CTX->DWARF_CALL.
* dwarf2expr.c (execute_stack_op <DW_OP_call2>)
(execute_stack_op <DW_OP_call4>): New.
* dwarf2expr.h (struct dwarf_expr_context <dwarf_call>): New.
(struct dwarf_expr_context <get_subr>): Remove the #if0-ed field.
* dwarf2loc.c (struct dwarf_expr_baton <objfile>): Remove.
(struct dwarf_expr_baton <per_cu>): New.
(dwarf_expr_tls_address): Use PER_CU instead of OBJFILE now.
(per_cu_dwarf_call, dwarf_expr_dwarf_call): New functions.
(dwarf2_evaluate_loc_desc): Drop initialization of BATON.OBJFILE.
Initialize new BATON.PER_CU. Adjust CTX->GDBARCH initialization for
this change. Initialize CTX->DWARF_CALL.
(struct needs_frame_baton <per_cu>): New field.
(needs_frame_dwarf_call, locexpr_dwarf_call): New function.
(dwarf2_loc_desc_needs_frame): Initialize new BATON.PER_CU. Initialize
CTX->DWARF_CALL.
(dwarf2_locexpr_funcs): Include locexpr_dwarf_call.
* dwarf2loc.h (dwarf2_per_cu_get_die_symbol): New declaration.
* dwarf2read.c (struct dwarf2_per_cu_data <symbol_hash>): New.
(set_die_symbol): New function.
(new_symbol): Call set_die_symbol.
(struct dwarf2_offset_and_type): Move it above set_die_type.
(offset_and_type_hash): Rename to ...
(offset_hash): ... and make it reference only the offset.
(offset_and_type_eq): Rename to ...
(offset_eq): ... and make it reference only the offset.
(set_die_type): Reference the new function names.
(struct dwarf2_offset_and_symbol, set_die_symbol)
(dwarf2_per_cu_get_die_symbol): New.
* symtab.h (struct dwarf_expr_context): New opaque declaration.
(struct symbol_computed_ops <dwarf_call>): New.
gdb/testsuite/
2009-09-20 Jan Kratochvil <jan.kratochvil@redhat.com>
Test PR 10640.
* gdb.dwarf2/dw2-op-call.exp, gdb.dwarf2/dw2-op-call.S: New.
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -318,6 +318,8 @@ no_get_frame_cfa (void *baton)
_("Support for DW_OP_call_frame_cfa is unimplemented"));
}
+/* Helper function for execute_stack_op. */
+
static CORE_ADDR
no_get_tls_address (void *baton, CORE_ADDR offset)
{
@@ -325,6 +327,15 @@ no_get_tls_address (void *baton, CORE_ADDR offset)
_("Support for DW_OP_GNU_push_tls_address is unimplemented"));
}
+/* Helper function for execute_stack_op. */
+
+static void
+no_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset)
+{
+ internal_error (__FILE__, __LINE__,
+ _("Support for DW_OP_call* is invalid in CFI"));
+}
+
/* Execute the required actions for both the DW_CFA_restore and
DW_CFA_restore_extended instructions. */
static void
@@ -375,6 +386,7 @@ execute_stack_op (gdb_byte *exp, ULONGEST len, int addr_size,
ctx->get_frame_base = no_get_frame_base;
ctx->get_frame_cfa = no_get_frame_cfa;
ctx->get_tls_address = no_get_tls_address;
+ ctx->dwarf_call = no_dwarf_call;
dwarf_expr_push (ctx, initial, initial_in_stack_memory);
dwarf_expr_eval (ctx, exp, len);
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -848,6 +848,18 @@ execute_stack_op (struct dwarf_expr_context *ctx,
ctx->initialized = 0;
goto no_push;
+ case DW_OP_call2:
+ result = extract_unsigned_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
+ ctx->dwarf_call (ctx, result);
+ goto no_push;
+
+ case DW_OP_call4:
+ result = extract_unsigned_integer (op_ptr, 4, byte_order);
+ op_ptr += 4;
+ ctx->dwarf_call (ctx, result);
+ goto no_push;
+
default:
error (_("Unhandled dwarf expression opcode 0x%x"), op);
}
--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -94,15 +94,14 @@ struct dwarf_expr_context
DW_OP_GNU_push_tls_address. */
CORE_ADDR (*get_tls_address) (void *baton, CORE_ADDR offset);
+ /* Execute DW_AT_location expression for the DWARF expression subroutine in
+ the die at DIE_OFFSET in the CU from CTX. Do not touch STACK while it
+ being passed to and returned from the called DWARF subroutine. */
+ void (*dwarf_call) (struct dwarf_expr_context *ctx, size_t die_offset);
+
#if 0
/* Not yet implemented. */
- /* Return the location expression for the dwarf expression
- subroutine in the die at OFFSET in the current compilation unit.
- The result must be live until the current expression evaluation
- is complete. */
- unsigned char *(*get_subr) (void *baton, off_t offset, size_t *length);
-
/* Return the `object address' for DW_OP_push_object_address. */
CORE_ADDR (*get_object_address) (void *baton);
#endif
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -115,7 +115,7 @@ find_location_expression (struct dwarf2_loclist_baton *baton,
struct dwarf_expr_baton
{
struct frame_info *frame;
- struct objfile *objfile;
+ struct dwarf2_per_cu_data *per_cu;
};
/* Helper functions for dwarf2_evaluate_loc_desc. */
@@ -211,8 +211,51 @@ static CORE_ADDR
dwarf_expr_tls_address (void *baton, CORE_ADDR offset)
{
struct dwarf_expr_baton *debaton = (struct dwarf_expr_baton *) baton;
+ struct objfile *objfile = dwarf2_per_cu_objfile (debaton->per_cu);
- return target_translate_tls_address (debaton->objfile, offset);
+ return target_translate_tls_address (objfile, offset);
+}
+
+/* Call DWARF subroutine from DW_AT_location of DIE at DIE_OFFSET in current CU
+ (as is PER_CU). Pass to it and return from it CTX.
+
+ We ignore any failures as DWARF says "If there is no such attribute, then
+ there is no effect." and it would be difficult to distinguish valid and
+ invalid DWARF cases after the DWARF symbol has been read in. */
+
+static void
+per_cu_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset,
+ struct dwarf2_per_cu_data *per_cu)
+{
+ struct symbol *sym = dwarf2_per_cu_get_die_symbol (die_offset, per_cu);
+
+ if (sym == NULL)
+ return;
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_COMPUTED:
+ if (SYMBOL_COMPUTED_OPS (sym)->dwarf_call == NULL)
+ error (_("Symbol class of \"%s\" does not support DW_OP_call*"),
+ SYMBOL_PRINT_NAME (sym));
+
+ SYMBOL_COMPUTED_OPS (sym)->dwarf_call (sym, ctx);
+ break;
+
+ case LOC_STATIC:
+ dwarf_expr_push (ctx, SYMBOL_VALUE_ADDRESS (sym), 0);
+ break;
+ }
+}
+
+/* Helper interface of per_cu_dwarf_call for dwarf2_evaluate_loc_desc. */
+
+static void
+dwarf_expr_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset)
+{
+ struct dwarf_expr_baton *debaton = ctx->baton;
+
+ return per_cu_dwarf_call (ctx, die_offset, debaton->per_cu);
}
struct piece_closure
@@ -395,12 +438,12 @@ dwarf2_evaluate_loc_desc (struct symbol *var, struct frame_info *frame,
}
baton.frame = frame;
- baton.objfile = dwarf2_per_cu_objfile (per_cu);
+ baton.per_cu = per_cu;
ctx = new_dwarf_expr_context ();
old_chain = make_cleanup_free_dwarf_expr_context (ctx);
- ctx->gdbarch = get_objfile_arch (baton.objfile);
+ ctx->gdbarch = get_objfile_arch (dwarf2_per_cu_objfile (per_cu));
ctx->addr_size = dwarf2_per_cu_addr_size (per_cu);
ctx->baton = &baton;
ctx->read_reg = dwarf_expr_read_reg;
@@ -408,6 +451,7 @@ dwarf2_evaluate_loc_desc (struct symbol *var, struct frame_info *frame,
ctx->get_frame_base = dwarf_expr_frame_base;
ctx->get_frame_cfa = dwarf_expr_frame_cfa;
ctx->get_tls_address = dwarf_expr_tls_address;
+ ctx->dwarf_call = dwarf_expr_dwarf_call;
dwarf_expr_eval (ctx, data, size);
if (ctx->num_pieces > 0)
@@ -496,6 +540,7 @@ dwarf2_evaluate_loc_desc (struct symbol *var, struct frame_info *frame,
struct needs_frame_baton
{
int needs_frame;
+ struct dwarf2_per_cu_data *per_cu;
};
/* Reads from registers do require a frame. */
@@ -546,6 +591,16 @@ needs_frame_tls_address (void *baton, CORE_ADDR offset)
return 1;
}
+/* Helper interface of per_cu_dwarf_call for dwarf2_loc_desc_needs_frame. */
+
+static void
+needs_frame_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset)
+{
+ struct needs_frame_baton *nf_baton = ctx->baton;
+
+ return per_cu_dwarf_call (ctx, die_offset, nf_baton->per_cu);
+}
+
/* Return non-zero iff the location expression at DATA (length SIZE)
requires a frame to evaluate. */
@@ -559,6 +614,7 @@ dwarf2_loc_desc_needs_frame (gdb_byte *data, unsigned short size,
struct cleanup *old_chain;
baton.needs_frame = 0;
+ baton.per_cu = per_cu;
ctx = new_dwarf_expr_context ();
old_chain = make_cleanup_free_dwarf_expr_context (ctx);
@@ -571,6 +627,7 @@ dwarf2_loc_desc_needs_frame (gdb_byte *data, unsigned short size,
ctx->get_frame_base = needs_frame_frame_base;
ctx->get_frame_cfa = needs_frame_frame_cfa;
ctx->get_tls_address = needs_frame_tls_address;
+ ctx->dwarf_call = needs_frame_dwarf_call;
dwarf_expr_eval (ctx, data, size);
@@ -758,13 +815,28 @@ locexpr_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch,
dlbaton->data, dlbaton->size);
}
+/* Execute DWARF DW_AT_location block of SYMBOL for STACK of CTX. */
+
+static void
+locexpr_dwarf_call (struct symbol *symbol, struct dwarf_expr_context *ctx)
+{
+ struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);
+ struct objfile *objfile = dwarf2_per_cu_objfile (dlbaton->per_cu);
+
+ gdb_assert (ctx->gdbarch == get_objfile_arch (objfile));
+ gdb_assert (ctx->addr_size == dwarf2_per_cu_addr_size (dlbaton->per_cu));
+
+ dwarf_expr_eval (ctx, dlbaton->data, dlbaton->size);
+}
+
/* The set of location functions used with the DWARF-2 expression
evaluator. */
const struct symbol_computed_ops dwarf2_locexpr_funcs = {
locexpr_read_variable,
locexpr_read_needs_frame,
locexpr_describe_location,
- locexpr_tracepoint_var_ref
+ locexpr_tracepoint_var_ref,
+ locexpr_dwarf_call
};
--- a/gdb/dwarf2loc.h
+++ b/gdb/dwarf2loc.h
@@ -33,6 +33,9 @@ struct objfile *dwarf2_per_cu_objfile (struct dwarf2_per_cu_data *cu);
/* Return the address size given in the compilation unit header for CU. */
CORE_ADDR dwarf2_per_cu_addr_size (struct dwarf2_per_cu_data *cu);
+struct symbol *dwarf2_per_cu_get_die_symbol (size_t die_offset,
+ struct dwarf2_per_cu_data *per_cu);
+
/* The symbol location baton types used by the DWARF-2 reader (i.e.
SYMBOL_LOCATION_BATON for a LOC_COMPUTED symbol). "struct
dwarf2_locexpr_baton" is for a symbol with a single location
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -377,6 +377,9 @@ struct dwarf2_per_cu_data
it. */
htab_t type_hash;
+ /* Like type_hash above but map DIE offsets to `struct symbol's. */
+ htab_t symbol_hash;
+
/* The partial symbol table associated with this compilation unit,
or NULL for partial units (which do not have an associated
symtab). */
@@ -1151,6 +1154,9 @@ static void free_one_cached_comp_unit (void *);
static struct type *set_die_type (struct die_info *, struct type *,
struct dwarf2_cu *);
+static void set_die_symbol (struct die_info *die, struct symbol *symbol,
+ struct dwarf2_cu *cu);
+
static void create_all_comp_units (struct objfile *);
static void load_full_comp_unit (struct dwarf2_per_cu_data *,
@@ -8340,6 +8346,8 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
}
}
+ set_die_symbol (die, sym, cu);
+
switch (die->tag)
{
case DW_TAG_label:
@@ -11634,35 +11642,39 @@ dwarf2_free_objfile (struct objfile *objfile)
/* Everything else should be on the objfile obstack. */
}
-/* A pair of DIE offset and GDB type pointer. We store these
- in a hash table separate from the DIEs, and preserve them
- when the DIEs are flushed out of cache. */
-
-struct dwarf2_offset_and_type
-{
- unsigned int offset;
- struct type *type;
-};
-
-/* Hash function for a dwarf2_offset_and_type. */
+/* Hash function for a structure containing `unsigned int' DIE offset as its
+ very first element. */
static hashval_t
-offset_and_type_hash (const void *item)
+offset_hash (const void *item)
{
- const struct dwarf2_offset_and_type *ofs = item;
- return ofs->offset;
+ const unsigned int *offsetp = item;
+
+ return *offsetp;
}
-/* Equality function for a dwarf2_offset_and_type. */
+/* Equality function for a structure containing `unsigned int' DIE offset as
+ its very first element. */
static int
-offset_and_type_eq (const void *item_lhs, const void *item_rhs)
+offset_eq (const void *item_lhs, const void *item_rhs)
{
- const struct dwarf2_offset_and_type *ofs_lhs = item_lhs;
- const struct dwarf2_offset_and_type *ofs_rhs = item_rhs;
- return ofs_lhs->offset == ofs_rhs->offset;
+ const unsigned int *offsetp_lhs = item_lhs;
+ const unsigned int *offsetp_rhs = item_rhs;
+
+ return *offsetp_lhs == *offsetp_rhs;
}
+/* A pair of DIE offset and GDB type pointer. We store these
+ in a hash table separate from the DIEs, and preserve them
+ when the DIEs are flushed out of cache. */
+
+struct dwarf2_offset_and_type
+{
+ unsigned int offset;
+ struct type *type;
+};
+
/* Set the type associated with DIE to TYPE. Save it in CU's hash
table if necessary. For convenience, return TYPE. */
@@ -11676,8 +11688,8 @@ set_die_type (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
gdb_assert (cu->per_cu != NULL);
cu->per_cu->type_hash
= htab_create_alloc_ex (cu->header.length / 24,
- offset_and_type_hash,
- offset_and_type_eq,
+ offset_hash,
+ offset_eq,
NULL,
&cu->objfile->objfile_obstack,
hashtab_obstack_allocate,
@@ -11694,6 +11706,64 @@ set_die_type (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
return type;
}
+/* A pair of DIE offset and GDB symbol pointer. We store these
+ in a hash table separate from the DIEs, and preserve them
+ when the DIEs are flushed out of cache. */
+
+struct dwarf2_offset_and_symbol
+{
+ unsigned int offset;
+ struct symbol *symbol;
+};
+
+/* Set the symbol associated with DIE to SYMBOL. Save it in PER_CU's hash
+ table. */
+
+static void
+set_die_symbol (struct die_info *die, struct symbol *symbol, struct dwarf2_cu *cu)
+{
+ struct dwarf2_per_cu_data *per_cu = cu->per_cu;
+ struct dwarf2_offset_and_symbol **slot, ofs;
+
+ gdb_assert (per_cu != NULL);
+
+ if (per_cu->symbol_hash == NULL)
+ per_cu->symbol_hash = htab_create_alloc_ex (cu->header.length / 24,
+ offset_hash,
+ offset_eq,
+ NULL,
+ &cu->objfile->objfile_obstack,
+ hashtab_obstack_allocate,
+ dummy_obstack_deallocate);
+
+ ofs.offset = die->offset;
+ ofs.symbol = symbol;
+ slot = (struct dwarf2_offset_and_symbol **)
+ htab_find_slot_with_hash (per_cu->symbol_hash, &ofs, ofs.offset, INSERT);
+ *slot = obstack_alloc (&cu->objfile->objfile_obstack, sizeof (**slot));
+ **slot = ofs;
+}
+
+/* Find the symbol for DIE at DIE_OFFSET in PER_CU's symbol_hash, or return
+ NULL if DIE does not have a saved symbol. */
+
+struct symbol *
+dwarf2_per_cu_get_die_symbol (size_t die_offset, struct dwarf2_per_cu_data *per_cu)
+{
+ struct dwarf2_offset_and_symbol *slot, ofs;
+ htab_t symbol_hash = per_cu->symbol_hash;
+
+ if (symbol_hash == NULL)
+ return NULL;
+
+ ofs.offset = die_offset;
+ slot = htab_find_with_hash (symbol_hash, &ofs, ofs.offset);
+ if (slot)
+ return slot->symbol;
+ else
+ return NULL;
+}
+
/* Find the type for DIE in CU's type_hash, or return NULL if DIE does
not have a saved type. */
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -32,6 +32,7 @@ struct block;
struct blockvector;
struct axs_value;
struct agent_expr;
+struct dwarf_expr_context;
/* Some of the structures in this file are space critical.
The space-critical structures are:
@@ -529,6 +530,11 @@ struct symbol_computed_ops
void (*tracepoint_var_ref) (struct symbol *symbol, struct gdbarch *gdbarch,
struct agent_expr *ax, struct axs_value *value);
+
+ /* Execute DW_AT_location expression for the DWARF expression subroutine in
+ the die at DIE_OFFSET in the CU from CTX. Do not touch STACK while it
+ being passed to and returned from the called DWARF subroutine. */
+ void (*dwarf_call) (struct symbol *symbol, struct dwarf_expr_context *ctx);
};
/* Functions used with LOC_REGISTER and LOC_REGPARM_ADDR. */
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-op-call.S
@@ -0,0 +1,119 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 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/>. */
+
+/* Test DW_OP_call2 and DW_OP_call4, PR gdb/10640. */
+
+ .section .data
+array1: .2byte 1
+array2: .2byte 2
+array3: .2byte 3
+
+ .section .debug_info
+.Lcu1_begin:
+ /* CU header */
+ .4byte .Lcu1_end - .Lcu1_start /* Length of Compilation Unit */
+.Lcu1_start:
+ .2byte 2 /* DWARF Version */
+ .4byte .Labbrev1_begin /* Offset into abbrev section */
+ .byte 4 /* Pointer size */
+
+ /* CU die */
+ .uleb128 1 /* Abbrev: DW_TAG_compile_unit */
+ .ascii "file1.txt\0" /* DW_AT_name */
+ .ascii "GNU C 3.3.3\0" /* DW_AT_producer */
+ .byte 2 /* DW_LANG_C (C) */
+
+.L2byte_type:
+ .uleb128 2 /* Abbrev: DW_TAG_base_type */
+ .ascii "2byte\0" /* DW_AT_name */
+ .byte 2 /* DW_AT_byte_size */
+ .byte 7 /* DW_AT_encoding: DW_ATE_unsigned */
+
+.Larray1:
+ .uleb128 3 /* Abbrev: DW_TAG_variable */
+ .ascii "array1\0" /* DW_AT_name */
+ .4byte .L2byte_type-.Lcu1_begin /* DW_AT_type */
+ .byte 2f - 1f /* DW_AT_location */
+1: .byte 3 /* DW_OP_addr */
+ .4byte array /* <addr> */
+2:
+
+ .uleb128 3 /* Abbrev: DW_TAG_variable */
+ .ascii "array2\0" /* DW_AT_name */
+ .4byte .L2byte_type-.Lcu1_begin /* DW_AT_type */
+ .byte 2f - 1f /* DW_AT_location */
+1: .byte 0x98 /* DW_OP_call2 */
+ .2byte .Larray1-.Lcu1_begin /* <current CU offset> */
+ .byte 0x23 /* DW_OP_plus_uconst */
+ .uleb128 array2-array1 /* <uconst> */
+2:
+
+ .uleb128 3 /* Abbrev: DW_TAG_variable */
+ .ascii "array3\0" /* DW_AT_name */
+ .4byte .L2byte_type-.Lcu1_begin /* DW_AT_type */
+ .byte 2f - 1f /* DW_AT_location */
+1: .byte 0x99 /* DW_OP_call4 */
+ .4byte .Larray1-.Lcu1_begin /* <current CU offset> */
+ .byte 0x23 /* DW_OP_plus_uconst */
+ .uleb128 array3-array1 /* <uconst> */
+2:
+
+ .byte 0 /* End of children of CU */
+
+.Lcu1_end:
+
+/* Abbrev table */
+ .section .debug_abbrev
+.Labbrev1_begin:
+ .uleb128 1 /* Abbrev code */
+ .uleb128 0x11 /* DW_TAG_compile_unit */
+ .byte 1 /* has_children */
+ .uleb128 0x3 /* DW_AT_name */
+ .uleb128 0x8 /* DW_FORM_string */
+ .uleb128 0x25 /* DW_AT_producer */
+ .uleb128 0x8 /* DW_FORM_string */
+ .uleb128 0x13 /* DW_AT_language */
+ .uleb128 0xb /* DW_FORM_data1 */
+ .byte 0x0 /* Terminator */
+ .byte 0x0 /* Terminator */
+
+ .uleb128 2 /* Abbrev code */
+ .uleb128 0x24 /* DW_TAG_base_type */
+ .byte 0 /* has_children */
+ .uleb128 0x3 /* DW_AT_name */
+ .uleb128 0x8 /* DW_FORM_string */
+ .uleb128 0xb /* DW_AT_byte_size */
+ .uleb128 0xb /* DW_FORM_data1 */
+ .uleb128 0x3e /* DW_AT_encoding */
+ .uleb128 0xb /* DW_FORM_data1 */
+ .byte 0x0 /* Terminator */
+ .byte 0x0 /* Terminator */
+
+ .uleb128 3 /* Abbrev code */
+ .uleb128 0x34 /* DW_TAG_variable */
+ .byte 0 /* has_children */
+ .uleb128 0x3 /* DW_AT_name */
+ .uleb128 0x8 /* DW_FORM_string */
+ .uleb128 0x49 /* DW_AT_type */
+ .uleb128 0x13 /* DW_FORM_ref4 */
+ .uleb128 0x2 /* DW_AT_location */
+ .uleb128 0xa /* DW_FORM_block1 */
+ .byte 0x0 /* Terminator */
+ .byte 0x0 /* Terminator */
+
+ .byte 0x0 /* Terminator */
+ .byte 0x0 /* Terminator */
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-op-call.exp
@@ -0,0 +1,41 @@
+# Copyright 2009 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/>.
+
+# Test DW_OP_call2 and DW_OP_call4, PR gdb/10640.
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+# For now pick a sampling of likely targets.
+if {![istarget *-*-linux*]
+ && ![istarget *-*-gnu*]
+ && ![istarget *-*-elf*]
+ && ![istarget *-*-openbsd*]
+ && ![istarget arm-*-eabi*]
+ && ![istarget powerpc-*-eabi*]} {
+ return 0
+}
+
+set testfile "dw2-op-call"
+set srcfile ${testfile}.S
+set executable ${testfile}.x
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${objdir}/${subdir}/${executable}" object {nodebug}] != "" } {
+ return -1
+}
+
+clean_restart $executable
+
+gdb_test "p array1" " = 1"
+gdb_test "p array2" " = 2" "array2 using DW_OP_call2"
+gdb_test "p array3" " = 3" "array3 using DW_OP_call4"