3250 lines
103 KiB
C++
3250 lines
103 KiB
C++
#ifndef __ELF_H__
|
|
#define __ELF_H__
|
|
|
|
#include <diskio.hpp>
|
|
#include <fixup.hpp>
|
|
|
|
#pragma pack(push, 4)
|
|
|
|
// gcc does not allow to initialize indexless arrays for some reason
|
|
// put an arbitrary number here. the same with visual c++
|
|
#if defined(__GNUC__) || defined(_MSC_VER)
|
|
#define MAXRELSYMS 64
|
|
#else
|
|
#define MAXRELSYMS
|
|
#endif
|
|
|
|
struct elf_loader_t;
|
|
struct elf_file_info_t;
|
|
typedef Elf64_Shdr elf_shdr_t;
|
|
typedef Elf64_Phdr elf_phdr_t;
|
|
struct elf_sym_t : public Elf64_Sym
|
|
{
|
|
elf_sym_t()
|
|
{
|
|
st_name = 0;
|
|
st_info = 0;
|
|
st_other = 0;
|
|
st_shndx = 0;
|
|
st_value = 0;
|
|
st_size = 0;
|
|
}
|
|
};
|
|
typedef Elf64_Dyn elf_dyn_t;
|
|
typedef Elf64_Rel elf_rel_t;
|
|
struct elf_rela_t : public Elf64_Rela
|
|
{
|
|
elf_rela_t()
|
|
{
|
|
r_offset = 0;
|
|
r_info = 0;
|
|
r_addend = 0;
|
|
}
|
|
};
|
|
|
|
typedef qvector<elf_rela_t> elf_rela_vec_t;
|
|
|
|
class reader_t;
|
|
struct sym_rel;
|
|
typedef uint32 elf_sym_idx_t;
|
|
struct elf_symbol_version_t;
|
|
struct dynamic_info_t;
|
|
|
|
typedef uint32 elf_shndx_t;
|
|
typedef qvector<elf_shdr_t> elf_shdrs_t;
|
|
typedef qvector<elf_phdr_t> elf_phdrs_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_ehdr_t : public Elf64_Ehdr
|
|
{
|
|
elf_shndx_t real_shnum; // number of entries in the SHT
|
|
// (may be greater than 0xFF00)
|
|
elf_shndx_t real_shstrndx; // section name string table section index
|
|
// (may be greater than 0xFF00)
|
|
|
|
bool has_pht() const { return e_phoff != 0; }
|
|
void set_no_pht()
|
|
{
|
|
e_phoff = 0;
|
|
e_phnum = 0;
|
|
}
|
|
|
|
bool has_sht() const { return e_shoff != 0; }
|
|
void set_no_sht()
|
|
{
|
|
e_shoff = 0;
|
|
e_shnum = 0;
|
|
real_shnum = 0;
|
|
}
|
|
bool is_valid_shndx(elf_shndx_t idx) const
|
|
{
|
|
return idx < real_shnum;
|
|
}
|
|
|
|
bool has_shstrtab() const { return real_shstrndx != 0; }
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
typedef Elf64_Chdr elf_chdr_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// rel_data_t holds whatever relocation information appears to be common
|
|
// to most ELF relocation "algorithms", as defined in the per-CPU
|
|
// addenda.
|
|
// Note: Most comments below were picked from the abi386.pdf file.
|
|
struct rel_data_t
|
|
{
|
|
// Relocation type: R_<processor>_<reloc-type>.
|
|
uint32 type;
|
|
|
|
// abi386.pdf: This means the place (section offset or address) of
|
|
// the storage unit being relocated (computed using r_offset).
|
|
ea_t P;
|
|
|
|
// abi386.pdf: This means the value of the symbol whose index
|
|
// resides in the relocation entry.
|
|
uval_t S;
|
|
|
|
// S, plus addend
|
|
ea_t Sadd;
|
|
|
|
// Whether the 'reloc' parameter passed to 'proc_handle_reloc()'
|
|
// is a REL, or a RELA (the actual reloc entry object
|
|
// itself will always be a elf_rela_t instance).
|
|
enum rel_entry_t
|
|
{
|
|
re_rel,
|
|
re_rela
|
|
};
|
|
rel_entry_t rel_entry;
|
|
bool is_rel() const { return rel_entry == re_rel; }
|
|
bool is_rela() const { return !is_rel(); }
|
|
};
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
enum slice_type_t
|
|
{
|
|
SLT_INVALID = 0,
|
|
SLT_SYMTAB = 1,
|
|
SLT_DYNSYM = 2,
|
|
SLT_WHOLE = 3,
|
|
};
|
|
struct symrel_idx_t
|
|
{
|
|
symrel_idx_t() : type(SLT_INVALID), idx(0) {}
|
|
symrel_idx_t(slice_type_t t, elf_sym_idx_t i) : type(t), idx(i) {}
|
|
|
|
slice_type_t type;
|
|
elf_sym_idx_t idx;
|
|
|
|
bool operator==(const symrel_idx_t &other) const
|
|
{
|
|
return other.type == type
|
|
&& other.idx == idx;
|
|
}
|
|
bool operator<(const symrel_idx_t &other) const
|
|
{
|
|
if ( type < other.type )
|
|
return true;
|
|
if ( type > other.type )
|
|
return false;
|
|
return idx < other.idx;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct got_access_t : public range_t
|
|
{
|
|
elf_loader_t &ldr;
|
|
// the name _GLOBAL_OFFSET_TABLE_
|
|
static const char gtb_name[];
|
|
|
|
// is the given symbol the _GLOBAL_OFFSET_TABLE_?
|
|
static bool is_symbol_got(const sym_rel &sym, const char *name);
|
|
|
|
got_access_t(elf_loader_t &_ldr) : range_t(0, 0), ldr(_ldr) {}
|
|
|
|
// Get the start address of the GOT.
|
|
// If no GOT currently exists, and 'create' is true, one will
|
|
// be created in a segment called ".got".
|
|
// If no GOT exists or an error occurred while creating .got segment,
|
|
// the function returns 0.
|
|
ea_t get_start_ea(elf_file_info_t &finfo, bool create = false);
|
|
|
|
//
|
|
void set_start_ea(ea_t ea) { start_ea = ea; }
|
|
|
|
// get .got segment using the storage of well-known sections.
|
|
// If it didn't exist and the flag `create' is set then create an empty
|
|
// ".got" segment with the given initial size.
|
|
// Set the flag `create' if such segment is just created.
|
|
// If .got segment doesn't exist or an error occurred while creating it,
|
|
// the function returns NULL.
|
|
segment_t *get_got_segment(elf_file_info_t &finfo, bool *create, uint32 init_size = 0) const;
|
|
|
|
// Some relocations, such as ARM's R_ARM_TLS_IE32, require that a got entry
|
|
// be present in the linked file, in order to point to that entry.
|
|
// However, when we are loading a simple relocatable ELF object file,
|
|
// there's no GOT present. This is problematic because,
|
|
// although we _could_ be taking a shortcut and patch the fixup to refer
|
|
// to the extern:__variable directly, it is semantically different.
|
|
// As as example, when we meet:
|
|
// LDR R3, #00000000 ; R_ARM_TLS_IE32; Offset in GOT to __libc_errno
|
|
// generating:
|
|
// LDR R3, [__libc_errno_tls_offset]
|
|
// is not the same as:
|
|
// LDR R3, [address, in got, of libc_errno_tls_offset]
|
|
//
|
|
// This is obviously the last formatting that's correct, as we don't
|
|
// even *know* what the address of __libc_errno_tls_offset is; we just
|
|
// know where to go and look for it.
|
|
//
|
|
// The solution is to create a .got section anyway, and allocate an entry
|
|
// in there with the name of the symbol, suffixed with '_tpoff'.
|
|
//
|
|
// - finfo : The elf file info (reader, ...)
|
|
// - sym : The symbol. We create the one entry for each symbol of
|
|
// each GOT-type.
|
|
// - suffix : A suffix, to be added to the symbol name. We consider
|
|
// the suffix as a GOT-type of the entry. It is sometimes
|
|
// necessary to create multiple entries for the symbol.
|
|
// E.g., '_tpoff', '_ptr', ...
|
|
// - created : The flag indicating the allocation of the new entry.
|
|
// - n : The number of allocated entries.
|
|
// - returns : An address in the .got segment.
|
|
// 0 is returned in the case of error.
|
|
ea_t allocate_entry(
|
|
elf_file_info_t &finfo,
|
|
const sym_rel &sym,
|
|
const char *suffix,
|
|
bool *created = NULL,
|
|
size_t n = 1);
|
|
|
|
// Get the ea in the GOT section, corresponding
|
|
// the the 'A' addend.
|
|
//
|
|
// * If the file already had a GOT section, then
|
|
// the returned ea is simply got_segment->start_ea + A.
|
|
// * On the other hand, if this file had no GOT
|
|
// this calls #allocate_got_entry().
|
|
//
|
|
// - finfo : The elf file info (reader, ...)
|
|
// - A : The 'addend' (i.e., displacement) in the GOT.
|
|
// Ignored if the original file had *no* GOT.
|
|
// - sym : The symbol.
|
|
// Ignored if the original file *did* have a GOT.
|
|
// - suffix : A suffix, to be added to the symbol name. E.g., '_tpoff', '_ptr', ...
|
|
// Ignored if the original file *did* have a GOT.
|
|
//
|
|
// - returns : An address in the GOT segment, possibly creating one.
|
|
// 0 is returned in the case of error.
|
|
ea_t get_or_allocate_entry(
|
|
elf_file_info_t &finfo,
|
|
uval_t A,
|
|
const sym_rel &sym,
|
|
const char *suffix);
|
|
|
|
// create GOT entry for the address (used in MIPS local entries)
|
|
ea_t allocate_entry(
|
|
elf_file_info_t &finfo,
|
|
ea_t addr,
|
|
bool *created = NULL,
|
|
size_t n = 1);
|
|
|
|
private:
|
|
// used only when original file has no GOT.
|
|
// an unique id of the symbol's GOT entry
|
|
struct symrel_id_t
|
|
{
|
|
symrel_idx_t idx; // symbol
|
|
const char *suffix; // subtype of the GOT-entry
|
|
symrel_id_t(symrel_idx_t idx_, const char *suffix_)
|
|
: idx(idx_), suffix(suffix_) {}
|
|
bool operator<(const symrel_id_t &rhs) const
|
|
{
|
|
if ( idx < rhs.idx )
|
|
return true;
|
|
if ( !(idx == rhs.idx) )
|
|
return false;
|
|
return strcmp(suffix, rhs.suffix) < 0;
|
|
}
|
|
};
|
|
std::map<symrel_id_t,ea_t> allocated_sym_entries;
|
|
std::map<ea_t,ea_t> allocated_addr_entries;
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct reloc_tools_t
|
|
{
|
|
// dynamic information
|
|
const dynamic_info_t *di;
|
|
// reader_t container, with preprocessed data
|
|
elf_file_info_t *finfo;
|
|
// GOT accessor/creator.
|
|
got_access_t got;
|
|
ea_t load_base;
|
|
|
|
reloc_tools_t(elf_loader_t &_ldr, const dynamic_info_t *di_,
|
|
elf_file_info_t *finfo_, const ea_t load_base);
|
|
bool section_header_overlaps(elf_shndx_t sh_idx) const;
|
|
bool has_dlt() const;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GOT support for the relocatable files.
|
|
// the addend of the GOT reloc is applied to the insn,
|
|
// 'P - A' should point to the current or to the next insn
|
|
bool check_got_addend(ea_t P, adiff_t A, adiff_t good_addend);
|
|
bool check_got_addend(ea_t P, adiff_t A, adiff_t good1, adiff_t good2);
|
|
|
|
//--------------------------------------------------------------------------
|
|
namespace tls_relocs_t
|
|
{
|
|
// generic TLS relocs
|
|
enum type_t
|
|
{
|
|
BAD,
|
|
NTPOFF, // variant 2: @ntpoff(x) (x86) or @tpoff(x) (x64), calculates
|
|
// the negative TLS offset relative to the TLS block end;
|
|
// variant 1: TPREL(S+A), resolves to the offset from the
|
|
// current thread pointer (TP) of the thread local variable.
|
|
DTPOFF, // @dtpoff(x), calculates the TLS offset relative to the
|
|
// TLS block;
|
|
// DTPREL(S+A), resolves to the offset from its module's TLS
|
|
// block of the thread local variable.
|
|
PTPOFF, // $x@tpoff (x86 only), calculates the _positive_ TLS
|
|
// offset relative to the TLS block end.
|
|
DTPMOD, // @dtpmod(x), calculates the object identifier of the
|
|
// object containing a TLS symbol;
|
|
// LDM(S), resolves to the load module index of the symbol S.
|
|
DESC, // TLSDESC(S+A), resolves to a contiguous pair of 64-bit
|
|
// values which contain a 'tlsdesc' structure describing the
|
|
// thread local variable. The first entry holds a pointer to
|
|
// the variable's TLS descriptor resolver function and the
|
|
// second entry holds a platform-specific offset or pointer.
|
|
GOTGD, // @tlsgd(x) (x86) or @tlsgd(%rip) (x64), allocates two
|
|
// contiguous entries in the GOT to hold a `tls_index'
|
|
// structure, uses the offset of the first entry.
|
|
GOTLD, // @tlsldm(x) (x86) or @tlsld(%rip) (x64), allocates two
|
|
// contiguous entries in the GOT to hold a `tls_index'
|
|
// structure, uses the offset of the first entry. The
|
|
// `ti_tlsoffset' field of the `tls_index' is set to 0.
|
|
GOTIE, // @gotntpoff(x) (x86) or @gottpoff(%rip) (x64), allocates
|
|
// an entry in the GOT, uses the offset of this entry. The
|
|
// entry holds a variable offset in the initial TLS block.
|
|
// This negative offset is relative to the TLS blocks end.
|
|
// GTPREL(S+A), represents a 64-bit entry in the GOT for the
|
|
// offset from the current thread pointer (TP) of the
|
|
// thread local variable, see TPREL(S+A).
|
|
// @indntpoff(x) (x86), like GOTIE but uses the absolute
|
|
// GOT slot address (got_mode_t::ABS).
|
|
GOTIEP, // @tpoff(x) (x86 only), allocates an entry in the GOT,
|
|
// uses the offset of this entry. The entry holds a
|
|
// variable offset in the initial TLS block. This
|
|
// _positive_ offset is relative to the TLS blocks end.
|
|
GOTDESC, // GTLSDESC(S+A), represents a consecutive pair of 64-bit
|
|
// entries in the GOT which contain a 'tlsdesc' structure.
|
|
};
|
|
inline bool is_got_reloc(type_t type)
|
|
{
|
|
return type >= GOTGD;
|
|
}
|
|
|
|
// GOT slot addressing mode
|
|
enum got_mode_t
|
|
{
|
|
GOT, // offset in GOT
|
|
ABS, // absolute address
|
|
RIP, // offset relative to P-A (the linker sets the addend
|
|
// so that it points to the next insn)
|
|
};
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct proc_def_t
|
|
{
|
|
elf_loader_t &ldr;
|
|
reader_t &reader;
|
|
uint32 relsyms[MAXRELSYMS] = { 0 }; // relocation types which must be to loaded symbols
|
|
|
|
#define E_RELOC_PATCHING (const char *)1
|
|
#define E_RELOC_UNKNOWN (const char *)2
|
|
#define E_RELOC_UNIMPL (const char *)3
|
|
#define E_RELOC_UNTESTED (const char *)4
|
|
#define E_RELOC_NOSYM (const char *)5
|
|
#define E_RELOC_LAST (const char *)6
|
|
const char *stubname = nullptr;
|
|
|
|
// order in which relocations should be applied. A name denotes a section
|
|
// to which we are applying relocations. NULL means all other sections.
|
|
enum { MAXRELORDERS = 5 };
|
|
const char *relsecord[MAXRELORDERS] = { nullptr };
|
|
|
|
// types of supported special segments
|
|
enum spec_type_t
|
|
{
|
|
SPEC_XTRN,
|
|
SPEC_COMM,
|
|
SPEC_ABS,
|
|
SPEC_TLS,
|
|
NSPEC_SEGMS
|
|
};
|
|
// additional reserved indexes of special segments (see MIPS)
|
|
uint16 additional_spec_secidx[NSPEC_SEGMS] = { 0 };
|
|
|
|
// TLS support of the static access model (0 - no static model)
|
|
// TP - static thread pointer, TCB - thread control block
|
|
// variant 1 (if tls_tcb_size < 0):
|
|
// +---+---+-----------------------
|
|
// |TCB|xxx|
|
|
// +---+---+-----------------------
|
|
// ^ TP ^ TLS offset of modules
|
|
// variant 2 (if tls_tcb_size > 0):
|
|
// +----------------------+---+---+
|
|
// | |xxx|TCB|
|
|
// +----------------------+---+---+
|
|
// ^ TLS offset of modules ^ TP
|
|
int tls_tcb_size = 0;
|
|
int tls_tcb_align = 0; // if bit 0 set then store TP at the start of TCB
|
|
|
|
#define AUTO_NO_CTOR 0x01 // disbale ctor/dtor renaming
|
|
#define AUTO_NO_DATA 0x02 // disable data coagulation
|
|
#define AUTO_NO_NAME 0x04 // disable Alternative name storing
|
|
#define AUTO_NO_THUNK 0x08 // disable plt-thunk renaming
|
|
// see set_reloc_cmt() for other bits
|
|
#define AUTO_NO_CTBL_MASK (AUTO_NO_DATA | AUTO_NO_CTOR)
|
|
ushort auto_mode = AUTO_NO_DATA;
|
|
ushort patch_mode = 0;
|
|
|
|
proc_def_t(elf_loader_t &_ldr, reader_t &reader);
|
|
virtual ~proc_def_t() {}
|
|
|
|
// the sequence of callbacks during the call of elf_load_file()
|
|
// proc_is_acceptable_image_type
|
|
// proc_on_start_data_loading
|
|
// proc_should_load_section* (for each section before load)
|
|
// proc_load_unknown_sec* (for each section with unknown type)
|
|
// proc_on_create_section* (for each section before defining segment)
|
|
// proc_handle_dynamic_tag* (for each dynamic tag)
|
|
// proc_describe_flag_bit* (for each flag from header.e_flags)
|
|
// proc_on_loading_symbols* (for each symbol table)
|
|
// proc_handle_special_symbol* (for each symbol from unknown section)
|
|
// proc_handle_symbol* (for each symbol)
|
|
// proc_handle_reloc* (for each relocation)
|
|
// proc_create_got_offsets* ( TODO )
|
|
// proc_perform_patching* (up to 2 times, see above)
|
|
// proc_convert_pic_got* ( TODO )
|
|
// proc_adjust_entry* (for init_ea, fini_ea, header.e_entry)
|
|
// proc_on_end_data_loading
|
|
|
|
virtual bool proc_supports_relocs() const;
|
|
|
|
// Relocator function.
|
|
// Note: symbol might be NULL
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools);
|
|
|
|
// Force conversion of all GOT entries to offsets
|
|
virtual bool proc_create_got_offsets(
|
|
const elf_shdr_t *gotps,
|
|
reloc_tools_t *tools);
|
|
|
|
// Patcher function
|
|
// There are 2 passes distinguishable by function argument gotps:
|
|
// 1. ELF_RPL_PTEST NULL - check ".plt" section
|
|
// 2. pass 2 gotplt
|
|
virtual bool proc_perform_patching(
|
|
const elf_shdr_t *plt,
|
|
const elf_shdr_t *gotps);
|
|
|
|
// Convert PIC form of loading '_GLOBAL_OFFSET_TABLE_[]' of address
|
|
virtual bool proc_can_convert_pic_got() const;
|
|
virtual size_t proc_convert_pic_got(
|
|
const segment_t *gotps,
|
|
reloc_tools_t *tools);
|
|
|
|
// Return a bit description from e_flags and remove it.
|
|
// This function may be called in a loop to document all bits.
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags);
|
|
|
|
// called for processor-specific section types
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force);
|
|
|
|
// called for each dynamic tag. It returns NULL to continue with a
|
|
// standard tag processing, or "" to finish tag processing, or the
|
|
// description of the tag to show.
|
|
virtual const char *proc_handle_dynamic_tag(const Elf64_Dyn *dyn);
|
|
|
|
virtual bool proc_is_acceptable_image_type(ushort filetype);
|
|
|
|
// called after header loading (before load_pht/load_simage)
|
|
virtual void proc_on_start_data_loading(elf_ehdr_t &header);
|
|
|
|
// called after loading data from the input file
|
|
virtual bool proc_on_end_data_loading();
|
|
|
|
virtual void proc_on_loading_symbols();
|
|
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname);
|
|
|
|
// Handle a dynamic symbol
|
|
virtual void proc_handle_dynsym(
|
|
const sym_rel &symrel,
|
|
elf_sym_idx_t isym,
|
|
const char *symname);
|
|
|
|
// 0-reaccept, 1-set name only, else: non-existing section
|
|
virtual int proc_handle_special_symbol(
|
|
sym_rel *st,
|
|
const char *name,
|
|
ushort type);
|
|
|
|
// called from a function should_load_segment for _every_ section.
|
|
// It returns 'false' to skip loading of the given section.
|
|
virtual bool proc_should_load_section(
|
|
const elf_shdr_t &sh,
|
|
elf_shndx_t idx,
|
|
const qstring &name);
|
|
|
|
// called before the segment creation. It may set <sa> to the ea at
|
|
// which a given section should be loaded. In the other case the
|
|
// default segment ea computation is used. Also it may return 'false'
|
|
// to skip creation of a segment for this section.
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa);
|
|
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname);
|
|
|
|
// for some 64-bit architectures e_entry holds not a real entry point
|
|
// but a function descriptor
|
|
// E.g. 64-bit PowerPC ELF Application Binary Interface Supplement 1.9
|
|
// section 4.1. ELF Header
|
|
// "The e_entry field in the ELF header holds the address of a function
|
|
// descriptor. This function descriptor supplies both the address of the
|
|
// function entry point and the initial value of the TOC pointer
|
|
// register."
|
|
// this callback should translate this address to the real entry.
|
|
virtual ea_t proc_adjust_entry(ea_t entry);
|
|
|
|
// helper functions
|
|
bool in_relsyms(uint32 r_type) const
|
|
{
|
|
for ( int i=0; i < MAXRELSYMS; ++i )
|
|
{
|
|
if ( relsyms[i] == 0 )
|
|
break;
|
|
if ( relsyms[i] == r_type )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool section_in_relsecord(const qstring &name) const
|
|
{
|
|
for ( int i = 0; i < MAXRELORDERS; ++i )
|
|
{
|
|
if ( relsecord[i] == NULL )
|
|
return false;
|
|
if ( name == relsecord[i] )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Note: this function should be used only for complex cases.
|
|
// For simple relocations it is recommended to use
|
|
// set_reloc_fixup().
|
|
void set_reloc(
|
|
ea_t P, // The ea of the data to be modified.
|
|
ea_t target_ea, // The ea that the instruction would point to,
|
|
// if it were interpreted by the CPU.
|
|
uval_t patch_data, // The data to be inserted at 'P'. Depending on whether
|
|
// 'type' is a 64-bit fixup type or not, either the
|
|
// first 32-bits, or the full 64 bits of 'data' will be
|
|
// put in the database. Of course, this patch data
|
|
// must hold the possible instruction bits, if they
|
|
// are interleaved w/ the address data
|
|
// (e.g., R_ARM_THM_MOVW_ABS_NC, ...).
|
|
adiff_t displ,
|
|
fixup_type_t type, // The type of the relocation, see fixup.hpp's FIXUP_*.
|
|
// It may be standard or custom.
|
|
uval_t offbase = 0, // base of the relative fixup
|
|
uint32 flags = 0, // The flags of the relocation, see fixup.hpp's FIXUPF_*.
|
|
// You can specify additional flags:
|
|
#define FIXUPF_ELF_DO_NOT_PATCH 0x80000000 // do not patch at all
|
|
#define FIXUPF_ELF_FIXUP_PATCH 0x40000000 // do not use patch_data, patch
|
|
// using patch_fixup_value()
|
|
#define FIXUPF_ELF_DISPLACEMENT 0x20000000 // set displacement flag
|
|
// (even rel_mode == 1)
|
|
#define FIXUPF_ELF_SET_RELATIVE 0x10000000 // set fixup base
|
|
// (even offbase == 0)
|
|
adiff_t fixup_offset=0); // offset of the fixup relative to P
|
|
|
|
// this function patches the relocatable field using patch_fixup_value()
|
|
// and store the fixup.
|
|
// It set the displacement taking in account the addend, the fact that
|
|
// the symbol is external or it is a section.
|
|
// It uses static variables REL_MODE and PRGEND.
|
|
// It is recommended to use set_reloc_fixup() instead of set_reloc(),
|
|
// because it makes the code clearer.
|
|
void set_reloc_fixup(
|
|
ea_t P, // ea of the reloc
|
|
ea_t S, // symbol of the reloc
|
|
adiff_t A, // addend of the reloc
|
|
fixup_type_t type, // the type of the fixup to store, see fixup.hpp's FIXUP_*.
|
|
// It may be standard or custom.
|
|
bool do_not_patch = false, // do not patch at all
|
|
uval_t offbase = 0, // base of the relative fixup
|
|
adiff_t fixup_offset = 0); // offset of the fixup relative to P
|
|
|
|
void process_TLS(
|
|
tls_relocs_t::type_t type,
|
|
tls_relocs_t::got_mode_t got_mode,
|
|
fixup_type_t fixup_type,
|
|
const char *cfh_name,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t &reloc_info,
|
|
reloc_tools_t *tools);
|
|
|
|
// allocate GOT-entry and return it in GOT_ENTRY_EA
|
|
// \param P relocation address (to show in messages)
|
|
// \param S data to place in the entry
|
|
// \param symbol symbol of the entry,
|
|
// we allocate the single entry for the symbol,
|
|
// we create a label for the entry if the symbol has a name,
|
|
// if it is NULL we allocate the entry for the S address
|
|
bool process_GOT(
|
|
ea_t *got_ea, // may be NULL
|
|
ea_t *got_entry_ea, // not NULL
|
|
ea_t P,
|
|
ea_t S,
|
|
const sym_rel *symbol,
|
|
reloc_tools_t *tools);
|
|
|
|
enum reloc_cmt_id_t
|
|
{
|
|
RCM_NONE = -1,
|
|
RCM_PIC = 0,
|
|
RCM_ATT = 1,
|
|
RCM_COPY = 2,
|
|
RCM_TLS = 3,
|
|
RCM_IREL = 4,
|
|
};
|
|
void set_reloc_cmt(ea_t ea, reloc_cmt_id_t cmt) const;
|
|
void set_thunk_name(
|
|
ea_t ea,
|
|
ea_t name_ea,
|
|
const char *prefix = ".",
|
|
const char *postfix = "");
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct arm_base_t : public proc_def_t
|
|
{
|
|
bool already_parsed = false;
|
|
|
|
arm_base_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force) override;
|
|
virtual void proc_on_loading_symbols() override;
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_arm_t : public arm_base_t
|
|
{
|
|
fixup_type_t prel31_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_arm_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual bool proc_perform_patching(
|
|
const elf_shdr_t *plt,
|
|
const elf_shdr_t *gotps) override;
|
|
virtual bool proc_can_convert_pic_got() const override { return true; }
|
|
virtual size_t proc_convert_pic_got(
|
|
const segment_t *gotps,
|
|
reloc_tools_t *tools) override;
|
|
virtual void proc_on_start_data_loading(elf_ehdr_t &header) override;
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_aarch64_t : public arm_base_t
|
|
{
|
|
elf_aarch64_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual bool proc_perform_patching(
|
|
const elf_shdr_t *plt,
|
|
const elf_shdr_t *gotps) override;
|
|
#ifndef __EA64__
|
|
virtual bool proc_supports_relocs() const override { return false; }
|
|
#endif
|
|
private:
|
|
bool aarch64_process_TLS(
|
|
int rel_type,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t &reloc_info,
|
|
reloc_tools_t *tools);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_alpha_t : public proc_def_t
|
|
{
|
|
const elf_shdr_t *sh_plt = nullptr; // for R_ALPHA_JMP_SLOT
|
|
|
|
elf_alpha_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force) override;
|
|
virtual bool proc_should_load_section(
|
|
const elf_shdr_t &sh,
|
|
elf_shndx_t idx,
|
|
const qstring &name) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_avr_t : public proc_def_t
|
|
{
|
|
netnode avr_helper;
|
|
fixup_type_t avr16_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_avr_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct elf_c166_t : public proc_def_t
|
|
{
|
|
elf_c166_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct arc_base_t : public proc_def_t
|
|
{
|
|
arc_base_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual void proc_handle_dynsym(
|
|
const sym_rel &symrel,
|
|
elf_sym_idx_t isym,
|
|
const char *symname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_arcompact_t : public arc_base_t
|
|
{
|
|
elf_arcompact_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_arc_t : public arc_base_t
|
|
{
|
|
elf_arc_t(elf_loader_t &l, reader_t &r) : arc_base_t(l, r) {}
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_h8_t : public proc_def_t
|
|
{
|
|
elf_h8_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_fr_t : public proc_def_t
|
|
{
|
|
elf_fr_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_i960_t : public proc_def_t
|
|
{
|
|
elf_i960_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_ia64_t : public proc_def_t
|
|
{
|
|
elf_ia64_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_hppa_t : public proc_def_t
|
|
{
|
|
fixup_type_t l21_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t r11_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_hppa_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct ppc_base_t : public proc_def_t
|
|
{
|
|
fixup_type_t ha16_reltype = FIXUP_CUSTOM;
|
|
|
|
// PLT support
|
|
enum plt_type_t
|
|
{
|
|
PLT_UNKNOWN,
|
|
PLT_SKIP, // illegal .plt section
|
|
PLT_BSS, // PLT entry contains executable code
|
|
PLT_SECURE, // PLT entry contains address
|
|
PLT_FARCALL, // PLT_BSS + should add .plt_farcall
|
|
PLT_64, // PLT entry contains function description
|
|
PLT_64V2, // PLT entry contains global entry point
|
|
};
|
|
plt_type_t plt_type = PLT_UNKNOWN;
|
|
ea_t plt_start = BADADDR;
|
|
asize_t plt_size = 0;
|
|
bool is_dt_ppc_got_present = false;
|
|
|
|
ppc_base_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual bool proc_should_load_section(
|
|
const elf_shdr_t &sh,
|
|
elf_shndx_t idx,
|
|
const qstring &name) override;
|
|
protected:
|
|
const char *process_JMP_SLOT(const sym_rel *symbol, ea_t P, ea_t target_ea);
|
|
int32 calc_plt_entry(adiff_t off) const;
|
|
asize_t calc_plt_size(uint32 n) const;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_ppc_t : public ppc_base_t
|
|
{
|
|
elf_ppc_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_perform_patching(
|
|
const elf_shdr_t *plt,
|
|
const elf_shdr_t *gotps) override;
|
|
virtual const char *proc_handle_dynamic_tag(const Elf64_Dyn *dyn) override;
|
|
virtual void proc_on_start_data_loading(elf_ehdr_t &header) override;
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname) override;
|
|
public:
|
|
const char *process_EMB_SDA21(ea_t P, ea_t target_ea, uint32 insn);
|
|
const char *process_GOT(
|
|
uchar type,
|
|
ea_t P,
|
|
ea_t S,
|
|
const sym_rel *symbol,
|
|
reloc_tools_t *tools,
|
|
reloc_cmt_id_t got_entry_cmt = RCM_NONE);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_ppc64_t : public ppc_base_t
|
|
{
|
|
// how did we define the `toc_base_ea`?
|
|
// Doc: "To support position-independent code, a Global Offset Table (GOT)
|
|
// shall be constructed by the link editor in the data segment when linking
|
|
// code that contains any of the various R_PPC64_GOT* relocations or when
|
|
// linking code that references the **.TOC.** address. The GOT consists of
|
|
// an 8-byte header that contains **the TOC base** (the first TOC base when
|
|
// multiple TOCs are present), followed by an array of 8-byte addresses."
|
|
enum toc_type_t
|
|
{
|
|
TOC_UNK,
|
|
// For the relocatable object file:
|
|
TOC_CREATED, // it points to the created .tocstart section
|
|
// For the executable or shared object file:
|
|
TOC_SECTION, // it points to the first TOC's section
|
|
TOC_GOT, // we got it as the first GOT entry
|
|
TOC_SYMBOL, // it points to the .TOC. symbol
|
|
};
|
|
toc_type_t toc_type = TOC_UNK;
|
|
|
|
symrel_idx_t toc_sym_idx; // index of the .TOC. symbol
|
|
|
|
// the TOC base
|
|
// We prepare this address for a relocatable object file because for an
|
|
// executable or shared object the linker already defines and uses such
|
|
// a symbol. Doc: "relocation types that refer to .TOC. may only appear
|
|
// in relocatable object files, not in executables or shared objects".
|
|
// For an executable or shared object we get this address in the same way
|
|
// but we overwrite it using the .TOC. symbol or the first GOT entry.
|
|
ea_t toc_base_ea = BADADDR;
|
|
|
|
// offset of the TOC base from the TOC start
|
|
adiff_t toc_base_offset = 0x8000;
|
|
|
|
bool got_already_checked = false;
|
|
|
|
elf_ppc64_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_on_end_data_loading() override;
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname) override;
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa) override;
|
|
virtual ea_t proc_adjust_entry(ea_t entry) override;
|
|
private:
|
|
const char *process_TOC16(
|
|
uchar type,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A);
|
|
void set_offset_to_toc(ea_t ea) const;
|
|
void improve_toc_using_got();
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct pc_base_t : public proc_def_t
|
|
{
|
|
bool plt_warned = false;
|
|
pc_base_t(elf_loader_t &ldr, reader_t &reader);
|
|
bool patch_jmp_slot_target(
|
|
const reloc_tools_t *reloc_tools,
|
|
bool is_64,
|
|
ea_t fixup_ea,
|
|
ea_t final_ea);
|
|
bool patch_irelative_target(
|
|
bool is_64,
|
|
ea_t fixup_ea,
|
|
ea_t final_ea,
|
|
const reloc_tools_t *reloc_tools);
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct elf_pc_t : public pc_base_t
|
|
{
|
|
elf_pc_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
private:
|
|
bool convert_got32x(
|
|
ea_t P,
|
|
ea_t S,
|
|
reloc_tools_t *tools);
|
|
bool convert_call_jmp(ea_t P, ea_t S);
|
|
bool convert_mov_mem_to_imm(ea_t P, ea_t S);
|
|
bool convert_test(ea_t P, ea_t S);
|
|
bool convert_binop(ea_t P, ea_t S, uchar opcode);
|
|
bool convert_mov_to_lea(ea_t P, ea_t S, reloc_tools_t *tools);
|
|
bool patch_glob_dat_target(
|
|
ea_t fixup_ea,
|
|
ea_t final_ea,
|
|
const reloc_tools_t *tools);
|
|
bool intel_process_TLS(
|
|
int rel_type,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t &reloc_info,
|
|
reloc_tools_t *tools);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_m16c_t : public proc_def_t
|
|
{
|
|
elf_m16c_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa) override;
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_m32r_t : public proc_def_t
|
|
{
|
|
elf_m32r_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_mc12_t : public proc_def_t
|
|
{
|
|
elf_mc12_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_mc68k_t : public proc_def_t
|
|
{
|
|
elf_mc68k_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual bool proc_supports_relocs() const override { return false; }
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_s390_t : public proc_def_t
|
|
{
|
|
fixup_type_t pc16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t pc32_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo12_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo20_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_s390_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual bool proc_on_end_data_loading() override;
|
|
private:
|
|
bool s390_process_TLS(
|
|
int rel_type,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t &reloc_info,
|
|
reloc_tools_t *tools);
|
|
bool handle_plt1_64(ea_t ea);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_mips_t : public proc_def_t
|
|
{
|
|
fixup_type_t ha16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t off16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t b26_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mips16_b26_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mips16_ha16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mips16_lo16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mips16_off16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_b26_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_ha16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_lo16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_off16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_b16_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_b10_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t mmips_b7_reltype = FIXUP_CUSTOM;
|
|
|
|
ea_t save_rsolve = BADADDR;
|
|
bool isPS2IRX = false;
|
|
bool isPSP = false;
|
|
bool use_relocs = true;
|
|
// The g_localgotno node will be used twice:
|
|
// 1) When processing SHT_DYNSYM syms, to associate a symbol with a .got slot
|
|
// 2) After processing symbols, during the 'patch' phase, to create
|
|
// all relocs in all "local" .got entries at the beginning of the .got.
|
|
int g_localgotno = 0;
|
|
elf_sym_idx_t g_gotsym = 0;
|
|
// offset of the gp value from the GOT start
|
|
const adiff_t gpval_offset = 0x7FF0;
|
|
// initial value of GP (GP0)
|
|
// for executable and shared objects it is the actual gp value
|
|
// for relocatable objects it is the offset of the gp value from the global area start
|
|
ea_t initial_gp = BADADDR;
|
|
// start of the global data area. this area should be addressable using $gp.
|
|
ea_t global_data_ea = BADADDR;
|
|
|
|
ea_t last_hi16_ea = BADADDR;
|
|
adiff_t last_hi16_addend = 0;
|
|
uchar last_hi16_rel_type = 0;
|
|
bool last_hi16_done = false;
|
|
|
|
bool got_inited = false;
|
|
|
|
elf_mips_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual bool proc_create_got_offsets(
|
|
const elf_shdr_t *gotps,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual bool proc_load_unknown_sec(Elf64_Shdr *sh, bool force) override;
|
|
virtual int proc_handle_special_symbol(
|
|
sym_rel *st,
|
|
const char *name,
|
|
ushort type) override;
|
|
virtual const char *proc_handle_dynamic_tag(const Elf64_Dyn *dyn) override;
|
|
virtual bool proc_is_acceptable_image_type(ushort filetype) override;
|
|
virtual void proc_on_start_data_loading(elf_ehdr_t &header) override;
|
|
virtual bool proc_on_end_data_loading() override;
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname) override;
|
|
virtual void proc_handle_dynsym(
|
|
const sym_rel &symrel,
|
|
elf_sym_idx_t isym,
|
|
const char *symname) override;
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa) override;
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
private:
|
|
void relocate_psp_section1(
|
|
off_t file_offset,
|
|
size_t sec_size,
|
|
const sym_rel *symbol);
|
|
void set_initial_gp(uint64 offset, bool options=true);
|
|
ea_t get_final_gp(reloc_tools_t *tools);
|
|
|
|
typedef fixup_type_t (elf_mips_t::*get_rel_type_t)();
|
|
static const get_rel_type_t hlo_relocs[3][3];
|
|
fixup_type_t get_ha16_reltype();
|
|
fixup_type_t get_lo16_reltype();
|
|
fixup_type_t get_off16_reltype();
|
|
fixup_type_t get_b26_reltype();
|
|
fixup_type_t get_mips16_b26_reltype();
|
|
fixup_type_t get_mips16_ha16_reltype();
|
|
fixup_type_t get_mips16_lo16_reltype();
|
|
fixup_type_t get_mips16_off16_reltype();
|
|
fixup_type_t get_mmips_b26_reltype();
|
|
fixup_type_t get_mmips_ha16_reltype();
|
|
fixup_type_t get_mmips_lo16_reltype();
|
|
fixup_type_t get_mmips_off16_reltype();
|
|
fixup_type_t get_mmips_b16_reltype();
|
|
fixup_type_t get_mmips_b10_reltype();
|
|
fixup_type_t get_mmips_b7_reltype();
|
|
|
|
enum reloc_kind_t { NONE, MIPS, MIPS16, MICROMIPS };
|
|
enum reloc_hlo_t { HA16, LO16, OFF16 };
|
|
static reloc_kind_t find_reloc16_kind(uchar rel_type);
|
|
fixup_type_t get_reloc16_fixup(
|
|
reloc_hlo_t hlo,
|
|
reloc_kind_t reloc_kind);
|
|
|
|
enum symkind_t { SYM, LOCALGP, GPDISP };
|
|
void process_ahl_reloc(
|
|
uchar rel_type,
|
|
ea_t P,
|
|
ea_t S,
|
|
symkind_t symkind,
|
|
bool is_rela,
|
|
adiff_t A,
|
|
reloc_tools_t *tools);
|
|
bool get_ssym_ea(ea_t *ssym_ea, uint8 ssym, ea_t P) const;
|
|
bool is_inside_got(reloc_tools_t *tools, ea_t P);
|
|
|
|
// convenient function to use instead of proc_def_t::set_reloc()
|
|
void _set_reloc(
|
|
ea_t P,
|
|
fixup_type_t type,
|
|
ea_t target_ea,
|
|
adiff_t displ = 0,
|
|
uval_t offbase = 0);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_v800_t : public proc_def_t
|
|
{
|
|
elf_v800_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_v850_t : public proc_def_t
|
|
{
|
|
elf_v850_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_sparc_t : public proc_def_t
|
|
{
|
|
fixup_type_t hi22_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo10_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_sparc_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
virtual void proc_on_start_data_loading(elf_ehdr_t &header) override;
|
|
virtual bool proc_can_convert_pic_got() const override { return true; }
|
|
virtual size_t proc_convert_pic_got(
|
|
const segment_t *gotps,
|
|
reloc_tools_t *tools) override;
|
|
|
|
fixup_type_t get_hi22_reltype();
|
|
fixup_type_t get_lo10_reltype();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_mn10200_t : public proc_def_t
|
|
{
|
|
elf_mn10200_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_mn10300_t : public proc_def_t
|
|
{
|
|
elf_mn10300_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_riscv_t : public proc_def_t
|
|
{
|
|
fixup_type_t hi20_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t got20_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo12i_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t lo12s_reltype = FIXUP_CUSTOM;
|
|
fixup_type_t call_reltype = FIXUP_CUSTOM;
|
|
std::map<ea_t,ea_t> symtable;
|
|
|
|
elf_riscv_t(elf_loader_t &ldr, reader_t &reader);
|
|
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual bool proc_handle_symbol(sym_rel &sym, const char *symname) override;
|
|
virtual bool proc_on_create_section(
|
|
const elf_shdr_t &sh,
|
|
const qstring &name,
|
|
ea_t *sa) override;
|
|
|
|
fixup_type_t get_hi20_reltype();
|
|
fixup_type_t get_got20_reltype();
|
|
fixup_type_t get_lo12i_reltype();
|
|
fixup_type_t get_lo12s_reltype();
|
|
fixup_type_t get_call_reltype();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_sh_t : public proc_def_t
|
|
{
|
|
elf_sh_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *calc_procname(uint32 *e_flags, const char *procname) override;
|
|
virtual void proc_handle_dynsym(
|
|
const sym_rel &symrel,
|
|
elf_sym_idx_t isym,
|
|
const char *symname) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_st9_t : public proc_def_t
|
|
{
|
|
elf_st9_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_x64_t : public pc_base_t
|
|
{
|
|
// TODO use this custom fixup in RIP-based relocs
|
|
fixup_type_t rip_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_x64_t(elf_loader_t &ldr, reader_t &reader);
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
private:
|
|
bool convert_gotpcrelx(int rel_type, ea_t P, ea_t S);
|
|
bool x64_process_TLS(
|
|
int rel_type,
|
|
ea_t P,
|
|
ea_t S,
|
|
adiff_t A,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t &reloc_info,
|
|
reloc_tools_t *tools);
|
|
bool convert_call_jmp(ea_t P, ea_t S);
|
|
bool convert_mov_to_lea(ea_t P, ea_t S);
|
|
bool convert_mov_mem_to_imm(int rel_type, ea_t P, ea_t S);
|
|
bool convert_test_binop(int rel_type, ea_t P, ea_t S, uchar opcode);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_tricore_t : public proc_def_t
|
|
{
|
|
fixup_type_t ha16_reltype = FIXUP_CUSTOM;
|
|
|
|
elf_tricore_t(elf_loader_t &l, reader_t &r) : proc_def_t(l, r) {}
|
|
virtual const char *proc_handle_reloc(
|
|
const rel_data_t &rel_data,
|
|
const sym_rel *symbol,
|
|
const elf_rela_t *reloc,
|
|
reloc_tools_t *tools) override;
|
|
virtual const char *proc_describe_flag_bit(uint32 *e_flags) override;
|
|
private:
|
|
void make_reloc(
|
|
const rel_data_t &rel_data,
|
|
ea_t target_ea,
|
|
uval_t patch_data,
|
|
fixup_type_t type,
|
|
bool nodispl = false);
|
|
void tricore_handle_reloc(const rel_data_t &rel_data, ea_t target_ea);
|
|
void tricore_reloc_relB(const rel_data_t &rel_data, ea_t target_ea);
|
|
void tricore_reloc_absB(const rel_data_t &rel_data, ea_t target_ea);
|
|
void tricore_reloc_abs(const rel_data_t &rel_data, ea_t target_ea);
|
|
void tricore_reloc_br(const rel_data_t &rel_data, ea_t target_ea);
|
|
void tricore_reloc_rlc(
|
|
const rel_data_t &rel_data,
|
|
uint32 patch_data,
|
|
fixup_type_t fixup_type);
|
|
void tricore_reloc_bol(
|
|
const rel_data_t &rel_data,
|
|
uint32 patch_data,
|
|
fixup_type_t fixup_type);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct sym_rel;
|
|
class symrel_cache_t
|
|
{
|
|
public:
|
|
|
|
symrel_cache_t()
|
|
: storage(),
|
|
dynsym_index(0) {}
|
|
|
|
static void check_type(slice_type_t t)
|
|
{
|
|
QASSERT(20098, t > SLT_INVALID && t <= SLT_WHOLE);
|
|
}
|
|
|
|
elf_sym_idx_t slice_size(slice_type_t t) const { return elf_sym_idx_t(slice_end(t) - slice_start(t)); }
|
|
const sym_rel &get(slice_type_t t, elf_sym_idx_t idx) const { return storage[slice_start(t) + idx]; }
|
|
sym_rel &get(slice_type_t t, elf_sym_idx_t idx) { return storage[slice_start(t) + idx]; }
|
|
sym_rel &append(slice_type_t t);
|
|
|
|
void qclear(uint64 room)
|
|
{
|
|
// the number in the section header may be too big (see
|
|
// pc_bad_nyms_elf.elf) so we limit it
|
|
if ( room > 65536 )
|
|
room = 65536;
|
|
storage.qclear();
|
|
storage.reserve(room);
|
|
}
|
|
|
|
symrel_idx_t get_idx(const sym_rel *symbol) const;
|
|
|
|
// this method is used in pattern/pelf.cpp
|
|
struct ptr_t : public symrel_idx_t
|
|
{
|
|
ptr_t() : symrel_idx_t(), symbols(NULL) {}
|
|
ptr_t(symrel_cache_t *s, symrel_idx_t i)
|
|
: symrel_idx_t(i),
|
|
symbols(s) {}
|
|
symrel_cache_t *symbols;
|
|
sym_rel &deref() const { return symbols->get(type, idx); }
|
|
};
|
|
ptr_t get_ptr(const sym_rel &sym)
|
|
{
|
|
return ptr_t(this, get_idx(&sym));
|
|
}
|
|
|
|
private:
|
|
qvector<sym_rel> storage;
|
|
size_t dynsym_index;
|
|
size_t slice_start(slice_type_t t) const;
|
|
size_t slice_end(slice_type_t t) const;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// relocation speed
|
|
struct sym_rel
|
|
{
|
|
mutable qstring original_name;
|
|
qstring name; // temporary for NOTYPE only
|
|
elf_sym_t original;
|
|
uint64 size;
|
|
uval_t value; // absolute value or addr
|
|
elf_shndx_t sec; // index of the section to which this symbol
|
|
// applies. For special sections it is 0 (see
|
|
// original.st_shndx).
|
|
uchar bind; // binding
|
|
char type; // type (-1 - not defined,
|
|
// -2 UNDEF SYMTAB symbol which probably is
|
|
// the same as the DYNSYM symbol,
|
|
// -3 to add an additional comment to relocs to
|
|
// unloaded symbols)
|
|
uchar flags;
|
|
|
|
sym_rel()
|
|
: original(),
|
|
size(0),
|
|
value(0),
|
|
sec(0),
|
|
bind(0),
|
|
type(0),
|
|
flags(0) {}
|
|
|
|
sym_rel(const sym_rel &r)
|
|
{
|
|
original_name = r.original_name;
|
|
name = r.name;
|
|
original = r.original;
|
|
size = r.size;
|
|
value = r.value;
|
|
sec = r.sec;
|
|
bind = r.bind;
|
|
type = r.type;
|
|
flags = r.flags;
|
|
}
|
|
|
|
sym_rel &operator=(const sym_rel &r)
|
|
{
|
|
if ( this == &r )
|
|
return *this;
|
|
this->~sym_rel();
|
|
new (this) sym_rel(r);
|
|
return *this;
|
|
}
|
|
|
|
void swap(sym_rel &r)
|
|
{
|
|
qswap(*this, r);
|
|
}
|
|
|
|
void set_section_index(const reader_t &reader);
|
|
bool defined_in_special_section() const
|
|
{
|
|
CASSERT(SHN_HIRESERVE == 0xFFFF);
|
|
// assert: original.st_shndx <= SHN_HIRESERVE
|
|
return sec == 0 && original.st_shndx >= SHN_LORESERVE;
|
|
}
|
|
// for debug purpose
|
|
const char *get_section_str(char *buf, size_t bufsize) const
|
|
{
|
|
if ( defined_in_special_section() )
|
|
qsnprintf(buf, bufsize, "%04X", uint(original.st_shndx));
|
|
else
|
|
qsnprintf(buf, bufsize, "%u", sec);
|
|
return buf;
|
|
}
|
|
|
|
bool overlaps(elf_shndx_t section_index, uint64 offset) const
|
|
{
|
|
return sec == section_index
|
|
&& offset >= value
|
|
&& offset < value + size;
|
|
}
|
|
|
|
void set_name(const qstring &n)
|
|
{
|
|
set_name(n.c_str());
|
|
}
|
|
|
|
void set_name(const char *n)
|
|
{
|
|
name.qclear();
|
|
if ( n != NULL && n[0] != '\0' )
|
|
name = n;
|
|
}
|
|
|
|
ea_t get_ea(const reader_t &reader, ea_t _debug_segbase=0) const;
|
|
|
|
const qstring &get_original_name(const reader_t &reader) const;
|
|
|
|
void set_flag(uchar flag) { flags |= flag; }
|
|
bool has_flag(uchar flag) const { return (flags & flag) != 0; }
|
|
void clr_flag(uchar flag) { flags &= ~flag; }
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(sym_rel);
|
|
|
|
//--------------------------------------------------------------------------
|
|
inline symrel_idx_t symrel_cache_t::get_idx(const sym_rel *symbol) const
|
|
{
|
|
qvector<sym_rel>::const_iterator beg = storage.begin();
|
|
if ( symbol == NULL || symbol < beg || symbol > storage.end() )
|
|
return symrel_idx_t();
|
|
size_t idx = symbol - beg;
|
|
if ( idx < dynsym_index )
|
|
return symrel_idx_t(SLT_SYMTAB, elf_sym_idx_t(idx));
|
|
else
|
|
return symrel_idx_t(SLT_DYNSYM, elf_sym_idx_t(idx - dynsym_index));
|
|
}
|
|
|
|
void overflow(ea_t fixaddr, ea_t ea);
|
|
void std_handle_dynsym(const sym_rel &symrel, elf_sym_idx_t, const char *symname);
|
|
|
|
#define CASE_NAME(n) case n: return # n
|
|
const char *get_reloc_name(const reader_t &reader, int type);
|
|
|
|
void parse_attributes(reader_t &reader, uint32 offset, size_t size);
|
|
int elf_machine_2_proc_module_id(reader_t &reader);
|
|
|
|
//--------------------------------------------------------------------------
|
|
// user parameters. these definitions and the input form are interdependent
|
|
// if you change the 'dialog_form' string, change these definitions too!
|
|
// (and don't forget to update the environment variables in the hints files)
|
|
|
|
// IDA_ELF_LOAD_OPTS (may be set by environment variable)
|
|
#define ELF_USE_PHT 0x0001 // Use PHT if available
|
|
#define ELF_USE_SHT 0x0002 // Use SHT if available
|
|
#define ELF_LD_CHNK 0x0004 // Load huge segments by chunks
|
|
|
|
// IDA_ELF_PATCH_MODE (may be set by environment variable)
|
|
#define ELF_RPL_PLP 0x0001 // Replace PIC form of 'Procedure Linkage Table' to non PIC form
|
|
#define ELF_RPL_PLD 0x0002 // Direct jumping from PLT (without GOT) irrespective of its form
|
|
#define ELF_RPL_GL 0x0004 // Convert PIC form of loading '_GLOBAL_OFFSET_TABLE_[]' of address
|
|
#define ELF_RPL_GOTX 0x0010 // Convert PIC form of name@GOT
|
|
#define ELF_AT_LIB 0x0020 // Mark 'allocated' objects as library-objects (MIPS only)
|
|
#define ELF_BUG_GOT 0x0040 // Force conversion of all GOT entries to offsets
|
|
#define ELF_FORM_MASK 0x0FFF // Mask for 'dialog_form' options
|
|
|
|
// noform bits
|
|
#define ELF_DIS_GPLT 0x4000 // disable search got reference in plt
|
|
#define ELF_DIS_OFFW 0x8000 // can present offset bypass segment's
|
|
|
|
#define ELF_RPL_PTEST (ELF_RPL_PLP | ELF_RPL_PLD)
|
|
|
|
#define FLAGS_CMT(bit, text) if ( *e_flags & bit ) \
|
|
{ \
|
|
*e_flags &= ~bit; \
|
|
return text; \
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
inline uval_t make64(uval_t oldval, uval_t newval, uval_t mask)
|
|
{
|
|
return (oldval & ~mask) | (newval & mask);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
inline uint32 make32(uint32 oldval, uint32 newval, uint32 mask)
|
|
{
|
|
return (oldval & ~mask) | (newval & mask);
|
|
}
|
|
|
|
#define MASK(x) ((uval_t(1) << x) - 1)
|
|
|
|
const uval_t M32 = uint32(-1);
|
|
const uval_t M24 = MASK(24);
|
|
const uval_t M16 = MASK(16);
|
|
const uval_t M8 = MASK(8);
|
|
|
|
inline uval_t extend_sign(uval_t value, uint bits)
|
|
{
|
|
uval_t mask = make_mask<uval_t>(bits);
|
|
return (value & left_shift<uval_t>(1, bits-1)) != 0
|
|
? value | ~mask
|
|
: value & mask;
|
|
}
|
|
|
|
#undef MASK
|
|
|
|
#pragma pack(pop)
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct dynamic_linking_tables_t
|
|
{
|
|
dynamic_linking_tables_t()
|
|
: offset(0),
|
|
addr(0),
|
|
size(0),
|
|
link(0) {}
|
|
|
|
dynamic_linking_tables_t(size_t _o, ea_t _a, size_t _s, elf_shndx_t _l)
|
|
: offset(_o),
|
|
addr(_a),
|
|
size(_s),
|
|
link(_l) {}
|
|
|
|
bool is_valid() const { return offset != 0; }
|
|
|
|
size_t offset;
|
|
ea_t addr;
|
|
size_t size;
|
|
elf_shndx_t link;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class dynamic_linking_tables_provider_t
|
|
{
|
|
public:
|
|
dynamic_linking_tables_provider_t()
|
|
: dlt() {}
|
|
const dynamic_linking_tables_t &get_dynamic_linking_tables_info() const { return dlt; }
|
|
bool has_valid_dynamic_linking_tables_info() const { return dlt.is_valid(); }
|
|
void set_dynlink_table_info(size_t offset, ea_t addr, size_t size, int link)
|
|
{
|
|
dlt = dynamic_linking_tables_t(offset, addr, size, link);
|
|
}
|
|
|
|
private:
|
|
dynamic_linking_tables_t dlt;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
enum dynamic_info_type_t
|
|
{
|
|
DIT_STRTAB,
|
|
DIT_SYMTAB,
|
|
DIT_REL,
|
|
DIT_RELA,
|
|
DIT_ANDROID_REL,
|
|
DIT_ANDROID_RELA,
|
|
DIT_PLT,
|
|
DIT_HASH,
|
|
DIT_GNU_HASH,
|
|
DIT_PREINIT_ARRAY,
|
|
DIT_INIT_ARRAY,
|
|
DIT_FINI_ARRAY,
|
|
DIT_VERDEF,
|
|
DIT_VERNEED,
|
|
DIT_VERSYM,
|
|
DIT_TYPE_COUNT, // number of dyninfo types
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// various information parsed from the .dynamic section or DYNAMIC segment
|
|
struct dynamic_info_t
|
|
{
|
|
dynamic_info_t()
|
|
{
|
|
memset(this, 0, sizeof(dynamic_info_t));
|
|
}
|
|
|
|
void initialize(const reader_t &reader);
|
|
|
|
struct entry_t
|
|
{
|
|
entry_t() { clear(); }
|
|
bool is_valid() const { return offset > 0 && size != 0; }
|
|
int64 offset;
|
|
uint64 addr;
|
|
uint64 size;
|
|
uint16 entsize;
|
|
uint32 info;
|
|
|
|
void clear()
|
|
{
|
|
offset = 0;
|
|
addr = 0;
|
|
size = 0;
|
|
entsize = 0;
|
|
info = 0;
|
|
}
|
|
|
|
void guess_size(const sizevec_t &offsets)
|
|
{
|
|
size = BADADDR;
|
|
for ( int i = 0; i < offsets.size(); i++ )
|
|
{
|
|
size_t off = offsets[i];
|
|
if ( offset != 0 && off > offset )
|
|
size = qmin(size, off - offset);
|
|
}
|
|
if ( size == BADADDR )
|
|
size = 0;
|
|
}
|
|
} entries[DIT_TYPE_COUNT];
|
|
|
|
entry_t &strtab() { return entries[DIT_STRTAB]; }
|
|
entry_t &symtab() { return entries[DIT_SYMTAB]; }
|
|
const entry_t &strtab() const { return entries[DIT_STRTAB]; }
|
|
const entry_t &symtab() const { return entries[DIT_SYMTAB]; }
|
|
entry_t &rel() { return entries[DIT_REL]; }
|
|
entry_t &rela() { return entries[DIT_RELA]; }
|
|
entry_t &plt() { return entries[DIT_PLT]; }
|
|
entry_t &hash() { return entries[DIT_HASH]; }
|
|
entry_t &gnu_hash() { return entries[DIT_GNU_HASH]; }
|
|
entry_t &preinit_array() { return entries[DIT_PREINIT_ARRAY]; }
|
|
entry_t &init_array() { return entries[DIT_INIT_ARRAY]; }
|
|
entry_t &fini_array() { return entries[DIT_FINI_ARRAY]; }
|
|
entry_t &verdef() { return entries[DIT_VERDEF]; }
|
|
entry_t &verneed() { return entries[DIT_VERNEED]; }
|
|
entry_t &versym() { return entries[DIT_VERSYM]; }
|
|
|
|
const entry_t &rel() const { return entries[DIT_REL]; }
|
|
const entry_t &rela() const { return entries[DIT_RELA]; }
|
|
const entry_t &plt() const { return entries[DIT_PLT]; }
|
|
|
|
uint32 plt_rel_type; // type of entries in the PLT relocation table (DT_RELENT)
|
|
|
|
static qstring d_un_str(const reader_t &reader, int64 d_tag, int64 d_un);
|
|
static const char *d_tag_str(const reader_t &reader, int64 d_tag); // may return null
|
|
static qstring d_tag_str_ext(const reader_t &reader, int64 d_tag);
|
|
|
|
// Fill a "fake" header, typically to be used w/
|
|
// a buffered_input_t.
|
|
bool fill_section_header(elf_shdr_t *sh, dynamic_info_type_t type) const;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Well-known sections
|
|
enum wks_t
|
|
{
|
|
WKS_BSS = 1,
|
|
WKS_BORLANDCOMMENT,
|
|
WKS_COMMENT,
|
|
WKS_DATA,
|
|
WKS_DYNAMIC,
|
|
WKS_DYNSYM,
|
|
WKS_GOT,
|
|
WKS_GOTPLT,
|
|
WKS_HASH,
|
|
WKS_INTERP,
|
|
WKS_NOTE,
|
|
WKS_PLT,
|
|
WKS_RODATA,
|
|
WKS_SYMTAB,
|
|
WKS_TEXT,
|
|
WKS_OPD,
|
|
WKS_SYMTAB_SHNDX,
|
|
WKS_DYNSYM_SHNDX,
|
|
WKS_PLTGOT,
|
|
WKS_VERDEF,
|
|
WKS_VERNEED,
|
|
WKS_VERSYM,
|
|
WKS_PLTSEC,
|
|
WKS_GNU_DEBUGDATA,
|
|
WKS_LAST
|
|
};
|
|
|
|
class section_headers_t : public dynamic_linking_tables_provider_t
|
|
{
|
|
elf_shdrs_t headers;
|
|
uint32 wks_lut[WKS_LAST];
|
|
reader_t *reader;
|
|
bool initialized;
|
|
bool got_is_original; // Was .got section present in the input file?
|
|
dynamic_info_t::entry_t strtab;
|
|
|
|
friend class reader_t;
|
|
|
|
section_headers_t(reader_t *_r)
|
|
: reader(_r), initialized(false), got_is_original(false), strtab()
|
|
{
|
|
memset(wks_lut, 0, sizeof(wks_lut));
|
|
}
|
|
void assert_initialized() const
|
|
{
|
|
QASSERT(20099, initialized);
|
|
}
|
|
public:
|
|
bool is_initialized() const { return initialized; }
|
|
const elf_shdr_t *getn(elf_shndx_t index) const;
|
|
const elf_shdr_t *get_wks(wks_t wks) const
|
|
{
|
|
elf_shndx_t index = get_index(wks);
|
|
return index == 0 ? NULL : getn(index);
|
|
}
|
|
const elf_shdr_t *get(uint32 sh_type, const char *name) const;
|
|
|
|
#define CONST_THIS CONST_CAST(const section_headers_t*)(this)
|
|
#define NCONST_SHDR(x) CONST_CAST(elf_shdr_t *)(x)
|
|
elf_shdr_t *getn(elf_shndx_t index) { return NCONST_SHDR(CONST_THIS->getn(index)); }
|
|
elf_shdr_t *get_wks(wks_t wks) { return NCONST_SHDR(CONST_THIS->get_wks(wks)); }
|
|
elf_shdr_t *get(uint32 sh_type, const char *name) { return NCONST_SHDR(CONST_THIS->get(sh_type, name)); }
|
|
#undef CONST_THIS
|
|
#undef NCONST_SHDR
|
|
|
|
// Look for '.rel.<section_name>', or '.rela.<section_name>'.
|
|
const elf_shdr_t *get_rel_for(elf_shndx_t index, bool *is_rela = NULL) const;
|
|
elf_shndx_t get_index(wks_t wks) const;
|
|
void set_index(wks_t wks, elf_shndx_t index);
|
|
int add(const elf_shdr_t &);
|
|
void clear() // FIXME: This shouldn't be part of the public API
|
|
{
|
|
headers.clear();
|
|
memset(wks_lut, 0, sizeof(wks_lut));
|
|
}
|
|
bool empty() const { return headers.empty(); }
|
|
void resize(size_t size) { headers.resize(size); } // FIXME: This shouldn't be part of the public API
|
|
bool get_name(qstring *out, elf_shndx_t index) const;
|
|
bool get_name(qstring *out, const elf_shdr_t*) const;
|
|
bool get_name(qstring *out, const elf_shdr_t &sh) const { return get_name(out, &sh); }
|
|
elf_shdrs_t::const_iterator begin() const { return headers.begin(); }
|
|
elf_shdrs_t::const_iterator end () const { return headers.end(); }
|
|
elf_shdrs_t::iterator begin() { return headers.begin(); }
|
|
elf_shdrs_t::iterator end () { return headers.end(); }
|
|
size_t size() { return headers.size(); }
|
|
|
|
const char *sh_type_str(uint32 sh_type) const; // may return null
|
|
qstring sh_type_qstr(uint32 sh_type) const;
|
|
|
|
bool is_got_original(void) const { return got_is_original; }
|
|
void set_got_original(void) { got_is_original = true; }
|
|
|
|
// Get the size of the section. That is, the minimum between
|
|
// what is advertized (sh_size) and the number of bytes between
|
|
// this, and the next section.
|
|
uint64 get_size_in_file(const elf_shdr_t &sh) const;
|
|
|
|
// Read the section contents into the 'out' byte vector.
|
|
// This doesn't blindly rely on sh.sh_size, but will use
|
|
// get_size_in_file() instead.
|
|
// Also, the position of the linput_t will be preserved.
|
|
void read_file_contents(bytevec_t *out, const elf_shdr_t &sh) const;
|
|
|
|
bool read_gnu_debuglink(qstring *out_link, uint32 *out_crc) const;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class program_headers_t : public dynamic_linking_tables_provider_t
|
|
{
|
|
elf_phdrs_t pheaders;
|
|
ea_t image_base;
|
|
reader_t *reader;
|
|
bool initialized;
|
|
public:
|
|
program_headers_t(reader_t *_r)
|
|
: image_base(BADADDR), reader(_r), initialized(false)
|
|
{
|
|
}
|
|
elf_phdrs_t::const_iterator begin() const { return pheaders.begin(); }
|
|
elf_phdrs_t::const_iterator end () const { return pheaders.end(); }
|
|
elf_phdrs_t::iterator begin() { return pheaders.begin(); }
|
|
elf_phdrs_t::iterator end () { return pheaders.end(); }
|
|
elf_phdr_t *get(uint32 index) { assert_initialized(); return &pheaders[index]; }
|
|
ea_t get_image_base() const { return image_base; }
|
|
void set_image_base(ea_t ea) { image_base = ea; }
|
|
inline size_t size() const { return pheaders.size(); }
|
|
void resize(size_t sz) { pheaders.resize(sz); } // FIXME: This shouldn't be part of the pu
|
|
const char *p_type_str(uint32 p_type) const; // may return null
|
|
qstring p_type_qstr(uint32 p_type) const;
|
|
|
|
// Get the size of the segment. That is, the minimum between
|
|
// what is advertized (p_filesz) and the number of bytes between
|
|
// this, and the next segment.
|
|
uint64 get_size_in_file(const elf_phdr_t &p) const;
|
|
|
|
// Read the segment contents into the 'out' byte vector.
|
|
// This doesn't blindly rely on p.p_size, but will use
|
|
// get_size_in_file() instead.
|
|
// Also, the position of the linput_t will be preserved.
|
|
void read_file_contents(bytevec_t *out, const elf_phdr_t &p) const;
|
|
|
|
private:
|
|
friend class reader_t;
|
|
void assert_initialized() const { QASSERT(20100, initialized); }
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Note Section
|
|
// Sections of type SHT_NOTE and program header elements of type PT_NOTE
|
|
|
|
// entry
|
|
struct elf_note_t
|
|
{
|
|
qstring name; // entry owner or originator
|
|
qstring desc; // descriptor
|
|
uint32 type; // interpretation of descriptor
|
|
|
|
// fill entry and return new start offset
|
|
static bool unpack(elf_note_t *entry, size_t *start, const bytevec_t &buf, bool mf);
|
|
|
|
private:
|
|
static bool unpack_sz(size_t *out, size_t *start, const bytevec_t &buf, bool mf);
|
|
static bool unpack_strz(qstring *out, const bytevec_t &buf, size_t start, size_t len);
|
|
static bool unpack_mem(qstring *out, const bytevec_t &buf, size_t start, size_t len, bool as_strz=false);
|
|
};
|
|
typedef qvector<elf_note_t> elf_notes_t;
|
|
|
|
// entry originators and types
|
|
#define NT_NAME_GNU "GNU"
|
|
#define NT_GNU_BUILD_ID 3
|
|
|
|
class notes_t
|
|
{
|
|
public:
|
|
notes_t(reader_t *_r) : reader(_r), initialized(false)
|
|
{}
|
|
|
|
elf_notes_t::const_iterator begin() const { return notes.begin(); }
|
|
elf_notes_t::const_iterator end () const { return notes.end(); }
|
|
void clear(void) { notes.clear(); }
|
|
void add(const bytevec_t &buf);
|
|
|
|
// convenience functions
|
|
|
|
// Build ID
|
|
bool get_build_id(qstring *out) const;
|
|
|
|
private:
|
|
reader_t *reader;
|
|
elf_notes_t notes;
|
|
bool initialized;
|
|
|
|
friend class reader_t;
|
|
void assert_initialized() const { QASSERT(20082, initialized); }
|
|
};
|
|
|
|
const char *d_note_gnu_type_str(uint64 type);
|
|
const char *d_note_linux_type_str(uint64 type);
|
|
const char *d_note_core_type_str(uint64 type);
|
|
|
|
//----------------------------------------------------------------------------
|
|
class arch_specific_t
|
|
{
|
|
public:
|
|
virtual ~arch_specific_t() {}
|
|
virtual void on_start_symbols(reader_t &/*reader*/) {}
|
|
virtual void on_symbol_read(reader_t &/*reader*/, sym_rel &/*sym*/) {}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
//-V:reader_t:730 Not all members of a class are initialized inside the constructor
|
|
class reader_t
|
|
{
|
|
public:
|
|
// Type definitions
|
|
// DOCME
|
|
enum unhandled_section_handling_t
|
|
{
|
|
ush_none = 0,
|
|
ush_load,
|
|
ush_skip
|
|
};
|
|
|
|
/*
|
|
* The list of notifications to which the user of the reader
|
|
* can react.
|
|
* It is documented as follows:
|
|
* 1) a short description of the notification, and possibly a hint
|
|
* on how it should be considered/treated.
|
|
* 2) the list of arguments, to be consumed in a vararg fashion.
|
|
*/
|
|
enum errcode_t
|
|
{
|
|
/*
|
|
* The "class" of the ELF module is not properly defined. It
|
|
* should really be one of (ELFCLASS32, ELFCLASS64).
|
|
* We will fallback to the ELFCLASS32 class.
|
|
* - uint8: the ELF class, as defined in the file.
|
|
*/
|
|
BAD_CLASS = 1,
|
|
|
|
/*
|
|
* The size of the ELF header conflicts with what was expected.
|
|
* - uint16: the size of the ELF header, as defined in the file
|
|
* (i.e., eh_ehsize)
|
|
* - uint16: the expected size.
|
|
*/
|
|
BAD_EHSIZE,
|
|
|
|
/*
|
|
* The byte ordering is not properly defineed. It should
|
|
* really be one of (ELFDATA2LSB, ELFDATA2MSB).
|
|
* We will fallback to the ELFDATA2LSB ordering.
|
|
* - uint8: the byte ordering, as defined in the file.
|
|
*/
|
|
BAD_ENDIANNESS,
|
|
|
|
/*
|
|
* The ELF module defines there are Program Header entries,
|
|
* but it defines an entry size to be of an odd size.
|
|
* We will fallback to the default size for program header
|
|
* entries, which depends on the "class" of this ELF module.
|
|
* - uint16: the size of a program header entry, as defined in
|
|
* the file.
|
|
* - uint16: the expected size (to which we will fallback).
|
|
*/
|
|
BAD_PHENTSIZE,
|
|
|
|
/*
|
|
* The ELF module either:
|
|
* 1) defines an offset for the Program Header entries data but a
|
|
* count of 0 entries, or
|
|
* 2) defines no offset for the Program Header entries data but a
|
|
* count of 1+ entries.
|
|
* We will not use the program header table.
|
|
* - uint16: the number of entries, as defined in the file.
|
|
* - uint64: the offset for the entries data.
|
|
*/
|
|
BAD_PHLOC,
|
|
|
|
/*
|
|
* The ELF module defines there are Section Header entries,
|
|
* but it defines an entry size to be of an odd size.
|
|
* We will fallback to the default size for section header
|
|
* entries, which depends on the "class" of this ELF module.
|
|
* - uint16: the size of a section header entry, as defined in
|
|
* the file.
|
|
* - uint16: the expected size (to which we will fallback).
|
|
*/
|
|
BAD_SHENTSIZE,
|
|
|
|
/*
|
|
* The ELF module:
|
|
* 1) defines an offset for the Section Header entries data but a
|
|
* count of 0 entries, or
|
|
* 2) defines no offset for the Section Header entries data but a
|
|
* count of 1+ entries, or
|
|
* 3) defines too many entries, which would cause an EOF to occur
|
|
* while reading those.
|
|
* We will not use the section header table.
|
|
* - uint32: the number of entries, as defined in the file.
|
|
* - uint64: the offset for the entries data.
|
|
* - int64 : the size of the file.
|
|
*/
|
|
BAD_SHLOC,
|
|
|
|
/*
|
|
* The reader has encountered an unhandled section.
|
|
* - uint16 : The index of the section header.
|
|
* - Elf64_Shdr*: A pointer to the section header structure.
|
|
* If handled, this notification should return a
|
|
* "unhandled_section_handling_t", specifying how the
|
|
* reader should proceed with it.
|
|
*/
|
|
UNHANDLED_SECHDR,
|
|
|
|
/*
|
|
* The reader has encountered an unhandled section,
|
|
* which even the reader instance user couldn't handle.
|
|
* - uint16 : The index of the section header.
|
|
* - Elf64_Shdr*: A pointer to the section header structure.
|
|
*/
|
|
UNKNOWN_SECHDR,
|
|
|
|
/*
|
|
* The reader has spotted that the section's address
|
|
* in memory (i.e., sh_addr) is not aligned on the
|
|
* specified alignment (i.e., sh_addralign).
|
|
* - uint16 : The index of the section header.
|
|
* - Elf64_Shdr*: A pointer to the section header structure.
|
|
*/
|
|
BAD_SECHDR_ALIGN,
|
|
|
|
/*
|
|
* The section header 0 is supposed to be SHT_NULL. But it wasn't.
|
|
*/
|
|
BAD_SECHDR0,
|
|
|
|
/*
|
|
* The type of file (e_type) appears to be ET_CORE, and the
|
|
* machine is SPARC. Those files usually have wrong SHT's. We
|
|
* will rather opt for the PHT view.
|
|
*/
|
|
USING_PHT_SPARC_CORE,
|
|
|
|
/*
|
|
* TLS definitions occurred more than once in the file.
|
|
*/
|
|
EXCESS_TLS_DEF,
|
|
|
|
/*
|
|
* The section with the given name is being redefined.
|
|
* - const char *: The name of the section
|
|
*/
|
|
SECTION_REDEFINED,
|
|
|
|
/*
|
|
* While parsing the dynamic_info_t, the reader spotted
|
|
* an invalid value for the DT_PLTREL entry.
|
|
* - uint32: The 'value' of that entry.
|
|
*/
|
|
BAD_DYN_PLT_TYPE,
|
|
|
|
/*
|
|
* One of the symbols in the symbols tables has a bad binding.
|
|
* - unsigned char: The binding.
|
|
*/
|
|
BAD_SYMBOL_BINDING,
|
|
|
|
/*
|
|
* The ELF module has a Section Header String Table index, but
|
|
* it is out-of-bounds.
|
|
* - uint32: the section header string table index;
|
|
* - uint16: the number of section header entries;
|
|
*/
|
|
BAD_SHSTRNDX,
|
|
|
|
/*
|
|
* The ELF module has Program Header entries, which means it's
|
|
* ready to be loaded as a process image, but claims it is of
|
|
* type ET_REL which makes it a relocatable object file.
|
|
*/
|
|
CONFLICTING_FILE_TYPE,
|
|
|
|
LAST_WARNING = CONFLICTING_FILE_TYPE,
|
|
|
|
/*
|
|
* Couldn't read as many bytes as required.
|
|
* This is a fatal issue, and should be treated as such.
|
|
* - size_t: expected bytes.
|
|
* - size_t: actually read.
|
|
* - int32 : position in file when reading was initiated.
|
|
*/
|
|
ERR_READ,
|
|
|
|
LAST_ERROR = ERR_READ
|
|
};
|
|
|
|
// Data members
|
|
program_headers_t pheaders;
|
|
section_headers_t sections;
|
|
symrel_cache_t symbols;
|
|
dynamic_info_t::entry_t sym_strtab; // for SYMTAB
|
|
dynamic_info_t::entry_t dyn_strtab; // for DYNSYM
|
|
|
|
struct standard_sizes_in_file_t
|
|
{
|
|
int ehdr;
|
|
int phdr;
|
|
int shdr;
|
|
|
|
struct
|
|
{
|
|
uint sym;
|
|
int dyn;
|
|
int rel;
|
|
int rela;
|
|
} entries;
|
|
|
|
struct
|
|
{
|
|
uint sym; // DT_SYMENT
|
|
uint rel; // DT_RELENT
|
|
uint rela; // DT_RELAENT
|
|
} dyn;
|
|
|
|
struct
|
|
{
|
|
int elf_addr;
|
|
int elf_off;
|
|
int elf_xword;
|
|
int elf_sxword;
|
|
} types;
|
|
} stdsizes;
|
|
|
|
private:
|
|
linput_t *li;
|
|
int64 sif; // ELF start in file
|
|
uint64 filesize;
|
|
// Handle an error. If this function returns false, the reader will stop.
|
|
bool (*handle_error)(const reader_t &reader, errcode_t notif, ...);
|
|
elf_ehdr_t header;
|
|
|
|
struct mapping_t
|
|
{
|
|
uint64 offset;
|
|
uint64 size;
|
|
uint64 ea;
|
|
};
|
|
qvector<mapping_t> mappings;
|
|
|
|
arch_specific_t *arch_specific;
|
|
adiff_t load_bias; // An offset to apply to the ea's
|
|
// when loading the program in memory.
|
|
// real endianness and bitness of the file
|
|
// some loaders (e.g. Linux) ignore values in the ident header
|
|
// so we set the effective ones here
|
|
bool eff_msb;
|
|
bool eff_64; // is elf64?
|
|
bool seg_64; // segments are 32bit or 64bit?
|
|
|
|
bool check_ident();
|
|
public:
|
|
reader_t(linput_t *_li, int64 _start_in_file = 0);
|
|
~reader_t()
|
|
{
|
|
delete arch_specific;
|
|
}
|
|
void set_linput(linput_t *_li) { li = _li; }
|
|
linput_t *get_linput() const { return li; }
|
|
void set_load_bias(adiff_t lb) { load_bias = lb; }
|
|
adiff_t get_load_bias() const { return load_bias; }
|
|
|
|
bool is_warning(errcode_t notif) const;
|
|
bool is_error(errcode_t notif) const;
|
|
ssize_t prepare_error_string(char *buf, size_t bufsize, reader_t::errcode_t notif, va_list va) const;
|
|
|
|
void set_handler(bool (*_handler)(const reader_t &reader, errcode_t notif, ...));
|
|
|
|
int read_addr(void *buf) const;
|
|
int read_off(void *buf) const;
|
|
int read_xword(void *buf) const;
|
|
int read_sxword(void *buf) const;
|
|
int read_word(uint32 *buf) const;
|
|
int read_half(uint16 *buf) const;
|
|
int read_byte(uint8 *buf) const;
|
|
int read_symbol(elf_sym_t *buf) const;
|
|
int safe_read(void *buf, size_t size, bool apply_endianness = true) const;
|
|
|
|
bool read_ident(); // false - bad elf file
|
|
|
|
// Is the file a valid relocatable file? That is, it must have
|
|
// the ET_REL ehdr e_type, and have a proper section table.
|
|
bool is_valid_rel_file() const
|
|
{
|
|
return sections.initialized && !sections.empty() && get_header().e_type == ET_REL;
|
|
}
|
|
const elf_ident_t &get_ident() const { return header.e_ident; }
|
|
|
|
bool read_header();
|
|
elf_ehdr_t &get_header () { return header; }
|
|
const elf_ehdr_t &get_header () const { return header; }
|
|
|
|
bool read_section_headers();
|
|
bool read_program_headers();
|
|
bool read_notes(notes_t *notes);
|
|
|
|
// Android elf files can have a prelink.
|
|
// If such a prelink was found, this will return 'true' and
|
|
// '*base' will be set to that prelink address.
|
|
bool read_prelink_base(uint32 *base);
|
|
|
|
int64 get_start_in_file() const { return sif; }
|
|
|
|
// Seek to section header #index.
|
|
// (Note that this does not seek to the section's contents!)
|
|
bool seek_to_section_header(elf_shndx_t index)
|
|
{
|
|
uint64 pos = header.e_shoff + uint64(index) * uint64(header.e_shentsize);
|
|
if ( pos < header.e_shoff )
|
|
return false;
|
|
if ( seek(pos) == -1 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// read the section header from the current position
|
|
// (should be called after seek_to_section_header)
|
|
bool read_section_header(elf_shdr_t *sh);
|
|
|
|
// Seek to program header #index.
|
|
// (Note that this does not seek to the segment's contents!)
|
|
bool seek_to_program_header(uint32 index)
|
|
{
|
|
uint64 pos = header.e_phoff + uint64(index) * uint64(header.e_phentsize);
|
|
if ( pos < header.e_phoff )
|
|
return false;
|
|
if ( seek(pos) == -1 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// read the compression header from the current position
|
|
// (should be called after seek_to_compression_header)
|
|
bool read_compression_header(elf_chdr_t *out);
|
|
|
|
// Get the current position, in the elf module (which could
|
|
// start at an offset different than 0 in the file).
|
|
int64 tell() const { return qltell(li) - sif; }
|
|
int64 size() const { return filesize - sif; }
|
|
|
|
// Seek in the elf module, at the given position. If the elf module has an
|
|
// offset in the file, it will be added to 'pos' to compose the final
|
|
// position in file.
|
|
qoff64_t seek(int64 pos) const { return qlseek(li, sif+pos); }
|
|
|
|
//
|
|
elf_sym_idx_t rel_info_index(const elf_rela_t &r) const;
|
|
uint32 rel_info_type(const elf_rela_t &r) const;
|
|
void set_rel_info_index(elf_rela_t *r, uint32 symidx) const;
|
|
void set_rel_info_type(elf_rela_t *r, uint32 type) const;
|
|
|
|
void add_mapping(const elf_phdr_t &p);
|
|
// Searches all defined mappings for one that would
|
|
// encompass 'ea'. Returns -1 if not found.
|
|
int64 file_offset(uint64 ea) const;
|
|
// Searches all defined mappings for one that would
|
|
// encompass file offset 'offset'. Returns BADADDR if not found.
|
|
ea_t file_vaddr(uint64 offset) const;
|
|
|
|
elf_shndx_t get_shndx_at(uint64 offset) const;
|
|
|
|
// string tables
|
|
void set_sh_strtab(
|
|
dynamic_info_t::entry_t &strtab,
|
|
const elf_shdr_t &strtab_sh,
|
|
bool replace);
|
|
void set_di_strtab(
|
|
dynamic_info_t::entry_t &strtab,
|
|
const dynamic_info_t::entry_t &strtab_di);
|
|
bool get_string_at(qstring *out, uint64 offset) const;
|
|
bool get_name(
|
|
qstring *out,
|
|
const dynamic_info_t::entry_t &strtab,
|
|
uint32 name_idx) const;
|
|
bool get_name(qstring *out, slice_type_t slice_type, uint32 name_idx) const;
|
|
|
|
// Sets the dynamic info
|
|
typedef qvector<elf_dyn_t> dyninfo_tags_t;
|
|
bool read_dynamic_info_tags(
|
|
dyninfo_tags_t *dyninfo_tags,
|
|
const dynamic_linking_tables_t &dlt);
|
|
bool parse_dynamic_info(
|
|
dynamic_info_t *dyninfo,
|
|
const dyninfo_tags_t &dyninfo_tags);
|
|
|
|
// Symbol versions
|
|
bool read_symbol_versions(
|
|
elf_symbol_version_t *symver,
|
|
const dynamic_info_t &di,
|
|
bool use_pht);
|
|
|
|
arch_specific_t *get_arch_specific() const { return arch_specific; }
|
|
|
|
// Human-friendly representations of
|
|
// header (or ident) values.
|
|
const char *file_type_str() const;
|
|
const char *os_abi_str() const;
|
|
const char *machine_name_str() const; // may return NULL
|
|
qstring get_machine_name() const;
|
|
|
|
// effective endianness
|
|
bool is_msb() const { return eff_msb; }
|
|
// effective bitness (elf32 or elf64)
|
|
bool is_64() const { return eff_64; }
|
|
// effective bitness for segments
|
|
int get_seg_bitness() const { return seg_64 ? 2 : 1; }
|
|
|
|
// pointer size for some 64bit ABIs may be 32bit
|
|
bool is_32_ptr() const
|
|
{
|
|
return !is_64()
|
|
|| header.e_machine == EM_PPC64
|
|
&& header.e_ident.osabi == ELFOSABI_CELLOSLV2
|
|
|| header.e_machine == EM_X86_64
|
|
&& header.e_ident.osabi == ELFOSABI_NACL;
|
|
}
|
|
|
|
bool is_arm() const
|
|
{
|
|
return header.e_machine == EM_ARM || header.e_machine == EM_AARCH64;
|
|
}
|
|
bool is_mips() const
|
|
{
|
|
return header.e_machine == EM_MIPS;
|
|
}
|
|
|
|
// validate section size and return number of elements
|
|
void validate_section_size(
|
|
uint64 *count,
|
|
size_t *entsize,
|
|
const elf_shdr_t §ion,
|
|
const char *counter_name);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct input_status_t
|
|
{
|
|
input_status_t(const reader_t &_reader)
|
|
: reader(_reader),
|
|
pos(reader.tell())
|
|
{
|
|
}
|
|
|
|
qoff64_t seek(int64 new_pos)
|
|
{
|
|
return reader.seek(new_pos);
|
|
}
|
|
|
|
~input_status_t()
|
|
{
|
|
reader.seek(pos);
|
|
}
|
|
private:
|
|
const reader_t &reader;
|
|
int64 pos;
|
|
input_status_t();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
#define _safe(expr) if ( (expr) < 0 ) return false
|
|
struct elf_verdef_t : public Elf_Verdef
|
|
{
|
|
bool read(const reader_t &reader)
|
|
{
|
|
_safe(reader.read_half(&vd_version));
|
|
_safe(reader.read_half(&vd_flags));
|
|
_safe(reader.read_half(&vd_ndx));
|
|
_safe(reader.read_half(&vd_cnt));
|
|
_safe(reader.read_word(&vd_hash));
|
|
_safe(reader.read_word(&vd_aux));
|
|
_safe(reader.read_word(&vd_next));
|
|
return true;
|
|
}
|
|
uint16 cnt()
|
|
{
|
|
return vd_cnt;
|
|
}
|
|
uint32 aux()
|
|
{
|
|
return vd_aux;
|
|
}
|
|
uint32 next()
|
|
{
|
|
return vd_next;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_verdaux_t : public Elf_Verdaux
|
|
{
|
|
bool read(const reader_t &reader)
|
|
{
|
|
_safe(reader.read_word(&vda_name));
|
|
_safe(reader.read_word(&vda_next));
|
|
return true;
|
|
}
|
|
uint32 next()
|
|
{
|
|
return vda_next;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_verneed_t : public Elf_Verneed
|
|
{
|
|
bool read(const reader_t &reader)
|
|
{
|
|
_safe(reader.read_half(&vn_version));
|
|
_safe(reader.read_half(&vn_cnt));
|
|
_safe(reader.read_word(&vn_file));
|
|
_safe(reader.read_word(&vn_aux));
|
|
_safe(reader.read_word(&vn_next));
|
|
return true;
|
|
}
|
|
uint16 cnt()
|
|
{
|
|
return vn_cnt;
|
|
}
|
|
uint32 aux()
|
|
{
|
|
return vn_aux;
|
|
}
|
|
uint32 next()
|
|
{
|
|
return vn_next;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_vernaux_t : public Elf_Vernaux
|
|
{
|
|
bool read(const reader_t &reader)
|
|
{
|
|
_safe(reader.read_word(&vna_hash));
|
|
_safe(reader.read_half(&vna_flags));
|
|
_safe(reader.read_half(&vna_other));
|
|
_safe(reader.read_word(&vna_name));
|
|
_safe(reader.read_word(&vna_next));
|
|
return true;
|
|
}
|
|
uint32 next()
|
|
{
|
|
return vna_next;
|
|
}
|
|
#undef _safe
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct symbol_verdaux_t
|
|
{
|
|
int64 offset;
|
|
size_t name; // index in symver.version_names
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(symbol_verdaux_t);
|
|
typedef qvector<symbol_verdaux_t> symbol_verdaux_vec_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct symbol_verdef_t
|
|
{
|
|
int64 offset;
|
|
symbol_verdaux_vec_t auxs;
|
|
uint16 flags;
|
|
uint16 ndx;
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(symbol_verdef_t);
|
|
typedef qvector<symbol_verdef_t> symbol_verdef_vec_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct symbol_vernaux_t
|
|
{
|
|
int64 offset;
|
|
size_t name; // index in symver.version_names
|
|
uint16 other;
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(symbol_vernaux_t);
|
|
typedef qvector<symbol_vernaux_t> symbol_vernaux_vec_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct symbol_verneed_t
|
|
{
|
|
int64 offset;
|
|
size_t name; // index in symver.file_names
|
|
symbol_vernaux_vec_t auxs;
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(symbol_verneed_t);
|
|
typedef qvector<symbol_verneed_t> symbol_verneed_vec_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct vermap_item_t
|
|
{
|
|
size_t fname_idx; // index in symver.file_names
|
|
size_t vname_idx; // index in symver.version_names
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
typedef std::map<uint16, vermap_item_t> symbol_version_map_t;
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct elf_symbol_version_t
|
|
{
|
|
// from DT_VERDEF
|
|
symbol_verdef_vec_t defs;
|
|
// from DT_VERNEED
|
|
symbol_verneed_vec_t reqs;
|
|
// from DT_VERSYM
|
|
qvector<uint16> symbols;
|
|
|
|
// item with VER_FLG_BASE in verdaux
|
|
size_t def_base; // index in file_names
|
|
|
|
// file and version strings referenced throughout this structure
|
|
qstrvec_t file_names;
|
|
qstrvec_t version_names;
|
|
|
|
// map for entries in DT_VERSYM
|
|
symbol_version_map_t vermap;
|
|
|
|
elf_symbol_version_t()
|
|
: def_base(0)
|
|
{}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
template<typename T> class buffered_input_t
|
|
{
|
|
protected:
|
|
reader_t &reader;
|
|
uint64 offset;
|
|
uint64 count;
|
|
size_t isize; // entry size
|
|
|
|
qvector<T> buffer;
|
|
uint64 read; // number of items we already read from the input
|
|
uint32 cur; // ptr to the next item in 'buffer' to be served
|
|
uint32 end; // number of items in 'buffer'
|
|
|
|
public:
|
|
buffered_input_t(reader_t &_reader, const elf_shdr_t §ion)
|
|
: reader(_reader),
|
|
offset(section.sh_offset),
|
|
count (0),
|
|
isize (section.sh_entsize),
|
|
read (0),
|
|
cur (0),
|
|
end (0)
|
|
{
|
|
count = reader.sections.get_size_in_file(section);
|
|
if ( isize != 0 )
|
|
count /= isize;
|
|
}
|
|
buffered_input_t(
|
|
reader_t &_reader,
|
|
uint64 _offset,
|
|
uint64 _count,
|
|
size_t entsize)
|
|
: reader(_reader),
|
|
offset(_offset),
|
|
count (_count),
|
|
isize (entsize),
|
|
read (0),
|
|
cur (0),
|
|
end (0) {}
|
|
virtual ~buffered_input_t() {}
|
|
|
|
virtual bool next(T *&storage) newapi
|
|
{
|
|
if ( cur >= end )
|
|
{
|
|
uint64 left = count - read;
|
|
if ( left == 0 )
|
|
return false;
|
|
buffer.resize(left);
|
|
|
|
cur = 0;
|
|
if ( read == 0 )
|
|
start_reading();
|
|
end = read_items(left);
|
|
if ( end == 0 )
|
|
return false;
|
|
read += end;
|
|
}
|
|
|
|
if ( cur >= end )
|
|
return false;
|
|
|
|
storage = &buffer[cur];
|
|
cur++;
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
buffered_input_t();
|
|
// default and dumb implementation of read_items()
|
|
// it reads items one by one from the input.
|
|
// see other implementations in template specializations
|
|
ssize_t read_items(size_t max)
|
|
{
|
|
size_t i = 0;
|
|
if ( is_mul_ok<uint64>(read, isize) && is_mul_ok(max, isize) )
|
|
{
|
|
input_status_t save_excursion(reader);
|
|
if ( save_excursion.seek(offset + (read * isize)) != -1 )
|
|
for ( ; i < max; i++ )
|
|
if ( !read_item(buffer[i]) )
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
bool read_item(T &) { return false; }
|
|
void start_reading() {}
|
|
};
|
|
|
|
class buffered_rel_t : public buffered_input_t<elf_rel_t>
|
|
{
|
|
typedef buffered_input_t<elf_rel_t> inherited;
|
|
elf_rela_t rela; // to return from next_rela()
|
|
|
|
public:
|
|
buffered_rel_t(reader_t &_reader, const elf_shdr_t §ion)
|
|
: inherited(_reader, section) {}
|
|
buffered_rel_t(
|
|
reader_t &_reader,
|
|
uint64 _offset,
|
|
uint64 _count,
|
|
size_t entsize)
|
|
: inherited(_reader, _offset, _count, entsize) {}
|
|
bool next_rela(elf_rela_t *&storage)
|
|
{
|
|
elf_rel_t *rel;
|
|
if ( !inherited::next(rel) )
|
|
return false;
|
|
rela.r_offset = rel->r_offset;
|
|
rela.r_info = rel->r_info;
|
|
rela.r_addend = 0;
|
|
storage = &rela;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class buffered_rela_t : public buffered_input_t<elf_rela_t>
|
|
{
|
|
typedef buffered_input_t<elf_rela_t> inherited;
|
|
|
|
bool packed;
|
|
public:
|
|
buffered_rela_t(reader_t &_reader, const elf_shdr_t §ion, bool _packed)
|
|
: inherited(_reader, section), packed(_packed) {}
|
|
buffered_rela_t(
|
|
reader_t &_reader,
|
|
uint64 _offset,
|
|
uint64 _count,
|
|
size_t entsize,
|
|
bool _packed)
|
|
: inherited(_reader, _offset, _count, entsize), packed(_packed) {}
|
|
virtual bool next(elf_rela_t *&storage) override;
|
|
bool next_rela(elf_rela_t *&storage) { return next(storage); }
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct shdr_def_t
|
|
{
|
|
range_t range;
|
|
sel_t sel; // selectors of loaded sections,
|
|
// BADSEL-section not loaded
|
|
bool may_overlap; // may overlap with other sections.
|
|
// no warning is shown, but sh_overlaps is still set.
|
|
|
|
shdr_def_t() : sel(BADSEL), may_overlap(false) {}
|
|
shdr_def_t(const elf_shdr_t *sh, bool may_overlap_ = false)
|
|
: range(ea_t(sh->sh_offset), ea_t(sh->sh_offset + sh->sh_size)),
|
|
sel(BADSEL),
|
|
may_overlap(may_overlap_) {}
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(shdr_def_t);
|
|
typedef qvector<shdr_def_t> shdr_defs_t;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// description of special segments
|
|
struct elf_spec_segm_t
|
|
{
|
|
//lint --e{958} Padding of ... is required to align member on ... boundary
|
|
// description
|
|
const char *seg_name = nullptr;
|
|
uint16 shn_type = 0;
|
|
uchar seg_type = 0;
|
|
// allocation
|
|
uint32 nsyms = 0;
|
|
ea_t start = 0;
|
|
ea_t end = 0;
|
|
ea_t cur = 0;
|
|
|
|
elf_spec_segm_t(uchar seg_type_=0, const char *seg_name_=nullptr, uint16 shn_type_=0)
|
|
: seg_name(seg_name_),
|
|
shn_type(shn_type_),
|
|
seg_type(seg_type_)
|
|
{
|
|
}
|
|
|
|
static proc_def_t::spec_type_t symbol2spec(
|
|
const proc_def_t *pd,
|
|
uint16 secidx,
|
|
ushort type);
|
|
bool not_allocated() const { return start == 0 && end == 0; }
|
|
|
|
bool allocate_spec_segm(elf_loader_t &ldr, reader_t &reader);
|
|
|
|
// it shrinks the segment and returns its new end
|
|
ea_t end_seg();
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
struct elf_range_info_t
|
|
{
|
|
ea_t start;
|
|
ea_t end;
|
|
int64 offset;
|
|
};
|
|
DECLARE_TYPE_AS_MOVABLE(elf_range_info_t);
|
|
typedef std::map<ea_t, elf_range_info_t> range_info_map_t;
|
|
typedef range_info_map_t::const_iterator range_info_map_citerator_t;
|
|
|
|
class loaded_ranges_t
|
|
{
|
|
rangeset_t ranges;
|
|
range_info_map_t range_info_map;
|
|
|
|
public:
|
|
bool add(int64 offset, ea_t start, ea_t _end)
|
|
{
|
|
bool ok = ranges.add(start, _end);
|
|
if ( ok )
|
|
{
|
|
elf_range_info_t &info = range_info_map[start];
|
|
info.start = start;
|
|
info.end = _end;
|
|
info.offset = offset;
|
|
}
|
|
return ok;
|
|
}
|
|
const range_t *find_range(ea_t ea) const
|
|
{
|
|
return ranges.find_range(ea);
|
|
}
|
|
ea_t next_range(ea_t ea) const
|
|
{
|
|
return ranges.next_range(ea);
|
|
}
|
|
const elf_range_info_t *range_info(ea_t ea) const
|
|
{
|
|
const range_t *range = ranges.find_range(ea);
|
|
if ( range == NULL )
|
|
return NULL;
|
|
range_info_map_citerator_t it = range_info_map.find(range->start_ea);
|
|
if ( it == range_info_map.end() )
|
|
return NULL;
|
|
return &it->second;
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
class tlsinfo2_public_t
|
|
{
|
|
friend struct process_tls_t;
|
|
friend struct elf_loader_t;
|
|
struct tlsinfo2_t *pimpl;
|
|
public:
|
|
tlsinfo2_public_t(elf_loader_t &l);
|
|
tlsinfo2_public_t();
|
|
~tlsinfo2_public_t();
|
|
void set_env(reader_t &reader_, elf_spec_segm_t &spec_tls_);
|
|
void set_from_ph(const elf_phdr_t &p);
|
|
// should be called after set_from_ph()
|
|
void set_from_tdata_sh(
|
|
const elf_shdr_t &sh,
|
|
elf_shndx_t secidx,
|
|
const char *name);
|
|
// should be called after set_from_ph()
|
|
void set_from_tbss_sh(
|
|
const elf_shdr_t &sh,
|
|
elf_shndx_t secidx,
|
|
const char *name);
|
|
void create_tls_template();
|
|
// define 'tls_index' structures
|
|
void analyze_tis();
|
|
ea_t get_tls_end() const;
|
|
ea_t symbol2ea(const sym_rel &symrel) const;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class gnu_debugdata_t
|
|
{
|
|
public:
|
|
enum xz_ret_t
|
|
{
|
|
XZRET_OK,
|
|
XZRET_OUT_OF_MEMORY,
|
|
XZRET_DECODE_ERROR,
|
|
XZRET_CHECKSUM_ERROR,
|
|
XZRET_BAD_HEADERS
|
|
};
|
|
|
|
private:
|
|
bytevec_t decompressed;
|
|
linput_t *li = nullptr;
|
|
elf_file_info_t *finfo = nullptr;
|
|
xz_ret_t xz_comment = XZRET_OK;
|
|
|
|
public:
|
|
~gnu_debugdata_t()
|
|
{
|
|
clear_finfo();
|
|
clear_li();
|
|
}
|
|
|
|
elf_file_info_t *file_info() { return finfo; }
|
|
|
|
xz_ret_t comment() const { return xz_comment; }
|
|
|
|
bool init();
|
|
bool create_reader(const bytevec_t &compressed);
|
|
|
|
private:
|
|
void clear_li();
|
|
void clear_finfo();
|
|
|
|
protected:
|
|
xz_ret_t xz_decompress_memory(bytevec_t *buf_out, const bytevec_t &buf_in);
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct elf_loader_t
|
|
{
|
|
linput_t *li = nullptr;
|
|
struct processor_t &ph;
|
|
elf_spec_segm_t spec_segms[proc_def_t::NSPEC_SEGMS];
|
|
|
|
sel_t cursel = 0;
|
|
sel_t datasel = 0;
|
|
|
|
elf_shndx_t ct_sec = 0;
|
|
elf_shndx_t dt_sec = 0;
|
|
ea_t init_ea = 0;
|
|
ea_t fini_ea = 0;
|
|
ea_t init_proc_ea = 0;
|
|
ea_t fini_proc_ea = 0;
|
|
|
|
tlsinfo2_public_t tlsinfo2;
|
|
|
|
proc_def_t *pd = nullptr;
|
|
|
|
qstrvec_t implibs;
|
|
netnode impnode = BADNODE;
|
|
|
|
qstring interpreter;
|
|
|
|
netnode elfnode;
|
|
|
|
// The program end. Does *not* contain the 'extern' segment. It does,
|
|
// however, contain .bss and .tbss data.
|
|
ea_t prgend = 0;
|
|
|
|
// index of the _GLOBAL_OFFSET_TABLE_ symbol for the relocatable files
|
|
symrel_idx_t got_sym_idx;
|
|
|
|
// This symbol is used only for debugging purposes.
|
|
// It specifies the segment base to be used for all segments.
|
|
// Usually it is zero (ELF files use flat memory without any segments)
|
|
uval_t debug_segbase = 0;
|
|
|
|
loaded_ranges_t loaded_ranges;
|
|
size_t overlapped_range_cnt = 0;
|
|
|
|
// structure ids for VERDEF, VERDAUX, etc
|
|
tid_t verdef_sid = BADADDR;
|
|
tid_t verdaux_sid = BADADDR;
|
|
tid_t vernaux_sid = BADADDR;
|
|
tid_t verneed_sid = BADADDR;
|
|
tid_t prstatus_sid = BADADDR;
|
|
tid_t prpsinfo_sid = BADADDR;
|
|
tid_t elf_sym_sid = BADADDR;
|
|
tid_t elf_rel_sid = BADADDR;
|
|
tid_t elf_rela_sid = BADADDR;
|
|
|
|
size_t stubsize = 0;
|
|
ea_t topaddr = 0;
|
|
|
|
ushort neflags;
|
|
fixup_type_t ptpoff_reltype = FIXUP_CUSTOM;
|
|
|
|
bool broken_output_warning_displayed = false;
|
|
bool widebyte = false;
|
|
bool was_empty_idb = false;
|
|
bool gnuunx_loaded = false; // loaded gnuunx.til?
|
|
bool rel_present = false;
|
|
char rel_mode = 0; // 1 - STT_SECTION
|
|
// 0 - !STT_SECTION
|
|
//-1 - STT_NOTYPE or undefined
|
|
|
|
gnu_debugdata_t gnu_debugdata;
|
|
|
|
elf_loader_t(linput_t *li, ushort neflags);
|
|
~elf_loader_t();
|
|
void load_file();
|
|
ushort ana_hdr(reader_t &reader);
|
|
void collect_loader_options(struct elf_load_options *load_opts);
|
|
const char *get_processor_name(
|
|
reader_t &reader,
|
|
const elf_ehdr_t &header,
|
|
bool msb);
|
|
size_t max_loaded_size(ea_t ea, size_t size) const;
|
|
bool is_range_loaded(ea_t ea, size_t size) const;
|
|
bool load_section_headers(reader_t &reader);
|
|
elf_file_info_t *find_companion_file(
|
|
const reader_t &main_reader,
|
|
const notes_t ¬es,
|
|
const elf_load_options &load_opts,
|
|
rangevec_t *loaded_notes);
|
|
ea_t preload_pht(reader_t &reader);
|
|
void load_pht(
|
|
reader_t &reader,
|
|
const elf_load_options &load_opts,
|
|
rangevec_t *loaded_notes);
|
|
bool should_load_segment(
|
|
reader_t &reader,
|
|
const elf_shdr_t *sh,
|
|
elf_shndx_t idx,
|
|
const qstring &name);
|
|
bool is_uniform(reader_t &reader, size_t offset, asize_t size);
|
|
void find_and_load_gaps(reader_t &reader);
|
|
void load_section_contents(
|
|
elf_file_info_t &finfo,
|
|
rangevec_t *loaded_notes);
|
|
bool belongs_to_skipped_section(
|
|
elf_file_info_t &finfo,
|
|
const sym_rel &sym);
|
|
void detect_overlapping_pht(int64 offset, ea_t startaddr, ea_t endaddr);
|
|
void validate_dynamic_info(reader_t &reader, dynamic_info_t *di) const;
|
|
void annotate_headers(reader_t &reader);
|
|
void annotate_loaded_notes(reader_t &reader, const rangevec_t &loaded_notes);
|
|
void annotate_note_core(reader_t &reader, const struct print_note_t ¬e);
|
|
void make_verdef_section(
|
|
reader_t &reader,
|
|
const dynamic_info_t::entry_t &entry,
|
|
const elf_symbol_version_t &symver);
|
|
void make_verneed_section(
|
|
reader_t &reader,
|
|
const dynamic_info_t::entry_t &entry,
|
|
const elf_symbol_version_t &symver);
|
|
void make_versym_section(
|
|
reader_t &reader,
|
|
const dynamic_info_t::entry_t &entry,
|
|
const elf_symbol_version_t &symver);
|
|
void make_rel_section(
|
|
reader_t &reader,
|
|
const dynamic_info_t::entry_t &entry,
|
|
bool is_rela,
|
|
const char *name);
|
|
void make_symtab_section(reader_t &reader, const dynamic_info_t::entry_t &entry);
|
|
ea_t unwide_ea(ea_t ea, const char *diagn) const;
|
|
void preprocess_symbols(
|
|
elf_file_info_t &finfo,
|
|
const elf_load_options &load_opts);
|
|
bool extract_gnu_debugdata(reader_t &main_reader);
|
|
int load_symbols(
|
|
elf_file_info_t &finfo,
|
|
slice_type_t slice_type,
|
|
uint32 section_symbols_count,
|
|
bool skip_undef_symbols);
|
|
void apply_file_symbols(elf_file_info_t &finfo);
|
|
void check_notype_symbols(
|
|
elf_file_info_t &finfo,
|
|
slice_type_t slice_type);
|
|
void define_gotnode(elf_file_info_t &finfo, const elf_dyn_t &dyn);
|
|
void describe_public(
|
|
ea_t ea,
|
|
ushort bind,
|
|
int entry_flag,
|
|
const char *name,
|
|
const elf_symbol_version_t *symver=NULL,
|
|
elf_sym_idx_t symidx=0);
|
|
ea_t create_addonce_var(
|
|
ea_t ea,
|
|
uint64 valsiz,
|
|
uchar seg_typ,
|
|
ushort type,
|
|
ushort bind,
|
|
bool isfunc,
|
|
const char *name,
|
|
const elf_symbol_version_t *symver,
|
|
elf_sym_idx_t symidx);
|
|
ea_t declare_spec(
|
|
reader_t &reader,
|
|
uint16 secidx,
|
|
uint64 size,
|
|
ushort type,
|
|
ushort bind,
|
|
bool isfunc,
|
|
const char *name,
|
|
const elf_symbol_version_t *symver=NULL,
|
|
elf_sym_idx_t symidx=0);
|
|
void count_spec_symbols(
|
|
reader_t &reader,
|
|
slice_type_t slice_type,
|
|
bool skip_undef_symbols);
|
|
bool call_add_segm_ex(
|
|
segment_t *s,
|
|
const char *name,
|
|
const char *sclass,
|
|
int flags) const;
|
|
bool add_or_merge_segm(
|
|
segment_t *s,
|
|
const char *name,
|
|
const char *sclass,
|
|
int flags);
|
|
bool do_define_segment(
|
|
reader_t &reader,
|
|
ea_t *out_sa,
|
|
ea_t *out_topseg,
|
|
elf_shdr_t &sh,
|
|
const qstring &name);
|
|
bool define_segment(
|
|
elf_file_info_t &finfo,
|
|
elf_shdr_t &sh,
|
|
elf_shndx_t idx,
|
|
const qstring &name,
|
|
bool force_load,
|
|
ea_t *startaddr = nullptr,
|
|
asize_t *_size = nullptr);
|
|
void load_huge_segment(
|
|
reader_t &reader,
|
|
const segment_t &s,
|
|
const elf_phdr_t &p,
|
|
const char *sclass,
|
|
const char *name,
|
|
int flags);
|
|
// create a new segment where a previous segment ended (at the top).
|
|
// Allocate new selector for the new segment. If the flag use_cursel is
|
|
// set then use currently allocated segment selector and allocate a new
|
|
// one after creation.
|
|
segment_t *create_segment_at_top(
|
|
reader_t &reader,
|
|
uchar type,
|
|
const char *name,
|
|
asize_t size,
|
|
uchar align,
|
|
bool use_cursel = false);
|
|
void look_for_prgend_symbols(reader_t &reader);
|
|
// for the relocation object file calculate a start address of the
|
|
// segment where previous segment ended (at the top). For executable or
|
|
// shared objects this function returns the address from <sh>.
|
|
ea_t get_default_segment_ea(reader_t &reader, const elf_shdr_t &sh) const;
|
|
bool should_rename(ushort type, const char *name) const;
|
|
void check_for_gnuc(const char *name);
|
|
void convert_sub_table(reader_t &reader, const elf_shdr_t *pps, char lsym);
|
|
void elf_get_rebased_eas(reader_t &reader, ea_t *pimagebase, ea_t *pload_base);
|
|
void load_ids_info(void);
|
|
// get fixup for PTPOFF generic TLS reloc,
|
|
// it returns FIXUP_CUSTOM if this reloc isn't supported
|
|
fixup_type_t get_ptpoff_reltype();
|
|
void comment_rename(ea_t ea, const char *name);
|
|
void broken_output_warning(void);
|
|
asize_t map_range_from_file(
|
|
reader_t &reader,
|
|
int64 offset,
|
|
ea_t startaddr,
|
|
ea_t endaddr,
|
|
const elf_phdr_t *pht_entry = NULL);
|
|
// FIXME this is wrong! it will be replaced by process_TLS()
|
|
// It is assumed the 'in_out_offset' is relative to the start of
|
|
// the TLS block at runtime. Since those blocks have the following layout:
|
|
// +---------------+
|
|
// | |
|
|
// | .tdata |
|
|
// | |
|
|
// +---------------+
|
|
// | |
|
|
// | .tbss |
|
|
// | |
|
|
// +---------------+
|
|
// we'll associate 'in_out_offset' to either the '.tdata' segment,
|
|
// or the '.tbss' one, depending on whether it overflows
|
|
// .tdata or not.
|
|
//
|
|
// As a side-effect, note that the value pointed to by 'in_out_offset' will
|
|
// be different after this function returns, in case it lands into '.tbss'.
|
|
// (it will be: in_out_offset_after = in_out_offset - segment_size(".tdata"))
|
|
ea_t get_tls_ea_by_offset(uint32 *in_out_offset);
|
|
void relocate_rela(
|
|
reader_t &reader,
|
|
const elf_rela_t &rel,
|
|
bool is_rela,
|
|
int reloc_idx, // Current index in relocation section
|
|
slice_type_t slice_type,
|
|
uint32 info_idx,
|
|
const char *segname,
|
|
reloc_tools_t &tools);
|
|
void relocate_section_mips(
|
|
reader_t &reader,
|
|
const elf_shdr_t §ion,
|
|
const char *segname,
|
|
slice_type_t slice_type,
|
|
reloc_tools_t &tools,
|
|
bool packed,
|
|
int info_idx,
|
|
uint64 count,
|
|
size_t entsize);
|
|
void relocate_section(
|
|
elf_file_info_t &finfo,
|
|
const elf_shdr_t §ion,
|
|
const char *segname,
|
|
slice_type_t slice_type,
|
|
reloc_tools_t &tools,
|
|
bool packed);
|
|
void process_relocations(
|
|
elf_file_info_t &finfo,
|
|
const dynamic_info_t &di,
|
|
reloc_tools_t &tools);
|
|
bool use_displ_for_symbol(ea_t S, adiff_t A)
|
|
{
|
|
if ( S >= prgend )
|
|
return true;
|
|
if ( rel_mode == 1 ) // symbol is STT_SECTION ?
|
|
{
|
|
segment_t *s = getseg(S);
|
|
return s == NULL || A < 0 || A >= s->size();
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
inline int check_alignment(uint64 align)
|
|
{
|
|
if ( align > 1 && align <= 4096 && (align & (align - 1)) == 0 )
|
|
return int(align);
|
|
return 1; // ignore alignment
|
|
}
|
|
|
|
ushort get_algn_value(uval_t algn);
|
|
void get_versioned_symbol_name(
|
|
qstring *out,
|
|
const char *symname,
|
|
const elf_symbol_version_t &symver,
|
|
uint16 idx);
|
|
void annotate_got0(const reader_t &reader, ea_t got0);
|
|
void annotate_loaded_notes(
|
|
const reader_t &reader,
|
|
const rangevec_t &loaded_notes);
|
|
void set_got_entry(ea_t ea, const char *name, int flags, const char *suffix = "_ptr");
|
|
|
|
#endif
|