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); } }