From 688f7fcdb395cefef6a098b5ac9ddd167bb5fc3d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 8 May 2010 03:22:59 -0700 Subject: [PATCH] Fix DW_OP_call_ref operand handling. --- libdw/ChangeLog | 9 ++++++ libdw/dwarf_frame_cfa.c | 4 +-- libdw/dwarf_frame_register.c | 2 +- libdw/dwarf_getlocation.c | 17 +++++++++--- libdw/libdwP.h | 3 +- src/ChangeLog | 9 ++++++ src/readelf.c | 66 +++++++++++++++++++++++++++++--------------- 7 files changed, 80 insertions(+), 30 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index a6e8457..66e774a 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,12 @@ +2010-05-08 Roland McGrath + + * dwarf_getlocation.c (__libdw_intern_expression): Take new argument + REF_SIZE. Use that to handle DW_OP_call_ref correctly. + (getlocation): Update caller. + * dwarf_frame_cfa.c (dwarf_frame_cfa): Likewise. + * dwarf_frame_register.c (dwarf_frame_register): Likewise. + * libdwP.h: Update decl. + 2010-04-26 Roland McGrath * cfi.h (struct Dwarf_Frame_s): Add cfa_invalid alternative in cfa_rule. diff --git a/libdw/dwarf_frame_cfa.c b/libdw/dwarf_frame_cfa.c index 0ba26b2..2f3268a 100644 --- a/libdw/dwarf_frame_cfa.c +++ b/libdw/dwarf_frame_cfa.c @@ -1,5 +1,5 @@ /* Get CFA expression for frame. - Copyright (C) 2009 Red Hat, Inc. + Copyright (C) 2009-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -83,7 +83,7 @@ dwarf_frame_cfa (fs, ops, nops) /* Parse the expression into internal form. */ result = __libdw_intern_expression (NULL, fs->cache->other_byte_order, - fs->cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8, + fs->cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8, 4, &fs->cache->expr_tree, &fs->cfa_data.expr, false, false, ops, nops, IDX_debug_frame); break; diff --git a/libdw/dwarf_frame_register.c b/libdw/dwarf_frame_register.c index e42b76b..ae0db02 100644 --- a/libdw/dwarf_frame_register.c +++ b/libdw/dwarf_frame_register.c @@ -129,7 +129,7 @@ dwarf_frame_register (fs, regno, ops_mem, ops, nops) /* Parse the expression into internal form. */ if (__libdw_intern_expression (NULL, fs->cache->other_byte_order, - address_size, + address_size, 4, &fs->cache->expr_tree, &block, true, reg->rule == reg_val_expression, ops, nops, IDX_debug_frame) < 0) diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index f362fe2..ede8c3c 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -219,8 +219,8 @@ check_constant_offset (Dwarf_Attribute *attr, int internal_function -__libdw_intern_expression (Dwarf *dbg, - bool other_byte_order, unsigned int address_size, +__libdw_intern_expression (Dwarf *dbg, bool other_byte_order, + unsigned int address_size, unsigned int ref_size, void **cache, const Dwarf_Block *block, bool cfap, bool valuep, Dwarf_Op **llbuf, size_t *listlen, int sec_index) @@ -272,6 +272,13 @@ __libdw_intern_expression (Dwarf *dbg, return -1; break; + case DW_OP_call_ref: + /* DW_FORM_ref_addr, depends on offset size of CU. */ + if (__libdw_read_offset_inc (dbg, sec_index, &data, ref_size, + &newloc->number, IDX_debug_info, 0)) + return -1; + break; + case DW_OP_deref: case DW_OP_dup: case DW_OP_drop: @@ -303,7 +310,6 @@ __libdw_intern_expression (Dwarf *dbg, case DW_OP_reg0 ... DW_OP_reg31: case DW_OP_nop: case DW_OP_push_object_address: - case DW_OP_call_ref: case DW_OP_call_frame_cfa: case DW_OP_form_tls_address: case DW_OP_GNU_push_tls_address: @@ -521,7 +527,10 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, Dwarf_Op **llbuf, size_t *listlen, int sec_index) { return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order, - cu->address_size, &cu->locs, block, + cu->address_size, (cu->version == 2 + ? cu->address_size + : cu->offset_size), + &cu->locs, block, false, false, llbuf, listlen, sec_index); } diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 44beac6..b3402d8 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -432,11 +432,12 @@ extern int __libdw_visit_scopes (unsigned int depth, extern int __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, unsigned int address_size, + unsigned int ref_size, void **cache, const Dwarf_Block *block, bool cfap, bool valuep, Dwarf_Op **llbuf, size_t *listlen, int sec_index) - __nonnull_attribute__ (4, 5, 8, 9) internal_function; + __nonnull_attribute__ (5, 6, 9, 10) internal_function; /* Return error code of last failing function call. This value is kept diff --git a/src/ChangeLog b/src/ChangeLog index 5341542..0a81407 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2010-05-08 Roland McGrath + + * readelf.c (print_ops): Take new arg OFFSET_SIZE. + Use that for DW_OP_call_ref, not ADDRSIZE. + (print_cfa_program): Update caller. + (struct attrcb_args): Add offset_size field. + (attr_callback): Use it for print_ops call. + (print_debug_info_section): Initialize it. + 2010-04-14 Roland McGrath * readelf.c (handle_core_item): Fix bitmask printing. diff --git a/src/readelf.c b/src/readelf.c index 5255860..243337a 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -3774,7 +3774,8 @@ print_block (size_t n, const void *block) static void print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, - unsigned int addrsize, Dwarf_Word len, const unsigned char *data) + unsigned int addrsize, unsigned int offset_size, + Dwarf_Word len, const unsigned char *data) { static const char *const known[] = { @@ -3949,7 +3950,6 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, switch (op) { - case DW_OP_call_ref: case DW_OP_addr:; /* Address operand. */ Dwarf_Word addr; @@ -3964,18 +3964,31 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, data += addrsize; len -= addrsize; - if (op == DW_OP_addr) + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, known[op], a); + free (a); + + offset += 1 + addrsize; + break; + + case DW_OP_call_ref: + /* Offset operand. */ + NEED (offset_size); + if (offset_size == 4) + addr = read_4ubyte_unaligned (dbg, data); + else { - char *a = format_dwarf_addr (dwflmod, 0, addr); - printf ("%*s[%4" PRIuMAX "] %s %s\n", - indent, "", (uintmax_t) offset, known[op], a); - free (a); + assert (offset_size == 8); + addr = read_8ubyte_unaligned (dbg, data); } - else - printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", - indent, "", (uintmax_t) offset, - known[op], (uintmax_t) addr); - offset += 1 + addrsize; + data += offset_size; + len -= offset_size; + + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op], (uintmax_t) addr); + offset += 1 + offset_size; break; case DW_OP_deref_size: @@ -4520,7 +4533,7 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, // XXX overflow check get_uleb128 (op1, readp); /* Length of DW_FORM_block. */ printf (" def_cfa_expression %" PRIu64 "\n", op1); - print_ops (dwflmod, dbg, 10, 10, ptr_size, op1, readp); + print_ops (dwflmod, dbg, 10, 10, ptr_size, 0, op1, readp); readp += op1; break; case DW_CFA_expression: @@ -4529,7 +4542,7 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ printf (" expression r%" PRIu64 " (%s) \n", op1, regname (op1)); - print_ops (dwflmod, dbg, 10, 10, ptr_size, op2, readp); + print_ops (dwflmod, dbg, 10, 10, ptr_size, 0, op2, readp); readp += op2; break; case DW_CFA_offset_extended_sf: @@ -4572,7 +4585,7 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ printf (" val_expression r%" PRIu64 " (%s)\n", op1, regname (op1)); - print_ops (dwflmod, dbg, 10, 10, ptr_size, op2, readp); + print_ops (dwflmod, dbg, 10, 10, ptr_size, 0, op2, readp); readp += op2; break; case DW_CFA_MIPS_advance_loc8: @@ -5098,6 +5111,7 @@ struct attrcb_args Dwarf *dbg; int level; unsigned int addrsize; + unsigned int offset_size; Dwarf_Off cu_offset; }; @@ -5301,7 +5315,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_upper_bound: print_ops (cbargs->dwflmod, cbargs->dbg, 12 + level * 2, 12 + level * 2, - cbargs->addrsize, block.length, block.data); + cbargs->addrsize, cbargs->offset_size, + block.length, block.data); break; default: @@ -5359,11 +5374,14 @@ print_debug_info_section (Dwfl_Module *dwflmod, (uint64_t) offset, /*version*/2, abbroffset, addrsize, offsize); - struct attrcb_args args; - args.dwflmod = dwflmod; - args.dbg = dbg; - args.addrsize = addrsize; - args.cu_offset = offset; + struct attrcb_args args = + { + .dwflmod = dwflmod, + .dbg = dbg, + .addrsize = addrsize, + .offset_size = offsize, + .cu_offset = offset + }; offset += cuhl; @@ -5900,6 +5918,10 @@ print_debug_loc_section (Dwfl_Module *dwflmod, size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + /* XXX This is wrong! We can only know the right size given the CU that + points to this location list. */ + size_t offset_size = 4; + bool first = true; unsigned char *readp = data->d_buf; while (readp < (unsigned char *) data->d_buf + data->d_size) @@ -5952,7 +5974,7 @@ print_debug_loc_section (Dwfl_Module *dwflmod, free (e); print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), - address_size, len, readp); + address_size, offset_size, len, readp); first = false; readp += len; -- 2.7.4