#include "elf64_interpreter.hpp" #include "exceptions/index_exception.hpp" #include "exceptions/key_exception.hpp" #include #include #include #define NKG_CURRENT_SOURCE_FILE() ".\\navicat-patcher\\elf64_interpreter.cpp" #define NKG_CURRENT_SOURCE_LINE() __LINE__ namespace nkg { elf64_interpreter::elf64_interpreter() : m_elf_size(0), m_elf_header(nullptr), m_elf_program_headers(nullptr), m_elf_section_headers(nullptr), m_dynamic_rela(nullptr), m_dynamic_relasz(nullptr), m_dynamic_rel(nullptr), m_dynamic_relsz(nullptr), m_dynamic_pltgot(nullptr), m_dynamic_jmprel(nullptr), m_dynamic_pltrel(nullptr), m_dynamic_pltrelsz(nullptr), m_dynamic_symtab(nullptr), m_dynamic_strtab(nullptr) {} [[nodiscard]] elf64_interpreter elf64_interpreter::parse(void* image_ptr, size_t image_size) { elf64_interpreter new_image; // check ELF header new_image.m_elf_size = image_size; new_image.m_elf_header = reinterpret_cast(image_ptr); if (is_address_in_range(new_image.m_elf_header, sizeof(Elf64_Ehdr), image_ptr, image_size) == false) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); } if (memcmp(new_image.m_elf_header->e_ident, ELFMAG, SELFMAG) != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: header magic check failed."); } if (new_image.m_elf_header->e_ident[EI_CLASS] != ELFCLASS64) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: not ELF64 image."); } if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2LSB && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) { ; // pass } else if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2MSB && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) { ; // pass } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: unsupported endian."); } if (new_image.m_elf_header->e_ident[EI_VERSION] != EV_CURRENT) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident[EI_VERSION] check failed."); } // new_image.m_elf_header->e_ident[EI_OSABI] // new_image.m_elf_header->e_ident[EI_ABIVERSION] for (int i = EI_PAD; i < sizeof(new_image.m_elf_header->e_ident); ++i) { if (new_image.m_elf_header->e_ident[i] != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident padding contains non-zero byte(s)."); } } if (new_image.m_elf_header->e_version != EV_CURRENT) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_version check failed."); } if (new_image.m_elf_header->e_ehsize != sizeof(Elf64_Ehdr)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ehsize check failed."); } if (new_image.m_elf_header->e_phoff && new_image.m_elf_header->e_phentsize && new_image.m_elf_header->e_phnum) { if (new_image.m_elf_header->e_phentsize != sizeof(Elf64_Phdr)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_phentsize check failed."); } new_image.m_elf_program_headers = address_offset_cast(image_ptr, new_image.m_elf_header->e_phoff); if (is_address_in_range(new_image.m_elf_program_headers, new_image.m_elf_header->e_phnum * sizeof(Elf64_Phdr), image_ptr, image_size) == false) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); } } else if (new_image.m_elf_header->e_phoff == 0 && new_image.m_elf_header->e_phentsize == 0 && new_image.m_elf_header->e_phnum == 0) { new_image.m_elf_program_headers = nullptr; } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ph* check failed."); } if (new_image.m_elf_header->e_shoff && new_image.m_elf_header->e_shentsize && new_image.m_elf_header->e_shnum) { if (new_image.m_elf_header->e_shentsize != sizeof(Elf64_Shdr)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shentsize check failed."); } new_image.m_elf_section_headers = address_offset_cast(image_ptr, new_image.m_elf_header->e_shoff); if (is_address_in_range(new_image.m_elf_section_headers, new_image.m_elf_header->e_shnum * sizeof(Elf64_Shdr), image_ptr, image_size) == false) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); } } else if (new_image.m_elf_header->e_shoff == 0 && new_image.m_elf_header->e_shentsize == 0 && new_image.m_elf_header->e_shnum == 0) { new_image.m_elf_section_headers = nullptr; } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_sh* check failed."); } if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) { if (new_image.m_elf_header->e_shstrndx >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shstrndx is out of range."); } } // check program header table and section header table are not overlapped if (new_image.m_elf_program_headers && new_image.m_elf_section_headers) { auto a1 = new_image.m_elf_program_headers; auto a2 = new_image.m_elf_program_headers + new_image.m_elf_header->e_phnum; auto b1 = new_image.m_elf_section_headers; auto b2 = new_image.m_elf_section_headers + new_image.m_elf_header->e_shnum; bool not_overlapped = address_delta(a1, b1) < 0 && address_delta(a2, b1) <= 0 || address_delta(b1, a1) < 0 && address_delta(b2, a1) <= 0; if (!not_overlapped) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: program header table and section header table overlapped."); } } // parse program header for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) { auto& prog_hdr = new_image.m_elf_program_headers[i]; if (!is_address_in_range(address_offset(image_ptr, static_cast(prog_hdr.p_offset)), prog_hdr.p_filesz, image_ptr, image_size)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); } auto prog_hdr_align = prog_hdr.p_align; if (prog_hdr_align) { // align must be a power of 2 if ((prog_hdr_align & (prog_hdr_align - 1)) != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Phdr[{}]: p_align is not a power of 2.", i)); } if (prog_hdr.p_offset % prog_hdr_align != prog_hdr.p_vaddr % prog_hdr_align) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Phdr[{}]: p_offset !== p_vaddr (mod prog_hdr_align).", i)); } } if (prog_hdr.p_type == PT_LOAD) { new_image.m_segment_va_lookup_table.emplace(std::make_pair(prog_hdr.p_vaddr, &prog_hdr)); new_image.m_segment_fo_lookup_table.emplace(std::make_pair(prog_hdr.p_offset, &prog_hdr)); } } // parse section header if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) { auto sect_hdr_strtab = &new_image.m_elf_section_headers[new_image.m_elf_header->e_shstrndx]; auto sect_view_strtab = address_offset_cast(image_ptr, sect_hdr_strtab->sh_offset); if (sect_hdr_strtab->sh_type != SHT_STRTAB) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: sect_hdr_strtab->sh_type != SHT_STRTAB."); } if (!is_address_in_range(sect_view_strtab, sect_hdr_strtab->sh_size, image_ptr, image_size)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); } for (size_t i = 0; i < new_image.m_elf_header->e_shnum; ++i) { new_image.m_section_name_lookup_table .emplace(std::make_pair(std::string_view(address_offset(sect_view_strtab, new_image.m_elf_section_headers[i].sh_name)), &new_image.m_elf_section_headers[i])); } } for (int i = 0; i < new_image.m_elf_header->e_shnum; ++i) { auto& sect_hdr = new_image.m_elf_section_headers[i]; switch (sect_hdr.sh_type) { case SHT_SYMTAB: if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Sym).", i)); } // check sh_link if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); } // todo: check sh_info break; case SHT_RELA: if (sect_hdr.sh_entsize != sizeof(Elf64_Rela)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rela).", i)); } // check sh_link if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[%u]: bad value of sh_link.", i)); } // check sh_info if (sect_hdr.sh_flags & SHF_INFO_LINK) { if (sect_hdr.sh_info == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i)); } if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info is out of range.", i)); } } else { if (sect_hdr.sh_info != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); } } break; case SHT_HASH: if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); } if (sect_hdr.sh_info != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); } break; case SHT_DYNAMIC: if (sect_hdr.sh_entsize != sizeof(Elf64_Dyn)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i)); } // check sh_link if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); } // check sh_info if (sect_hdr.sh_info != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); } break; case SHT_REL: if (sect_hdr.sh_entsize != sizeof(Elf64_Rel)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rel).", i)); } // check sh_link if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[%u]: bad value of sh_link.", i)); } // check sh_info if (sect_hdr.sh_flags & SHF_INFO_LINK) { if (sect_hdr.sh_info == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i)); } if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info is out of range.", i)); } } else { if (sect_hdr.sh_info != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); } } break; case SHT_DYNSYM: if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i)); } // check sh_link if (sect_hdr.sh_link == SHN_UNDEF) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); } if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); } if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); } // todo: check sh_info break; default: break; } if (sect_hdr.sh_type != SHT_NOBITS) { if (is_address_in_range(address_offset(image_ptr, sect_hdr.sh_offset), sect_hdr.sh_size, image_ptr, image_size) == false) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: image is corrupted.", i)); } new_image.m_section_fo_lookup_table.emplace(std::make_pair(sect_hdr.sh_offset, new_image.m_elf_section_headers + i)); } if (sect_hdr.sh_addr) { if (sect_hdr.sh_addralign && sect_hdr.sh_addr % sect_hdr.sh_addralign != 0) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_addr is not aligned to sh_addralign.", i)); } new_image.m_section_va_lookup_table.emplace(std::make_pair(sect_hdr.sh_addr, &new_image.m_elf_section_headers[i])); } } // parse program header, second parse for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) { auto& prog_hdr = new_image.m_elf_program_headers[i]; if (prog_hdr.p_type == PT_DYNAMIC) { auto seg_dynamic_base = address_offset_cast(image_ptr, prog_hdr.p_offset); auto seg_dyncmic_size = prog_hdr.p_filesz; // first parse for (size_t j = 0; j * sizeof(Elf64_Dyn) < seg_dyncmic_size && seg_dynamic_base[j].d_tag != DT_NULL; ++j) { auto& dyn_entry = seg_dynamic_base[j]; switch (dyn_entry.d_tag) { case DT_RELA: new_image.m_dynamic_rela = &seg_dynamic_base[j]; break; case DT_RELAENT: if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rela)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELAENT.dval != sizeof(Elf64_Rela)."); } break; case DT_RELASZ: new_image.m_dynamic_relasz = &seg_dynamic_base[j]; break; case DT_REL: new_image.m_dynamic_rel = &seg_dynamic_base[j]; break; case DT_RELENT: if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rel)) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELENT.dval != sizeof(Elf64_Rel)."); } break; case DT_RELSZ: new_image.m_dynamic_relsz = &seg_dynamic_base[j]; break; case DT_PLTGOT: new_image.m_dynamic_pltgot = &seg_dynamic_base[j]; break; case DT_JMPREL: new_image.m_dynamic_jmprel = &seg_dynamic_base[j]; break; case DT_PLTREL: if (seg_dynamic_base[j].d_un.d_val == DT_REL || seg_dynamic_base[j].d_un.d_val == DT_RELA) { new_image.m_dynamic_pltrel = &seg_dynamic_base[j]; } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_PLTREL.dval is neither DT_REL nor DT_RELA."); } break; case DT_PLTRELSZ: new_image.m_dynamic_pltrelsz = &seg_dynamic_base[j]; break; case DT_STRTAB: new_image.m_dynamic_strtab = &seg_dynamic_base[j]; break; case DT_SYMTAB: new_image.m_dynamic_symtab = &seg_dynamic_base[j]; break; default: break; } } } } if (new_image.m_dynamic_rela && new_image.m_dynamic_relasz) { auto rela_base = new_image.convert_va_to_ptr(new_image.m_dynamic_rela->d_un.d_ptr); auto rela_size = new_image.m_dynamic_relasz->d_un.d_val; for (size_t i = 0; i * sizeof(Elf64_Rela) < rela_size; ++i) { auto reloc_va = rela_base[i].r_offset; auto reloc_type = ELF64_R_TYPE(rela_base[i].r_info); switch(reloc_type) { case R_X86_64_64: case R_X86_64_GLOB_DAT: case R_X86_64_RELATIVE: new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); break; default: throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Unsupported ELF file: unhandled relocation type({}).", reloc_type)); } } } if (new_image.m_dynamic_rel && new_image.m_dynamic_relsz) { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: DT_REL is not parsed."); } if (new_image.m_dynamic_jmprel && new_image.m_dynamic_pltrel && new_image.m_dynamic_pltrelsz) { if (new_image.m_dynamic_pltrel->d_un.d_val == DT_RELA) { auto jmprel_base = new_image.convert_va_to_ptr(new_image.m_dynamic_jmprel->d_un.d_ptr); auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val; for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) { auto reloc_va = jmprel_base[i].r_offset; auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info); if (reloc_type == R_X86_64_JUMP_SLOT) { new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: bad relocation type({}) in JMPREL relocation table.", reloc_type)); } } } else { auto jmprel_base = new_image.convert_va_to_ptr(new_image.m_dynamic_jmprel->d_un.d_ptr); auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val; for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) { auto reloc_va = jmprel_base[i].r_offset; auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info); if (reloc_type == R_X86_64_JUMP_SLOT) { new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); } else { throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: bad relocation type({}) in JMPREL relocation table.", reloc_type)); } } } } return new_image; } [[nodiscard]] size_t elf64_interpreter::elf_size() const noexcept { return m_elf_size; } [[nodiscard]] Elf64_Ehdr* elf64_interpreter::elf_header() const noexcept { return m_elf_header; } [[nodiscard]] Elf64_Phdr* elf64_interpreter::elf_program_header(size_t n) const { if (n < m_elf_header->e_phnum) { return m_elf_program_headers + n; } else { throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range."); } } [[nodiscard]] Elf64_Phdr* elf64_interpreter::elf_program_header_from_fo(fo_t file_offset) const { auto it = m_segment_fo_lookup_table.upper_bound(file_offset); if (it != m_segment_fo_lookup_table.begin()) { --it; if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) { return it->second; } } throw bad_fo_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("File offset({:#x}) doesn't point to any segment.")); } [[nodiscard]] Elf64_Phdr* elf64_interpreter::elf_program_header_from_rva(rva_t rva) const { return elf_program_header_from_va(convert_rva_to_va(rva)); } [[nodiscard]] Elf64_Phdr* elf64_interpreter::elf_program_header_from_va(va_t va) const { auto it = m_segment_va_lookup_table.upper_bound(va); if (it != m_segment_va_lookup_table.begin()) { --it; if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) { return it->second; } } throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x}).", va)); } [[nodiscard]] size_t elf64_interpreter::elf_program_headers_num() const noexcept { return m_elf_header->e_shnum; } [[nodiscard]] Elf64_Shdr* elf64_interpreter::elf_section_header(size_t n) const { if (n < m_elf_header->e_shnum) { return m_elf_section_headers + n; } else { throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range."); } } [[nodiscard]] Elf64_Shdr* elf64_interpreter::elf_section_header(std::string_view section_name) const { auto it = m_section_name_lookup_table.find(section_name); if (it != m_section_name_lookup_table.end()) { return it->second; } else { throw exceptions::key_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Section `{}` is not found.", section_name.data())); } } [[nodiscard]] size_t elf64_interpreter::elf_section_headers_num() const noexcept { return m_elf_header->e_shnum; } [[nodiscard]] elf64_interpreter::fo_t elf64_interpreter::convert_rva_to_fo(rva_t rva) const { return convert_va_to_fo(convert_rva_to_va(rva)); } [[nodiscard]] elf64_interpreter::fo_t elf64_interpreter::convert_va_to_fo(va_t va) const { auto it = m_segment_va_lookup_table.upper_bound(va); if (it != m_segment_va_lookup_table.begin()) { --it; if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) { if (va - it->second->p_vaddr < it->second->p_filesz) { return it->second->p_offset + (va - it->second->p_vaddr); } else { throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Virtual address({:#016x}) doesn't have corresponding file offset.", va)); } } } throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x})", va)); } [[nodiscard]] elf64_interpreter::rva_t elf64_interpreter::convert_fo_to_rva(fo_t file_offset) const { return convert_va_to_rva(convert_fo_to_va(file_offset)); } elf64_interpreter::rva_t elf64_interpreter::convert_va_to_rva(va_t va) const { return va - m_segment_va_lookup_table.begin()->first; } [[nodiscard]] elf64_interpreter::va_t elf64_interpreter::convert_fo_to_va(fo_t file_offset) const { auto it = m_segment_fo_lookup_table.upper_bound(file_offset); if (it != m_segment_fo_lookup_table.begin()) { --it; if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) { return it->second->p_vaddr + (file_offset - it->second->p_offset); } } throw bad_fo_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("File offset({:#x}) doesn't have corresponding virtual address.", file_offset)); } [[nodiscard]] elf64_interpreter::va_t elf64_interpreter::convert_rva_to_va(rva_t rva) const { return m_segment_va_lookup_table.begin()->first + rva; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_rela() const { return m_dynamic_rela ? std::make_optional(m_dynamic_rela->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_relasz() const { return m_dynamic_relasz ? std::make_optional(m_dynamic_relasz->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_rel() const { return m_dynamic_rel ? std::make_optional(m_dynamic_rel->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_relsz() const { return m_dynamic_relsz ? std::make_optional(m_dynamic_relsz->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_pltgot() const { return m_dynamic_pltgot ? std::make_optional(m_dynamic_pltgot->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_jmprel() const { return m_dynamic_jmprel ? std::make_optional(m_dynamic_jmprel->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_pltrel() const { return m_dynamic_pltrel ? std::make_optional(m_dynamic_pltrel->d_un.d_val) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_pltrelsz() const { return m_dynamic_pltrelsz ? std::make_optional(m_dynamic_pltrelsz->d_un.d_val) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_symtab() const { return m_dynamic_symtab ? std::make_optional(m_dynamic_symtab->d_un.d_ptr) : std::nullopt; } [[nodiscard]] std::optional elf64_interpreter::elf_dynamic_strtab() const { return m_dynamic_strtab ? std::make_optional(m_dynamic_strtab->d_un.d_ptr) : std::nullopt; } [[nodiscard]] const std::map& elf64_interpreter::relocation_distribute() const { return m_relocation_distribute; } } #undef NKG_CURRENT_SOURCE_LINE #undef NKG_CURRENT_SOURCE_FILE