
通过解析ELF文件,实现ELF依赖库的加载
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <link.h>
#include <err.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
}
if (elf_version(EV_CURRENT) == EV_NONE)
{
errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1));
}
const char *filename = argv[1];
int fd = open(filename, O_RDONLY);
if (fd < 0)
{
errx(EXIT_FAILURE, "open \"%s\" failed", filename);
}
Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
if (!elf)
{
close(fd);
errx(EXIT_FAILURE, "elf_begin() failed: %s.", elf_errmsg(-1));
}
if (elf_kind(elf) != ELF_K_ELF)
{
elf_end(elf);
close(fd);
errx(EXIT_FAILURE, "\"%s\" is not an ELF object.", filename);
}
GElf_Ehdr ehdr;
if (gelf_getehdr(elf, &ehdr) != &ehdr)
{
elf_end(elf);
close(fd);
err(EXIT_FAILURE, "gelf_getehdr failed");
}
void *base_addr = 0;
GElf_Addr rela_addr = 0;
size_t rela_size = 0;
// GElf_Addr strtab_addr = 0;
void *handle = NULL; // 目前就libc,先测试下
Elf64_Sym *symtab = NULL; // 字符串偏移表
const char *strtab = NULL;
// 获取字符串表数据
// Elf_Data *strtab_data = elf_getdata_rawchunk(elf, strtab_addr, 1024, ELF_T_BYTE); // 假定字符串表大小为 1024 字节
// if (!strtab_data)
// {
// elf_end(elf);
// close(fd);
// errx(EXIT_FAILURE, "elf_getdata_rawchunk() failed to get string table: %s\n", elf_errmsg(-1));
// }
for (int i = 0; i < ehdr.e_phnum; i++)
{
GElf_Phdr phdr;
if (gelf_getphdr(elf, i, &phdr) != &phdr)
{
elf_end(elf);
close(fd);
errx(EXIT_FAILURE, "get phdr %d error", i);
}
if (phdr.p_type == PT_LOAD) // LOAD
{
/*
Elf64_Off offset = phdr.p_offset & ~(phdr.p_align - 1);
Elf64_Addr vaddr = phdr.p_vaddr & ~(phdr.p_align - 1);
// Elf64_Xword filesz = phdr.p_filesz + (phdr.p_offset - offset);
Elf64_Xword memsz = phdr.p_memsz + (phdr.p_vaddr - vaddr);
int prot = 0;
if (phdr.p_flags & PF_R)
prot |= PROT_READ;
if (phdr.p_flags & PF_W)
prot |= PROT_WRITE;
if (phdr.p_flags & PF_X)
prot |= PROT_EXEC;
void *addr = mmap((void *)vaddr, memsz, prot, MAP_PRIVATE | MAP_FIXED, fd, offset);
if (addr == MAP_FAILED)
{
elf_end(elf);
close(fd);
err(EXIT_FAILURE, "mmap load error");
}
if (!base_addr)
{
base_addr = addr;
}
// 清零bss段(未初始化数据部分)
if (!(prot & PROT_WRITE) && memsz > phdr.p_filesz)
{
memset((void *)(addr + phdr.p_paddr - offset + phdr.p_filesz), 0, memsz - phdr.p_filesz);
}*/
size_t align = phdr.p_align ? phdr.p_align : sysconf(_SC_PAGESIZE);
// 文件偏移量对齐到页边界,向下取整
off_t aligned_offset = phdr.p_offset & ~(align - 1);
// 调整虚拟地址,对齐到页边界,向下取整
uintptr_t aligned_vaddr = phdr.p_vaddr & ~(align - 1);
// 计算实际需要映射的大小,向上取整
size_t offset_adjustment = phdr.p_offset - aligned_offset;
size_t segment_size = phdr.p_filesz + offset_adjustment;
size_t aligned_segment_size = (segment_size + align - 1) & ~(align - 1);
int prot = 0;
if (phdr.p_flags & PF_R)
prot |= PROT_READ;
if (phdr.p_flags & PF_W)
prot |= PROT_WRITE;
if (phdr.p_flags & PF_X)
prot |= PROT_EXEC;
// 映射内存
void *mapped_addr = mmap((void *)/*aligned_vaddr*/NULL, aligned_segment_size,
prot, MAP_PRIVATE, fd, aligned_offset);
if (mapped_addr == MAP_FAILED)
{
elf_end(elf);
close(fd);
err(EXIT_FAILURE, "mmap load error");
}
if (0 == base_addr)
{
base_addr = (GElf_Addr)mapped_addr + offset_adjustment;
}
// 清理段末尾的多余部分(BSS段)
if (phdr.p_memsz > phdr.p_filesz)
{
uintptr_t bss_start = (uintptr_t)mapped_addr + phdr.p_filesz + offset_adjustment;
size_t bss_size = phdr.p_memsz - phdr.p_filesz;
memset((void *)bss_start, 0, bss_size);
}
// Clear the area after the file data up to the memory size
// if (aligned_size < phdr.p_memsz) {
// size_t extra_size = phdr.p_memsz - aligned_size;
// memset((char *)seg_addr + aligned_size, 0, extra_size);
// }
}
if (phdr.p_type == PT_DYNAMIC) // DYNAMIC
{
Elf_Data *data = elf_getdata_rawchunk(elf, phdr.p_offset, phdr.p_filesz, ELF_T_DYN);
size_t num_entries = phdr.p_filesz / sizeof(Elf64_Dyn);
for (size_t j = 0; j < num_entries; ++j)
{
GElf_Dyn dyn;
if (!gelf_getdyn(data, j, &dyn))
{
elf_end(elf);
close(fd);
errx(EXIT_FAILURE, "gelf_getdyn() failed: %s\n", elf_errmsg(-1));
}
if (dyn.d_tag == DT_STRTAB)
{
strtab = (const char *)(base_addr + dyn.d_un.d_ptr);
// strtab = (const char *)(filename + dyn_entries[i].d_un.d_ptr);
}
if (dyn.d_tag == DT_SYMTAB)
{
symtab = (Elf64_Sym *)(base_addr + dyn.d_un.d_ptr);
}
}
for (size_t j = 0; j < num_entries; ++j)
{
GElf_Dyn dyn;
if (!gelf_getdyn(data, j, &dyn))
{
elf_end(elf);
close(fd);
errx(EXIT_FAILURE, "gelf_getdyn() failed: %s\n", elf_errmsg(-1));
}
if (dyn.d_tag == DT_NEEDED)
{
const char *library_name = strtab + dyn.d_un.d_val;
if (*library_name == '\0')
{
continue;
}
printf("Needed library: %s\n", library_name);
handle = dlopen(library_name, RTLD_NOW);
if (!handle)
{
errx(EXIT_FAILURE, "Failed to load library: %s\n", library_name);
}
printf("Loaded library: %s\n", library_name);
}
else if (dyn.d_tag == DT_RELA)
{
rela_addr = (GElf_Addr)(base_addr + dyn.d_un.d_ptr);
}
else if (dyn.d_tag == DT_RELASZ)
{
rela_size = dyn.d_un.d_val;
}
}
}
}
if (rela_addr == 0 || rela_size == 0)
{
elf_end(elf);
close(fd);
err(EXIT_FAILURE, "No DT_RELA or DT_RELASZ found");
}
printf("Found DT_RELA at 0x%lx with size %zu\n", rela_addr, rela_size);
// 获取重定位表的起始地址
Elf64_Rela *rela_entries = (Elf64_Rela *)(rela_addr);
size_t num_relas = rela_size / sizeof(Elf64_Rela);
// 处理每个重定位条目
for (size_t i = 0; i < num_relas; ++i)
{
Elf64_Rela *rela = &rela_entries[i];
// 应用重定位
switch (ELF64_R_TYPE(rela->r_info))
{
case R_X86_64_RELATIVE:
{
void *reloc_addr = base_addr + rela->r_offset;
*(uintptr_t *)reloc_addr = (uintptr_t)(base_addr + rela->r_addend);
printf("Applied R_X86_64_RELATIVE relocation at %p: %lx\n",
reloc_addr, *(uintptr_t *)reloc_addr);
break;
}
case R_X86_64_JUMP_SLOT: // S
case R_X86_64_GLOB_DAT:
{
void *reloc_addr = base_addr + rela->r_offset;
// *location =base_addr + rela->r_addend;
// 这里我们假设重定位目标已经确定,例如目标函数地址
// 通常情况下,你可能需要从符号表或其他地方获取目标函数地址
Elf64_Sym *sym = &symtab[ELF64_R_SYM(rela->r_info)];
const char *sym_name = strtab + sym->st_name;
// 获取动态库的名称
// Dl_info dlinfo;
// if (dladdr((void *)sym->st_value, &dlinfo) && dlinfo.dli_fname) {
// printf("Symbol: %s, Library: %s\n", sym_name, dlinfo.dli_fname);
// }
void *symbol_addr = dlsym(handle, sym_name);
if (!symbol_addr)
{
fprintf(stderr, "dlsym failed: %s\n", dlerror());
exit(EXIT_FAILURE);
}
*(uintptr_t *)reloc_addr = (uintptr_t)symbol_addr;
printf("Applied R_X86_64_JUMP_SLOT relocation at %p: %lx\n",
reloc_addr, *(uintptr_t *)reloc_addr);
break;
}
default:
break;
}
}
Elf64_Addr entry_point = ehdr.e_entry;
// 调用入口函数
typedef void (*entry_func_t)(void);
// 获取程序头表和入口地址
entry_func_t entry_func = (entry_func_t)entry_point;
entry_func();
elf_end(elf);
close(fd);
return 0;
}