freebsd_amp_hwpstate/contrib/elftoolchain/libdwarf/libdwarf_elf_init.c

458 lines
11 KiB
C

/*-
* Copyright (c) 2009 Kai Wang
* 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.
*/
#include "_libdwarf.h"
#include <zlib.h>
ELFTC_VCSID("$Id: libdwarf_elf_init.c 3475 2016-05-18 18:11:26Z emaste $");
static const char *debug_name[] = {
".debug_abbrev",
".debug_aranges",
".debug_frame",
".debug_info",
".debug_types",
".debug_line",
".debug_pubnames",
".eh_frame",
".debug_macinfo",
".debug_str",
".debug_loc",
".debug_pubtypes",
".debug_ranges",
".debug_static_func",
".debug_static_vars",
".debug_typenames",
".debug_weaknames",
NULL
};
static void
_dwarf_elf_apply_rel_reloc(Dwarf_Debug dbg, void *buf, uint64_t bufsize,
Elf_Data *rel_data, Elf_Data *symtab_data, int endian)
{
Dwarf_Unsigned type;
GElf_Rel rel;
GElf_Sym sym;
size_t symndx;
uint64_t offset;
uint64_t addend;
int size, j;
j = 0;
while (gelf_getrel(rel_data, j++, &rel) != NULL) {
symndx = GELF_R_SYM(rel.r_info);
type = GELF_R_TYPE(rel.r_info);
if (gelf_getsym(symtab_data, symndx, &sym) == NULL)
continue;
size = _dwarf_get_reloc_size(dbg, type);
if (size == 0)
continue; /* Unknown or non-absolute relocation. */
offset = rel.r_offset;
if (offset + size >= bufsize)
continue;
if (endian == ELFDATA2MSB)
addend = _dwarf_read_msb(buf, &offset, size);
else
addend = _dwarf_read_lsb(buf, &offset, size);
offset = rel.r_offset;
if (endian == ELFDATA2MSB)
_dwarf_write_msb(buf, &offset, sym.st_value + addend,
size);
else
_dwarf_write_lsb(buf, &offset, sym.st_value + addend,
size);
}
}
static void
_dwarf_elf_apply_rela_reloc(Dwarf_Debug dbg, void *buf, uint64_t bufsize,
Elf_Data *rel_data, Elf_Data *symtab_data, int endian)
{
Dwarf_Unsigned type;
GElf_Rela rela;
GElf_Sym sym;
size_t symndx;
uint64_t offset;
int size, j;
j = 0;
while (gelf_getrela(rel_data, j++, &rela) != NULL) {
symndx = GELF_R_SYM(rela.r_info);
type = GELF_R_TYPE(rela.r_info);
if (gelf_getsym(symtab_data, symndx, &sym) == NULL)
continue;
offset = rela.r_offset;
size = _dwarf_get_reloc_size(dbg, type);
if (size == 0)
continue; /* Unknown or non-absolute relocation. */
if (offset + size >= bufsize)
continue;
if (endian == ELFDATA2MSB)
_dwarf_write_msb(buf, &offset,
sym.st_value + rela.r_addend, size);
else
_dwarf_write_lsb(buf, &offset,
sym.st_value + rela.r_addend, size);
}
}
static int
_dwarf_elf_relocate(Dwarf_Debug dbg, Elf *elf, Dwarf_Elf_Data *ed, size_t shndx,
size_t symtab, Elf_Data *symtab_data, Dwarf_Error *error)
{
GElf_Ehdr eh;
GElf_Shdr sh;
Elf_Scn *scn;
Elf_Data *rel;
int elferr;
if (symtab == 0 || symtab_data == NULL)
return (DW_DLE_NONE);
if (gelf_getehdr(elf, &eh) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
return (DW_DLE_ELF);
}
scn = NULL;
(void) elf_errno();
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
return (DW_DLE_ELF);
}
if ((sh.sh_type != SHT_REL && sh.sh_type != SHT_RELA) ||
sh.sh_size == 0)
continue;
if (sh.sh_info == shndx && sh.sh_link == symtab) {
if ((rel = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0) {
_DWARF_SET_ERROR(NULL, error,
DW_DLE_ELF, elferr);
return (DW_DLE_ELF);
} else
return (DW_DLE_NONE);
}
/*
* A copy may already have been created if the section
* is compressed.
*/
if (ed->ed_alloc == NULL) {
ed->ed_alloc = malloc(ed->ed_size);
if (ed->ed_alloc == NULL) {
DWARF_SET_ERROR(dbg, error,
DW_DLE_MEMORY);
return (DW_DLE_MEMORY);
}
memcpy(ed->ed_alloc, ed->ed_data->d_buf,
ed->ed_size);
}
if (sh.sh_type == SHT_REL)
_dwarf_elf_apply_rel_reloc(dbg,
ed->ed_alloc, ed->ed_size,
rel, symtab_data, eh.e_ident[EI_DATA]);
else
_dwarf_elf_apply_rela_reloc(dbg,
ed->ed_alloc, ed->ed_size, rel, symtab_data,
eh.e_ident[EI_DATA]);
return (DW_DLE_NONE);
}
}
elferr = elf_errno();
if (elferr != 0) {
DWARF_SET_ELF_ERROR(dbg, error);
return (DW_DLE_ELF);
}
return (DW_DLE_NONE);
}
static int
_dwarf_elf_decompress(Dwarf_Debug dbg, Dwarf_Elf_Object *e, Elf_Scn *scn,
Dwarf_Elf_Data *ed, GElf_Shdr *shdr, Dwarf_Error *error)
{
GElf_Chdr chdr;
size_t hsize;
unsigned long csize;
if (gelf_getchdr(scn, &chdr) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
return (DW_DLE_ELF);
}
if (chdr.ch_type != ELFCOMPRESS_ZLIB) {
DWARF_SET_ERROR(dbg, error, DW_DLE_COMPRESSION);
return (DW_DLE_COMPRESSION);
}
if ((ed->ed_alloc = malloc(chdr.ch_size)) == NULL) {
DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
return (DW_DLE_MEMORY);
}
csize = chdr.ch_size;
hsize = e->eo_ehdr.e_ident[EI_CLASS] == ELFCLASS64 ?
sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr);
if (uncompress(ed->ed_alloc, &csize, (char *)ed->ed_data->d_buf + hsize,
ed->ed_data->d_size - hsize) != Z_OK) {
DWARF_SET_ERROR(dbg, error, DW_DLE_COMPRESSION);
return (DW_DLE_COMPRESSION);
}
/* Sanity check. */
if (csize != chdr.ch_size) {
DWARF_SET_ERROR(dbg, error, DW_DLE_COMPRESSION);
return (DW_DLE_COMPRESSION);
}
ed->ed_size = chdr.ch_size;
shdr->sh_size = chdr.ch_size;
shdr->sh_addralign = chdr.ch_addralign;
return (DW_DLE_NONE);
}
int
_dwarf_elf_init(Dwarf_Debug dbg, Elf *elf, Dwarf_Error *error)
{
Dwarf_Obj_Access_Interface *iface;
Dwarf_Elf_Data *ed;
Dwarf_Elf_Object *e;
const char *name;
GElf_Shdr *es, sh;
Elf_Scn *scn;
Elf_Data *symtab_data;
size_t symtab_ndx;
int elferr, i, j, n, ret;
ret = DW_DLE_NONE;
if ((iface = calloc(1, sizeof(*iface))) == NULL) {
DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
return (DW_DLE_MEMORY);
}
if ((e = calloc(1, sizeof(*e))) == NULL) {
free(iface);
DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
return (DW_DLE_MEMORY);
}
e->eo_elf = elf;
e->eo_methods.get_section_info = _dwarf_elf_get_section_info;
e->eo_methods.get_byte_order = _dwarf_elf_get_byte_order;
e->eo_methods.get_length_size = _dwarf_elf_get_length_size;
e->eo_methods.get_pointer_size = _dwarf_elf_get_pointer_size;
e->eo_methods.get_section_count = _dwarf_elf_get_section_count;
e->eo_methods.load_section = _dwarf_elf_load_section;
iface->object = e;
iface->methods = &e->eo_methods;
dbg->dbg_iface = iface;
if (gelf_getehdr(elf, &e->eo_ehdr) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
dbg->dbg_machine = e->eo_ehdr.e_machine;
if (!elf_getshstrndx(elf, &e->eo_strndx)) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
n = 0;
symtab_ndx = 0;
symtab_data = NULL;
scn = NULL;
(void) elf_errno();
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
if (sh.sh_type == SHT_NOBITS)
continue;
if ((name = elf_strptr(elf, e->eo_strndx, sh.sh_name)) ==
NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
if (!strcmp(name, ".symtab")) {
symtab_ndx = elf_ndxscn(scn);
if ((symtab_data = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0) {
_DWARF_SET_ERROR(NULL, error,
DW_DLE_ELF, elferr);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
}
continue;
}
for (i = 0; debug_name[i] != NULL; i++) {
if (!strcmp(name, debug_name[i]))
n++;
}
}
elferr = elf_errno();
if (elferr != 0) {
DWARF_SET_ELF_ERROR(dbg, error);
return (DW_DLE_ELF);
}
e->eo_seccnt = n;
if (n == 0)
return (DW_DLE_NONE);
if ((e->eo_data = calloc(n, sizeof(Dwarf_Elf_Data))) == NULL ||
(e->eo_shdr = calloc(n, sizeof(GElf_Shdr))) == NULL) {
DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
ret = DW_DLE_MEMORY;
goto fail_cleanup;
}
scn = NULL;
j = 0;
while ((scn = elf_nextscn(elf, scn)) != NULL && j < n) {
if (gelf_getshdr(scn, &sh) == NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
if (sh.sh_type == SHT_NOBITS)
continue;
if ((name = elf_strptr(elf, e->eo_strndx, sh.sh_name)) ==
NULL) {
DWARF_SET_ELF_ERROR(dbg, error);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
ed = &e->eo_data[j];
es = &e->eo_shdr[j];
memcpy(es, &sh, sizeof(sh));
for (i = 0; debug_name[i] != NULL; i++) {
if (strcmp(name, debug_name[i]))
continue;
(void) elf_errno();
if ((ed->ed_data = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0) {
_DWARF_SET_ERROR(dbg, error,
DW_DLE_ELF, elferr);
ret = DW_DLE_ELF;
goto fail_cleanup;
}
}
if ((sh.sh_flags & SHF_COMPRESSED) != 0) {
if ((ret = _dwarf_elf_decompress(dbg, e, scn,
ed, es, error)) != DW_DLE_NONE)
goto fail_cleanup;
} else {
ed->ed_size = ed->ed_data->d_size;
}
if (_libdwarf.applyreloc) {
if ((ret = _dwarf_elf_relocate(dbg, elf,
&e->eo_data[j], elf_ndxscn(scn), symtab_ndx,
symtab_data, error)) != DW_DLE_NONE)
goto fail_cleanup;
}
j++;
}
}
assert(j == n);
return (DW_DLE_NONE);
fail_cleanup:
_dwarf_elf_deinit(dbg);
return (ret);
}
void
_dwarf_elf_deinit(Dwarf_Debug dbg)
{
Dwarf_Obj_Access_Interface *iface;
Dwarf_Elf_Object *e;
int i;
iface = dbg->dbg_iface;
assert(iface != NULL);
e = iface->object;
assert(e != NULL);
if (e->eo_data) {
for (i = 0; (Dwarf_Unsigned) i < e->eo_seccnt; i++) {
if (e->eo_data[i].ed_alloc)
free(e->eo_data[i].ed_alloc);
}
free(e->eo_data);
}
if (e->eo_shdr)
free(e->eo_shdr);
free(e);
free(iface);
dbg->dbg_iface = NULL;
}