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