Files
sigmaker-ida/idasdk76/ldr/elf/reader.cpp
2021-10-31 21:20:46 +02:00

3559 lines
99 KiB
C++

#ifndef ELF_READER_CPP
#define ELF_READER_CPP
#include <functional>
#include <pro.h>
#include <diskio.hpp>
#include "elfbase.h"
#include "elf.h"
#include "elfr_arm.h"
#include "elfr_mips.h"
#include "elfr_ia64.h"
#include "elfr_ppc.h"
#include "../idaldr.h"
//----------------------------------------------------------------------------
ssize_t reader_t::prepare_error_string(
char *buf,
size_t bufsize,
reader_t::errcode_t code,
va_list va) const
{
int len;
switch ( code )
{
case BAD_CLASS:
{
int eclass = va_arg(va, int);
len = qsnprintf(buf, bufsize,
"Unknown ELF class %d (should be %d for 32-bit, %d for 64-bit)",
eclass,
ELFCLASS32,
ELFCLASS64);
}
break;
case BAD_ENDIANNESS:
{
int endian = va_arg(va, int);
if ( endian != ELFDATA2LSB && endian != ELFDATA2MSB )
len = qsnprintf(buf, bufsize,
"Unknown ELF byte sex %d (should be %d for LSB, %d for MSB)",
endian,
ELFDATA2LSB,
ELFDATA2MSB);
else
len = qsnprintf(buf, bufsize,
"Bad ELF byte sex %d for the indicated machine",
endian);
}
break;
case BAD_EHSIZE:
{
int sz = va_arg(va, int);
int fb = va_arg(va, int);
len = qsnprintf(buf, bufsize,
"The ELF header entry size is invalid (%d, expected %d)",
sz, fb);
}
break;
case BAD_PHENTSIZE:
{
int sz = va_arg(va, int);
int fb = va_arg(va, int);
len = qsnprintf(buf, bufsize,
"PHT entry size is invalid: %d. Falling back to %d",
sz, fb);
}
break;
case BAD_PHLOC:
len = qstpncpy(buf, "The PHT table size or offset is invalid", bufsize) - buf;
break;
case BAD_SHENTSIZE:
len = qstpncpy(buf, "The SHT entry size is invalid", bufsize) - buf;
break;
case BAD_SHLOC:
len = qstpncpy(buf, "SHT table size or offset is invalid", bufsize) - buf;
break;
case BAD_DYN_PLT_TYPE:
len = qsnprintf(buf, bufsize, "Bad DT_PLTREL value (%d)", va_arg(va, int));
break;
case CONFLICTING_FILE_TYPE:
len = qstpncpy(buf, "ELF file with PHT cannot be ET_REL", bufsize) - buf;
break;
case BAD_SHSTRNDX:
{
uint idx = va_arg(va, uint);
uint num = va_arg(va, uint);
len = qsnprintf(buf, bufsize,
"Section header string table index %u is out of bounds",
idx);
if ( num > 0 )
len += qsnprintf(buf + len, bufsize - len,
" (max %u)", num - 1);
}
break;
case ERR_READ:
{
size_t d1 = va_arg(va, size_t); // size
size_t d2 = va_arg(va, size_t); // return code
qnotused(d1);
qnotused(d2);
len = qsnprintf(buf, bufsize,
"Bad file structure or read error (offset %" FMT_64 "u)",
va_arg(va, int64));
}
break;
default:
if ( is_error(code) )
INTERR(20034);
len = qsnprintf(buf, bufsize, "Unknown ELF warning %d", code);
break;
}
return len;
}
//----------------------------------------------------------------------------
static bool default_error_handler(const reader_t &reader, reader_t::errcode_t code, ...)
{
va_list va;
va_start(va, code);
char buf[MAXSTR];
reader.prepare_error_string(buf, sizeof(buf), code, va);
va_end(va);
warning("%s", buf);
return reader.is_warning(code); // resume after warnings
}
//----------------------------------------------------------------------------
const qstring &sym_rel::get_original_name(const reader_t &reader) const
{
if ( original_name.empty() )
{
symrel_idx_t sym_idx = reader.symbols.get_idx(this);
reader.get_name(&original_name, sym_idx.type, original.st_name);
}
return original_name;
}
//----------------------------------------------------------------------------
ea_t sym_rel::get_ea(const reader_t &reader, ea_t _debug_segbase) const
{
ea_t ea = value;
if ( reader.is_valid_rel_file() || is_psp_file(reader) )
{
const elf_shdr_t *sh = reader.sections.getn(sec);
if ( sh != NULL )
ea += sh->sh_addr;
}
else
{
ea += _debug_segbase;
}
return ea;
}
//----------------------------------------------------------------------------
void sym_rel::set_section_index(const reader_t &reader)
{
sec = 0;
if ( original.st_shndx == SHN_XINDEX )
{
symrel_idx_t sym_idx = reader.symbols.get_idx(this);
const elf_shdr_t *sh_shndx;
switch ( sym_idx.type )
{
case SLT_SYMTAB:
sh_shndx = reader.sections.get_wks(WKS_SYMTAB_SHNDX);
break;
case SLT_DYNSYM:
sh_shndx = reader.sections.get_wks(WKS_DYNSYM_SHNDX);
break;
default:
INTERR(20088);
}
// doc: "The section is an array of Elf32_Word values."
uint64 offset = sym_idx.idx * sizeof(uint32);
if ( sh_shndx != NULL
&& sh_shndx->sh_offset != 0
&& offset < sh_shndx->sh_size )
{
sec = reader.get_shndx_at(sh_shndx->sh_offset + offset);
}
if ( sec == 0 )
{
warning("AUTOHIDE SESSION\n"
"Illegal section indirect index for symbol %u",
sym_idx.idx);
}
}
else if ( original.st_shndx < SHN_LORESERVE )
{
sec = original.st_shndx;
}
}
//----------------------------------------------------------------------------
reader_t::reader_t(linput_t *_li, int64 _start_in_file)
: pheaders(this),
sections(this),
sym_strtab(),
dyn_strtab(),
li(_li),
sif(_start_in_file),
mappings(),
arch_specific(NULL),
load_bias(0),
eff_msb(false),
eff_64(false),
seg_64(false)
{
set_handler(default_error_handler);
filesize = qlsize(li);
}
//----------------------------------------------------------------------------
bool reader_t::is_warning(errcode_t code) const
{
return code <= LAST_WARNING;
}
//----------------------------------------------------------------------------
bool reader_t::is_error(errcode_t code) const
{
QASSERT(20035, code <= LAST_ERROR);
return code > LAST_WARNING;
}
//-------------------------------------------------------------------------
static bool _silent_handler(const reader_t &reader, reader_t::errcode_t code, ...)
{
return reader.is_warning(code); // resume after warnings
}
//----------------------------------------------------------------------------
void reader_t::set_handler(bool (*_handler)(const reader_t &reader, errcode_t code, ...))
{
handle_error = _handler == NULL ? _silent_handler : _handler;
}
bool reader_t::read_ident()
{
input_status_t save_excursion(*this);
if ( save_excursion.seek(0) == -1 )
return false;
uint64 fsize = size();
uint64 fpos = tell();
if ( fpos >= fsize )
return false;
uint64 bytes_left = fsize - fpos;
if ( bytes_left < sizeof(elf_ident_t) )
return false;
memset(&header, 0, sizeof(header));
if ( qlread(li, &header.e_ident, sizeof(elf_ident_t)) != sizeof(elf_ident_t) )
return false;
if ( !header.e_ident.is_valid() )
return false;
size_t ehdr_sz = is_64() ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
if ( bytes_left < ehdr_sz )
return false;
return true;
}
//----------------------------------------------------------------------------
int reader_t::safe_read(void *buf, size_t sz, bool apply_endianness) const
{
int rc = lreadbytes(li, buf, sz, apply_endianness && is_msb());
if ( rc < 0 )
handle_error(*this, ERR_READ, sz, size_t(rc), qltell(li));
return rc;
}
//----------------------------------------------------------------------------
int reader_t::read_addr(void *buf) const
{
return safe_read(buf, stdsizes.types.elf_addr);
}
//----------------------------------------------------------------------------
int reader_t::read_off(void *buf) const
{
return safe_read(buf, stdsizes.types.elf_off);
}
//----------------------------------------------------------------------------
int reader_t::read_xword(void *buf) const
{
return safe_read(buf, stdsizes.types.elf_xword);
}
//----------------------------------------------------------------------------
int reader_t::read_sxword(void *buf) const
{
return safe_read(buf, stdsizes.types.elf_sxword);
}
//----------------------------------------------------------------------------
int reader_t::read_word(uint32 *buf) const
{
return safe_read(buf, 4);
}
//----------------------------------------------------------------------------
int reader_t::read_half(uint16 *buf) const
{
return safe_read(buf, 2);
}
//----------------------------------------------------------------------------
int reader_t::read_byte(uint8 *buf) const
{
return safe_read(buf, 1);
}
//-------------------------------------------------------------------------
int reader_t::read_symbol(elf_sym_t *buf) const
{
#define _safe(expr) if ( (expr) < 0 ) goto FAILED_RS
if ( is_64() )
{
_safe(read_word(&buf->st_name));
_safe(read_byte(&buf->st_info));
_safe(read_byte(&buf->st_other));
_safe(read_half(&buf->st_shndx));
_safe(read_addr(&buf->st_value));
_safe(read_xword(&buf->st_size));
}
else
{
_safe(read_word(&buf->st_name));
_safe(read_addr(&buf->st_value));
_safe(read_word((uint32 *) &buf->st_size));
_safe(read_byte(&buf->st_info));
_safe(read_byte(&buf->st_other));
_safe(read_half(&buf->st_shndx));
}
return 0;
FAILED_RS:
return -1;
#undef _safe
}
#define IS_EXEC_OR_DYN(x) ((x) == ET_EXEC || (x) == ET_DYN)
struct linuxcpu_t
{
uint16 machine;
bool msb;
bool _64;
};
static const linuxcpu_t lincpus[] =
{
// machine msb 64
{ EM_386, false, false },
{ EM_486, false, false },
{ EM_X86_64, false, true },
};
//----------------------------------------------------------------------------
// Linux kernel loader ignores class and endian fields for some(?) processors.
// check for such situation and set the effective endiannes/bitness
bool reader_t::check_ident()
{
for ( unsigned i = 0; i < qnumber(lincpus); i++ )
{
bool matched = false;
bool swap;
if ( eff_msb == lincpus[i].msb
&& header.e_machine == lincpus[i].machine
&& IS_EXEC_OR_DYN(header.e_type) )
{
matched = true;
swap = false;
}
else if ( eff_msb != lincpus[i].msb
&& swap16(header.e_machine) == lincpus[i].machine
&& IS_EXEC_OR_DYN(swap16(header.e_type)) )
{
matched = true;
swap = true;
}
if ( matched )
{
if ( swap )
{
header.e_machine = swap16(header.e_machine);
header.e_type = swap16(header.e_type);
if ( !handle_error(*this, BAD_ENDIANNESS, header.e_ident.bytesex) )
return false;
eff_msb = lincpus[i].msb;
}
// segment bitness can be different from elf bitness: apparently there
// are some files like that in the wild (see pc_odd_bitness_64.elf)
seg_64 = lincpus[i]._64;
// assume elf32 for EM_386/EM_486
if ( !seg_64 )
eff_64 = false;
break;
}
}
return true;
}
//----------------------------------------------------------------------------
bool reader_t::read_header()
{
// 32/64
uint8 elf_class = get_ident().elf_class;
if ( elf_class != ELFCLASS32
&& elf_class != ELFCLASS64 )
{
if ( !handle_error(*this, BAD_CLASS, elf_class) )
return false;
}
// lsb/msb
uint8 elf_do = get_ident().bytesex;
if ( elf_do != ELFDATA2LSB
&& elf_do != ELFDATA2MSB )
{
if ( !handle_error(*this, BAD_ENDIANNESS, elf_do) )
return false;
}
input_status_t save_excursion(*this);
if ( save_excursion.seek(sizeof(elf_ident_t)) == -1 )
return false;
// set the default values from ident
eff_msb = elf_do == ELFDATA2MSB;
eff_64 = elf_class == ELFCLASS64;
seg_64 = eff_64;
// Read the type and machine
#define _safe(expr) if ( (expr) < 0 ) goto FAILED
_safe(read_half(&header.e_type));
_safe(read_half(&header.e_machine));
if ( !check_ident() )
return false;
// Define sizes
if ( !is_64() )
{
stdsizes.ehdr = sizeof(Elf32_Ehdr);
stdsizes.phdr = sizeof(Elf32_Phdr);
stdsizes.shdr = sizeof(Elf32_Shdr);
stdsizes.entries.sym = sizeof(Elf32_Sym);
stdsizes.entries.dyn = sizeof(Elf32_Dyn);
stdsizes.entries.rel = sizeof(Elf32_Rel);
stdsizes.entries.rela = sizeof(Elf32_Rela);
stdsizes.types.elf_addr = 4;
stdsizes.types.elf_off = 4;
stdsizes.types.elf_xword= 4;
stdsizes.types.elf_sxword=4;
}
else
{
stdsizes.ehdr = sizeof(Elf64_Ehdr);
stdsizes.phdr = sizeof(Elf64_Phdr);
stdsizes.shdr = sizeof(Elf64_Shdr);
stdsizes.entries.sym = sizeof(Elf64_Sym);
stdsizes.entries.dyn = sizeof(Elf64_Dyn);
stdsizes.entries.rel = sizeof(Elf64_Rel);
stdsizes.entries.rela = sizeof(Elf64_Rela);
stdsizes.types.elf_addr = 8;
stdsizes.types.elf_off = 8;
stdsizes.types.elf_xword= 8;
stdsizes.types.elf_sxword=8;
}
stdsizes.dyn.sym = stdsizes.entries.sym;
stdsizes.dyn.rel = stdsizes.entries.rel;
stdsizes.dyn.rela = stdsizes.entries.rela;
// Read the rest of the header
_safe(read_word(&header.e_version));
_safe(read_addr(&header.e_entry));
_safe(read_off (&header.e_phoff));
_safe(read_off (&header.e_shoff));
_safe(read_word(&header.e_flags));
_safe(read_half(&header.e_ehsize));
_safe(read_half(&header.e_phentsize));
_safe(read_half(&header.e_phnum));
_safe(read_half(&header.e_shentsize));
_safe(read_half(&header.e_shnum));
_safe(read_half(&header.e_shstrndx));
#undef _safe
if ( header.e_ehsize != stdsizes.ehdr )
if ( !handle_error(*this, BAD_EHSIZE, header.e_ehsize, stdsizes.ehdr) )
{
FAILED:
return false;
}
// Sanitize PHT parameters
if ( (header.e_phnum == 0) != (header.e_phoff == 0) )
{
if ( !handle_error(*this, BAD_PHLOC, header.e_phnum, header.e_phoff) )
goto FAILED;
header.set_no_pht();
}
if ( header.has_pht() && header.e_phentsize != stdsizes.phdr )
{
if ( !handle_error(*this, BAD_PHENTSIZE, header.e_phentsize, stdsizes.phdr)
|| header.e_phentsize < stdsizes.phdr )
{
goto FAILED;
}
header.e_phentsize = stdsizes.phdr;
}
// process large number of sections
// "System V Application Binary Interface - DRAFT - 19 October 2010"
// If the number of sections is greater than or equal to SHN_LORESERVE
// (0xff00), this member has the value zero and the actual number of
// section header table entries is contained in the sh_size field of the
// section header at index 0. (Otherwise, the sh_size member of the
// initial entry contains 0.)
elf_shdr_t sh0;
bool is_sh0_read;
if ( header.e_shnum == 0
&& header.e_shoff != 0
&& seek(header.e_shoff) != -1
&& read_section_header(&sh0)
&& sh0.sh_type == SHT_NULL )
{
is_sh0_read = true;
header.real_shnum = sh0.sh_size;
}
else
{
is_sh0_read = false;
header.real_shnum = header.e_shnum;
}
// Sanitize SHT parameters
if ( (header.real_shnum == 0) != (header.e_shoff == 0) )
{
if ( !handle_error(*this, BAD_SHLOC, header.real_shnum, header.e_shoff, size()) )
goto FAILED;
header.set_no_sht(); // do not use sht
}
if ( header.has_sht() && header.e_shentsize != stdsizes.shdr )
{
if ( !handle_error(*this, BAD_SHENTSIZE, header.e_shentsize, stdsizes.shdr)
|| header.e_shentsize < stdsizes.shdr )
{
header.set_no_sht(); // do not use sht
}
}
{
uint64 sections_start = header.e_shoff;
uint64 sections_finish = header.e_shoff + uint64(header.real_shnum) * header.e_shentsize;
if ( sections_start > sections_finish || sections_finish > size() )
{
if ( !handle_error(*this, BAD_SHLOC, header.real_shnum, header.e_shoff, size()) )
goto FAILED;
header.set_no_sht(); // do not use sht
}
}
if ( header.has_sht() )
{
// process large section name string table section index
// "System V Application Binary Interface - DRAFT - 19 October 2010"
// If the section name string table section index is greater than or equal
// to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX
// (0xffff) and the actual index of the section name string table section
// is contained in the sh_link field of the section header at index 0.
// (Otherwise, the sh_link member of the initial entry contains 0.)
if ( header.e_shstrndx == SHN_XINDEX && is_sh0_read && sh0.sh_link != 0 )
header.real_shstrndx = sh0.sh_link;
else
header.real_shstrndx = header.e_shstrndx;
// Sanitize SHT string table index
if ( header.real_shstrndx > 0
&& header.real_shstrndx >= header.real_shnum )
{
if ( !handle_error(*this, BAD_SHSTRNDX, uint(header.real_shstrndx), uint(header.real_shnum)) )
goto FAILED;
header.real_shstrndx = 0;
}
}
//
if ( header.has_pht() && header.e_type == ET_REL )
{
if ( !handle_error(*this, CONFLICTING_FILE_TYPE) )
goto FAILED;
}
//
switch ( header.e_machine )
{
case EM_AARCH64:
// AARCH64 files must use 64-bit segments even for ELF32 (ILP32)
seg_64 = true;
// fallthrough
case EM_ARM:
delete arch_specific;
arch_specific = new arm_arch_specific_t;
break;
default:
arch_specific = new arch_specific_t; // Dummy
break;
}
return true;
}
//----------------------------------------------------------------------------
bool reader_t::read_section_header(elf_shdr_t *sh)
{
#define _safe(expr) if ( expr < 0 ) return false;
_safe(read_word (&sh->sh_name));
_safe(read_word (&sh->sh_type));
_safe(read_xword(&sh->sh_flags));
_safe(read_addr (&sh->sh_addr));
_safe(read_off (&sh->sh_offset));
_safe(read_xword(&sh->sh_size));
_safe(read_word (&sh->sh_link));
_safe(read_word (&sh->sh_info));
_safe(read_xword(&sh->sh_addralign));
_safe(read_xword(&sh->sh_entsize));
#undef _safe
return true;
}
//-------------------------------------------------------------------------
bool reader_t::read_compression_header(elf_chdr_t *out)
{
#define _safe(expr) if ( expr < 0 ) return false;
_safe(read_word(&out->ch_type));
if ( is_64() )
_safe(read_word(&out->ch_reserved));
_safe(read_xword(&out->ch_size));
_safe(read_xword(&out->ch_addralign));
#undef _safe
return true;
}
//----------------------------------------------------------------------------
bool reader_t::read_section_headers()
{
if ( !header.has_sht() )
return false;
input_status_t save_excursion(*this);
if ( save_excursion.seek(header.e_shoff) == -1 )
return false;
sections.resize(header.real_shnum);
sections.initialized = true;
for ( elf_shndx_t i = 0; i < header.real_shnum; i++ )
{
if ( !seek_to_section_header(i) )
return false;
elf_shdr_t *sh = sections.getn(i);
if ( !read_section_header(sh) )
return false;
}
// in the first pass we store WKS_SYMTAB/WKS_DYNSYM to process sections
// of type SHT_SYMTAB_SHNDX in the second pass
typedef elf_shdrs_t::const_iterator const_iter;
const_iter it = sections.begin();
const_iter end = sections.end();
for ( elf_shndx_t i = 0; it != end; ++it, ++i )
{
if ( i == 0 ) // Skip first header
continue;
const elf_shdr_t &sh = *it;
if ( sh.sh_size == 0 )
continue;
switch ( sh.sh_type )
{
case SHT_SYMTAB:
{
sections.set_index(WKS_SYMTAB, i);
elf_shdr_t *strtab_sh = sections.getn(sh.sh_link);
if ( strtab_sh == NULL )
{
msg("Illegal link section %d of the string table "
"for symbols\n",
sh.sh_link);
}
else
{
set_sh_strtab(sym_strtab, *strtab_sh, true);
}
}
break;
case SHT_DYNSYM:
{
sections.set_index(WKS_DYNSYM, i);
elf_shdr_t *strtab_sh = sections.getn(sh.sh_link);
if ( strtab_sh == NULL )
{
msg("Illegal link section %d of the string table "
"for dynamic linking symbols\n",
sh.sh_link);
}
else
{
set_sh_strtab(dyn_strtab, *strtab_sh, true);
}
}
break;
case SHT_DYNAMIC:
{
elf_shdr_t *strtab_sh = sections.getn(sh.sh_link);
if ( strtab_sh == NULL )
{
msg("Illegal link section %d of the dynamic linking "
"information section\n",
sh.sh_link);
}
else if ( strtab_sh->sh_type == SHT_DYNSYM )
{
// OAT file: .dynamic section links to .dynsym section
strtab_sh->sh_link = 0;
}
else
{
set_sh_strtab(dyn_strtab, *strtab_sh, true);
}
}
break;
}
}
// initialize the section name string table
if ( header.real_shstrndx != 0 )
{
elf_shdr_t *shstrtab = sections.getn(header.real_shstrndx);
if ( shstrtab != NULL )
{
// we do not check type of this section here
// (in some cases it may be not SHT_STRTAB)
sections.strtab.offset = shstrtab->sh_offset;
sections.strtab.addr = shstrtab->sh_addr;
sections.strtab.size = shstrtab->sh_size;
}
}
qstring name;
it = sections.begin();
for ( elf_shndx_t i = 0; it != end; ++it, ++i )
{
if ( i == 0 ) // Skip first header
continue;
const elf_shdr_t &sh = *it;
if ( sh.sh_size == 0 )
continue;
name.qclear();
sections.get_name(&name, &sh);
switch ( sh.sh_type )
{
case SHT_STRTAB:
// we specify replace = false as this section has less priority
// compared to the sh_link section
if ( name == ".strtab" )
set_sh_strtab(sym_strtab, sh, false);
else if ( name == ".dynstr" )
set_sh_strtab(dyn_strtab, sh, false);
break;
case SHT_SYMTAB_SHNDX:
if ( sh.sh_link != 0 )
{
if ( sh.sh_link == sections.get_index(WKS_SYMTAB) )
sections.set_index(WKS_SYMTAB_SHNDX, i);
if ( sh.sh_link == sections.get_index(WKS_DYNSYM) )
sections.set_index(WKS_DYNSYM_SHNDX, i);
}
break;
case SHT_GNU_verdef:
sections.set_index(WKS_VERDEF, i);
break;
case SHT_GNU_verneed:
sections.set_index(WKS_VERNEED, i);
break;
case SHT_GNU_versym:
sections.set_index(WKS_VERSYM, i);
break;
case SHT_PROGBITS:
if ( name == ".interp" )
{
sections.set_index(WKS_INTERP, i);
break;
}
else if ( name == ".got" )
{
sections.set_index(WKS_GOT, i);
sections.set_got_original();
break;
}
else if ( name == ".got.plt" )
{
sections.set_index(WKS_GOTPLT, i);
break;
}
else if ( name == ".plt.got" )
{
sections.set_index(WKS_PLTGOT, i);
break;
}
else if ( name == ".plt.sec" )
{
sections.set_index(WKS_PLTSEC, i);
break;
}
else if ( name == ".gnu_debugdata" )
{
sections.set_index(WKS_GNU_DEBUGDATA, i);
break;
}
// function pointers for PPC64 (may be for IA64, HPPA64)
else if ( is_64() && name == ".opd" )
{
sections.set_index(WKS_OPD, i);
break;
}
// no break
case SHT_NOBITS:
if ( name == ".plt" )
sections.set_index(WKS_PLT, i);
break;
}
}
if ( sections.get_index(WKS_GOTPLT) == 0 )
sections.set_index(WKS_GOTPLT, sections.get_index(WKS_GOT));
else if ( sections.get_index(WKS_GOT) == 0 )
sections.set_index(WKS_GOTPLT, 0); // unsupported format
return true;
}
//----------------------------------------------------------------------------
bool reader_t::read_program_headers()
{
if ( !header.has_pht() )
return false;
input_status_t save_excursion(*this);
if ( save_excursion.seek(header.e_phoff) == -1 )
return false;
int count = header.e_phnum;
validate_array_count_or_die(get_linput(), count, header.e_phentsize, "PHT entries");
pheaders.resize(count);
pheaders.initialized = true;
elf_phdr_t *dyn_phdr = NULL;
for ( int i = 0; i < count; i++ )
{
if ( !seek_to_program_header(i) )
return false;
elf_phdr_t *phdr = pheaders.get(i);
#define _safe(expr) \
do \
{ \
if ( expr < 0 ) \
{ \
pheaders.resize(i == 0 ? 0 : i-1); \
return false; \
} \
} while ( false )
_safe(read_word(&phdr->p_type));
if ( is_64() )
_safe(read_word(&phdr->p_flags));
_safe(read_off(&phdr->p_offset));
_safe(read_addr(&phdr->p_vaddr));
_safe(read_addr(&phdr->p_paddr));
_safe(read_xword(&phdr->p_filesz));
_safe(read_xword(&phdr->p_memsz));
if ( !is_64() )
_safe(read_word(&phdr->p_flags));
_safe(read_xword(&phdr->p_align));
#undef _safe
switch ( phdr->p_type )
{
case PT_LOAD:
add_mapping(*phdr);
// ELF_Format.pdf page 2-4
// The base address in ELF is "the lowest virtual address associated
// with the memory image of the program's object file".
if ( phdr->p_vaddr < pheaders.get_image_base() )
{
// NOTE The default maximum page size is processor specific.
// The ELF standard vaguely mentions ("Program Loading")
// that 4 KB is the maximum page size for the SYSTEM V
// architecture, so we use this value.
const ea_t maximum_page_size = 0x1000;
// Truncate the memory address to the nearest multiple of the
// maximum page size.
ea_t image_base = phdr->p_vaddr & ~(maximum_page_size - 1);
pheaders.set_image_base(image_base);
}
break;
case PT_DYNAMIC:
dyn_phdr = phdr;
break;
}
}
if ( dyn_phdr != NULL )
{
// in some files, p_filesz is 0, so take max of the two
// TODO: use the size of the surrounding PT_LOAD segment,
// since the dynamic loader does not use the size field
size_t dsize = qmax(dyn_phdr->p_filesz, dyn_phdr->p_memsz);
// p_offset may be wrong, always use p_vaddr
size_t fileoff = file_offset(dyn_phdr->p_vaddr);
pheaders.set_dynlink_table_info(fileoff, dyn_phdr->p_vaddr, dsize, -1);
}
return true;
}
//----------------------------------------------------------------------------
void reader_t::set_sh_strtab(
dynamic_info_t::entry_t &strtab,
const elf_shdr_t &strtab_sh,
bool replace)
{
// we don't check that type of section should be SHT_STRTAB,
// we just reject illegal section type
if ( strtab_sh.sh_type == SHT_NULL
|| strtab_sh.sh_type == SHT_REL
|| strtab_sh.sh_type == SHT_RELA
|| strtab_sh.sh_type == SHT_DYNAMIC
|| strtab_sh.sh_type == SHT_DYNSYM
|| strtab_sh.sh_type == SHT_SYMTAB )
{
msg("Illegal type %s of the string table section\n",
sections.sh_type_qstr(strtab_sh.sh_type).c_str());
return;
}
if ( strtab_sh.sh_offset == 0 )
{
msg("Illegal offset of the string table section\n");
return;
}
// store the string table info
if ( strtab.is_valid() )
{
if ( strtab.offset == strtab_sh.sh_offset )
return;
warning("AUTOHIDE SESSION\n"
"More than one string table for %ssymbols, "
"using one at offset %08" FMT_64 "X",
&strtab == &dyn_strtab ? "dynamic linking " : "",
replace ? strtab_sh.sh_offset : strtab.offset);
if ( !replace )
return;
}
strtab.offset = strtab_sh.sh_offset;
strtab.addr = strtab_sh.sh_addr;
strtab.size = strtab_sh.sh_size;
}
//----------------------------------------------------------------------------
void reader_t::set_di_strtab(
dynamic_info_t::entry_t &strtab,
const dynamic_info_t::entry_t &strtab_di)
{
if ( !strtab_di.is_valid() )
return;
if ( strtab.is_valid() )
{
if ( strtab.offset == strtab_di.offset )
return;
warning("The dynamic section string table "
"from section header (%08" FMT_64 "X) differs "
"from DT_STRTAB's one (%08" FMT_64 "X), "
"using the latter",
strtab.offset,
strtab_di.offset);
}
strtab = strtab_di;
}
//----------------------------------------------------------------------------
bool reader_t::read_notes(notes_t *notes)
{
notes->clear();
if ( sections.initialized )
{
for ( elf_shdrs_t::const_iterator p=sections.begin(); p != sections.end(); ++p )
{
const elf_shdr_t &sh = *p;
if ( sh.sh_type != SHT_NOTE )
continue;
bytevec_t buf;
sections.read_file_contents(&buf, sh);
notes->add(buf);
}
}
if ( pheaders.initialized )
{
for ( elf_phdrs_t::const_iterator q=pheaders.begin(); q != pheaders.end(); ++q )
{
const elf_phdr_t &p = *q;
if ( p.p_type != PT_NOTE )
continue;
bytevec_t buf;
pheaders.read_file_contents(&buf, p);
notes->add(buf);
}
}
notes->initialized = true;
return true;
}
//----------------------------------------------------------------------------
elf_sym_idx_t reader_t::rel_info_index(const elf_rela_t &r) const
{
if ( is_64() && !is_msb() && is_mips() )
{
// MIPS64 does not use 64-bit r_info but always puts symbol index at low address
// so for LE we need to return low part of r_info
return uint32(r.r_info);
}
if ( is_64() )
return ELF64_R_SYM(r.r_info);
else
return ELF32_R_SYM(r.r_info);
}
//----------------------------------------------------------------------------
uint32 reader_t::rel_info_type(const elf_rela_t &r) const
{
if ( is_64() && !is_msb() && is_mips() )
{
// MIPS64 does not use a 64-bit r_info but always puts symbol index at low address
// so in case of LE we need to return the high part of r_info
// we also need to convert it to BE
// so that macros like ELF64_MIPS_R_TYPE work properly
return swap32(r.r_info>>32);
}
if ( is_64() )
return ELF64_R_TYPE(r.r_info);
else
return ELF32_R_TYPE(r.r_info);
}
//----------------------------------------------------------------------------
void reader_t::set_rel_info_index(elf_rela_t *r, uint32 symidx) const
{
if ( !is_64() )
{
r->r_info = ELF32_R_INFO(uint64(symidx), ELF32_R_TYPE(r->r_info));
}
else if ( !is_msb() && is_mips() )
{
// MIPS64 does not use the 64-bit r_info, but always puts the symbol
// index at the low address, so for LE we need to update the low part of
// r_info
r->r_info &= ~0xFFFFFFFF;
r->r_info |= uint64(symidx) << 32;
}
else
{
r->r_info &= 0xFFFFFFFF;
r->r_info |= symidx;
}
}
//----------------------------------------------------------------------------
void reader_t::set_rel_info_type(elf_rela_t *r, uint32 type) const
{
if ( !is_64() )
{
r->r_info = ELF32_R_INFO(ELF32_R_SYM(r->r_info), type);
}
else if ( !is_msb() && is_mips() )
{
// MIPS64 does not use the 64-bit r_info, but always puts the symbol
// index at the low address, so for LE we need to update the high part
// of r_info.
// we also need to convert it to BE
r->r_info &= 0xFFFFFFFF;
r->r_info |= uint64(swap32(type)) << 32;
}
else
{
r->r_info &= ~0xFFFFFFFF;
r->r_info |= type;
}
}
//----------------------------------------------------------------------------
const char *reader_t::file_type_str() const
{
const char *file_type = "Unknown";
switch ( header.e_type )
{
case ET_NONE: file_type = "None"; break;
case ET_REL: file_type = "Relocatable"; break;
case ET_EXEC: file_type = "Executable"; break;
case ET_DYN: file_type = "Shared object"; break;
case ET_CORE: file_type = "Core file"; break;
case ET_LOPROC: file_type = "Processor specific"; break;
case ET_HIPROC: file_type = "Processor specific"; break;
case ET_IRX:
if ( is_mips() )
file_type = "PS2 IRX";
break;
case ET_PSPEXEC:
if ( is_mips() )
file_type = "PSP executable";
break;
case ET_PS3PRX:
if ( header.e_machine == EM_PPC64 )
file_type = "Sony PS3 PRX file";
break;
}
return file_type;
}
//----------------------------------------------------------------------------
const char *reader_t::os_abi_str() const
{
uint8 os_abi = get_ident().osabi;
const char *abi;
switch ( os_abi )
{
case ELFOSABI_NONE: abi = "UNIX System V ABI"; break;
case ELFOSABI_HPUX: abi = "HP-UX operating system"; break;
case ELFOSABI_NETBSD: abi = "NetBSD"; break;
case ELFOSABI_LINUX: abi = "GNU/Linux"; break;
case ELFOSABI_HURD: abi = "GNU/Hurd"; break;
case ELFOSABI_SOLARIS: abi = "Solaris"; break;
case ELFOSABI_AIX: abi = "AIX"; break;
case ELFOSABI_IRIX: abi = "IRIX"; break;
case ELFOSABI_FREEBSD: abi = "FreeBSD"; break;
case ELFOSABI_TRU64: abi = "TRU64 UNIX"; break;
case ELFOSABI_MODESTO: abi = "Novell Modesto"; break;
case ELFOSABI_OPENBSD: abi = "OpenBSD"; break;
case ELFOSABI_OPENVMS: abi = "OpenVMS"; break;
case ELFOSABI_NSK: abi = "Hewlett-Packard Non-Stop Kernel"; break;
case ELFOSABI_AROS: abi = "Amiga Research OS"; break;
case ELFOSABI_ARM: abi = "ARM"; break;
case ELFOSABI_STANDALONE: abi = "Standalone (embedded) application"; break;
case ELFOSABI_CELLOSLV2:
if ( header.e_machine == EM_PPC64 )
{
abi = "PS3 Cell OS lv2";
break;
}
// fall through
default:
abi = "Unknown";
break;
}
return abi;
}
//----------------------------------------------------------------------------
const char *reader_t::machine_name_str() const
{
uint32 m = get_header().e_machine;
switch ( m )
{
case EM_NONE: return "<No machine>";
case EM_M32: return "AT & T WE 32100";
case EM_SPARC: return "SPARC";
case EM_386: return "Intel 386";
case EM_68K: return "Motorola 68000";
case EM_88K: return "Motorola 88000";
case EM_486: return "Intel 486";
case EM_860: return "Intel 860";
case EM_MIPS: return "MIPS";
case EM_S370: return "IBM System370";
case EM_MIPS_RS3_BE: return "MIPS R3000 Big Endian";
case EM_PARISC: return "PA-RISC";
case EM_VPP550: return "Fujitsu VPP500";
case EM_SPARC32PLUS: return "SPARC v8+";
case EM_I960: return "Intel 960";
case EM_PPC: return "PowerPC";
case EM_PPC64: return "PowerPC 64";
case EM_S390: return "IBM S/390";
case EM_S390_OLD: return "IBM S/390 (old magic)";
case EM_SPU: return "Cell BE SPU";
case EM_CISCO7200: return "Cisco 7200 Series Router (MIPS)";
case EM_CISCO3620: return "Cisco 3620/3640 Router (MIPS)";
case EM_V800: return "NEC V800 or Renesas RH850";
case EM_FR20: return "Fujitsu FR20";
case EM_RH32: return "TRW RH-22";
case EM_MCORE: return "Motorola M*Core";
case EM_ARM: return "ARM";
case EM_OLD_ALPHA: return "Digital Alpha";
case EM_SH: return "SuperH";
case EM_SPARC64: return "SPARC 64";
case EM_TRICORE: return "Siemens Tricore";
case EM_ARC: return "ARC";
case EM_H8300: return "H8/300";
case EM_H8300H: return "H8/300H";
case EM_H8S: return "Hitachi H8S";
case EM_H8500: return "H8/500";
case EM_IA64: return "Itanium IA64";
case EM_MIPS_X: return "Stanford MIPS-X";
case EM_COLDFIRE: return "Coldfire";
case EM_6812: return "MC68HC12";
case EM_MMA: return "Fujitsu MMA";
case EM_PCP: return "Siemens PCP";
case EM_NCPU: return "Sony nCPU";
case EM_NDR1: return "Denso NDR1";
case EM_STARCORE: return "Star*Core";
case EM_ME16: return "Toyota ME16";
case EM_ST100: return "ST100";
case EM_TINYJ: return "TinyJ";
case EM_X86_64: return "x86-64";
case EM_PDSP: return "PDSP";
case EM_PDP10: return "DEC PDP-10";
case EM_PDP11: return "DEC PDP-11";
case EM_FX66: return "Siemens FX66";
case EM_ST9: return "ST9+";
case EM_ST7: return "ST7";
case EM_68HC16: return "MC68HC16";
case EM_6811: return "MC68HC11";
case EM_68HC08: return "MC68HC08";
case EM_68HC05: return "MC68HC05";
case EM_SVX: return "Silicon Graphics SVx";
case EM_ST19: return "ST19";
case EM_VAX: return "VAX";
case EM_CRIS: return "CRIS";
case EM_JAVELIN: return "Infineon Javelin";
case EM_FIREPATH: return "Element 14 Firepath";
case EM_ZSP: return "ZSP";
case EM_MMIX: return "MMIX";
case EM_HUANY: return "Harvard HUANY";
case EM_PRISM: return "SiTera Prism";
case EM_AVR: return "Atmel";
case EM_FR: return "Fujitsu FR";
case EM_D10V: return "Mitsubishi D10V";
case EM_D30V: return "Mitsubishi D30V";
case EM_V850: // (GNU compiler)
case EM_CYGNUS_V850:
case EM_NECV850: return "NEC V850"; // (NEC compilers)
case EM_NECV850E: return "NEC v850E";
case EM_NECV850E2: return "NEC v850E2";
case EM_NECV850ES: return "NEC v850ES";
case EM_NECV850E2R1: return "NEC v850E2R1";
case EM_NECV850E2R2: return "NEC v850E2R2";
case EM_NECV850E2R3: return "NEC v850E2R3";
case EM_NECV850E2R4: return "NEC v850E2R4";
case EM_NECV850E3V5: return "NEC v850E3V5";
case EM_M32R: return "M32R";
case EM_MN10300: return "MN10300";
case EM_MN10200: return "MN10200";
case EM_PJ: return "picoJava";
case EM_OPENRISC : return "OpenRISC";
case EM_ARCOMPACT: return "ARCompact";
case EM_XTENSA: return "Xtensa";
case EM_VIDEOCORE: return "VideoCore";
case EM_TMM_GPP: return "Thompson GPP";
case EM_NS32K: return "NS 32000";
case EM_TPC: return "TPC";
case EM_SNP1K: return "SNP 1000";
case EM_ST200: return "ST200";
case EM_IP2K: return "IP2022";
case EM_MAX: return "MAX";
case EM_CR: return "CompactRISC";
case EM_F2MC16: return "F2MC16";
case EM_MSP430: return "MSP430";
case EM_BLACKFIN: return "ADI Blackfin";
case EM_SE_C33: return "S1C33";
case EM_SEP: return "SEP";
case EM_ARCA: return "Arca";
case EM_UNICORE: return "Unicore";
case EM_EXCESS: return "eXcess";
case EM_DXP: return "Icera DXP";
case EM_ALTERA_NIOS2: return "Nios II";
case EM_CRX: return "CRX";
case EM_XGATE: return "XGATE";
case EM_C166: return "C16x/XC16x/ST10";
case EM_M16C: return "M16C";
case EM_DSPIC30F: return "dsPIC30F";
case EM_CE: return "Freescale Communication Engine";
case EM_M32C: return "M32C";
case EM_TSK3000: return "TSK3000";
case EM_RS08: return "RS08";
case EM_ECOG2: return "eCOG2";
case EM_SCORE: return "Sunplus Score";
case EM_DSP24: return "NJR DSP24";
case EM_VIDEOCORE3: return "VideoCore III";
case EM_LATTICEMICO32: return "Lattice Mico32";
case EM_SE_C17: return "C17";
case EM_MMDSP_PLUS: return "MMDSP";
case EM_CYPRESS_M8C: return "M8C";
case EM_R32C: return "R32C";
case EM_TRIMEDIA: return "TriMedia";
case EM_QDSP6: return "QDSP6";
case EM_8051: return "i8051";
case EM_STXP7X: return "STxP7x";
case EM_NDS32: return "NDS32";
case EM_ECOG1X: return "eCOG1X";
case EM_MAXQ30: return "MAXQ30";
case EM_XIMO16: return "NJR XIMO16";
case EM_MANIK: return "M2000";
case EM_CRAYNV2: return "Cray NV2";
case EM_RX: return "RX";
case EM_METAG: return "Imagination Technologies META";
case EM_MCST_ELBRUS: return "MCST Elbrus";
case EM_ECOG16: return "eCOG16";
case EM_CR16: return "CompactRISC 16-bit";
case EM_ETPU: return "Freescale ETPU";
case EM_SLE9X: return "SLE9X";
case EM_L1OM: return "Intel L1OM";
case EM_K1OM: return "Intel K1OM";
case EM_INTEL182: return "Intel Reserved (182)";
case EM_AARCH64: return "ARM64";
case EM_ARM184: return "ARM Reserved (184)";
case EM_AVR32: return "AVR32";
case EM_STM8: return "STM8";
case EM_TILE64: return "Tilera TILE64";
case EM_TILEPRO: return "Tilera TILEPro";
case EM_MICROBLAZE: return "MicroBlaze";
case EM_CUDA: return "CUDA";
case EM_TILEGX: return "Tilera TILE-Gx";
case EM_CLOUDSHIELD: return "CloudShield";
case EM_COREA_1ST: return "Core-A 1st gen";
case EM_COREA_2ND: return "Core-A 2nd gen";
case EM_ARC_COMPACT2: return "ARCompactV2";
case EM_OPEN8: return "Open8";
case EM_RL78: return "RL78";
case EM_VIDEOCORE5: return "VideoCore V";
case EM_78K0R: return "78K0R";
case EM_56800EX: return "Freescale 56800EX";
case EM_BA1: return "Beyond BA1";
case EM_BA2: return "Beyond BA2";
case EM_XCORE: return "XMOS xCORE";
case EM_CYGNUS_POWERPC: return "PowerPC";
case EM_ALPHA: return "DEC Alpha";
case EM_TI_C6000: return "TMS320C6";
case EM_RISCV: return "Risc-V";
default: return nullptr;
}
}
//----------------------------------------------------------------------------
qstring reader_t::get_machine_name() const
{
qstring s = machine_name_str();
if ( s.empty() )
s.sprnt("Unknown CPU [%u]", get_header().e_machine);
return s;
}
//----------------------------------------------------------------------------
bool reader_t::read_prelink_base(uint32 *base)
{
int64 fsize = size();
input_status_t save_excursion(*this);
if ( save_excursion.seek(fsize - 4) == -1 )
return false;
char tag[4];
bool ok = false;
if ( qlread(li, tag, 4) == 4 )
{
if ( memcmp(tag, "PRE ", 4) == 0 )
{
if ( qlseek(li, fsize - 8) != -1 && read_word(base) >= 0 )
ok = true;
}
}
return ok;
}
//----------------------------------------------------------------------------
bool reader_t::get_string_at(qstring *out, uint64 offset) const
{
input_status_t save_excursion(*this);
if ( save_excursion.seek(offset) == -1 )
{
out->sprnt("bad offset %08x", low(offset));
return false;
}
bool ret = true;
out->clear();
char buffer[100];
while ( true )
{
int read = qlread(li, buffer, sizeof(buffer));
if ( read < 0 )
{
out->append("{truncated name}");
ret = false;
break;
}
// Find the position of the trailing zero
int pos;
for ( pos = 0; pos < read && buffer[pos] != '\0'; pos++ )
;
out->append(buffer, pos);
if ( pos < sizeof(buffer) )
break;
}
return ret;
}
//----------------------------------------------------------------------------
elf_shndx_t reader_t::get_shndx_at(uint64 offset) const
{
input_status_t save_excursion(*this);
if ( save_excursion.seek(offset) == -1 )
return 0;
CASSERT(sizeof(elf_shndx_t) == sizeof(uint32));
uint32 res;
if ( read_word(&res) < 0 )
return 0;
return res;
}
//--------------------------------------------------------------------------
// Fills 'out' with either a fake entry from the DHT or an entry from the SHT.
static bool get_versym_section(
elf_shdr_t *out,
slice_type_t *slice_type,
const reader_t &reader,
wks_t sht_idx,
const dynamic_info_t &di,
dynamic_info_type_t dht_idx,
bool use_pht)
{
*slice_type = SLT_INVALID;
if ( use_pht )
{
if ( di.fill_section_header(out, dht_idx) )
*slice_type = SLT_DYNSYM;
}
else
{
const elf_shdr_t *sht_entry = reader.sections.get_wks(sht_idx);
if ( sht_entry != NULL )
{
*out = *sht_entry;
if ( sht_entry->sh_link == reader.sections.get_index(WKS_SYMTAB)
&& reader.symbols.slice_size(SLT_SYMTAB) != 0 )
{
*slice_type = SLT_SYMTAB;
}
else if ( sht_entry->sh_link == reader.sections.get_index(WKS_DYNSYM)
&& reader.symbols.slice_size(SLT_DYNSYM) != 0 )
{
*slice_type = SLT_DYNSYM;
}
}
}
return *slice_type != SLT_INVALID;
}
//----------------------------------------------------------------------------
// Helper class for reading version info sections. DT_VERDEF and DT_VERNEED
// have similar structures for reading main and auxiliary entries.
template <class T_entry, class T_aux>
struct elf_ver_parser_t
{
const reader_t &reader;
elf_ver_parser_t(const reader_t &_reader)
: reader(_reader)
{}
virtual void entry_cb(const T_entry &, int64, size_t) = 0;
virtual void aux_cb(const T_aux &, int64, size_t) = 0;
void parse(int64 offset, size_t size)
{
int64 orig_offset = offset;
input_status_t save_excursion(reader);
int64 end = offset + size;
size_t entry_idx = 0;
std::set<int64> entry_seen;
while ( offset < end && entry_seen.insert(offset).second )
{
T_entry entry;
if ( save_excursion.seek(offset) == -1 || !entry.read(reader) )
break;
entry_cb(entry, offset - orig_offset, entry_idx++);
int64 aux_offset = offset + entry.aux();
size_t aux_idx = 0;
std::set<int64> aux_seen;
while ( aux_offset < end
&& aux_seen.insert(aux_offset).second
&& aux_idx < entry.cnt() )
{
T_aux aux;
if ( save_excursion.seek(aux_offset) == -1 || !aux.read(reader) )
break;
aux_cb(aux, aux_offset - orig_offset, aux_idx++);
aux_offset += aux.next();
}
offset += entry.next();
}
}
};
//----------------------------------------------------------------------------
bool reader_t::read_symbol_versions(
elf_symbol_version_t *symver,
const dynamic_info_t &di,
bool use_pht)
{
elf_shdr_t sh;
slice_type_t st;
// Read version requirement entries from DT_VERNEED
struct elf_verneed_parser_t
: public elf_ver_parser_t<elf_verneed_t, elf_vernaux_t>
{
typedef elf_ver_parser_t<elf_verneed_t, elf_vernaux_t> inherited;
elf_symbol_version_t &symver;
slice_type_t st;
elf_verneed_parser_t(
const reader_t &_reader,
elf_symbol_version_t &_symver,
slice_type_t _st)
: inherited(_reader),
symver(_symver),
st(_st)
{
}
virtual void entry_cb(const elf_verneed_t &verneed, int64 offset, size_t) override
{
symbol_verneed_t &reqfile = symver.reqs.push_back();
reqfile.offset = offset;
// TODO check verneed.vn_version
reqfile.name = symver.file_names.size();
qstring &fname = symver.file_names.push_back();
reader.get_name(&fname, st, verneed.vn_file);
}
virtual void aux_cb(const elf_vernaux_t &vernaux, int64 offset, size_t) override
{
symbol_verneed_t &reqfile = symver.reqs.back();
symbol_vernaux_t &reqver = reqfile.auxs.push_back();
reqver.offset = offset;
reqver.name = symver.version_names.size();
qstring &vname = symver.version_names.push_back();
reader.get_name(&vname, st, vernaux.vna_name);
uint16 idx = vernaux.vna_other & ~0x8000;
if ( idx != 0 )
{
vermap_item_t &vermap_item = symver.vermap[idx];
vermap_item.fname_idx = reqfile.name;
vermap_item.vname_idx = reqver.name;
}
}
};
if ( get_versym_section(&sh, &st, *this, WKS_VERNEED, di, DIT_VERNEED, use_pht) )
{
elf_verneed_parser_t parser(*this, *symver, st);
parser.parse(sh.sh_offset, sh.sh_size);
}
// Read version definition entries from DT_VERDEF
struct elf_verdef_parser_t
: public elf_ver_parser_t<elf_verdef_t, elf_verdaux_t>
{
typedef elf_ver_parser_t<elf_verdef_t, elf_verdaux_t> inherited;
elf_symbol_version_t &symver;
slice_type_t st;
elf_verdef_parser_t(
const reader_t &_reader,
elf_symbol_version_t &_symver,
slice_type_t _st)
: inherited(_reader),
symver(_symver),
st(_st)
{
}
virtual void entry_cb(const elf_verdef_t &verdef, int64 offset, size_t) override
{
symbol_verdef_t &deffile = symver.defs.push_back();
deffile.offset = offset;
// TODO check verdef.vd_version
deffile.flags = verdef.vd_flags;
deffile.ndx = verdef.vd_ndx;
}
virtual void aux_cb(const elf_verdaux_t &verdaux, int64 offset, size_t) override
{
symbol_verdef_t &deffile = symver.defs.back();
symbol_verdaux_t &defver = deffile.auxs.push_back();
defver.offset = offset;
if ( (deffile.flags & VER_FLG_BASE) != 0 )
{
symver.def_base = symver.file_names.size();
qstring &fname = symver.file_names.push_back();
reader.get_name(&fname, st, verdaux.vda_name);
}
defver.name = symver.version_names.size();
qstring &vname = symver.version_names.push_back();
reader.get_name(&vname, st, verdaux.vda_name);
uint16 idx = deffile.ndx;
if ( idx != 0 && deffile.auxs.size() == 1 )
{
vermap_item_t &vermap_item = symver.vermap[idx];
vermap_item.fname_idx = symver.def_base;
vermap_item.vname_idx = defver.name;
}
}
};
if ( get_versym_section(&sh, &st, *this, WKS_VERDEF, di, DIT_VERDEF, use_pht) )
{
elf_verdef_parser_t parser(*this, *symver, st);
parser.parse(sh.sh_offset, sh.sh_size);
}
// Read version symbol entries from DT_VERSYM
if ( get_versym_section(&sh, &st, *this, WKS_VERSYM, di, DIT_VERSYM, use_pht) )
{
input_status_t save_excursion(*this);
if ( save_excursion.seek(sh.sh_offset) != -1 )
for ( size_t i = 0; i < sh.sh_size / sizeof(uint16); i++ )
if ( read_half(&symver->symbols.push_back()) < 0 )
break;
}
return true;
}
//----------------------------------------------------------------------------
void reader_t::validate_section_size(
uint64 *count,
size_t *entsize,
const elf_shdr_t &section,
const char *counter_name)
{
*entsize = section.sh_entsize == 0 ? 1 : section.sh_entsize;
*count = section.sh_size / *entsize;
validate_array_count(
get_linput(),
count,
*entsize,
counter_name,
section.sh_offset);
}
//----------------------------------------------------------------------------
bool reader_t::read_dynamic_info_tags(
dyninfo_tags_t *dyninfo_tags,
const dynamic_linking_tables_t &dlt)
{
// assert: dlt.is_valid()
if ( dlt.size == 0 )
return false;
// read all 'elf_dyn_t' entries
elf_dyn_t *d;
const size_t isize = stdsizes.entries.dyn;
elf_shdr_t fake_section;
fake_section.sh_type = SHT_DYNAMIC;
fake_section.sh_offset = dlt.offset;
fake_section.sh_size = dlt.size;
fake_section.sh_entsize = isize;
uint64 count;
size_t entsize;
validate_section_size(&count, &entsize, fake_section, "Dynamic info size");
if ( count == 0 )
return false;
buffered_input_t<elf_dyn_t> dyn_input(*this, fake_section.sh_offset, count, entsize);
while ( dyn_input.next(d) )
{
dyninfo_tags->push_back(*d);
if ( d->d_tag == DT_NULL )
break;
}
return true;
}
//----------------------------------------------------------------------------
bool reader_t::parse_dynamic_info(
dynamic_info_t *dyninfo,
const dyninfo_tags_t &dyninfo_tags)
{
dyninfo->initialize(*this);
sizevec_t offsets;
// populate dyninfo structure
for ( dyninfo_tags_t::const_iterator dyn = dyninfo_tags.begin();
dyn != dyninfo_tags.end();
++dyn )
{
dynamic_info_type_t di_type = DIT_TYPE_COUNT;
switch ( dyn->d_tag )
{
case DT_STRTAB: di_type = DIT_STRTAB; break;
case DT_SYMTAB: di_type = DIT_SYMTAB; break;
case DT_REL: di_type = DIT_REL; break;
case DT_RELA: di_type = DIT_RELA; break;
case DT_JMPREL: di_type = DIT_PLT; break;
case DT_HASH: di_type = DIT_HASH; break;
case DT_GNU_HASH: di_type = DIT_GNU_HASH; break;
case DT_PREINIT_ARRAY: di_type = DIT_PREINIT_ARRAY; break;
case DT_INIT_ARRAY: di_type = DIT_INIT_ARRAY; break;
case DT_FINI_ARRAY: di_type = DIT_FINI_ARRAY; break;
case DT_VERDEF: di_type = DIT_VERDEF; break;
case DT_VERNEED: di_type = DIT_VERNEED; break;
case DT_VERSYM: di_type = DIT_VERSYM; break;
case DT_ANDROID_REL:
if ( is_arm() )
di_type = DIT_ANDROID_REL;
break;
case DT_ANDROID_RELA:
if ( is_arm() )
di_type = DIT_ANDROID_RELA;
break;
}
if ( di_type != DIT_TYPE_COUNT )
{
dynamic_info_t::entry_t &entry = dyninfo->entries[di_type];
entry.offset = file_offset(dyn->d_un);
offsets.push_back(entry.offset);
entry.addr = dyn->d_un;
continue;
}
switch ( dyn->d_tag )
{
case DT_STRSZ: di_type = DIT_STRTAB; break;
case DT_RELSZ: di_type = DIT_REL; break;
case DT_RELASZ: di_type = DIT_RELA; break;
case DT_ANDROID_RELSZ: di_type = DIT_ANDROID_REL; break;
case DT_ANDROID_RELASZ: di_type = DIT_ANDROID_RELA; break;
case DT_PLTRELSZ: di_type = DIT_PLT; break;
case DT_PREINIT_ARRAYSZ: di_type = DIT_PREINIT_ARRAY; break;
case DT_INIT_ARRAYSZ: di_type = DIT_INIT_ARRAY; break;
case DT_FINI_ARRAYSZ: di_type = DIT_FINI_ARRAY; break;
}
if ( di_type != DIT_TYPE_COUNT )
{
dyninfo->entries[di_type].size = dyn->d_un;
continue;
}
switch ( dyn->d_tag )
{
case DT_SYMENT: di_type = DIT_SYMTAB; break;
case DT_RELENT: di_type = DIT_REL; break;
case DT_RELAENT: di_type = DIT_RELA; break;
}
if ( di_type != DIT_TYPE_COUNT )
{
dyninfo->entries[di_type].entsize = dyn->d_un;
continue;
}
switch ( dyn->d_tag )
{
case DT_VERDEFNUM: di_type = DIT_VERDEF; break;
case DT_VERNEEDNUM: di_type = DIT_VERNEED; break;
}
if ( di_type != DIT_TYPE_COUNT )
{
dyninfo->entries[di_type].info = dyn->d_un;
continue;
}
switch ( dyn->d_tag )
{
case DT_PLTREL:
dyninfo->plt_rel_type = uint32(dyn->d_un);
if ( dyninfo->plt_rel_type != DT_REL && dyninfo->plt_rel_type != DT_RELA )
{
if ( !handle_error(*this, BAD_DYN_PLT_TYPE, dyninfo->plt_rel_type) )
return false;
}
continue;
case DT_INIT:
case DT_FINI:
case DT_PLTGOT:
offsets.push_back(file_offset(dyn->d_un));
continue;
default:
continue;
case DT_NULL:
break; // end of list
}
break;
}
// Guess size of sections that don't have an explicit size
dyninfo->symtab().guess_size(offsets);
dyninfo->hash().guess_size(offsets);
dyninfo->gnu_hash().guess_size(offsets);
dyninfo->verdef().guess_size(offsets);
dyninfo->verneed().guess_size(offsets);
dyninfo->versym().guess_size(offsets);
return true;
}
//----------------------------------------------------------------------------
void reader_t::add_mapping(const elf_phdr_t &p)
{
mapping_t &m = mappings.push_back();
m.offset = p.p_offset;
m.size = p.p_filesz;
m.ea = p.p_vaddr;
}
//----------------------------------------------------------------------------
int64 reader_t::file_offset(uint64 ea) const
{
for ( int i=0; i < mappings.size(); i++ )
{
const mapping_t &cur = mappings[i];
if ( cur.ea <= ea && (cur.ea + cur.size) > ea )
return low(ea - cur.ea) + cur.offset;
}
return -1;
}
//----------------------------------------------------------------------------
ea_t reader_t::file_vaddr(uint64 offset) const
{
for ( int i=0; i < mappings.size(); i++ )
{
const mapping_t &cur = mappings[i];
if ( cur.offset <= offset && (cur.offset + cur.size) > offset )
return low(offset - cur.offset) + cur.ea;
}
return BADADDR;
}
//----------------------------------------------------------------------------
elf_shndx_t section_headers_t::get_index(wks_t wks) const
{
QASSERT(20054, wks >= WKS_BSS && wks < WKS_LAST);
return wks_lut[int(wks)];
}
//----------------------------------------------------------------------------
void section_headers_t::set_index(wks_t wks, elf_shndx_t index)
{
QASSERT(20055, wks >= WKS_BSS && wks < WKS_LAST);
wks_lut[int(wks)] = index;
}
//----------------------------------------------------------------------------
const elf_shdr_t *section_headers_t::getn(elf_shndx_t index) const
{
assert_initialized();
if ( index >= headers.size() )
return NULL;
else
return &headers[index];
}
//----------------------------------------------------------------------------
const elf_shdr_t *section_headers_t::get(uint32 sh_type, const char *name) const
{
assert_initialized();
qstring n2;
for ( qvector<elf_shdr_t>::const_iterator it=begin(); it != end(); it++ )
{
const elf_shdr_t &cur = *it;
if ( cur.sh_type == sh_type )
{
n2.qclear();
get_name(&n2, &cur);
if ( n2 == name )
return &cur;
}
}
return NULL;
}
//----------------------------------------------------------------------------
const elf_shdr_t *section_headers_t::get_rel_for(elf_shndx_t index, bool *is_rela) const
{
assert_initialized();
if ( is_rela != NULL )
*is_rela = false;
QASSERT(20056, index > 0);
for ( elf_shdrs_t::const_iterator it=begin(); it != end(); it++ )
{
// for REL/RELA sections, sh_info contains the index to which the relocations apply
if ( it->sh_info == index
&& (it->sh_type == SHT_RELA || it->sh_type == SHT_REL) )
{
// found it
if ( is_rela != NULL )
*is_rela = it->sh_type == SHT_RELA;
return it;
}
}
return NULL;
}
//----------------------------------------------------------------------------
int section_headers_t::add(const elf_shdr_t &section)
{
headers.push_back(section);
return headers.size() - 1;
}
//----------------------------------------------------------------------------
bool section_headers_t::get_name(qstring *out, elf_shndx_t index) const
{
assert_initialized();
if ( index >= headers.size() )
return false;
else
return get_name(out, &headers[index]);
}
//----------------------------------------------------------------------------
bool section_headers_t::get_name(qstring *out, const elf_shdr_t *sh) const
{
if ( sh == NULL || !strtab.is_valid() )
return false;
return reader->get_name(out, strtab, sh->sh_name);
}
//----------------------------------------------------------------------------
bool reader_t::get_name(
qstring *out,
const dynamic_info_t::entry_t &strtab,
uint32 name_idx) const
{
if ( !strtab.is_valid() )
*out = "{no string table}";
// cisco ios files have size 0 for the string section
else if ( strtab.size != 0 && name_idx >= strtab.size )
out->sprnt("bad offset %08x", low(strtab.offset + name_idx));
else
return get_string_at(out, strtab.offset + name_idx);
return false;
}
//----------------------------------------------------------------------------
bool reader_t::get_name(
qstring *out,
slice_type_t slice_type,
uint32 name_idx) const
{
const dynamic_info_t::entry_t *strtab;
switch ( slice_type )
{
case SLT_SYMTAB:
strtab = &sym_strtab;
break;
case SLT_DYNSYM:
strtab = &dyn_strtab;
break;
default:
INTERR(20086);
}
return get_name(out, *strtab, name_idx); //-V614 Potentially uninitialized pointer 'strtab' used
}
//----------------------------------------------------------------------------
const char *section_headers_t::sh_type_str(uint32 sh_type) const
{
#define NM(tp) case SHT_##tp: return #tp
#define NM2(tp, nm) case SHT_##tp: return #nm
// OS-specific types
uint8 os_abi = reader->get_ident().osabi;
if ( os_abi == ELFOSABI_SOLARIS )
{
switch ( sh_type )
{
NM(SUNW_ancillary);
NM(SUNW_capchain);
NM(SUNW_capinfo);
NM(SUNW_symsort);
NM(SUNW_tlssort);
NM(SUNW_LDYNSYM);
NM(SUNW_dof);
NM(SUNW_cap);
NM(SUNW_SIGNATURE);
NM(SUNW_ANNOTATE);
NM(SUNW_DEBUGSTR);
NM(SUNW_DEBUG);
NM(SUNW_move);
NM(SUNW_COMDAT);
NM(SUNW_syminfo);
NM2(SUNW_verdef, VERDEF);
NM2(SUNW_verneed, VERNEEDED);
NM2(SUNW_versym, VERSYMBOL);
}
}
else
{
switch ( sh_type )
{
NM(GNU_INCREMENTAL_INPUTS);
NM(GNU_INCREMENTAL_SYMTAB);
NM(GNU_INCREMENTAL_RELOCS);
NM(GNU_INCREMENTAL_GOT_PLT);
NM(GNU_ATTRIBUTES);
NM(GNU_HASH);
NM(GNU_LIBLIST);
NM2(GNU_verdef, VERDEF);
NM2(GNU_verneed, VERNEEDED);
NM2(GNU_versym, VERSYMBOL);
}
}
switch ( sh_type )
{
NM(NULL);
NM(PROGBITS);
NM(SYMTAB);
NM(STRTAB);
NM(RELA);
NM(HASH);
NM(DYNAMIC);
NM(NOTE);
NM(NOBITS);
NM(REL);
NM(SHLIB);
NM(DYNSYM);
NM(COMDAT);
NM(INIT_ARRAY);
NM(FINI_ARRAY);
NM(PREINIT_ARRAY);
NM(GROUP);
NM(SYMTAB_SHNDX);
default:
{
if ( reader->is_arm() )
{
switch ( sh_type )
{
NM(ARM_EXIDX);
NM(ARM_PREEMPTMAP);
NM(ARM_ATTRIBUTES);
NM(ARM_DEBUGOVERLAY);
NM(ARM_OVERLAYSECTION);
NM(ANDROID_REL);
NM(ANDROID_RELA);
}
}
else if ( reader->is_mips() )
{
switch ( sh_type )
{
NM(MIPS_LIBLIST);
NM(MIPS_MSYM);
NM(MIPS_CONFLICT);
NM(MIPS_GPTAB);
NM(MIPS_UCODE);
NM(MIPS_DEBUG);
NM(MIPS_REGINFO);
NM(MIPS_IFACE);
NM(MIPS_CONTENT);
NM(MIPS_OPTIONS);
NM(MIPS_DWARF);
NM(MIPS_SYMBOL_LIB);
NM(MIPS_EVENTS);
NM2(DVP_OVERLAY_TABLE, MIPS_DVP_OVERLAY_TABLE);
NM2(DVP_OVERLAY, MIPS_DVP_OVERLAY);
NM(MIPS_IOPMOD);
NM(MIPS_PSPREL);
}
}
else if ( reader->get_header().e_machine == EM_PPC64 )
{
switch ( sh_type )
{
NM2(PS3PRX_RELA, PRXRELA);
}
}
break;
}
}
return nullptr;
#undef NM2
#undef NM
}
//-------------------------------------------------------------------------
qstring section_headers_t::sh_type_qstr(uint32 sh_type) const
{
qstring s = sh_type_str(sh_type);
if ( s.empty() )
s.sprnt("%X", sh_type);
return s;
}
//-------------------------------------------------------------------------
uint64 section_headers_t::get_size_in_file(const elf_shdr_t &sh) const
{
if ( sh.sh_type == SHT_NOBITS )
return 0;
uint64 next_boundary = reader->size();
// It may happen that we receive a section header that is _not_ part
// of the list of original section headers. E.g., when we load symbols
// from the dynamic-provided information.
const elf_shdr_t *next_sh = &sh + 1;
if ( next_sh >= begin()
&& next_sh < end()
&& next_sh->sh_offset < next_boundary
&& next_sh->sh_offset >= sh.sh_offset )
{
next_boundary = next_sh->sh_offset;
}
if ( sh.sh_offset >= next_boundary )
return 0;
return qmin(sh.sh_size, next_boundary - sh.sh_offset);
}
//-------------------------------------------------------------------------
void section_headers_t::read_file_contents(
bytevec_t *out,
const elf_shdr_t &sh) const
{
uint64 nbytes = sh.sh_size;
qstring hdrname = "?";
get_name(&hdrname, sh);
qstring text;
text.sprnt("Size of %s", hdrname.c_str());
validate_array_count(
reader->get_linput(),
&nbytes,
1,
text.c_str(),
sh.sh_offset);
out->resize(nbytes);
reader->seek(sh.sh_offset);
reader->safe_read(out->begin(), nbytes, /*apply_endianness=*/ false);
}
//-------------------------------------------------------------------------
bool section_headers_t::read_gnu_debuglink(
qstring *out_link,
uint32 *out_crc) const
{
const elf_shdr_t *s = get(SHT_PROGBITS, ".gnu_debuglink");
bool found = s != NULL;
if ( found )
{
bytevec_t buf;
read_file_contents(&buf, *s);
size_t sz = buf.size();
found = sz > 4;
if ( found )
{
if ( out_crc != NULL )
{
uint32 crc = *(uint32 *)(buf.begin() + (sz - 4));
if ( reader->get_ident().bytesex == ELFDATA2MSB )
crc = swap32(crc);
*out_crc = crc;
}
sz -= 4;
if ( out_link != NULL )
{
out_link->reserve(sz);
for ( size_t i=0; i < sz; ++i )
{
uchar ch = buf[i];
if ( ch == 0 )
break;
out_link->append(ch);
}
}
}
}
return found;
}
//-------------------------------------------------------------------------
// program_headers_t
//----------------------------------------------------------------------------
const char *program_headers_t::p_type_str(uint32 p_type) const
{
#define NM(tp) case PT_##tp: return #tp
#define NM2(tp, nm) case PT_##tp: return #nm
// OS-specific types
uint8 os_abi = reader->get_ident().osabi;
if ( os_abi == ELFOSABI_SOLARIS )
{
switch ( p_type )
{
NM2(SUNW_UNWIND, UNWIND);
NM2(SUNW_EH_FRAME, EH_FRAME);
NM(SUNWBSS);
NM2(SUNWSTACK, STACK);
NM2(SUNWDTRACE, DTRACE);
NM(SUNWCAP);
}
}
else
{
switch ( p_type )
{
NM2(GNU_EH_FRAME, EH_FRAME);
NM2(GNU_STACK, STACK);
NM2(GNU_RELRO, RO-AFTER);
}
}
switch ( p_type )
{
NM(NULL);
NM(LOAD);
NM(DYNAMIC);
NM(INTERP);
NM(NOTE);
NM(SHLIB);
NM(PHDR);
NM(TLS);
NM2(PAX_FLAGS, PAX-FLAG);
default:
{
uint32 m = reader->get_header().e_machine;
if ( m == EM_ARM )
{
switch ( p_type )
{
NM2(ARM_ARCHEXT, ARCHEXT);
NM2(ARM_EXIDX, EXIDX);
}
}
else if ( m == EM_AARCH64 )
{
switch ( p_type )
{
NM2(AARCH64_ARCHEXT, ARCHEXT);
NM2(AARCH64_UNWIND, EXIDX);
}
}
else if ( m == EM_IA64 )
{
switch ( p_type )
{
NM(HP_TLS);
NM(HP_CORE_NONE);
NM(HP_CORE_VERSION);
NM(HP_CORE_KERNEL);
NM(HP_CORE_COMM);
NM(HP_CORE_PROC);
NM(HP_CORE_LOADABLE);
NM(HP_CORE_STACK);
NM(HP_CORE_SHM);
NM(HP_CORE_MMF);
NM(HP_PARALLEL);
NM(HP_FASTBIND);
NM(HP_OPT_ANNOT);
NM(HP_HSL_ANNOT);
NM(HP_STACK);
NM(HP_CORE_UTSNAME);
NM(HP_LINKER_FOOTPRINT);
NM(IA_64_ARCHEXT);
NM(IA_64_UNWIND);
}
}
else if ( m == EM_MIPS )
{
switch ( p_type )
{
NM2(MIPS_IOPMOD, IOPMOD);
NM2(MIPS_EEMOD, EEMOD);
NM2(MIPS_PSPREL, PSPREL);
NM2(MIPS_PSPREL2, PSPREL2);
NM2(MIPS_REGINFO, REGINFO);
NM2(MIPS_RTPROC, RTPROC);
NM2(MIPS_OPTIONS, OPTIONS);
NM2(MIPS_ABIFLAGS, ABIFLAGS);
}
}
else if ( m == EM_PPC64 )
{
switch ( p_type )
{
case PHT_PS3PRX_RELA : return "PRXRELA";
}
}
return nullptr;
}
}
#undef NM2
#undef NM
}
//----------------------------------------------------------------------------
qstring program_headers_t::p_type_qstr(uint32 p_type) const
{
qstring s = p_type_str(p_type);
if ( s.empty() )
s.sprnt("%08X", p_type);
return s;
}
//----------------------------------------------------------------------------
uint64 program_headers_t::get_size_in_file(const elf_phdr_t &p) const
{
assert_initialized();
if ( p.p_type != PT_LOAD
&& p.p_type != PT_INTERP
&& p.p_type != PT_NOTE
&& p.p_type != PT_PHDR )
{
return 0;
}
uint64 next_boundary = reader->size();
if ( p.p_offset >= next_boundary )
return 0;
int idx = &p - begin();
if ( idx > -1 && (idx+1) < pheaders.size() )
{
const elf_phdr_t *np = CONST_CAST(program_headers_t*)(this)->get(idx+1);
if ( np->p_offset >= p.p_offset )
next_boundary = np->p_offset;
}
return qmin(p.p_filesz, next_boundary - p.p_offset);
}
//----------------------------------------------------------------------------
void program_headers_t::read_file_contents(
bytevec_t *out,
const elf_phdr_t &p) const
{
assert_initialized();
uint64 nbytes = p.p_filesz;
qstring text;
text.sprnt("Size of %s", p_type_qstr(p.p_type).c_str());
validate_array_count(
reader->get_linput(),
&nbytes,
1,
text.c_str(),
p.p_offset);
out->resize(nbytes);
reader->seek(p.p_offset);
reader->safe_read(out->begin(), nbytes, /*apply_endianness=*/ false);
}
//----------------------------------------------------------------------------
// Note Section
//
bool elf_note_t::unpack_sz(
size_t *out,
size_t *pstart,
const bytevec_t &buf,
bool mf)
{
const size_t start = *pstart;
const size_t szel = sizeof(uint32);
if ( !is_add_ok(start, szel) || start + szel > buf.size() )
return false;
if ( out != nullptr )
{
uint32 res = *(uint32 *)&buf[start];
if ( mf )
res = swap32(res);
*out = res;
}
*pstart += szel;
return true;
}
//----------------------------------------------------------------------------
bool elf_note_t::unpack_mem(
qstring *out,
const bytevec_t &buf,
size_t start,
size_t len,
bool as_strz)
{
if ( !is_add_ok(start, len) || start + len > buf.size() )
return false;
out->qclear();
if ( as_strz )
{
out->reserve(len);
for ( int i=0; i < len; ++i )
{
char ch = buf[start + i];
if ( ch == '\0' )
break;
out->append(ch);
}
}
else
{
out->resize(len, '\0');
memcpy(out->begin(), &buf[start], len);
}
return true;
}
//----------------------------------------------------------------------------
bool elf_note_t::unpack_strz(
qstring *out,
const bytevec_t &buf,
size_t start,
size_t len)
{
return unpack_mem(out, buf, start, len, /*as_strz=*/ true);
}
//----------------------------------------------------------------------------
bool elf_note_t::unpack(
elf_note_t *entry,
size_t *start,
const bytevec_t &buf,
bool mf)
{
size_t end = *start;
size_t namesz;
size_t descsz;
size_t type;
if ( !elf_note_t::unpack_sz(&namesz, &end, buf, mf)
|| !elf_note_t::unpack_sz(&descsz, &end, buf, mf)
|| !elf_note_t::unpack_sz(&type, &end, buf, mf) )
{
return false;
}
qstring name;
if ( !elf_note_t::unpack_strz(&name, buf, end, namesz) )
return false;
end += align_up(namesz, 4);
qstring desc;
if ( !elf_note_t::unpack_mem(&desc, buf, end, descsz) )
return false;
end += align_up(descsz, 4);
if ( entry != NULL )
{
entry->name = name;
entry->desc = desc;
entry->type = type;
}
*start = end;
return true;
}
//----------------------------------------------------------------------------
void notes_t::add(const bytevec_t &buf)
{
bool mf = reader->is_msb();
size_t start = 0;
while ( start < buf.size() )
{
elf_note_t &n = notes.push_back();
if ( !elf_note_t::unpack(&n, &start, buf, mf) )
{
notes.pop_back();
break;
}
}
}
//----------------------------------------------------------------------------
bool notes_t::get_build_id(qstring *out) const
{
assert_initialized();
for ( elf_notes_t::const_iterator p=notes.begin(); p != notes.end(); ++p )
{
const elf_note_t &en = *p;
if ( en.name == NT_NAME_GNU && en.type == NT_GNU_BUILD_ID )
{
int sz = en.desc.length();
out->qclear();
out->reserve(2*sz);
for ( int i=0; i < sz; ++i )
out->cat_sprnt("%02x", (unsigned char)en.desc[i]);
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
const char *d_note_gnu_type_str(uint64 type)
{
#define NM(tp) case tp: return #tp
switch ( type )
{
NM(NT_GNU_ABI_TAG);
NM(NT_GNU_HWCAP);
NM(NT_GNU_BUILD_ID);
NM(NT_GNU_GOLD_VERSION);
NM(NT_GNU_PROPERTY_TYPE_0);
}
return NULL;
#undef NM
}
//----------------------------------------------------------------------------
const char *d_note_linux_type_str(uint64 type)
{
#define NM(tp) case tp: return #tp
switch ( type )
{
NM(NT_PRXFPREG);
NM(NT_PPC_VMX);
NM(NT_PPC_VSX);
NM(NT_PPC_TAR);
NM(NT_PPC_PPR);
NM(NT_PPC_DSCR);
NM(NT_PPC_EBB);
NM(NT_PPC_PMU);
NM(NT_PPC_TM_CGPR);
NM(NT_PPC_TM_CFPR);
NM(NT_PPC_TM_CVMX);
NM(NT_PPC_TM_CVSX);
NM(NT_PPC_TM_SPR);
NM(NT_PPC_TM_CTAR);
NM(NT_PPC_TM_CPPR);
NM(NT_PPC_TM_CDSCR);
NM(NT_386_TLS);
NM(NT_386_IOPERM);
NM(NT_X86_XSTATE);
NM(NT_S390_HIGH_GPRS);
NM(NT_S390_TIMER);
NM(NT_S390_TODCMP);
NM(NT_S390_TODPREG);
NM(NT_S390_CTRS);
NM(NT_S390_PREFIX);
NM(NT_S390_LAST_BREAK);
NM(NT_S390_SYSTEM_CALL);
NM(NT_S390_TDB);
NM(NT_S390_VXRS_LOW);
NM(NT_S390_VXRS_HIGH);
NM(NT_S390_GS_CB);
NM(NT_S390_GS_BC);
NM(NT_ARM_VFP);
NM(NT_ARM_TLS);
NM(NT_ARM_HW_BREAK);
NM(NT_ARM_HW_WATCH);
NM(NT_ARM_SVE);
}
return NULL;
#undef NM
}
//----------------------------------------------------------------------------
const char *d_note_core_type_str(uint64 type)
{
#define NM(tp) case tp: return #tp
switch ( type )
{
NM(NT_PRSTATUS);
NM(NT_FPREGSET);
NM(NT_PRPSINFO);
NM(NT_TASKSTRUCT);
NM(NT_AUXV);
NM(NT_SIGINFO);
NM(NT_FILE);
NM(NT_PSTATUS);
NM(NT_FPREGS);
NM(NT_PSINFO);
NM(NT_LWPSTATUS);
NM(NT_LWPSINFO);
NM(NT_WIN32PSTATUS);
}
return NULL;
#undef NM
}
//----------------------------------------------------------------------------
template<> void buffered_input_t<sym_rel>::start_reading()
{
reader.get_arch_specific()->on_start_symbols(reader);
}
//----------------------------------------------------------------------------
template<> bool buffered_input_t<sym_rel>::read_item(sym_rel &storage)
{
storage = sym_rel();
elf_sym_t &orig = storage.original;
#define _safe(expr) \
do \
{ \
if ( expr < 0 ) \
return false; \
} while ( 0 )
_safe(reader.read_symbol(&orig));
ushort bind = ELF_ST_BIND(orig.st_info);
// assert: bind <= STB_HIPROC
if ( bind > STB_WEAK )
{
CASSERT(STB_LOCAL < STB_WEAK && STB_GLOBAL < STB_WEAK); //-V590 expression is excessive
if ( reader.get_header().e_machine == EM_ARM && bind == STB_LOPROC+1 )
// codewarrior for arm seems to use this binding type similar to local or weak
bind = STB_WEAK;
else if ( bind < STB_LOOS )
bind = STB_INVALID;
}
storage.bind = (uchar) bind;
storage.sec = 0;
storage.type = ELF_ST_TYPE(orig.st_info);
storage.value = orig.st_value + reader.get_load_bias();
storage.size = orig.st_size;
return true;
}
//----------------------------------------------------------------------------
static inline void swap_64_at(uint64 *ptr)
{
*ptr = swap64(*ptr);
}
//----------------------------------------------------------------------------
static inline void swap_64_at(int64 *ptr)
{
*ptr = swap64(*ptr);
}
//----------------------------------------------------------------------------
#define swap_addr(ptr) swap_64_at(ptr);
#define swap_xword(ptr) swap_64_at(ptr);
#define swap_sxword(ptr) swap_64_at(ptr);
//----------------------------------------------------------------------------
template<> ssize_t buffered_input_t<elf_rel_t>::read_items(size_t max)
{
if ( isize != sizeof(Elf32_Rel) && isize != sizeof(Elf64_Rel) )
return 0;
if ( !is_mul_ok<uint64>(read, isize) || !is_mul_ok(max, isize) )
return 0;
input_status_t save_excursion(reader);
if ( save_excursion.seek(offset + (read * isize)) == -1 )
return 0;
ssize_t bytes = max * isize;
if ( qlread(reader.get_linput(), buffer.begin(), bytes) != bytes )
return 0;
#if __MF__
bool swap = !reader.is_msb();
#else
bool swap = reader.is_msb();
#endif
if ( isize == sizeof(Elf32_Rel) )
{
Elf32_Rel *rel32 = (Elf32_Rel *) buffer.begin();
Elf64_Rel *rel64 = (Elf64_Rel *) buffer.begin();
rel32 += max - 1;
rel64 += max - 1;
uint64 inf64, off64;
for ( size_t i = 0; i < max; i++, rel32--, rel64-- )
{
if ( swap )
{
inf64 = swap32(rel32->r_info);
off64 = swap32(rel32->r_offset);
}
else
{
inf64 = rel32->r_info;
off64 = rel32->r_offset;
}
rel64->r_info = inf64;
rel64->r_offset = off64;
}
}
else
{
if ( swap )
{
elf_rel_t *rel64 = (elf_rel_t *)buffer.begin();
for ( size_t i = 0; i < max; i++, rel64++ )
{
swap_addr(&rel64->r_offset);
swap_xword(&rel64->r_info);
}
}
}
return max;
}
//----------------------------------------------------------------------------
template<> ssize_t buffered_input_t<elf_rela_t>::read_items(size_t max)
{
if ( isize != sizeof(Elf32_Rela) && isize != sizeof(Elf64_Rela) )
return 0;
if ( !is_mul_ok<uint64>(read, isize) || !is_mul_ok(max, isize) )
return 0;
input_status_t save_excursion(reader);
if ( save_excursion.seek(offset + (read * isize)) == -1 )
return 0;
ssize_t bytes = max * isize;
if ( qlread(reader.get_linput(), buffer.begin(), bytes) != bytes )
return 0;
#if __MF__
bool swap = !reader.is_msb();
#else
bool swap = reader.is_msb();
#endif
if ( isize == sizeof(Elf32_Rela) )
{
Elf32_Rela *rela32 = (Elf32_Rela *) buffer.begin();
Elf64_Rela *rela64 = (Elf64_Rela *) buffer.begin();
rela32 += max - 1;
rela64 += max - 1;
uint64 inf64, off64;
int64 addend;
for ( size_t i = 0; i < max; i++, rela32--, rela64-- )
{
if ( swap )
{
inf64 = swap32(rela32->r_info);
off64 = swap32(rela32->r_offset);
addend = swap32(rela32->r_addend);
}
else
{
inf64 = rela32->r_info;
off64 = rela32->r_offset;
addend = rela32->r_addend;
}
rela64->r_info = inf64;
rela64->r_offset = off64;
rela64->r_addend = addend;
}
}
else
{
if ( swap )
{
elf_rela_t *rela64 = (elf_rela_t *)buffer.begin();
for ( size_t i = 0; i < max; i++, rela64++ )
{
swap_addr(&rela64->r_offset);
swap_xword(&rela64->r_info);
swap_sxword(&rela64->r_addend);
}
}
}
return max;
}
//--------------------------------------------------------------------------
class aps2_unpacker_t
{
enum
{
RELOCATION_GROUPED_BY_INFO_FLAG = 1,
RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2,
RELOCATION_GROUPED_BY_ADDEND_FLAG = 4,
RELOCATION_GROUP_HAS_ADDEND_FLAG = 8
};
const uchar *ptr;
const uchar *end;
int flags;
bool ok;
bool grouped_by_offset_delta() const { return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; }
bool grouped_by_info() const { return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; }
bool grouped_by_addend() const { return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; }
bool group_has_addend() const { return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; }
int64 getval()
{
int64 v;
if ( !unpack_sleb128(&v, &ptr, end) )
ok = false;
return v;
}
public:
aps2_unpacker_t(const uchar *p, const uchar *e)
: ptr(p), end(e), flags(0), ok(true) {}
//--------------------------------------------------------------------------
void unpack(elf_rela_vec_t *out, int64 nrelocs)
{
elf_rela_t rela;
rela.r_offset = getval();
rela.r_addend = 0;
rela.r_info = 0;
out->resize(nrelocs);
size_t n = 0;
while ( ok ) // repeat for all groups
{
int64 nrels = getval(); // number of relocs in the group
flags = getval(); // flags for the group
int64 offset_delta = 0;
if ( grouped_by_offset_delta() )
offset_delta = getval();
if ( grouped_by_info() )
rela.r_info = getval();
if ( grouped_by_addend() && group_has_addend() )
rela.r_addend += getval();
else if ( !group_has_addend() )
rela.r_addend = 0;
// unpack all relocs in the group
for ( int64 i=0; i < nrels; i++ )
{
if ( !grouped_by_offset_delta() )
offset_delta = getval();
rela.r_offset += offset_delta;
if ( !grouped_by_info() )
rela.r_info = getval();
if ( group_has_addend() && !grouped_by_addend() )
rela.r_addend += getval();
if ( !ok )
{ // some kind of error occurred, remove the uninitialized relocs
out->resize(n);
return;
}
if ( n == nrelocs )
return; // safety check
out->at(n++) = rela;
}
}
}
};
//----------------------------------------------------------------------------
bool buffered_rela_t::next(elf_rela_t *&storage)
{
if ( !packed )
return inherited::next(storage);
if ( cur >= end )
{
if ( end != 0 )
return false;
// read everything at once and unpack
bytevec_t bytes;
bytes.resize(count);
input_status_t save_excursion(reader);
if ( save_excursion.seek(offset) == -1 )
return false;
ssize_t nread = qlread(reader.get_linput(), bytes.begin(), count);
if ( nread < count )
{
msg("READ ERROR: read only %" FMT_ZS " bytes instead of %" FMT_64 "d bytes\n",
nread, count);
if ( nread <= 8 )
return false;
bytes.resize(nread);
}
if ( bytes.size() < sizeof(uint32) )
return false;
const uchar *ptr = bytes.begin();
const uchar *endp = bytes.end();
uint32 magic = *(uint32*)ptr;
ptr += 4;
#define PKREL_APR1 MC4('A','P','R','1')
#define PKREL_APA1 MC4('A','P','A','1')
#define PKREL_APS2 MC4('A','P','S','2')
int64 n;
if ( !unpack_sleb128(&n, &ptr, endp) )
return false;
if ( n <= 0 || n >= count ) // sanity check
return false;
switch ( magic )
{
default: // unknown magic
// case PKREL_APR1:
// case PKREL_APA1:
ask_for_feedback(
"The relocation packing method '%4.4s' is not supported yet.",
(const char *)bytes.begin());
return false;
case PKREL_APS2:
aps2_unpacker_t unp(ptr, endp);
unp.unpack(&buffer, n);
break;
}
end = n;
}
storage = &buffer[cur++];
return true;
}
//----------------------------------------------------------------------------
template<> bool buffered_input_t<elf_dyn_t>::read_item(elf_dyn_t &storage)
{
// FIXME: Load bias?
memset(&storage, 0, sizeof(storage));
_safe(reader.read_sxword(&storage.d_tag));
_safe(reader.read_addr(&storage.d_un));
#undef _safe
return true;
}
//-------------------------------------------------------------------------
void dynamic_info_t::initialize(const reader_t &reader)
{
symtab().entsize = reader.stdsizes.entries.sym;
rel().entsize = reader.stdsizes.dyn.rel;
rela().entsize = reader.stdsizes.dyn.rela;
QASSERT(20037, symtab().entsize != 0 && rel().entsize != 0 && rela().entsize != 0);
}
//----------------------------------------------------------------------------
bool dynamic_info_t::fill_section_header(
elf_shdr_t *sh,
dynamic_info_type_t type) const
{
int shtype;
switch ( type )
{
case DIT_SYMTAB: shtype = SHT_DYNSYM; break;
case DIT_REL: shtype = SHT_REL; break;
case DIT_RELA: shtype = SHT_RELA; break;
case DIT_ANDROID_REL: shtype = SHT_REL; break;
case DIT_ANDROID_RELA: shtype = SHT_RELA; break;
case DIT_VERDEF: shtype = SHT_GNU_verdef; break;
case DIT_VERNEED: shtype = SHT_GNU_verneed; break;
case DIT_VERSYM: shtype = SHT_GNU_versym; break;
case DIT_PLT:
shtype = plt_rel_type == DT_RELA ? SHT_RELA : SHT_REL;
break;
default:
QASSERT(20101, false);
};
const entry_t &entry = entries[type];
if ( !entry.is_valid() )
return false;
memset(sh, 0, sizeof(*sh));
sh->sh_addr = entry.addr;
sh->sh_offset = entry.offset;
sh->sh_size = entry.size;
sh->sh_info = entry.info;
sh->sh_type = shtype;
if ( type != DIT_ANDROID_REL && type != DIT_ANDROID_RELA )
{
sh->sh_entsize = sh->sh_type == SHT_DYNSYM ? entry.entsize
: sh->sh_type == SHT_RELA ? rela().entsize
: rel().entsize;
}
return true;
}
//----------------------------------------------------------------------------
qstring dynamic_info_t::d_tag_str_ext(const reader_t &reader, int64 d_tag)
{
qstring buf = d_tag_str(reader, d_tag);
if ( buf.empty() )
{
buf.sprnt("DT_???? Unknown (%08" FMT_64 "X)", d_tag);
return buf;
}
if ( buf.length() < 12 )
buf.resize(12, ' ');
uint16 e_machine = reader.get_header().e_machine;
const char *ext = NULL;
switch ( d_tag )
{
case DT_NULL:
ext = "end of _DYNAMIC array";
break;
case DT_NEEDED:
ext = "str-table offset name to needed library";
break;
case DT_PLTRELSZ:
ext = "tot.size in bytes of relocation entries";
break;
case DT_HASH:
ext = "addr. of symbol hash table";
break;
case DT_STRTAB:
ext = "addr of string table";
break;
case DT_SYMTAB:
ext = "addr of symbol table";
break;
case DT_RELA:
ext = "addr of relocation table";
break;
case DT_RELASZ:
ext = "size in bytes of DT_RELA table";
break;
case DT_RELAENT:
ext = "size in bytes of DT_RELA entry";
break;
case DT_STRSZ:
ext = "size in bytes of string table";
break;
case DT_SYMENT:
ext = "size in bytes of symbol table entry";
break;
case DT_INIT:
ext = "addr. of initialization function";
break;
case DT_FINI:
ext = "addr. of termination function";
break;
case DT_SONAME:
ext = "offs in str.-table - name of shared object";
break;
case DT_RPATH:
ext = "offs in str-table - search path";
break;
case DT_RUNPATH:
ext = "array of search pathes";
break;
case DT_SYMBOLIC:
ext = "start search of shared object";
break;
case DT_REL:
ext = "addr of relocation table";
break;
case DT_RELSZ:
ext = "tot.size in bytes of DT_REL";
break;
case DT_RELENT:
ext = "size in bytes of DT_REL entry";
break;
case DT_PLTREL:
ext = "type of relocation (DT_REL or DT_RELA)";
break;
case DT_DEBUG:
ext = "not specified";
break;
case DT_TEXTREL:
ext = "segment permisson";
break;
case DT_PLTGOT:
if ( e_machine == EM_PPC )
ext = "addr of PLT";
break;
case DT_JMPREL:
if ( e_machine == EM_PPC )
ext = "addr of JMP_SLOT relocation table";
else
ext = "addr of dlt procedure (if present)";
break;
case DT_PPC_GOT:
if ( e_machine == EM_PPC )
ext = "addr of _GLOBAL_OFFSET_TABLE_";
break;
}
if ( ext == NULL && reader.is_arm() )
{
switch ( d_tag )
{
case DT_ANDROID_REL:
ext = " offset of packed REL data";
break;
case DT_ANDROID_RELSZ:
ext = " size of DT_ANDROID_REL";
break;
case DT_ANDROID_RELA:
ext = " offset of packed RELA data";
break;
case DT_ANDROID_RELASZ:
ext = " size of DT_ANDROID_RELA";
break;
}
}
buf.append(ext);
return buf;
}
//----------------------------------------------------------------------------
const char *dynamic_info_t::d_tag_str(const reader_t &reader, int64 d_tag)
{
uint16 e_machine = reader.get_header().e_machine;
#define NM(tp) case tp: return #tp
// OS-specific types
uint8 os_abi = reader.get_ident().osabi;
if ( os_abi == ELFOSABI_SOLARIS )
{
switch ( d_tag )
{
NM(DT_SUNW_AUXILIARY);
// NM(DT_SUNW_RTLDINF);
NM(DT_SUNW_FILTER);
NM(DT_SUNW_CAP);
NM(DT_SUNW_SYMTAB);
NM(DT_SUNW_SYMSZ);
NM(DT_SUNW_ENCODING);
// NM(DT_SUNW_SORTENT);
NM(DT_SUNW_SYMSORT);
NM(DT_SUNW_SYMSORTSZ);
NM(DT_SUNW_TLSSORT);
NM(DT_SUNW_TLSSORTSZ);
NM(DT_SUNW_CAPINFO);
NM(DT_SUNW_STRPAD);
NM(DT_SUNW_CAPCHAIN);
NM(DT_SUNW_LDMACH);
NM(DT_SUNW_CAPCHAINENT);
NM(DT_SUNW_CAPCHAINSZ);
NM(DT_SUNW_PARENT);
NM(DT_SUNW_ASLR);
NM(DT_SUNW_RELAX);
NM(DT_SUNW_NXHEAP);
NM(DT_SUNW_NXSTACK);
}
}
switch ( d_tag )
{
NM(DT_NULL);
NM(DT_NEEDED);
NM(DT_PLTRELSZ);
NM(DT_HASH);
NM(DT_STRTAB);
NM(DT_SYMTAB);
NM(DT_RELA);
NM(DT_RELASZ);
NM(DT_RELAENT);
NM(DT_STRSZ);
NM(DT_SYMENT);
NM(DT_INIT);
NM(DT_FINI);
NM(DT_SONAME);
NM(DT_RPATH);
NM(DT_RUNPATH);
NM(DT_SYMBOLIC);
NM(DT_REL);
NM(DT_RELSZ);
NM(DT_RELENT);
NM(DT_PLTREL);
NM(DT_DEBUG);
NM(DT_TEXTREL);
NM(DT_PLTGOT);
NM(DT_JMPREL);
NM(DT_BIND_NOW);
NM(DT_PREINIT_ARRAY);
NM(DT_INIT_ARRAY);
NM(DT_FINI_ARRAY);
NM(DT_INIT_ARRAYSZ);
NM(DT_FINI_ARRAYSZ);
NM(DT_PREINIT_ARRAYSZ);
NM(DT_FLAGS);
NM(DT_VALRNGLO);
NM(DT_GNU_PRELINKED);
NM(DT_GNU_CONFLICTSZ);
NM(DT_GNU_LIBLISTSZ);
NM(DT_CHECKSUM);
NM(DT_PLTPADSZ);
NM(DT_MOVEENT);
NM(DT_MOVESZ);
NM(DT_FEATURE);
NM(DT_POSFLAG_1);
NM(DT_SYMINSZ);
NM(DT_SYMINENT);
// NM(DT_VALRNGHI);
NM(DT_ADDRRNGLO);
NM(DT_GNU_HASH);
NM(DT_TLSDESC_PLT);
NM(DT_TLSDESC_GOT);
NM(DT_GNU_CONFLICT);
NM(DT_GNU_LIBLIST);
NM(DT_CONFIG);
NM(DT_DEPAUDIT);
NM(DT_AUDIT);
NM(DT_PLTPAD);
NM(DT_MOVETAB);
NM(DT_SYMINFO);
// NM(DT_ADDRRNGHI);
NM(DT_RELACOUNT);
NM(DT_RELCOUNT);
NM(DT_FLAGS_1);
NM(DT_VERDEF);
NM(DT_VERDEFNUM);
NM(DT_VERNEED);
NM(DT_VERNEEDNUM);
NM(DT_VERSYM);
NM(DT_AUXILIARY);
NM(DT_USED);
NM(DT_FILTER);
}
if ( reader.is_mips() )
{
switch ( d_tag )
{
NM(DT_MIPS_RLD_VERSION);
NM(DT_MIPS_TIME_STAMP);
NM(DT_MIPS_ICHECKSUM);
NM(DT_MIPS_IVERSION);
NM(DT_MIPS_FLAGS);
NM(DT_MIPS_BASE_ADDRESS);
NM(DT_MIPS_MSYM);
NM(DT_MIPS_CONFLICT);
NM(DT_MIPS_LIBLIST);
NM(DT_MIPS_LOCAL_GOTNO);
NM(DT_MIPS_CONFLICTNO);
NM(DT_MIPS_LIBLISTNO);
NM(DT_MIPS_SYMTABNO);
NM(DT_MIPS_UNREFEXTNO);
NM(DT_MIPS_GOTSYM);
NM(DT_MIPS_HIPAGENO);
NM(DT_MIPS_RLD_MAP);
NM(DT_MIPS_DELTA_CLASS);
NM(DT_MIPS_DELTA_CLASS_NO);
NM(DT_MIPS_DELTA_INSTANCE);
NM(DT_MIPS_DELTA_INSTANCE_NO);
NM(DT_MIPS_DELTA_RELOC);
NM(DT_MIPS_DELTA_RELOC_NO);
NM(DT_MIPS_DELTA_SYM);
NM(DT_MIPS_DELTA_SYM_NO);
NM(DT_MIPS_DELTA_CLASSSYM);
NM(DT_MIPS_DELTA_CLASSSYM_NO);
NM(DT_MIPS_CXX_FLAGS);
NM(DT_MIPS_PIXIE_INIT);
NM(DT_MIPS_SYMBOL_LIB);
NM(DT_MIPS_LOCALPAGE_GOTIDX);
NM(DT_MIPS_LOCAL_GOTIDX);
NM(DT_MIPS_HIDDEN_GOTIDX);
NM(DT_MIPS_PROTECTED_GOTIDX);
NM(DT_MIPS_OPTIONS);
NM(DT_MIPS_INTERFACE);
NM(DT_MIPS_DYNSTR_ALIGN);
NM(DT_MIPS_INTERFACE_SIZE);
NM(DT_MIPS_RLD_TEXT_RESOLVE_ADDR);
NM(DT_MIPS_PERF_SUFFIX);
NM(DT_MIPS_COMPACT_SIZE);
NM(DT_MIPS_GP_VALUE);
NM(DT_MIPS_AUX_DYNAMIC);
NM(DT_MIPS_PLTGOT);
NM(DT_MIPS_RWPLT);
}
}
else if ( e_machine == EM_IA64 )
{
switch ( d_tag )
{
NM(DT_HP_LOAD_MAP);
NM(DT_HP_DLD_FLAGS);
NM(DT_HP_DLD_HOOK);
NM(DT_HP_UX10_INIT);
NM(DT_HP_UX10_INITSZ);
NM(DT_HP_PREINIT);
NM(DT_HP_PREINITSZ);
NM(DT_HP_NEEDED);
NM(DT_HP_TIME_STAMP);
NM(DT_HP_CHECKSUM);
NM(DT_HP_GST_SIZE);
NM(DT_HP_GST_VERSION);
NM(DT_HP_GST_HASHVAL);
NM(DT_HP_EPLTREL);
NM(DT_HP_EPLTRELSZ);
NM(DT_HP_FILTERED);
NM(DT_HP_FILTER_TLS);
NM(DT_HP_COMPAT_FILTERED);
NM(DT_HP_LAZYLOAD);
NM(DT_HP_BIND_NOW_COUNT);
NM(DT_PLT);
NM(DT_PLT_SIZE);
NM(DT_DLT);
NM(DT_DLT_SIZE);
NM(DT_HP_SYM_CHECKSUM);
NM(DT_IA_64_PLT_RESERVE);
}
}
else if ( e_machine == EM_PPC )
{
switch ( d_tag )
{
NM(DT_PPC_GOT);
}
}
else if ( reader.is_arm() )
{
switch ( d_tag )
{
NM(DT_ANDROID_REL);
NM(DT_ANDROID_RELSZ);
NM(DT_ANDROID_RELA);
NM(DT_ANDROID_RELASZ);
}
}
#undef NM
return NULL;
}
//----------------------------------------------------------------------------
qstring dynamic_info_t::d_un_str(const reader_t &reader, int64 d_tag, int64 d_un)
{
qstring name;
switch ( d_tag )
{
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH:
case DT_NEEDED:
case DT_AUXILIARY:
case DT_FILTER:
case DT_CONFIG:
case DT_DEPAUDIT:
case DT_AUDIT:
reader.get_name(&name, reader.dyn_strtab, uint32(d_un));
break;
}
return name;
}
//----------------------------------------------------------------------------
size_t symrel_cache_t::slice_start(slice_type_t t) const
{
check_type(t);
return t == SLT_DYNSYM ? dynsym_index : 0;
}
//----------------------------------------------------------------------------
size_t symrel_cache_t::slice_end(slice_type_t t) const
{
check_type(t);
return t == SLT_SYMTAB ? dynsym_index : storage.size();
}
//----------------------------------------------------------------------------
sym_rel &symrel_cache_t::append(slice_type_t t)
{
check_type(t);
size_t idx = slice_end(t);
if ( t == SLT_SYMTAB )
++dynsym_index;
if ( idx == storage.size() )
{
return storage.push_back();
}
else
{
QASSERT(20133, idx <= storage.size());
qvector<sym_rel>::iterator it = storage.begin() + idx;
storage.insert(it, sym_rel());
return *it;
}
}
// ===========================================================================
// ARM-specific code.
// ===========================================================================
//----------------------------------------------------------------------------
bool arm_arch_specific_t::is_mapping_symbol(const char *name) const
{
if ( name == NULL )
return false;
if ( name[0] == '$'
&& name[1] != '\0'
&& (name[2] == '\0' || name[2] == '.') )
{
switch ( name[1] )
{
case 'a': // labels the first byte of a sequence of ARM instructions. Its type is STT_FUNC.
case 't': // labels the first byte of a sequence of Thumb instructions. Its type is STT_FUNC.
case 'b': // labels a Thumb BL instruction. Its type is STT_FUNC.
case 'd': // labels the first byte of a sequence of data items. Its type is STT_OBJECT.
case 'p': // labels the final, PC-modifying instruction of an
// indirect function call. Its type is STT_FUNC.
// (An indirect call is a call through a function pointer
// variable). $p does not label the PC-modifying
// instruction of a function return sequence.
case 'f': // labels a function pointer constant (static pointer to code).
// Its type is STT_OBJECT.
case 'x': // Start of a sequence of A64 instructions
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
void arm_arch_specific_t::on_start_symbols(reader_t &)
{
has_mapsym = false;
}
//----------------------------------------------------------------------------
void arm_arch_specific_t::on_symbol_read(reader_t &reader, sym_rel &sym)
{
const char *name = sym.get_original_name(reader).c_str();
if ( is_mapping_symbol(name) )
{
has_mapsym = true;
// assert: name != NULL
char name1 = name[1];
if ( name1 == 'a' || name1 == 't' || name1 == 'x' )
{
isa_t isa = name1 == 'a' || name1 == 'x' ? isa_arm : isa_thumb;
if ( name1 == 't' )
sym.set_flag(thumb_function);
notify_isa(reader, sym, isa, true);
if ( is_mapping_symbols_tracking() )
set_isa(sym, isa);
}
}
else
{
uchar bind = sym.bind;
// Keep going _only_ if function
ushort orig_type = ELF_ST_TYPE(sym.original.st_info);
if ( (orig_type != STT_FUNC
&& orig_type != STT_ARM_TFUNC
&& orig_type != STT_ARM_16BIT )
|| (bind != STB_GLOBAL
&& bind != STB_LOCAL
&& bind != STB_WEAK) )
{
return;
}
sym.value &= ~1;
// If original type is ARM_TFUNC, make it FUNC,
// so it gets treated as a regular FUNC by
// upstream code.
if ( orig_type == STT_ARM_TFUNC )
sym.type = STT_FUNC;
if ( (orig_type == STT_ARM_TFUNC
|| orig_type == STT_ARM_16BIT
|| (sym.original.st_value & 1) != 0) )
{
sym.set_flag(thumb_function);
notify_isa(reader, sym, isa_thumb, false);
}
if ( !sym.has_flag(thumb_function)
&& is_mapping_symbols_tracking()
&& get_isa(sym) == isa_thumb )
{
sym.set_flag(thumb_function);
notify_isa(reader, sym, isa_thumb, false);
}
if ( !sym.has_flag(thumb_function) )
notify_isa(reader, sym, isa_arm, false);
}
}
//----------------------------------------------------------------------------
void arm_arch_specific_t::set_isa(const sym_rel &symbol, isa_t isa)
{
isa_ranges_t::iterator it = isa_ranges.find(symbol.sec);
if ( it == isa_ranges.end() )
{
isa_ranges[symbol.sec] = section_isa_ranges_t();
it = isa_ranges.find(symbol.sec);
}
section_isa_ranges_t &section_isa_ranges = it->second;
section_isa_ranges[symbol.original.st_value] = isa;
}
//----------------------------------------------------------------------------
arm_arch_specific_t::isa_t arm_arch_specific_t::get_isa(const sym_rel &symbol) const
{
isa_t current_isa = isa_arm;
isa_ranges_t::const_iterator it = isa_ranges.find(symbol.sec);
if ( it != isa_ranges.end() )
{
const section_isa_ranges_t &section_isa_ranges = it->second;
section_isa_ranges_t::const_iterator p;
section_isa_ranges_t::const_iterator end = section_isa_ranges.end();
for ( p = section_isa_ranges.begin(); p != end; ++p )
{
uint64 offset_in_section = p->first;
if ( offset_in_section > symbol.original.st_value )
break;
current_isa = p->second;
}
}
return current_isa;
}
#endif // ELF_READER_CPP