#ifndef __ELF_H__ #define __ELF_H__ #include #include #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_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_shdrs_t; typedef qvector 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__. 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 allocated_sym_entries; std::map 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 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 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 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::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(bits); return (value & left_shift(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.', or '.rela.'. 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_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 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 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_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_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_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_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 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 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 class buffered_input_t { protected: reader_t &reader; uint64 offset; uint64 count; size_t isize; // entry size qvector 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(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 { typedef buffered_input_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 { typedef buffered_input_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_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 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 . 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