void Loader::execute() { std::cout << "====== start loading elf ======" << std::endl; // 检查当前运行程序是否64位 if (sizeof(intptr_t) != sizeof(std::int64_t)) { throw std::runtime_error("please use x64 compile and run this program"); } // 读取ELF头 Elf64_External_Ehdr elfHeader = {}; fileStream_.seekg(0); fileStream_.read(reinterpret_cast<char*>(&elfHeader), sizeof(elfHeader)); ) { throw std::runtime_error("magic not match"); } else if (elfHeader.e_ident[EI_CLASS] != ELFCLASS64) { throw std::runtime_error("only support ELF64"); } else if (elfHeader.e_ident[EI_DATA] != ELFDATA2LSB) { throw std::runtime_error("only support little endian"); } // 获取program table的信息 std::uint32_t programTableOffset = *reinterpret_cast<std::uint32_t*>(elfHeader.e_phoff); std::uint16_t programTableEntrySize = *reinterpret_cast<std::uint16_t*>(elfHeader.e_phentsize); std::uint16_t programTableEntryNum = *reinterpret_cast<std::uint16_t*>(elfHeader.e_phnum); std::cout << "program table at: " << programTableOffset << ", " << programTableEntryNum << " x " << programTableEntrySize << std::endl; // 获取section table的信息 // section table只给linker用,loader中其实不需要访问section table std::uint32_t sectionTableOffset = *reinterpret_cast<std::uint32_t*>(elfHeader.e_shoff); std::uint16_t sectionTableEntrySize = *reinterpret_cast<std::uint16_t*>(elfHeader.e_shentsize); std::uint16_t sectionTableEntryNum = *reinterpret_cast<std::uint16_t*>(elfHeader.e_shentsize); std::cout << "section table at: " << sectionTableOffset << ", " << sectionTableEntryNum << " x " << sectionTableEntrySize << std::endl;
ELF文件的的开始部分就是ELF头,和Elf64_External_Ehdr结构体的结构相同, 我们可以读到Elf64_External_Ehdr结构体中,
然后ELF头包含了程序头和节头的偏移值, 我们可以预先获取到这些参数
节头在运行时不需要使用, 运行时需要遍历程序头
// 准备动态链接的信息 std::uint64_t jmpRelAddr = 0; // 重定位记录的开始地址 std::uint64_t pltRelType = 0; // 重定位记录的类型 RELA或REL std::uint64_t pltRelSize = 0; // 重定位记录的总大小 std::uint64_t symTabAddr = 0; // 动态符号表的开始地址 std::uint64_t strTabAddr = 0; // 动态符号名称表的开始地址 std::uint64_t strTabSize = 0; // 动态符号名称表的总大小 // 遍历program hedaer std::vector<Elf64_External_Phdr> programHeaders; programHeaders.resize(programTableEntryNum); fileStream_.read(reinterpret_cast<char*>(programHeaders.data()), programTableEntryNum * programTableEntrySize); std::vector<std::shared_ptr<void>> loadedSegments; for (const auto& programHeader : programHeaders) { std::uint32_t type = *reinterpret_cast<const std::uint32_t*>(programHeader.p_type); if (type == PT_LOAD) { // 把文件内容(包含程序代码和数据)加载到虚拟内存,这个示例不考虑地址冲突 std::uint64_t fileOffset = *reinterpret_cast<const std::uint64_t*>(programHeader.p_offset); std::uint64_t fileSize = *reinterpret_cast<const std::uint64_t*>(programHeader.p_filesz); std::uint64_t virtAddr = *reinterpret_cast<const std::uint64_t*>(programHeader.p_vaddr); std::uint64_t memSize = *reinterpret_cast<const std::uint64_t*>(programHeader.p_memsz); if (memSize < fileSize) { throw std::runtime_error("invalid memsz in program header, it shouldn't less than filesz"); } // 在指定的虚拟地址分配内存 std::cout << std::hex << "allocate address at: 0x" << virtAddr << " size: 0x" << memSize << std::dec << std::endl; void* addr = ::VirtualAlloc((void*)virtAddr, memSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == nullptr) { throw std::runtime_error("allocate memory at specific address failed"); } loadedSegments.emplace_back(addr, [](void* ptr) { ::VirtualFree(ptr, 0, MEM_RELEASE); }); // 复制文件内容到虚拟内存 fileStream_.seekg(fileOffset); if (!fileStream_.read(reinterpret_cast<char*>(addr), fileSize)) { throw std::runtime_error("read contents into memory from LOAD program header failed"); } } else if (type == PT_DYNAMIC) { // 遍历动态节 std::uint64_t fileOffset = *reinterpret_cast<const std::uint64_t*>(programHeader.p_offset); fileStream_.seekg(fileOffset); Elf64_External_Dyn dynSection = {}; std::uint64_t dynSectionTag = 0; std::uint64_t dynSectionVal = 0; do { if (!fileStream_.read(reinterpret_cast<char*>(&dynSection), sizeof(dynSection))) { throw std::runtime_error("read dynamic section failed"); } dynSectionTag = *reinterpret_cast<const std::uint64_t*>(dynSection.d_tag); dynSectionVal = *reinterpret_cast<const std::uint64_t*>(dynSection.d_un.d_val); if (dynSectionTag == DT_JMPREL) { jmpRelAddr = dynSectionVal; } else if (dynSectionTag == DT_PLTREL) { pltRelType = dynSectionVal; } else if (dynSectionTag == DT_PLTRELSZ) { pltRelSize = dynSectionVal; } else if (dynSectionTag == DT_SYMTAB) { symTabAddr = dynSectionVal; } else if (dynSectionTag == DT_STRTAB) { strTabAddr = dynSectionVal; } else if (dynSectionTag == DT_STRSZ) { strTabSize = dynSectionVal; } } while (dynSectionTag != 0); } }