#include "config.h"
#include <stdio.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#elif defined(_WIN32) && defined(_MSC_VER)
#include <io.h>
#endif
#if defined(_WIN32) && defined(HAVE_STDAFX_H)
#include "stdafx.h"
#endif
#include "libdwarf.h"
#include "libdwarfdefs.h"
#include "dwarf_base_types.h"
#include "dwarf_opaque.h"
#include "dwarf_error.h"
#include "dwarf_reading.h"
#include "memcpy_swap.h"
#include "dwarf_object_read_common.h"
#include "dwarf_object_detector.h"
#include "dwarf_elfstructs.h"
#include "dwarf_elf_defines.h"
#include "dwarf_elf_rel_detector.h"
#include "dwarf_elfread.h"
#ifndef TYP
#define TYP(n,l) char n[l]
#endif
#ifdef WORDS_BIGENDIAN
#define READ_UNALIGNED_SAFE(dbg,dest, source, length) \
do { \
Dwarf_Unsigned _ltmp = 0; \
dbg->de_copy_word( (((char *)(&_ltmp)) + \
sizeof(_ltmp) - length),source, length); \
dest = _ltmp; \
} while (0)
#define WRITE_UNALIGNED_LOCAL(dbg,dest,source, srclength,len_out) \
{ \
dbg->de_copy_word(dest, \
((char *)source) +srclength-len_out, \
len_out) ; \
}
#else
#define READ_UNALIGNED_SAFE(dbg,dest, source, srclength) \
do { \
Dwarf_Unsigned _ltmp = 0; \
dbg->de_copy_word( (char *)(&_ltmp), \
source, srclength) ; \
dest = _ltmp; \
} while (0)
#define WRITE_UNALIGNED_LOCAL(dbg,dest,source, srclength,len_out) \
{ \
dbg->de_copy_word( (dest) , \
((char *)source) , \
len_out) ; \
}
#endif
#ifdef WORDS_BIGENDIAN
#define ASNAR(func,t,s) \
do { \
unsigned tbyte = sizeof(t) - sizeof(s); \
t = 0; \
func(((char *)&t)+tbyte ,&s[0],sizeof(s)); \
} while (0)
#else
#define ASNAR(func,t,s) \
do { \
t = 0; \
func(&t,&s[0],sizeof(s)); \
} while (0)
#endif
static int
_dwarf_elf_object_access_init(
int fd,
unsigned ftype,
unsigned endian,
unsigned offsetsize,
size_t filesize,
Dwarf_Unsigned access,
Dwarf_Obj_Access_Interface **binary_interface,
int *localerrnum);
static Dwarf_Endianness elf_get_nolibelf_byte_order (void *obj)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
return elf->f_endian;
}
static Dwarf_Small elf_get_nolibelf_length_size (void *obj)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
return elf->f_offsetsize/8;
}
static Dwarf_Small elf_get_nolibelf_pointer_size (void *obj)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
return elf->f_pointersize/8;
}
static Dwarf_Unsigned elf_get_nolibelf_section_count (void *obj)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
return elf->f_loc_shdr.g_count;
}
static int elf_get_nolibelf_section_info (void *obj,
Dwarf_Half section_index,
Dwarf_Obj_Access_Section *return_section,
UNUSEDARG int *error)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
if (section_index < elf->f_loc_shdr.g_count) {
struct generic_shdr *sp = 0;
sp = elf->f_shdr + section_index;
return_section->addr = sp->gh_addr;
return_section->type = sp->gh_type;
return_section->size = sp->gh_size;
return_section->name = sp->gh_namestring;
return_section->link = sp->gh_link;
return_section->info = sp->gh_info;
return_section->entrysize = sp->gh_entsize;
return DW_DLV_OK;
}
return DW_DLV_NO_ENTRY;
}
static int
elf_load_nolibelf_section (void *obj, Dwarf_Half section_index,
Dwarf_Small **return_data, int *error)
{
dwarf_elf_object_access_internals_t *elf =
(dwarf_elf_object_access_internals_t*)(obj);
if (0 < section_index &&
section_index < elf->f_loc_shdr.g_count) {
int res = 0;
struct generic_shdr *sp =
elf->f_shdr + section_index;
if (sp->gh_content) {
*return_data = (Dwarf_Small *)sp->gh_content;
return DW_DLV_OK;
}
if (!sp->gh_size) {
return DW_DLV_NO_ENTRY;
}
if ((sp->gh_size + sp->gh_offset) >
elf->f_filesize) {
*error = DW_DLE_ELF_SECTION_ERROR;
return DW_DLV_ERROR;
}
sp->gh_content = malloc((size_t)sp->gh_size);
if(!sp->gh_content) {
*error = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
res = RRMOA(elf->f_fd,
sp->gh_content, (off_t)sp->gh_offset,
(size_t)sp->gh_size, (off_t)elf->f_filesize, error);
if (res != DW_DLV_OK) {
free(sp->gh_content);
sp->gh_content = 0;
return res;
}
*return_data = (Dwarf_Small *)sp->gh_content;
return DW_DLV_OK;
}
return DW_DLV_NO_ENTRY;
}
static int
_dwarf_get_elf_flags_func_nl(
void* obj_in,
Dwarf_Half section_index,
Dwarf_Unsigned *flags_out,
Dwarf_Unsigned *addralign_out,
int *error)
{
dwarf_elf_object_access_internals_t *ep = 0;
struct generic_shdr *shp = 0;
ep = (dwarf_elf_object_access_internals_t *)obj_in;
if (section_index == 0) {
return DW_DLV_OK;
}
if (section_index >= ep->f_loc_shdr.g_count) {
*error = DW_DLE_SECTION_INDEX_BAD;
return DW_DLV_ERROR;
}
shp = ep->f_shdr + section_index;
*flags_out = shp->gh_flags;
*addralign_out = shp->gh_addralign;
return DW_DLV_OK;
}
#define MATCH_REL_SEC(i_,s_,r_) \
if (i_ == s_.dss_index) { \
*r_ = &s_; \
return DW_DLV_OK; \
}
static int
find_section_to_relocate(Dwarf_Debug dbg,Dwarf_Half section_index,
struct Dwarf_Section_s **relocatablesec, int *error)
{
MATCH_REL_SEC(section_index,dbg->de_debug_info,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_abbrev,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_line,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_loc,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_aranges,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_macinfo,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_pubnames,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_ranges,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_frame,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_frame_eh_gnu,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_pubtypes,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_funcnames,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_typenames,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_varnames,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_weaknames,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_types,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_macro,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_rnglists,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_loclists,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_aranges,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_sup,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_str_offsets,relocatablesec);
MATCH_REL_SEC(section_index,dbg->de_debug_addr,relocatablesec);
*error = DW_DLE_RELOC_SECTION_MISMATCH;
return DW_DLV_ERROR;
}
static int
update_entry(Dwarf_Debug dbg,
dwarf_elf_object_access_internals_t*obj,
struct generic_rela *rela,
Dwarf_Small *target_section,
Dwarf_Unsigned target_section_size,
int *error)
{
unsigned int type = 0;
unsigned int sym_idx = 0;
Dwarf_Unsigned offset = 0;
Dwarf_Signed addend = 0;
Dwarf_Unsigned reloc_size = 0;
Dwarf_Half machine = obj->f_machine;
struct generic_symentry *symp = 0;
int is_rela = rela->gr_is_rela;
offset = rela->gr_offset;
addend = rela->gr_addend;
type = (unsigned int)rela->gr_type;
sym_idx = (unsigned int)rela->gr_sym;
if (sym_idx >= obj->f_loc_symtab.g_count) {
*error = DW_DLE_RELOC_SECTION_SYMBOL_INDEX_BAD;
return DW_DLV_ERROR;
}
symp = obj->f_symtab + sym_idx;
if (offset >= target_section_size) {
*error = DW_DLE_RELOC_INVALID;
return DW_DLV_ERROR;
}
if (_dwarf_is_32bit_abs_reloc(type, machine)) {
reloc_size = 4;
} else if (_dwarf_is_64bit_abs_reloc(type, machine)) {
reloc_size = 8;
} else {
*error = DW_DLE_RELOC_SECTION_RELOC_TARGET_SIZE_UNKNOWN;
return DW_DLV_ERROR;
}
if ( (offset + reloc_size) < offset) {
*error = DW_DLE_RELOC_INVALID;
return DW_DLV_ERROR;
}
if ( (offset + reloc_size) > target_section_size) {
*error = DW_DLE_RELOC_INVALID;
return DW_DLV_ERROR;
}
{
Dwarf_Small *targ = target_section+offset;
Dwarf_Unsigned presentval = 0;
Dwarf_Unsigned outval = 0;
if (!is_rela) {
READ_UNALIGNED_SAFE(dbg,presentval,
targ,reloc_size);
}
outval = presentval + symp->gs_value + addend;
WRITE_UNALIGNED_LOCAL(dbg,targ,
&outval,sizeof(outval),reloc_size);
}
return DW_DLV_OK;
}
static int
apply_rela_entries(
Dwarf_Debug dbg,
Dwarf_Half r_section_index,
dwarf_elf_object_access_internals_t*obj,
struct Dwarf_Section_s * relocatablesec,
int *error)
{
int return_res = DW_DLV_OK;
struct generic_shdr * rels_shp = 0;
Dwarf_Unsigned relcount;
Dwarf_Unsigned i = 0;
if (r_section_index >= obj->f_loc_shdr.g_count) {
*error = DW_DLE_SECTION_INDEX_BAD;
return DW_DLV_ERROR;
}
rels_shp = obj->f_shdr + r_section_index;
relcount = rels_shp->gh_relcount;
if (!relcount) {
return DW_DLV_OK;
}
if (!rels_shp->gh_rels) {
*error = DW_DLE_RELOCS_ERROR;
return DW_DLV_ERROR;
}
for (i = 0; i < relcount; i++) {
int res = update_entry(dbg,obj,
rels_shp->gh_rels+i,
relocatablesec->dss_data,
relocatablesec->dss_size,
error);
if (res != DW_DLV_OK) {
return_res = res;
}
}
return return_res;
}
static int
elf_relocations_nolibelf(void* obj_in,
Dwarf_Half section_index,
Dwarf_Debug dbg,
int* error)
{
int res = DW_DLV_ERROR;
dwarf_elf_object_access_internals_t*obj = 0;
struct Dwarf_Section_s * relocatablesec = 0;
unsigned section_with_reloc_records = 0;
if (section_index == 0) {
return DW_DLV_NO_ENTRY;
}
obj = (dwarf_elf_object_access_internals_t*)obj_in;
res = find_section_to_relocate(dbg, section_index,
&relocatablesec, error);
if (res != DW_DLV_OK) {
return res;
}
section_with_reloc_records = relocatablesec->dss_reloc_index;
if (!section_with_reloc_records) {
*error = DW_DLE_RELOC_SECTION_MISSING_INDEX;
return DW_DLV_ERROR;
}
if (!obj->f_symtab || !obj->f_symtab_sect_strings) {
*error = DW_DLE_DEBUG_SYMTAB_ERR;
return DW_DLV_ERROR;
}
if (obj->f_symtab_sect_index != relocatablesec->dss_reloc_link) {
*error = DW_DLE_RELOC_MISMATCH_RELOC_INDEX;
return DW_DLV_ERROR;
}
res = apply_rela_entries(dbg,section_with_reloc_records,
obj, relocatablesec,error);
return res;
}
void
_dwarf_destruct_elf_nlaccess(
struct Dwarf_Obj_Access_Interface_s *aip)
{
dwarf_elf_object_access_internals_t *ep = 0;
struct generic_shdr *shp = 0;
Dwarf_Unsigned shcount = 0;
Dwarf_Unsigned i = 0;
ep = (dwarf_elf_object_access_internals_t *)aip->object;
free(ep->f_ehdr);
shp = ep->f_shdr;
shcount = ep->f_loc_shdr.g_count;
for(i = 0; i < shcount; ++i,++shp) {
free(shp->gh_rels);
shp->gh_rels = 0;
free(shp->gh_content);
shp->gh_content = 0;
free(shp->gh_sht_group_array);
shp->gh_sht_group_array = 0;
shp->gh_sht_group_array_count = 0;
}
free(ep->f_shdr);
ep->f_loc_shdr.g_count = 0;
free(ep->f_phdr);
free(ep->f_elf_shstrings_data);
free(ep->f_dynamic);
free(ep->f_symtab_sect_strings);
free(ep->f_dynsym_sect_strings);
free(ep->f_symtab);
free(ep->f_dynsym);
if (ep->f_destruct_close_fd) {
close(ep->f_fd);
}
ep->f_ident[0] = 'X';
free(ep->f_path);
free(ep);
free(aip);
}
int
_dwarf_elf_nlsetup(int fd,
char *true_path,
unsigned ftype,
unsigned endian,
unsigned offsetsize,
size_t filesize,
Dwarf_Unsigned access,
unsigned groupnumber,
Dwarf_Handler errhand,
Dwarf_Ptr errarg,
Dwarf_Debug *dbg,Dwarf_Error *error)
{
Dwarf_Obj_Access_Interface *binary_interface = 0;
dwarf_elf_object_access_internals_t *intfc = 0;
int res = DW_DLV_OK;
int localerrnum = 0;
res = _dwarf_elf_object_access_init(
fd,
ftype,endian,offsetsize,filesize,access,
&binary_interface,
&localerrnum);
if (res != DW_DLV_OK) {
if (res == DW_DLV_NO_ENTRY) {
return res;
}
_dwarf_error(NULL, error, localerrnum);
return DW_DLV_ERROR;
}
res = dwarf_object_init_b(binary_interface, errhand, errarg,
groupnumber, dbg, error);
if (res != DW_DLV_OK){
_dwarf_destruct_elf_nlaccess(binary_interface);
return res;
}
intfc = binary_interface->object;
intfc->f_path = strdup(true_path);
return res;
}
static Dwarf_Obj_Access_Methods const elf_nlmethods = {
elf_get_nolibelf_section_info,
elf_get_nolibelf_byte_order,
elf_get_nolibelf_length_size,
elf_get_nolibelf_pointer_size,
elf_get_nolibelf_section_count,
elf_load_nolibelf_section,
elf_relocations_nolibelf
};
static int
_dwarf_elf_object_access_internals_init(
dwarf_elf_object_access_internals_t * internals,
int fd,
unsigned ftype,
unsigned endian,
unsigned offsetsize,
size_t filesize,
UNUSEDARG Dwarf_Unsigned access,
int *errcode)
{
dwarf_elf_object_access_internals_t * intfc = internals;
Dwarf_Unsigned i = 0;
struct Dwarf_Obj_Access_Interface_s *localdoas;
int res = 0;
localdoas = (struct Dwarf_Obj_Access_Interface_s *)
malloc(sizeof(struct Dwarf_Obj_Access_Interface_s));
if (!localdoas) {
free(internals);
*errcode = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
memset(localdoas,0,sizeof(struct Dwarf_Obj_Access_Interface_s));
intfc->f_ident[0] = 'F';
intfc->f_ident[1] = '1';
intfc->f_fd = fd;
intfc->f_is_64bit = ((offsetsize==64)?TRUE:FALSE);
intfc->f_offsetsize = offsetsize;
intfc->f_pointersize = offsetsize;
intfc->f_filesize = filesize;
intfc->f_ftype = ftype;
intfc->f_destruct_close_fd = FALSE;
#ifdef WORDS_BIGENDIAN
if (endian == DW_ENDIAN_LITTLE ) {
intfc->f_copy_word = _dwarf_memcpy_swap_bytes;
intfc->f_endian = DW_OBJECT_LSB;
} else {
intfc->f_copy_word = _dwarf_memcpy_noswap_bytes;
intfc->f_endian = DW_OBJECT_MSB;
}
#else
if (endian == DW_ENDIAN_LITTLE ) {
intfc->f_copy_word = _dwarf_memcpy_noswap_bytes;
intfc->f_endian = DW_OBJECT_LSB;
} else {
intfc->f_copy_word = _dwarf_memcpy_swap_bytes;
intfc->f_endian = DW_OBJECT_MSB;
}
#endif
_dwarf_get_elf_flags_func_ptr = _dwarf_get_elf_flags_func_nl;
res = _dwarf_load_elf_header(intfc,errcode);
if (res != DW_DLV_OK) {
localdoas->object = intfc;
localdoas->methods = 0;
_dwarf_destruct_elf_nlaccess(localdoas);
localdoas = 0;
return res;
}
res = _dwarf_load_elf_sectheaders(intfc,errcode);
if (res != DW_DLV_OK) {
localdoas->object = intfc;
localdoas->methods = 0;
_dwarf_destruct_elf_nlaccess(localdoas);
localdoas = 0;
return res;
}
res = _dwarf_load_elf_symstr(intfc,errcode);
if (res == DW_DLV_ERROR) {
localdoas->object = intfc;
localdoas->methods = 0;
_dwarf_destruct_elf_nlaccess(localdoas);
localdoas = 0;
return res;
}
res = _dwarf_load_elf_symtab_symbols(intfc,errcode);
if (res == DW_DLV_ERROR) {
localdoas->object = intfc;
localdoas->methods = 0;
_dwarf_destruct_elf_nlaccess(localdoas);
localdoas = 0;
return res;
}
for ( i = 1; i < intfc->f_loc_shdr.g_count; ++i) {
struct generic_shdr *shp = 0;
Dwarf_Unsigned section_type = 0;
enum RelocRela localrel = RelocIsRela;
shp = intfc->f_shdr +i;
section_type = shp->gh_type;
if (section_type == SHT_REL ||
(!strncmp(".rel.",shp->gh_namestring,5))) {
localrel = RelocIsRel;
} else if (section_type == SHT_RELA ||
(!strncmp(".rela.",shp->gh_namestring,6))) {
localrel = RelocIsRela;
} else {
continue;
}
res = _dwarf_load_elf_relx(intfc,i,localrel,errcode);
if (res == DW_DLV_ERROR) {
localdoas->object = intfc;
localdoas->methods = 0;
_dwarf_destruct_elf_nlaccess(localdoas);
localdoas = 0;
return res;
}
}
free(localdoas);
localdoas = 0;
return DW_DLV_OK;
}
static int
_dwarf_elf_object_access_init(
int fd,
unsigned ftype,
unsigned endian,
unsigned offsetsize,
size_t filesize,
Dwarf_Unsigned access,
Dwarf_Obj_Access_Interface **binary_interface,
int *localerrnum)
{
int res = 0;
dwarf_elf_object_access_internals_t *internals = 0;
Dwarf_Obj_Access_Interface *intfc = 0;
internals = malloc(sizeof(dwarf_elf_object_access_internals_t));
if (!internals) {
*localerrnum = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
memset(internals,0,sizeof(*internals));
res = _dwarf_elf_object_access_internals_init(internals,
fd,
ftype, endian, offsetsize, filesize,
access,
localerrnum);
if (res != DW_DLV_OK){
return res;
}
intfc = malloc(sizeof(Dwarf_Obj_Access_Interface));
if (!intfc) {
free(internals);
*localerrnum = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
intfc->object = internals;
intfc->methods = &elf_nlmethods;
*binary_interface = intfc;
return DW_DLV_OK;
}