9b2a96cc04
Reviewed by: jb Obtained from: Juniper Networks
753 lines
17 KiB
C
753 lines
17 KiB
C
/*-
|
|
* 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;
|
|
}
|
|
|
|
/* Build the function table. */
|
|
dwarf_build_function_table(dbg);
|
|
|
|
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);
|
|
STAILQ_INIT(&dbg->dbg_func);
|
|
|
|
*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;
|
|
}
|