diff options
Diffstat (limited to 'usr/src/lib/libdwarf/common/dwarf_gdbindex.c')
-rw-r--r-- | usr/src/lib/libdwarf/common/dwarf_gdbindex.c | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/usr/src/lib/libdwarf/common/dwarf_gdbindex.c b/usr/src/lib/libdwarf/common/dwarf_gdbindex.c new file mode 100644 index 0000000000..859ce6c2ef --- /dev/null +++ b/usr/src/lib/libdwarf/common/dwarf_gdbindex.c @@ -0,0 +1,546 @@ +/* + + Copyright (C) 2014-2019 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. + +*/ + +#include "config.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif /* HAVE_STDLIB_H */ +#include "dwarf_incl.h" +#include "dwarf_alloc.h" +#include "dwarf_error.h" +#include "dwarf_util.h" +#include "memcpy_swap.h" +#include "dwarf_gdbindex.h" + +#define TRUE 1 +#define FALSE 0 + +/* The dwarf_util macro READ_UNALIGNED + cannot be directly used because + gdb defines the section contents of + .gdb_index as little-endian always. +*/ + +#if WORDS_BIGENDIAN /* meaning on this host */ +#define READ_GDBINDEX(dest,desttype, source, length) \ + do { \ + BIGGEST_UINT _ltmp = 0; \ + _dwarf_memcpy_swap_bytes((((char *)(&_ltmp)) + sizeof(_ltmp) - length), \ + source, length) ; \ + dest = (desttype)_ltmp; \ + } while (0) +#else /* little-endian on this host */ +#define READ_GDBINDEX(dest,desttype, source, length) \ + do { \ + BIGGEST_UINT _ltmp = 0; \ + memcpy(((char *)(&_ltmp)) , \ + source, length) ; \ + dest = (desttype)_ltmp; \ + } while (0) + +#endif + + +struct gi_fileheader_s { + char gfs [4][6]; +}; + +struct dwarf_64bitpair { + gdbindex_64 offset; + gdbindex_64 length; +}; + +static int +set_base(Dwarf_Debug dbg, + struct Dwarf_Gdbindex_array_instance_s * hdr, + Dwarf_Small *start, + Dwarf_Small *end, + /* entrylen is the length of a single struct as seen in the object. */ + Dwarf_Unsigned entrylen, + /* The size of each field in the struct in the object. */ + Dwarf_Unsigned fieldlen, + enum gdbindex_type_e type, + Dwarf_Error * err) +{ + + if (type == git_std || type == git_cuvec) { + /* cuvec is sort of a fake as a simple + section, but a useful one. */ + Dwarf_Unsigned count = 0; + if( end < start) { + _dwarf_error(dbg, err,DW_DLE_GDB_INDEX_COUNT_ERROR); + return DW_DLV_ERROR; + } + count = end - start; + count = count / entrylen; + hdr->dg_type = type; + hdr->dg_base = start; + hdr->dg_count = count; + hdr->dg_entry_length = entrylen; + hdr->dg_fieldlen = fieldlen; + } else { + /* address area. */ + /* 64bit, 64bit, offset. Then 32bit pad. */ + Dwarf_Unsigned count = 0; + hdr->dg_base = start; + if( end < start) { + _dwarf_error(dbg, err,DW_DLE_GDB_INDEX_COUNT_ADDR_ERROR); + return DW_DLV_ERROR; + } + /* entry length includes pad. */ + hdr->dg_entry_length = 2*sizeof(gdbindex_64) + + DWARF_32BIT_SIZE; + count = end - start; + count = count / hdr->dg_entry_length; + hdr->dg_count = count; + /* The dg_fieldlen is a fake, the fields are not + all the same length. */ + hdr->dg_fieldlen = DWARF_32BIT_SIZE; + hdr->dg_type = type; + } + return DW_DLV_OK; +} + +int +dwarf_gdbindex_header(Dwarf_Debug dbg, + Dwarf_Gdbindex * gdbindexptr, + Dwarf_Unsigned * version, + Dwarf_Unsigned * cu_list_offset, + Dwarf_Unsigned * types_cu_list_offset, + Dwarf_Unsigned * address_area_offset, + Dwarf_Unsigned * symbol_table_offset, + Dwarf_Unsigned * constant_pool_offset, + Dwarf_Unsigned * section_size, + Dwarf_Unsigned * unused_reserved, + const char ** section_name, + Dwarf_Error * error) +{ + + struct gi_fileheader_s header; + Dwarf_Gdbindex indexptr = 0; + int res = DW_DLV_ERROR; + + if (!dbg->de_debug_gdbindex.dss_size) { + return DW_DLV_NO_ENTRY; + } + if (!dbg->de_debug_gdbindex.dss_data) { + res = _dwarf_load_section(dbg, &dbg->de_debug_gdbindex,error); + if (res != DW_DLV_OK) { + return res; + } + } + + if (dbg->de_debug_gdbindex.dss_size < + sizeof(struct gi_fileheader_s) ) { + _dwarf_error(dbg, error, DW_DLE_ERRONEOUS_GDB_INDEX_SECTION); + return (DW_DLV_ERROR); + } + memcpy(&header,dbg->de_debug_gdbindex.dss_data, + sizeof(struct gi_fileheader_s)); + indexptr = (Dwarf_Gdbindex)_dwarf_get_alloc(dbg,DW_DLA_GDBINDEX,1); + if (indexptr == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + indexptr->gi_dbg = dbg; + indexptr->gi_section_data = dbg->de_debug_gdbindex.dss_data; + indexptr->gi_section_length = dbg->de_debug_gdbindex.dss_size; + READ_GDBINDEX(indexptr->gi_version ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data, + DWARF_32BIT_SIZE); + READ_GDBINDEX(indexptr->gi_cu_list_offset ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data + DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE); + READ_GDBINDEX(indexptr->gi_types_cu_list_offset ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data + 2*DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE); + READ_GDBINDEX(indexptr->gi_address_area_offset ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data + 3*DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE); + READ_GDBINDEX(indexptr->gi_symbol_table_offset ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data + 4*DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE); + READ_GDBINDEX(indexptr->gi_constant_pool_offset ,Dwarf_Unsigned, + dbg->de_debug_gdbindex.dss_data + 5*DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE); + + res = set_base(dbg,&indexptr->gi_culisthdr, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_cu_list_offset, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_types_cu_list_offset, + 2*sizeof(gdbindex_64), + sizeof(gdbindex_64), + git_std,error); + if (res == DW_DLV_ERROR) { + return res; + } + res = set_base(dbg,&indexptr->gi_typesculisthdr, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_types_cu_list_offset, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_address_area_offset, + 3*sizeof(gdbindex_64), + sizeof(gdbindex_64), + git_std,error); + if (res == DW_DLV_ERROR) { + return res; + } + res = set_base(dbg,&indexptr->gi_addressareahdr, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_address_area_offset, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_symbol_table_offset, + 3*sizeof(gdbindex_64), + sizeof(gdbindex_64), + git_address,error); + if (res == DW_DLV_ERROR) { + return res; + } + res = set_base(dbg,&indexptr->gi_symboltablehdr, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_symbol_table_offset, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_constant_pool_offset, + 2*DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE, + git_std,error); + if (res == DW_DLV_ERROR) { + return res; + } + res = set_base(dbg,&indexptr->gi_cuvectorhdr, + dbg->de_debug_gdbindex.dss_data + indexptr->gi_constant_pool_offset, + /* There is no real single vector size. + but we'll use the entire rest as if there was. */ + dbg->de_debug_gdbindex.dss_data + indexptr->gi_section_length, + DWARF_32BIT_SIZE, + DWARF_32BIT_SIZE, + git_cuvec,error); + if (res == DW_DLV_ERROR) { + return res; + } + + /* Really just pointing to constant pool area. */ + indexptr->gi_string_pool = dbg->de_debug_gdbindex.dss_data + + indexptr->gi_constant_pool_offset; + *gdbindexptr = indexptr; + *version = indexptr->gi_version; + *cu_list_offset = indexptr->gi_cu_list_offset; + *types_cu_list_offset = indexptr->gi_types_cu_list_offset; + *address_area_offset = indexptr->gi_address_area_offset; + *symbol_table_offset = indexptr->gi_symbol_table_offset; + *constant_pool_offset = indexptr->gi_constant_pool_offset; + *section_size = indexptr->gi_section_length; + *unused_reserved = 0; + *section_name = dbg->de_debug_gdbindex.dss_name; + return DW_DLV_OK; + + +} + + +int +dwarf_gdbindex_culist_array(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned * list_length, + UNUSEDARG Dwarf_Error * error) +{ + *list_length = gdbindexptr->gi_culisthdr.dg_count; + return DW_DLV_OK; +} + +/* entryindex: 0 to list_length-1 */ +int +dwarf_gdbindex_culist_entry(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned entryindex, + Dwarf_Unsigned * cu_offset, + Dwarf_Unsigned * cu_length, + Dwarf_Error * error) +{ + Dwarf_Unsigned max = gdbindexptr->gi_culisthdr.dg_count; + Dwarf_Small * base = 0; + Dwarf_Unsigned offset = 0; + Dwarf_Unsigned length = 0; + unsigned fieldlen = gdbindexptr->gi_culisthdr.dg_fieldlen; + + if (entryindex >= max) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + base = gdbindexptr->gi_culisthdr.dg_base; + base += entryindex*gdbindexptr->gi_culisthdr.dg_entry_length; + + READ_GDBINDEX(offset ,Dwarf_Unsigned, + base, + fieldlen); + READ_GDBINDEX(length ,Dwarf_Unsigned, + base+ fieldlen, + fieldlen); + *cu_offset = offset; + *cu_length = length; + return DW_DLV_OK; +} + +int +dwarf_gdbindex_types_culist_array(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned * list_length, + UNUSEDARG Dwarf_Error * error) +{ + *list_length = gdbindexptr->gi_typesculisthdr.dg_count; + return DW_DLV_OK; +} + +/* entryindex: 0 to list_length-1 */ +int +dwarf_gdbindex_types_culist_entry(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned entryindex, + Dwarf_Unsigned * t_offset, + Dwarf_Unsigned * t_length, + Dwarf_Unsigned * t_signature, + Dwarf_Error * error) +{ + Dwarf_Unsigned max = gdbindexptr->gi_typesculisthdr.dg_count; + Dwarf_Small * base = 0; + Dwarf_Unsigned offset = 0; + Dwarf_Unsigned length = 0; + Dwarf_Unsigned signature = 0; + unsigned fieldlen = gdbindexptr->gi_typesculisthdr.dg_fieldlen; + + if (entryindex >= max) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + base = gdbindexptr->gi_typesculisthdr.dg_base; + base += entryindex*gdbindexptr->gi_typesculisthdr.dg_entry_length; + + READ_GDBINDEX(offset ,Dwarf_Unsigned, + base, + fieldlen); + READ_GDBINDEX(length ,Dwarf_Unsigned, + base+ (1*fieldlen), + fieldlen); + READ_GDBINDEX(signature ,Dwarf_Unsigned, + base+ (2*fieldlen), + fieldlen); + *t_offset = offset; + *t_length = length; + *t_signature = signature; + return DW_DLV_OK; +} + +int +dwarf_gdbindex_addressarea(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned * list_length, + UNUSEDARG Dwarf_Error * error) +{ + *list_length = gdbindexptr->gi_addressareahdr.dg_count; + return DW_DLV_OK; +} + +/* entryindex: 0 to addressarea_list_length-1 */ +int +dwarf_gdbindex_addressarea_entry( + Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned entryindex, + Dwarf_Unsigned * low_address, + Dwarf_Unsigned * high_address, + Dwarf_Unsigned * cu_index, + Dwarf_Error * error) +{ + Dwarf_Unsigned max = gdbindexptr->gi_addressareahdr.dg_count; + Dwarf_Small * base = 0; + Dwarf_Unsigned lowaddr = 0; + Dwarf_Unsigned highaddr = 0; + Dwarf_Unsigned cuindex = 0; + + if (entryindex >= max) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + base = gdbindexptr->gi_addressareahdr.dg_base; + base += entryindex*gdbindexptr->gi_addressareahdr.dg_entry_length; + + READ_GDBINDEX(lowaddr ,Dwarf_Unsigned, + base, + sizeof(gdbindex_64)); + READ_GDBINDEX(highaddr ,Dwarf_Unsigned, + base+ (1*sizeof(gdbindex_64)), + sizeof(gdbindex_64)); + READ_GDBINDEX(cuindex ,Dwarf_Unsigned, + base+ (2*sizeof(gdbindex_64)), + DWARF_32BIT_SIZE); + *low_address = lowaddr; + *high_address = highaddr; + *cu_index = cuindex; + return DW_DLV_OK; +} + +int +dwarf_gdbindex_symboltable_array(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned * list_length, + UNUSEDARG Dwarf_Error * error) +{ + *list_length = gdbindexptr->gi_symboltablehdr.dg_count; + return DW_DLV_OK; +} + +/* entryindex: 0 to symtab_list_length-1 */ +int +dwarf_gdbindex_symboltable_entry( + Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned entryindex, + Dwarf_Unsigned * string_offset, + Dwarf_Unsigned * cu_vector_offset, + Dwarf_Error * error) +{ + Dwarf_Unsigned max = gdbindexptr->gi_symboltablehdr.dg_count; + Dwarf_Small * base = 0; + Dwarf_Unsigned symoffset = 0; + Dwarf_Unsigned cuoffset = 0; + unsigned fieldlen = gdbindexptr->gi_symboltablehdr.dg_fieldlen; + + if (entryindex >= max) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + base = gdbindexptr->gi_symboltablehdr.dg_base; + base += entryindex*gdbindexptr->gi_symboltablehdr.dg_entry_length; + + READ_GDBINDEX(symoffset ,Dwarf_Unsigned, + base, + fieldlen); + READ_GDBINDEX(cuoffset ,Dwarf_Unsigned, + base + fieldlen, + fieldlen); + *string_offset = symoffset; + *cu_vector_offset = cuoffset; + return DW_DLV_OK; +} + +int +dwarf_gdbindex_cuvector_length(Dwarf_Gdbindex gdbindex, + Dwarf_Unsigned cuvector_offset, + Dwarf_Unsigned * innercount, + Dwarf_Error * error) +{ + Dwarf_Small *base = gdbindex->gi_cuvectorhdr.dg_base; + Dwarf_Small *end = gdbindex->gi_section_data + gdbindex->gi_section_length; + Dwarf_Unsigned val = 0; + unsigned fieldlen = gdbindex->gi_cuvectorhdr.dg_entry_length; + + base += cuvector_offset; + if ((base + fieldlen) >= end) { + _dwarf_error(gdbindex->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + + READ_GDBINDEX(val,Dwarf_Unsigned, + base, + fieldlen); + *innercount = val; + return DW_DLV_OK; +} + +int +dwarf_gdbindex_cuvector_inner_attributes(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned cuvector_offset, + Dwarf_Unsigned innerindex, + /* The attr_value is a field of bits. For expanded version + use dwarf_gdbindex_instance_expand_value() */ + Dwarf_Unsigned * attributes, + Dwarf_Error * error) +{ + Dwarf_Small *base = gdbindexptr->gi_cuvectorhdr.dg_base; + Dwarf_Small *end = gdbindexptr->gi_section_data + + gdbindexptr->gi_section_length; + Dwarf_Unsigned val = 0; + unsigned fieldlen = gdbindexptr->gi_cuvectorhdr.dg_entry_length; + + base += cuvector_offset; + if ((base+fieldlen) >= end) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + base += fieldlen; + base += innerindex*fieldlen; + + READ_GDBINDEX(val ,Dwarf_Unsigned, + base, + fieldlen); + *attributes = val; + return DW_DLV_OK; +} + + +int +dwarf_gdbindex_cuvector_instance_expand_value( + UNUSEDARG Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned value, + Dwarf_Unsigned * cu_index, + Dwarf_Unsigned * reserved1, + Dwarf_Unsigned * symbol_kind, + Dwarf_Unsigned * is_static, + UNUSEDARG Dwarf_Error * error) +{ + *cu_index = value & 0xffffff; + *reserved1 = (value >> 24) & 0xf; + *symbol_kind = (value >> 28) & 0x7; + *is_static = (value >> 31) & 1; + return DW_DLV_OK; +} + + +/* The strings in the pool follow (in memory) the cu index + set and are NUL terminated. */ +int +dwarf_gdbindex_string_by_offset(Dwarf_Gdbindex gdbindexptr, + Dwarf_Unsigned stringoffsetinpool, + const char ** string_ptr, + UNUSEDARG Dwarf_Error * error) +{ + Dwarf_Small *pooldata = 0; + Dwarf_Small *section_end = 0; + Dwarf_Small *stringitself = 0; + + /* If gdbindexptr NULL or gdbindexptr->gi_dbg is NULL + this is not going to go very well. Ugh. FIXME */ + pooldata = gdbindexptr->gi_section_data + + gdbindexptr->gi_constant_pool_offset; + section_end = gdbindexptr->gi_section_data + + gdbindexptr->gi_section_length; + stringitself = pooldata + stringoffsetinpool; + if (stringitself > section_end) { + _dwarf_error(gdbindexptr->gi_dbg, error,DW_DLE_GDB_INDEX_INDEX_ERROR); + return DW_DLV_ERROR; + } + *string_ptr = (const char *)stringitself; + return DW_DLV_OK; +} + + + + +void +dwarf_gdbindex_free(Dwarf_Gdbindex indexptr) +{ + if(indexptr) { + Dwarf_Debug dbg = indexptr->gi_dbg; + dwarf_dealloc(dbg,indexptr,DW_DLA_GDBINDEX); + } +} |