diff options
Diffstat (limited to 'lib/libdwarf/dwarf_init.c')
-rw-r--r-- | lib/libdwarf/dwarf_init.c | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/lib/libdwarf/dwarf_init.c b/lib/libdwarf/dwarf_init.c new file mode 100644 index 0000000..30b7b0f --- /dev/null +++ b/lib/libdwarf/dwarf_init.c @@ -0,0 +1,748 @@ +/*- + * Copyright (c) 2007 John Birrell (jb@freebsd.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <stdlib.h> +#include <string.h> +#include "_libdwarf.h" + +static const char *debug_snames[DWARF_DEBUG_SNAMES] = { + ".debug_abbrev", + ".debug_aranges", + ".debug_frame", + ".debug_info", + ".debug_line", + ".debug_pubnames", + ".eh_frame", + ".debug_macinfo", + ".debug_str", + ".debug_loc", + ".debug_pubtypes", + ".debug_ranges", + ".debug_static_func", + ".debug_static_vars", + ".debug_types", + ".debug_weaknames", + ".symtab", + ".strtab" +}; + +static uint64_t (*dwarf_read) (Elf_Data **, uint64_t *, int); +static void (*dwarf_write) (Elf_Data **, uint64_t *, uint64_t, int); + +static uint64_t +dwarf_read_lsb(Elf_Data **dp, uint64_t *offsetp, int bytes_to_read) +{ + uint64_t ret = 0; + + uint8_t *src = (uint8_t *) (*dp)->d_buf + *offsetp; + + switch (bytes_to_read) { + case 8: + ret |= ((uint64_t) src[4]) << 32 | ((uint64_t) src[5]) << 40; + ret |= ((uint64_t) src[6]) << 48 | ((uint64_t) src[7]) << 56; + case 4: + ret |= ((uint64_t) src[2]) << 16 | ((uint64_t) src[3]) << 24; + case 2: + ret |= ((uint64_t) src[1]) << 8; + case 1: + ret |= src[0]; + break; + default: + return 0; + break; + } + + *offsetp += bytes_to_read; + + return ret; +} + +static uint64_t +dwarf_read_msb(Elf_Data **dp, uint64_t *offsetp, int bytes_to_read) +{ + uint64_t ret = 0; + + uint8_t *src = (uint8_t *) (*dp)->d_buf + *offsetp; + + switch (bytes_to_read) { + case 1: + ret = src[0]; + break; + case 2: + ret = src[1] | ((uint64_t) src[0]) << 8; + break; + case 4: + ret = src[3] | ((uint64_t) src[2]) << 8; + ret |= ((uint64_t) src[1]) << 16 | ((uint64_t) src[0]) << 24; + break; + case 8: + ret = src[7] | ((uint64_t) src[6]) << 8; + ret |= ((uint64_t) src[5]) << 16 | ((uint64_t) src[4]) << 24; + ret |= ((uint64_t) src[3]) << 32 | ((uint64_t) src[2]) << 40; + ret |= ((uint64_t) src[1]) << 48 | ((uint64_t) src[0]) << 56; + break; + default: + return 0; + break; + } + + *offsetp += bytes_to_read; + + return ret; +} + +static void +dwarf_write_lsb(Elf_Data **dp, uint64_t *offsetp, uint64_t value, int bytes_to_write) +{ + uint8_t *dst = (uint8_t *) (*dp)->d_buf + *offsetp; + + switch (bytes_to_write) { + case 8: + dst[7] = (value >> 56) & 0xff; + dst[6] = (value >> 48) & 0xff; + dst[5] = (value >> 40) & 0xff; + dst[4] = (value >> 32) & 0xff; + case 4: + dst[3] = (value >> 24) & 0xff; + dst[2] = (value >> 16) & 0xff; + case 2: + dst[1] = (value >> 8) & 0xff; + case 1: + dst[0] = value & 0xff; + break; + default: + return; + break; + } + + *offsetp += bytes_to_write; +} + +static void +dwarf_write_msb(Elf_Data **dp, uint64_t *offsetp, uint64_t value, int bytes_to_write) +{ + uint8_t *dst = (uint8_t *) (*dp)->d_buf + *offsetp; + + switch (bytes_to_write) { + case 8: + dst[7] = value & 0xff; + dst[6] = (value >> 8) & 0xff; + dst[5] = (value >> 16) & 0xff; + dst[4] = (value >> 24) & 0xff; + value >>= 32; + case 4: + dst[3] = value & 0xff; + dst[2] = (value >> 8) & 0xff; + value >>= 16; + case 2: + dst[1] = value & 0xff; + value >>= 8; + case 1: + dst[0] = value & 0xff; + break; + default: + return; + break; + } + + *offsetp += bytes_to_write; +} + +static int64_t +dwarf_read_sleb128(Elf_Data **dp, uint64_t *offsetp) +{ + int64_t ret = 0; + uint8_t b; + int shift = 0; + + uint8_t *src = (uint8_t *) (*dp)->d_buf + *offsetp; + + do { + b = *src++; + + ret |= ((b & 0x7f) << shift); + + (*offsetp)++; + + shift += 7; + } while ((b & 0x80) != 0); + + if (shift < 32 && (b & 0x40) != 0) + ret |= (-1 << shift); + + return ret; +} + +static uint64_t +dwarf_read_uleb128(Elf_Data **dp, uint64_t *offsetp) +{ + uint64_t ret = 0; + uint8_t b; + int shift = 0; + + uint8_t *src = (uint8_t *) (*dp)->d_buf + *offsetp; + + do { + b = *src++; + + ret |= ((b & 0x7f) << shift); + + (*offsetp)++; + + shift += 7; + } while ((b & 0x80) != 0); + + return ret; +} + +static const char * +dwarf_read_string(Elf_Data **dp, uint64_t *offsetp) +{ + char *ret; + + char *src = (char *) (*dp)->d_buf + *offsetp; + + ret = src; + + while (*src != '\0' && *offsetp < (*dp)->d_size) { + src++; + (*offsetp)++; + } + + if (*src == '\0' && *offsetp < (*dp)->d_size) + (*offsetp)++; + + return ret; +} + +static uint8_t * +dwarf_read_block(Elf_Data **dp, uint64_t *offsetp, uint64_t length) +{ + uint8_t *ret; + + uint8_t *src = (char *) (*dp)->d_buf + *offsetp; + + ret = src; + + (*offsetp) += length; + + return ret; +} + +static int +dwarf_apply_relocations(Dwarf_Debug dbg, Elf_Data *reld, int secindx) +{ + Elf_Data *d; + GElf_Rela rela; + int indx = 0; + int ret = DWARF_E_NONE; + uint64_t offset; + + /* Point to the data to be relocated: */ + d = dbg->dbg_s[secindx].s_data; + + /* Enter a loop to process each relocation addend: */ + while (gelf_getrela(reld, indx++, &rela) != NULL) { + GElf_Sym sym; + Elf64_Xword symindx = ELF64_R_SYM(rela.r_info); + + if (gelf_getsym(dbg->dbg_s[DWARF_symtab].s_data, symindx, &sym) == NULL) { + printf("Couldn't find symbol index %lu for relocation\n",(u_long) symindx); + continue; + } + + offset = rela.r_offset; + + dwarf_write(&d, &offset, rela.r_addend, dbg->dbg_offsize); + } + + return ret; +} + +static int +dwarf_relocate(Dwarf_Debug dbg, Dwarf_Error *error) +{ + Elf_Scn *scn = NULL; + GElf_Shdr shdr; + int i; + int ret = DWARF_E_NONE; + + /* Look for sections which relocate the debug sections. */ + while ((scn = elf_nextscn(dbg->dbg_elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + if (shdr.sh_type != SHT_RELA || shdr.sh_size == 0) + continue; + + for (i = 0; i < DWARF_DEBUG_SNAMES; i++) { + if (dbg->dbg_s[i].s_shnum == shdr.sh_info && + dbg->dbg_s[DWARF_symtab].s_shnum == shdr.sh_link) { + Elf_Data *rd; + + /* Get the relocation data. */ + if ((rd = elf_getdata(scn, NULL)) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + /* Apply the relocations. */ + dwarf_apply_relocations(dbg, rd, i); + break; + } + } + } + + return ret; +} + +static int +dwarf_init_attr(Dwarf_Debug dbg, Elf_Data **dp, uint64_t *offsetp, + Dwarf_CU cu, Dwarf_Die die, Dwarf_Attribute at, uint64_t form, + Dwarf_Error *error) +{ + int ret = DWARF_E_NONE; + struct _Dwarf_AttrValue avref; + + memset(&avref, 0, sizeof(avref)); + avref.av_attrib = at->at_attrib; + avref.av_form = at->at_form; + + switch (form) { + case DW_FORM_addr: + avref.u[0].u64 = dwarf_read(dp, offsetp, cu->cu_pointer_size); + break; + case DW_FORM_block: + avref.u[0].u64 = dwarf_read_uleb128(dp, offsetp); + avref.u[1].u8p = dwarf_read_block(dp, offsetp, avref.u[0].u64); + break; + case DW_FORM_block1: + avref.u[0].u64 = dwarf_read(dp, offsetp, 1); + avref.u[1].u8p = dwarf_read_block(dp, offsetp, avref.u[0].u64); + break; + case DW_FORM_block2: + avref.u[0].u64 = dwarf_read(dp, offsetp, 2); + avref.u[1].u8p = dwarf_read_block(dp, offsetp, avref.u[0].u64); + break; + case DW_FORM_block4: + avref.u[0].u64 = dwarf_read(dp, offsetp, 4); + avref.u[1].u8p = dwarf_read_block(dp, offsetp, avref.u[0].u64); + break; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + avref.u[0].u64 = dwarf_read(dp, offsetp, 1); + break; + case DW_FORM_data2: + case DW_FORM_ref2: + avref.u[0].u64 = dwarf_read(dp, offsetp, 2); + break; + case DW_FORM_data4: + case DW_FORM_ref4: + avref.u[0].u64 = dwarf_read(dp, offsetp, 4); + break; + case DW_FORM_data8: + case DW_FORM_ref8: + avref.u[0].u64 = dwarf_read(dp, offsetp, 8); + break; + case DW_FORM_indirect: + form = dwarf_read_uleb128(dp, offsetp); + return dwarf_init_attr(dbg, dp, offsetp, cu, die, at, form, error); + case DW_FORM_ref_addr: + if (cu->cu_version == 2) + avref.u[0].u64 = dwarf_read(dp, offsetp, cu->cu_pointer_size); + else if (cu->cu_version == 3) + avref.u[0].u64 = dwarf_read(dp, offsetp, dbg->dbg_offsize); + break; + case DW_FORM_ref_udata: + case DW_FORM_udata: + avref.u[0].u64 = dwarf_read_uleb128(dp, offsetp); + break; + case DW_FORM_sdata: + avref.u[0].s64 = dwarf_read_sleb128(dp, offsetp); + break; + case DW_FORM_string: + avref.u[0].s = dwarf_read_string(dp, offsetp); + break; + case DW_FORM_strp: + avref.u[0].u64 = dwarf_read(dp, offsetp, dbg->dbg_offsize); + avref.u[1].s = elf_strptr(dbg->dbg_elf, + dbg->dbg_s[DWARF_debug_str].s_shnum, avref.u[0].u64); + break; + default: + DWARF_SET_ERROR(error, DWARF_E_NOT_IMPLEMENTED); + ret = DWARF_E_NOT_IMPLEMENTED; + break; + } + + if (ret == DWARF_E_NONE) + ret = dwarf_attrval_add(die, &avref, NULL, error); + + return ret; +} + +static int +dwarf_init_abbrev(Dwarf_Debug dbg, Dwarf_CU cu, Dwarf_Error *error) +{ + Dwarf_Abbrev a; + Elf_Data *d; + int ret = DWARF_E_NONE; + uint64_t attr; + uint64_t entry; + uint64_t form; + uint64_t offset; + uint64_t tag; + u_int8_t children; + + d = dbg->dbg_s[DWARF_debug_abbrev].s_data; + + offset = cu->cu_abbrev_offset; + + while (offset < d->d_size) { + + entry = dwarf_read_uleb128(&d, &offset); + + /* Check if this is the end of the data: */ + if (entry == 0) + break; + + tag = dwarf_read_uleb128(&d, &offset); + + children = dwarf_read(&d, &offset, 1); + + if ((ret = dwarf_abbrev_add(cu, entry, tag, children, &a, error)) != DWARF_E_NONE) + break; + + do { + attr = dwarf_read_uleb128(&d, &offset); + form = dwarf_read_uleb128(&d, &offset); + + if (attr != 0) + if ((ret = dwarf_attr_add(a, attr, form, NULL, error)) != DWARF_E_NONE) + return ret; + } while (attr != 0); + } + + return ret; +} + +static int +dwarf_init_info(Dwarf_Debug dbg, Dwarf_Error *error) +{ + Dwarf_CU cu; + Elf_Data *d = NULL; + Elf_Scn *scn; + int i; + int level = 0; + int relocated = 0; + int ret = DWARF_E_NONE; + uint64_t length; + uint64_t next_offset; + uint64_t offset = 0; + + scn = dbg->dbg_s[DWARF_debug_info].s_scn; + + d = dbg->dbg_s[DWARF_debug_info].s_data; + + while (offset < d->d_size) { + /* Allocate memory for the first compilation unit. */ + if ((cu = calloc(sizeof(struct _Dwarf_CU), 1)) == NULL) { + DWARF_SET_ERROR(error, DWARF_E_MEMORY); + return DWARF_E_MEMORY; + } + + /* Save the offet to this compilation unit: */ + cu->cu_offset = offset; + + length = dwarf_read(&d, &offset, 4); + if (length == 0xffffffff) { + length = dwarf_read(&d, &offset, 8); + dbg->dbg_offsize = 8; + } else + dbg->dbg_offsize = 4; + + /* + * Check if there is enough ELF data for this CU. + * This assumes that libelf gives us the entire + * section in one Elf_Data object. + */ + if (length > d->d_size - offset) { + free(cu); + DWARF_SET_ERROR(error, DWARF_E_INVALID_CU); + return DWARF_E_INVALID_CU; + } + + /* Relocate the DWARF sections if necessary: */ + if (!relocated) { + if ((ret = dwarf_relocate(dbg, error)) != DWARF_E_NONE) + return ret; + relocated = 1; + } + + /* Compute the offset to the next compilation unit: */ + next_offset = offset + length; + + /* Initialise the compilation unit. */ + cu->cu_length = length; + cu->cu_header_length = (dbg->dbg_offsize == 4) ? 4 : 12; + cu->cu_version = dwarf_read(&d, &offset, 2); + cu->cu_abbrev_offset = dwarf_read(&d, &offset, dbg->dbg_offsize); + cu->cu_pointer_size = dwarf_read(&d, &offset, 1); + cu->cu_next_offset = next_offset; + + /* Initialise the list of abbrevs. */ + STAILQ_INIT(&cu->cu_abbrev); + + /* Initialise the list of dies. */ + STAILQ_INIT(&cu->cu_die); + + /* Initialise the hash table of dies. */ + for (i = 0; i < DWARF_DIE_HASH_SIZE; i++) + STAILQ_INIT(&cu->cu_die_hash[i]); + + /* Add the compilation unit to the list. */ + STAILQ_INSERT_TAIL(&dbg->dbg_cu, cu, cu_next); + + if (cu->cu_version != 2 && cu->cu_version != 3) { + DWARF_SET_ERROR(error, DWARF_E_CU_VERSION); + ret = DWARF_E_CU_VERSION; + break; + } + + /* Parse the .debug_abbrev info for this CU: */ + if ((ret = dwarf_init_abbrev(dbg, cu, error)) != DWARF_E_NONE) + break; + + level = 0; + + while (offset < next_offset && offset < d->d_size) { + Dwarf_Abbrev a; + Dwarf_Attribute at; + Dwarf_Die die; + uint64_t abnum; + uint64_t die_offset = offset;; + + abnum = dwarf_read_uleb128(&d, &offset); + + if (abnum == 0) { + level--; + continue; + } + + if ((a = dwarf_abbrev_find(cu, abnum)) == NULL) { + DWARF_SET_ERROR(error, DWARF_E_MISSING_ABBREV); + return DWARF_E_MISSING_ABBREV; + } + + if ((ret = dwarf_die_add(cu, level, die_offset, + abnum, a, &die, error)) != DWARF_E_NONE) + return ret; + + STAILQ_FOREACH(at, &a->a_attrib, at_next) { + if ((ret = dwarf_init_attr(dbg, &d, &offset, + cu, die, at, at->at_form, error)) != DWARF_E_NONE) + return ret; + } + + if (a->a_children == DW_CHILDREN_yes) + level++; + } + + offset = next_offset; + } + + return ret; +} + +static int +dwarf_elf_read(Dwarf_Debug dbg, Dwarf_Error *error) +{ + GElf_Shdr shdr; + Elf_Scn *scn = NULL; + char *sname; + int i; + int ret = DWARF_E_NONE; + + /* Get a copy of the ELF header. */ + if (gelf_getehdr(dbg->dbg_elf, &dbg->dbg_ehdr) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + /* Check the ELF data format: */ + switch (dbg->dbg_ehdr.e_ident[EI_DATA]) { + case ELFDATA2MSB: + dwarf_read = dwarf_read_msb; + dwarf_write = dwarf_write_msb; + break; + + case ELFDATA2LSB: + case ELFDATANONE: + default: + dwarf_read = dwarf_read_lsb; + dwarf_write = dwarf_write_lsb; + break; + } + + /* Get the section index to the string table. */ + if (elf_getshstrndx(dbg->dbg_elf, &dbg->dbg_stnum) == 0) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + /* Look for the debug sections. */ + while ((scn = elf_nextscn(dbg->dbg_elf, scn)) != NULL) { + /* Get a copy of the section header: */ + if (gelf_getshdr(scn, &shdr) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + /* Get a pointer to the section name: */ + if ((sname = elf_strptr(dbg->dbg_elf, dbg->dbg_stnum, shdr.sh_name)) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + + /* + * Look up the section name to check if it's + * one we need for DWARF. + */ + for (i = 0; i < DWARF_DEBUG_SNAMES; i++) { + if (strcmp(sname, debug_snames[i]) == 0) { + dbg->dbg_s[i].s_sname = sname; + dbg->dbg_s[i].s_shnum = elf_ndxscn(scn); + dbg->dbg_s[i].s_scn = scn; + memcpy(&dbg->dbg_s[i].s_shdr, &shdr, sizeof(shdr)); + if ((dbg->dbg_s[i].s_data = elf_getdata(scn, NULL)) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ELF; + } + break; + } + } + } + + /* Check if any of the required sections are missing: */ + if (dbg->dbg_s[DWARF_debug_abbrev].s_scn == NULL || + dbg->dbg_s[DWARF_debug_info].s_scn == NULL) { + /* Missing debug information. */ + DWARF_SET_ERROR(error, DWARF_E_DEBUG_INFO); + return DWARF_E_DEBUG_INFO; + } + + /* Initialise the compilation-units: */ + ret = dwarf_init_info(dbg, error); + + return ret; +} + +int +dwarf_elf_init(Elf *elf, int mode, Dwarf_Debug *ret_dbg, Dwarf_Error *error) +{ + Dwarf_Debug dbg; + int ret = DWARF_E_NONE; + + if (error == NULL) + /* Can only return a generic error. */ + return DWARF_E_ERROR; + + if (elf == NULL || ret_dbg == NULL) { + DWARF_SET_ERROR(error, DWARF_E_ARGUMENT); + ret = DWARF_E_ARGUMENT; + } else if ((dbg = calloc(sizeof(struct _Dwarf_Debug), 1)) == NULL) { + DWARF_SET_ERROR(error, DWARF_E_MEMORY); + ret = DWARF_E_MEMORY; + } else { + dbg->dbg_elf = elf; + dbg->dbg_elf_close = 0; + dbg->dbg_mode = mode; + + STAILQ_INIT(&dbg->dbg_cu); + + *ret_dbg = dbg; + + /* Read the ELF sections. */ + ret = dwarf_elf_read(dbg, error); + } + + return ret; +} + +int +dwarf_init(int fd, int mode, Dwarf_Debug *ret_dbg, Dwarf_Error *error) +{ + Dwarf_Error lerror; + Elf *elf; + Elf_Cmd c; + int ret; + + if (error == NULL) + /* Can only return a generic error. */ + return DWARF_E_ERROR; + + if (fd < 0 || ret_dbg == NULL) { + DWARF_SET_ERROR(error, DWARF_E_ARGUMENT); + return DWARF_E_ERROR; + } + + /* Translate the DWARF mode to ELF mode. */ + switch (mode) { + default: + case DW_DLC_READ: + c = ELF_C_READ; + break; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ERROR; + } + + if ((elf = elf_begin(fd, c, NULL)) == NULL) { + DWARF_SET_ELF_ERROR(error, elf_errno()); + return DWARF_E_ERROR; + } + + ret = dwarf_elf_init(elf, mode, ret_dbg, error); + + if (*ret_dbg != NULL) + /* Remember to close the ELF file. */ + (*ret_dbg)->dbg_elf_close = 1; + + if (ret != DWARF_E_NONE) { + if (*ret_dbg != NULL) { + dwarf_finish(ret_dbg, &lerror); + } else + elf_end(elf); + } + + return ret; +} |