From bb1c3da3c12651f1c408d96dd6d33ae157bdadd6 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sat, 20 Oct 2012 14:42:03 +0400 Subject: Imported Upstream version 20120410 --- libdwarf/dwarf_addr_finder.c | 680 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 680 insertions(+) create mode 100644 libdwarf/dwarf_addr_finder.c (limited to 'libdwarf/dwarf_addr_finder.c') diff --git a/libdwarf/dwarf_addr_finder.c b/libdwarf/dwarf_addr_finder.c new file mode 100644 index 0000000..303ca45 --- /dev/null +++ b/libdwarf/dwarf_addr_finder.c @@ -0,0 +1,680 @@ +/* + + Copyright (C) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + Portions Copyright 2011 David Anderson. All Rights Reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it would be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Further, this software is distributed without any warranty that it is + free of the rightful claim of any third person regarding infringement + or the like. Any license provided herein, whether implied or + otherwise, applies only to this software file. Patent licenses, if + any, provided herein do not apply to combinations of this program with + other software, or any other product whatsoever. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, + USA. + + Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ +/* This code used by SGI-IRIX rqs processing, not needed by + any other system or application. + The 'We need' and 'FIX' here are left in for historical + interest, the code is not really used in the public libdwarf + or applications. +*/ + +#include "config.h" +#include "libdwarfdefs.h" +#ifdef HAVE_ELF_H +#include +#endif +#include +#include +#include "dwarf_base_types.h" +#include "dwarf_alloc.h" +#include "dwarf_opaque.h" +#include "dwarf_arange.h" +#include "dwarf_line.h" +#include "dwarf_frame.h" +#include +#include "dwarf_error.h" + +typedef unsigned long long ull; + +static int do_this_die_and_dealloc(Dwarf_Debug dbg, Dwarf_Die die, + int *errval); +static int handle_debug_info(Dwarf_Debug dbg, int *errval); +static int handle_debug_frame(Dwarf_Debug dbg, + Dwarf_addr_callback_func cb_func, int *errval); +static int handle_debug_aranges(Dwarf_Debug dbg, + Dwarf_addr_callback_func cb_func, int *errval); +static int handle_debug_line(Dwarf_Debug dbg, + Dwarf_Die cu_die, Dwarf_addr_callback_func cb_func, int *errval); +static int handle_debug_loc(void); + + +/* A static variable is not thread-safe, but this is only + used by SGI-internal software which is single-threaded + so the static variable is safe. +*/ +static Dwarf_addr_callback_func send_addr_note; + +/* A function which should probably not be used outside + of SGI. Intended to be used by executable and + shared-library post-processor software. +*/ +int +_dwarf_addr_finder(dwarf_elf_handle elf_file_ptr, + Dwarf_addr_callback_func cb_func, int *dwerr) +{ + + Dwarf_Error err = 0; + Dwarf_Debug dbg = 0; + int res = 0; + int errval = 0; + int sections_found = 0; + + res = dwarf_elf_init(elf_file_ptr, DW_DLC_READ, /* errhand */ 0, + /* errarg */ 0, &dbg, &err); + if (res == DW_DLV_ERROR) { + int errv = (int) dwarf_errno(err); + return errv; + } + if (res == DW_DLV_NO_ENTRY) { + return res; + } + send_addr_note = cb_func; + res = handle_debug_info(dbg, &errval); + switch (res) { + case DW_DLV_OK: + ++sections_found; + break; + case DW_DLV_NO_ENTRY: + + break; + default: + case DW_DLV_ERROR: + dwarf_finish(dbg, &err); + *dwerr = errval; + return res; + } + + res = handle_debug_aranges(dbg, cb_func, &errval); + switch (res) { + case DW_DLV_OK: + ++sections_found; + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + dwarf_finish(dbg, &err); + *dwerr = errval; + return res; + } + res = handle_debug_frame(dbg, cb_func, &errval); + switch (res) { + case DW_DLV_OK: + ++sections_found; + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + dwarf_finish(dbg, &err); + *dwerr = errval; + return res; + } + + res = handle_debug_loc(); /* does nothing */ + switch (res) { + case DW_DLV_OK: + ++sections_found; + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + /* IMPOSSIBLE : handle_debug_loc cannot return this */ + dwarf_finish(dbg, &err); + *dwerr = errval; + return res; + } + + + + *dwerr = 0; + res = dwarf_finish(dbg, &err); + if (res == DW_DLV_ERROR) { + *dwerr = (int) dwarf_errno(err); + return DW_DLV_ERROR; + } + if (sections_found == 0) { + return DW_DLV_NO_ENTRY; + } + return DW_DLV_OK; + +} + +/* Return DW_DLV_OK, ERROR, or NO_ENTRY. */ +static int +handle_debug_info(Dwarf_Debug dbg, int *errval) +{ + Dwarf_Unsigned nxtoff = 1; + Dwarf_Unsigned hdr_length; + Dwarf_Half version_stamp; + Dwarf_Unsigned abbrev_offset; + Dwarf_Half addr_size; + Dwarf_Error err; + int terminate_now = 0; + int res = 0; + Dwarf_Die sibdie; + int sibres; + int nres = DW_DLV_OK; + + + for (nres = dwarf_next_cu_header(dbg, &hdr_length, &version_stamp, + &abbrev_offset, + &addr_size, &nxtoff, &err); + terminate_now == 0 && nres == DW_DLV_OK; + nres = dwarf_next_cu_header(dbg, &hdr_length, &version_stamp, + &abbrev_offset, + &addr_size, &nxtoff, &err)) { + + Dwarf_Die curdie = 0; + + /* Try to get the compilation unit die */ + sibres = dwarf_siblingof(dbg, curdie, &sibdie, &err); + if (sibres == DW_DLV_OK) { + res = do_this_die_and_dealloc(dbg, sibdie, errval); + switch (res) { + case DW_DLV_OK: + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + return DW_DLV_ERROR; + } + } else if (sibres == DW_DLV_ERROR) { + *errval = (int) dwarf_errno(err); + return DW_DLV_ERROR; + } else { + /* NO ENTRY! */ + /* impossible? */ + } + + } + if (nres == DW_DLV_ERROR) { + int localerr = (int) dwarf_errno(err); + + *errval = localerr; + return DW_DLV_ERROR; + } + return DW_DLV_OK; +} + +static int + might_have_addr[] = { + DW_AT_high_pc, + DW_AT_low_pc, +}; +static int might_have_locdesc[] = { + DW_AT_segment, + DW_AT_return_addr, + DW_AT_frame_base, + DW_AT_static_link, + DW_AT_data_member_location, + DW_AT_string_length, + DW_AT_location, + DW_AT_use_location, + DW_AT_vtable_elem_location, +}; + +/* Return DW_DLV_OK if handling this went ok. */ +static int +handle_attr_addr(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Half attrnum, + Dwarf_Error * perr) +{ + int res = DW_DLV_OK; + Dwarf_Off offset; + Dwarf_Addr addr; + Dwarf_Half form; + int ares; + + Dwarf_Attribute attr; + + ares = dwarf_attr(die, attrnum, &attr, perr); + if (ares == DW_DLV_OK) { + int formres = dwarf_whatform(attr, &form, perr); + + switch (formres) { + case DW_DLV_OK: + break; + case DW_DLV_ERROR: + case DW_DLV_NO_ENTRY: /* impossible. */ + return formres; + + } + + switch (form) { + case DW_FORM_ref_addr: + case DW_FORM_addr: + res = dwarf_attr_offset(die, attr, &offset, perr); + if (res == DW_DLV_OK) { + ares = dwarf_formaddr(attr, &addr, perr); + if (ares == DW_DLV_OK) { + send_addr_note(DW_SECTION_INFO, offset, addr); + } else if (ares == DW_DLV_ERROR) { + return ares; + } + /* no entry: ok. */ + } else { + /* NO_ENTRY is impossible. */ + res = DW_DLV_ERROR; + } + break; + + default: + /* surprising! An error? */ + /* do nothing */ + ; + } + dwarf_dealloc(dbg, attr, DW_DLA_ATTR); + } else { + res = ares; + } + return res; +} + +/* Return DW_DLV_OK if handling this went ok. */ +static int +handle_attr_locdesc(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Half attrnum, + Dwarf_Error * perr) +{ + int retval = DW_DLV_OK; + Dwarf_Attribute attr; + Dwarf_Locdesc *llbuf; + Dwarf_Signed i; + Dwarf_Off offset; + Dwarf_Loc *locp; + unsigned int entindx; + int res; + int ares; + + + ares = dwarf_attr(die, attrnum, &attr, perr); + if (ares == DW_DLV_OK) { + Dwarf_Half form; + int fres = dwarf_whatform(attr, &form, perr); + + if (fres == DW_DLV_OK) { + switch (form) { + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + /* must be location description */ + res = dwarf_attr_offset(die, attr, &offset, perr); + llbuf = 0; + if (res == DW_DLV_OK) { + Dwarf_Signed count; + int lres = dwarf_loclist(attr, &llbuf, &count, perr); + if (lres != DW_DLV_OK) { + return lres; + } + if (count != 1) { + /* this cannot happen! */ + /* perr? */ + _dwarf_error(dbg, perr, + DW_DLE_LOCDESC_COUNT_WRONG); + retval = DW_DLV_ERROR; + return retval; + } + for (i = 0; i < count; ++i) { + unsigned int ents = llbuf[i].ld_cents; + + locp = llbuf[i].ld_s; + for (entindx = 0; entindx < ents; entindx++) { + Dwarf_Loc *llocp; + + llocp = locp + entindx; + if (llocp->lr_atom == DW_OP_addr) { + send_addr_note(DW_SECTION_INFO, offset + + llocp->lr_offset + 1 + /* The offset is the + offset of the atom, + ** and we know the + addr is 1 past it. */ + , llocp->lr_number); + } + } + } + + + if (count > 0) { + for (i = 0; i < count; ++i) { + dwarf_dealloc(dbg, llbuf[i].ld_s, + DW_DLA_LOC_BLOCK); + } + dwarf_dealloc(dbg, llbuf, DW_DLA_LOCDESC); + } + } else { + retval = res; + } + break; + + default: + /* must be a const offset in debug_loc */ + ; /* do nothing */ + } + dwarf_dealloc(dbg, attr, DW_DLA_ATTR); + } /* else error or no entry */ + retval = fres; + } else { + retval = ares; + } + return retval; +} + +/* Return DW_DLV_OK, or DW_DLV_ERROR + + Handle the addrs in a single die. */ +static int +process_this_die_attrs(Dwarf_Debug dbg, Dwarf_Die newdie, int *errval) +{ + Dwarf_Error err; + Dwarf_Half i; + Dwarf_Half newattrnum; + int res; + int tres; + Dwarf_Half ltag; + + Dwarf_Off doff; + int doffres = dwarf_dieoffset(newdie, &doff, &err); + + if (doffres != DW_DLV_OK) { + if (doffres == DW_DLV_ERROR) { + *errval = (int) dwarf_errno(err); + } + return doffres; + } + tres = dwarf_tag(newdie, <ag, &err); + if (tres != DW_DLV_OK) { + return tres; + } + if (DW_TAG_compile_unit == ltag) { + /* Because of the way the dwarf_line code works, we do lines + only per compile unit. This may turn out to be wrong if + we have lines left unconnected to a CU. of course such + lines will not, at present, be used by gnome. This is + not ideal as coded due to the dwarf_line.c issue. */ + int lres = handle_debug_line(dbg, newdie, send_addr_note, errval); + if (lres == DW_DLV_ERROR) { + return lres; + } + } + + for (i = 0; i < sizeof(might_have_addr) / sizeof(int); i++) { + int resattr; + Dwarf_Bool hasattr; + + newattrnum = might_have_addr[i]; + err = 0; + resattr = dwarf_hasattr(newdie, newattrnum, &hasattr, &err); + if (DW_DLV_OK == resattr) { + if (hasattr) { + res = handle_attr_addr(dbg, newdie, newattrnum, &err); + if (res != DW_DLV_OK) { + *errval = (int) dwarf_errno(err); + return DW_DLV_ERROR; + } + } + } else { + if (resattr == DW_DLV_ERROR) { + *errval = (int) dwarf_errno(err); + return resattr; + } + } + } + for (i = 0; i < sizeof(might_have_locdesc) / sizeof(int); i++) { + int resattr; + Dwarf_Bool hasattr; + + newattrnum = might_have_locdesc[i]; + err = 0; + resattr = dwarf_hasattr(newdie, newattrnum, &hasattr, &err); + if (DW_DLV_OK == resattr) { + if (hasattr) { + res = + handle_attr_locdesc(dbg, newdie, newattrnum, &err); + if (res != DW_DLV_OK) { + *errval = (int) dwarf_errno(err); + return DW_DLV_ERROR; + } + } + } else { + if (resattr == DW_DLV_ERROR) { + *errval = (int) dwarf_errno(err); + return resattr; + } + } + } + + return DW_DLV_OK; +} + +/* Handle siblings as a list, + Do children by recursing. + Effectively this is walking the tree preorder. + + This dealloc's any die passed to it, so the + caller should not do that dealloc. + It seems more logical to have the one causing + the alloc to do the dealloc, but that way this + routine became a mess. +*/ +static int +do_this_die_and_dealloc(Dwarf_Debug dbg, Dwarf_Die die, int *errval) +{ + + Dwarf_Die prevdie = 0; + Dwarf_Die newdie = die; + Dwarf_Error err = 0; + int res = 0; + int sibres = DW_DLV_OK; + int tres = DW_DLV_OK; + Dwarf_Die sibdie; + + while (sibres == DW_DLV_OK) { + Dwarf_Die ch_die; + + + res = process_this_die_attrs(dbg, newdie, errval); + switch (res) { + case DW_DLV_OK: + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + if (prevdie) { + dwarf_dealloc(dbg, prevdie, DW_DLA_DIE); + prevdie = 0; + } + return DW_DLV_ERROR; + } + + tres = dwarf_child(newdie, &ch_die, &err); + + if (tres == DW_DLV_OK) { + res = do_this_die_and_dealloc(dbg, ch_die, errval); + switch (res) { + case DW_DLV_OK: + break; + case DW_DLV_NO_ENTRY: + break; + default: + case DW_DLV_ERROR: + if (prevdie) { + dwarf_dealloc(dbg, prevdie, DW_DLA_DIE); + prevdie = 0; + } + return DW_DLV_ERROR; + } + } else if (tres == DW_DLV_ERROR) { + /* An error! */ + *errval = (int) dwarf_errno(err); + if (prevdie) { + dwarf_dealloc(dbg, prevdie, DW_DLA_DIE); + prevdie = 0; + } + dwarf_dealloc(dbg, err, DW_DLA_ERROR); + return DW_DLV_ERROR; + } /* else was NO ENTRY */ + prevdie = newdie; + sibdie = 0; + sibres = dwarf_siblingof(dbg, newdie, &sibdie, &err); + if (prevdie) { + dwarf_dealloc(dbg, prevdie, DW_DLA_DIE); + prevdie = 0; + } + newdie = sibdie; + + } + if (sibres == DW_DLV_NO_ENTRY) { + return DW_DLV_OK; + } + /* error. */ + *errval = (int) dwarf_errno(err); + if (prevdie) { + dwarf_dealloc(dbg, prevdie, DW_DLA_DIE); + prevdie = 0; + } + dwarf_dealloc(dbg, err, DW_DLA_ERROR); + return DW_DLV_ERROR; + +} + + +static int +handle_debug_frame(Dwarf_Debug dbg, Dwarf_addr_callback_func cb_func, + int *errval) +{ + int retval = DW_DLV_OK; + int res; + Dwarf_Error err; + Dwarf_Addr *addrlist; + Dwarf_Off *offsetlist; + Dwarf_Signed count; + int i; + + res = + _dwarf_frame_address_offsets(dbg, &addrlist, &offsetlist, + &count, &err); + if (res == DW_DLV_OK) { + for (i = 0; i < count; i++) { + cb_func(DW_SECTION_FRAME, offsetlist[i], addrlist[i]); + } + dwarf_dealloc(dbg, offsetlist, DW_DLA_ADDR); + dwarf_dealloc(dbg, addrlist, DW_DLA_ADDR); + } else if (res == DW_DLV_NO_ENTRY) { + retval = res; + } else { + *errval = (int) dwarf_errno(err); + retval = DW_DLV_ERROR; + } + return retval; + +} +static int +handle_debug_aranges(Dwarf_Debug dbg, Dwarf_addr_callback_func cb_func, + int *errval) +{ + int retval = DW_DLV_OK; + Dwarf_Error err; + Dwarf_Addr *aranges; + Dwarf_Signed count; + int indx; + Dwarf_Off *offsets; + + retval = + _dwarf_get_aranges_addr_offsets(dbg, &aranges, &offsets, &count, + &err); + if (retval == DW_DLV_OK) { + if (count == 0) { + retval = DW_DLV_NO_ENTRY; + } else { + for (indx = 0; indx < count; indx++) { + cb_func(DW_SECTION_ARANGES, offsets[indx], + aranges[indx]); + } + } + dwarf_dealloc(dbg, aranges, DW_DLA_ADDR); + dwarf_dealloc(dbg, offsets, DW_DLA_ADDR); + } else if (retval == DW_DLV_NO_ENTRY) { + ; /* do nothing */ + } else { + *errval = (int) dwarf_errno(err); + retval = DW_DLV_ERROR; + } + return retval; +} +static int +handle_debug_line(Dwarf_Debug dbg, Dwarf_Die cu_die, + Dwarf_addr_callback_func cb_func, int *errval) +{ + int retval = DW_DLV_OK; + int res; + Dwarf_Error err; + Dwarf_Addr *addrlist; + Dwarf_Off *offsetlist; + Dwarf_Unsigned count; + Dwarf_Unsigned i; + + res = + _dwarf_line_address_offsets(dbg, cu_die, &addrlist, &offsetlist, + &count, &err); + if (res == DW_DLV_OK) { + for (i = 0; i < count; i++) { + cb_func(DW_SECTION_LINE, offsetlist[i], addrlist[i]); + + } + dwarf_dealloc(dbg, offsetlist, DW_DLA_ADDR); + dwarf_dealloc(dbg, addrlist, DW_DLA_ADDR); + } else if (res == DW_DLV_NO_ENTRY) { + retval = res; + } else { + *errval = (int) dwarf_errno(err); + retval = DW_DLV_ERROR; + } + return retval; +} + +/* We need to add support for this. Currently we do not + generate this section. + FIX! +*/ +static int +handle_debug_loc(void) +{ + int retval = DW_DLV_NO_ENTRY; + + return retval; +} -- cgit v1.2.3