#include "loader/elf.hpp" #include #include static const uint64_t EI_NIDENT = 16; static const uint32_t PT_LOAD = 1; static const uint32_t PT_DYNAMIC = 2; static const uint32_t DT_NULL = 0; static const uint32_t DT_NEEDED = 1; static const uint32_t DT_INIT = 12; static const uint32_t DT_FINI = 13; static const uint32_t DT_INIT_ARRAY = 25; static const uint32_t DT_FINI_ARRAY = 26; static const uint32_t DT_INIT_ARRAYSZ = 27; static const uint32_t DT_FINI_ARRAYSZ = 28; static const uint32_t DT_STRTAB = 5; static const uint32_t DT_SYMTAB = 6; static const uint32_t DT_STRSZ = 10; static const uint32_t SHT_SYMTAB = 2; static const uint32_t SHT_DYNSYM = 11; static const uint32_t SHT_RELA = 4; static const uint32_t SHT_REL = 9; ELFLoader::ELFLoader() : m_arch(Arch::X64), m_entry(0), m_init_array(0), m_init_array_size(0), m_fini_array(0), m_fini_array_size(0), m_init(0), m_fini(0) {} ELFLoader::~ELFLoader() {} bool ELFLoader::load(const string& path) { m_path = path; ifstream f(path, ios::binary); if (!f) return false; f.seekg(0, ios::end); size_t sz = f.tellg(); f.seekg(0, ios::beg); m_data.resize(sz); f.read((char*)m_data.data(), sz); f.close(); if (m_data.size() < EI_NIDENT) return false; if (m_data[0] != 0x7F || m_data[1] != 'E' || m_data[2] != 'L' || m_data[3] != 'F') return false; return parseHeader(); } static uint16_t r16(const vector& d, size_t off) { if (off + 2 > d.size()) return 0; return d[off] | (d[off+1] << 8); } static uint32_t r32(const vector& d, size_t off) { if (off + 4 > d.size()) return 0; return d[off] | (d[off+1] << 8) | (d[off+2] << 16) | (d[off+3] << 24); } static uint64_t r64(const vector& d, size_t off) { if (off + 8 > d.size()) return 0; return (uint64_t)d[off] | ((uint64_t)d[off+1] << 8) | ((uint64_t)d[off+2] << 16) | ((uint64_t)d[off+3] << 24) | ((uint64_t)d[off+4] << 32) | ((uint64_t)d[off+5] << 40) | ((uint64_t)d[off+6] << 48) | ((uint64_t)d[off+7] << 56); } struct Elf64_Ehdr { uint8_t ident[16]; uint16_t type; uint16_t machine; uint32_t version; uint64_t entry; uint64_t phoff; uint64_t shoff; uint32_t flags; uint16_t ehsize; uint16_t phentsize; uint16_t phnum; uint16_t shentsize; uint16_t shnum; uint16_t shstrndx; }; struct Elf64_Phdr { uint32_t type; uint32_t flags; uint64_t offset; uint64_t vaddr; uint64_t paddr; uint64_t filesz; uint64_t memsz; uint64_t align; }; struct Elf64_Shdr { uint32_t name; uint32_t type; uint64_t flags; uint64_t addr; uint64_t offset; uint64_t size; uint32_t link; uint32_t info; uint64_t addralign; uint64_t entsize; }; struct Elf64_Dyn { int64_t d_tag; uint64_t d_val; }; bool ELFLoader::parseHeader() { if (m_data.size() < 64) return false; uint8_t cls = m_data[4]; bool is_64 = (cls == 2); if (is_64) { m_arch = Arch::X64; if (m_data.size() < 64) return false; uint16_t type = r16(m_data, 16); m_entry = r64(m_data, 24); uint64_t phoff = r64(m_data, 32); uint64_t shoff = r64(m_data, 40); uint16_t phnum = r16(m_data, 56); uint16_t shnum = r16(m_data, 60); uint16_t shstrndx = r16(m_data, 62); for (uint16_t i = 0; i < phnum; i++) { size_t off = phoff + i * 56; if (off + 56 > m_data.size()) break; uint32_t ptype = r32(m_data, off); uint32_t pflags = r32(m_data, off + 4); uint64_t poffset = r64(m_data, off + 8); uint64_t pvaddr = r64(m_data, off + 16); uint64_t pfilesz = r64(m_data, off + 32); uint64_t pmemsz = r64(m_data, off + 40); if (ptype == PT_LOAD) { m_file_map.push_back({poffset, pvaddr, pmemsz}); if (pflags & 1) m_exec_ranges.push_back({pvaddr, pvaddr + pmemsz}); } if (ptype == PT_DYNAMIC) { addr_t dyn_va = pvaddr; size_t dyn_sz = pmemsz; size_t dyn_off = poffset; size_t end = dyn_off + dyn_sz; for (size_t j = dyn_off; j + 16 <= end; j += 16) { int64_t tag = (int64_t)r64(m_data, j); uint64_t val = r64(m_data, j + 8); if (tag == DT_NULL) break; if (tag == DT_NEEDED && val && m_needed_libs.empty()) { uint64_t strtab = 0; for (size_t k = dyn_off; k + 16 <= end; k += 16) { int64_t t2 = (int64_t)r64(m_data, k); uint64_t v2 = r64(m_data, k + 8); if (t2 == DT_STRTAB) { strtab = v2; break; } } if (strtab) { size_t name_off = strtab + val; if (name_off < m_data.size()) { for (size_t c = name_off; c < m_data.size() && m_data[c]; c++) m_needed_libs += (char)m_data[c]; } } } if (tag == DT_INIT) m_init = val; if (tag == DT_FINI) m_fini = val; if (tag == DT_INIT_ARRAY) m_init_array = val; if (tag == DT_INIT_ARRAYSZ) m_init_array_size = val; if (tag == DT_FINI_ARRAY) m_fini_array = val; if (tag == DT_FINI_ARRAYSZ) m_fini_array_size = val; } } } struct SectionInfo { uint32_t type; uint64_t addr; uint64_t offset; uint64_t size; uint32_t link; uint64_t entsize; uint32_t name_idx; }; vector sections(shnum); uint64_t shstrtab_off = 0; for (uint16_t i = 0; i < shnum; i++) { size_t off = shoff + i * 64; if (off + 64 > m_data.size()) break; sections[i].type = r32(m_data, off + 4); sections[i].addr = r64(m_data, off + 16); sections[i].offset = r64(m_data, off + 24); sections[i].size = r64(m_data, off + 32); sections[i].link = r32(m_data, off + 40); sections[i].entsize = r64(m_data, off + 56); sections[i].name_idx = r32(m_data, off); if (i == shstrndx) shstrtab_off = sections[i].offset; } auto sectionName = [&](uint32_t idx) -> string { if (!shstrtab_off) return ""; size_t pos = shstrtab_off + idx; string s; for (; pos < m_data.size() && m_data[pos]; pos++) s += (char)m_data[pos]; return s; }; vector strtab_offsets(shnum, 0); for (uint16_t i = 0; i < shnum; i++) { if (sections[i].type == SHT_SYMTAB || sections[i].type == SHT_DYNSYM) { uint32_t link = sections[i].link; if (link < shnum) strtab_offsets[i] = sections[link].offset; } } for (uint16_t i = 0; i < shnum; i++) { if (sections[i].type != SHT_SYMTAB && sections[i].type != SHT_DYNSYM) continue; uint64_t sym_off = sections[i].offset; uint64_t sym_sz = sections[i].size; uint64_t entsize = sections[i].entsize ? sections[i].entsize : 24; uint64_t str_off = strtab_offsets[i]; size_t nsym = sym_sz / entsize; for (size_t j = 0; j < nsym; j++) { size_t ent = sym_off + j * entsize; if (ent + 24 > m_data.size()) break; uint32_t sym_name_off = r32(m_data, ent); uint8_t sym_info = m_data[ent + 4]; uint8_t sym_other = m_data[ent + 5]; uint16_t sym_shndx = r16(m_data, ent + 6); uint64_t sym_val = r64(m_data, ent + 8); uint64_t sym_size = r64(m_data, ent + 16); uint8_t sym_type = sym_info & 0xF; uint8_t sym_bind = sym_info >> 4; if (sym_type != 2) continue; if (!sym_name_off || !str_off) continue; size_t name_pos = str_off + sym_name_off; if (name_pos >= m_data.size()) continue; string sym_name; for (size_t c = name_pos; c < m_data.size() && m_data[c]; c++) sym_name += (char)m_data[c]; if (sym_name.empty()) continue; if (sym_shndx != 0 && sym_val) { m_symbols[sym_val] = sym_name; if (sym_bind == 1) m_exports.push_back({sym_val, sym_name}); } } } for (uint16_t i = 0; i < shnum; i++) { string sname = sectionName(sections[i].name_idx); if (sections[i].type != SHT_RELA && sname != ".rela.plt" && sname != ".rela.dyn") continue; uint64_t rela_off = sections[i].offset; uint64_t rela_sz = sections[i].size; size_t nrela = rela_sz / 24; if (nrela == 0) continue; uint32_t symtab_link = sections[i].link; if (symtab_link >= shnum) continue; uint64_t sym_off = sections[symtab_link].offset; uint64_t sym_sz = sections[symtab_link].size; uint64_t str_off2 = 0; uint32_t str_link = sections[symtab_link].link; if (str_link < shnum) str_off2 = sections[str_link].offset; uint64_t sym_entsize = sections[symtab_link].entsize ? sections[symtab_link].entsize : 24; string lib_name = m_needed_libs; for (size_t j = 0; j < nrela; j++) { size_t ent = rela_off + j * 24; if (ent + 24 > m_data.size()) break; uint64_t r_offset = r64(m_data, ent); uint64_t r_info = r64(m_data, ent + 8); uint32_t sym_idx = (uint32_t)(r_info >> 32); if (!sym_idx) continue; size_t sym_ent = sym_off + sym_idx * sym_entsize; if (sym_ent + 24 > m_data.size()) continue; uint32_t func_name_off = r32(m_data, sym_ent); if (!func_name_off || !str_off2) continue; size_t fn_pos = str_off2 + func_name_off; if (fn_pos >= m_data.size()) continue; string func_name; for (size_t c = fn_pos; c < m_data.size() && m_data[c]; c++) func_name += (char)m_data[c]; if (func_name.empty()) continue; bool dup = false; for (const auto& imp : m_imports) if (imp.address == r_offset) { dup = true; break; } if (dup) continue; ImportEntry e; e.address = r_offset; e.name = func_name; e.library = lib_name; m_imports.push_back(e); } } } else { m_arch = Arch::X86; } return true; } bool ELFLoader::parseDynamic() { return true; } bool ELFLoader::parseSections() { return true; } bool ELFLoader::parseSymbols() { return true; } vector ELFLoader::readBytes(addr_t vaddr, size_t size) const { for (const auto& fmap : m_file_map) { addr_t seg_vaddr = get<1>(fmap); uint64_t seg_size = get<2>(fmap); if (vaddr >= seg_vaddr && vaddr < seg_vaddr + seg_size) { uint64_t file_off = get<0>(fmap) + (vaddr - seg_vaddr); size_t avail = min(size, (size_t)(seg_vaddr + seg_size - vaddr)); if (file_off + avail > m_data.size()) avail = m_data.size() - file_off; vector result(m_data.begin() + file_off, m_data.begin() + file_off + avail); return result; } } return {}; } vector> ELFLoader::getExecutableRanges() const { return m_exec_ranges; } vector> ELFLoader::getExportedFunctions() const { return m_exports; } vector ELFLoader::getImportedFunctions() const { return m_imports; }