geode/loader/dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc
2022-07-30 19:24:03 +03:00

292 lines
No EOL
8.3 KiB
C++

#include "SymbolResolver/dobby_symbol_resolver.h"
#include "common_header.h"
#include <elf.h>
#include <dlfcn.h>
#include <link.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <string.h>
#include "PlatformUtil/ProcessRuntimeUtility.h"
#include <vector>
#undef LOG_TAG
#define LOG_TAG "DobbySymbolResolver"
static void file_mmap(const char *file_path, uint8_t **data_ptr, size_t *data_size_ptr) {
uint8_t *mmap_data = NULL;
size_t file_size = 0;
int fd = open(file_path, O_RDONLY, 0);
if (fd < 0) {
ERROR_LOG("%s open failed", file_path);
goto finished;
}
{
struct stat s;
int rt = fstat(fd, &s);
if (rt != 0) {
ERROR_LOG("mmap failed");
goto finished;
}
file_size = s.st_size;
}
// auto align
mmap_data = (uint8_t *)mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, 0);
if (mmap_data == MAP_FAILED) {
ERROR_LOG("mmap failed");
goto finished;
}
finished:
close(fd);
if (data_size_ptr)
*data_size_ptr = file_size;
if (data_ptr)
*data_ptr = mmap_data;
}
static void file_unmap(void *data, size_t data_size) {
int ret = munmap(data, data_size);
if (ret != 0) {
ERROR_LOG("munmap failed");
return;
}
}
typedef struct elf_ctx {
void *header;
uintptr_t load_bias;
ElfW(Shdr) * sym_sh_;
ElfW(Shdr) * dynsym_sh_;
const char *strtab_;
ElfW(Sym) * symtab_;
const char *dynstrtab_;
ElfW(Sym) * dynsymtab_;
size_t nbucket_;
size_t nchain_;
uint32_t *bucket_;
uint32_t *chain_;
size_t gnu_nbucket_;
uint32_t *gnu_bucket_;
uint32_t *gnu_chain_;
uint32_t gnu_maskwords_;
uint32_t gnu_shift2_;
ElfW(Addr) * gnu_bloom_filter_;
} elf_ctx_t;
static void get_syms(ElfW(Ehdr) * header, ElfW(Sym) * *symtab_ptr, char **strtab_ptr, int *count_ptr) {
ElfW(Shdr) *section_header = NULL;
section_header = (ElfW(Shdr) *)((addr_t)header + header->e_shoff);
ElfW(Shdr) *section_strtab_section_header = NULL;
section_strtab_section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shstrndx * header->e_shentsize);
char *section_strtab = NULL;
section_strtab = (char *)((addr_t)header + section_strtab_section_header->sh_offset);
for (int i = 0; i < header->e_shnum; ++i) {
const char *section_name = (const char *)(section_strtab + section_header->sh_name);
if (section_header->sh_type == SHT_SYMTAB && strcmp(section_name, ".symtab") == 0) {
*symtab_ptr = (ElfW(Sym) *)((addr_t)header + section_header->sh_offset);
*count_ptr = section_header->sh_size / sizeof(ElfW(Sym));
}
if (section_header->sh_type == SHT_STRTAB && strcmp(section_name, ".strtab") == 0) {
*strtab_ptr = (char *)((addr_t)header + section_header->sh_offset);
}
section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shentsize);
}
}
int elf_ctx_init(elf_ctx_t *ctx, void *header_) {
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)header_;
ctx->header = ehdr;
ElfW(Addr) ehdr_addr = (ElfW(Addr))ehdr;
// Handle dynamic segment
{
ElfW(Addr) addr = 0;
ElfW(Dyn) *dyn = NULL;
ElfW(Phdr) *phdr = reinterpret_cast<ElfW(Phdr) *>(ehdr_addr + ehdr->e_phoff);
for (size_t i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_DYNAMIC) {
dyn = reinterpret_cast<ElfW(Dyn) *>(ehdr_addr + phdr[i].p_offset);
} else if (phdr[i].p_type == PT_LOAD) {
addr = ehdr_addr + phdr[i].p_offset - phdr[i].p_vaddr;
if (ctx->load_bias == 0)
ctx->load_bias = ehdr_addr - (phdr[i].p_vaddr - phdr[i].p_offset);
} else if (phdr[i].p_type == PT_PHDR) {
ctx->load_bias = (ElfW(Addr))phdr - phdr[i].p_vaddr;
}
}
// ctx->load_bias =
#if 0
const char *strtab = nullptr;
ElfW(Sym) *symtab = nullptr;
for (ElfW(Dyn) *d = dyn; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_STRTAB) {
strtab = reinterpret_cast<const char *>(addr + d->d_un.d_ptr);
} else if (d->d_tag == DT_SYMTAB) {
symtab = reinterpret_cast<ElfW(Sym) *>(addr + d->d_un.d_ptr);
}
}
#endif
}
// Handle section
{
ElfW(Shdr) * dynsym_sh, *dynstr_sh;
ElfW(Shdr) * sym_sh, *str_sh;
ElfW(Shdr) *shdr = reinterpret_cast<ElfW(Shdr) *>(ehdr_addr + ehdr->e_shoff);
ElfW(Shdr) *shstr_sh = NULL;
shstr_sh = &shdr[ehdr->e_shstrndx];
char *shstrtab = NULL;
shstrtab = (char *)((addr_t)ehdr_addr + shstr_sh->sh_offset);
for (size_t i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB) {
sym_sh = &shdr[i];
ctx->sym_sh_ = sym_sh;
ctx->symtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".strtab") == 0) {
str_sh = &shdr[i];
ctx->strtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_DYNSYM) {
dynsym_sh = &shdr[i];
ctx->dynsym_sh_ = dynsym_sh;
ctx->dynsymtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".dynstr") == 0) {
dynstr_sh = &shdr[i];
ctx->dynstrtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset);
}
}
}
return 0;
}
static void *iterate_symbol_table_impl(const char *symbol_name, ElfW(Sym) * symtab, const char *strtab, int count) {
for (int i = 0; i < count; ++i) {
ElfW(Sym) *sym = symtab + i;
const char *symbol_name_ = strtab + sym->st_name;
if (strcmp(symbol_name_, symbol_name) == 0) {
return (void *)sym->st_value;
}
}
return NULL;
}
void *elf_ctx_iterate_symbol_table(elf_ctx_t *ctx, const char *symbol_name) {
void *result = NULL;
if (ctx->symtab_ && ctx->strtab_) {
size_t count = ctx->sym_sh_->sh_size / sizeof(ElfW(Sym));
result = iterate_symbol_table_impl(symbol_name, ctx->symtab_, ctx->strtab_, count);
if (result)
return result;
}
if (ctx->dynsymtab_ && ctx->dynstrtab_) {
size_t count = ctx->dynsym_sh_->sh_size / sizeof(ElfW(Sym));
result = iterate_symbol_table_impl(symbol_name, ctx->dynsymtab_, ctx->dynstrtab_, count);
if (result)
return result;
}
return NULL;
}
void *resolve_elf_internal_symbol(const char *library_name, const char *symbol_name) {
void *result = NULL;
if (library_name) {
RuntimeModule module = ProcessRuntimeUtility::GetProcessModule(library_name);
uint8_t *file_mem = NULL;
size_t file_mem_size = 0;
if (module.load_address)
file_mmap(module.path, &file_mem, &file_mem_size);
elf_ctx_t ctx;
memset(&ctx, 0, sizeof(elf_ctx_t));
if (file_mem) {
elf_ctx_init(&ctx, file_mem);
result = elf_ctx_iterate_symbol_table(&ctx, symbol_name);
}
if (result)
result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias));
if (file_mem)
file_unmap(file_mem, file_mem_size);
}
if (!result) {
std::vector<RuntimeModule> ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap();
for (auto module : ProcessModuleMap) {
uint8_t *file_mem = NULL;
size_t file_mem_size = 0;
if (module.load_address)
file_mmap(module.path, &file_mem, &file_mem_size);
elf_ctx_t ctx;
memset(&ctx, 0, sizeof(elf_ctx_t));
if (file_mem) {
elf_ctx_init(&ctx, file_mem);
result = elf_ctx_iterate_symbol_table(&ctx, symbol_name);
}
if (result)
result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias));
if (file_mem)
file_unmap(file_mem, file_mem_size);
if (result)
break;
}
}
return result;
}
// impl at "android_restriction.cc"
extern std::vector<void *> linker_get_solist();
PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) {
void *result = NULL;
#if 0
auto solist = linker_get_solist();
for (auto soinfo : solist) {
uintptr_t handle = linker_soinfo_to_handle(soinfo);
if (image_name == NULL || strstr(linker_soinfo_get_realpath(soinfo), image_name) != 0) {
result = dlsym((void *)handle, symbol_name_pattern);
if (result)
return result;
}
}
#endif
result = dlsym(RTLD_DEFAULT, symbol_name_pattern);
if (result)
return result;
result = resolve_elf_internal_symbol(image_name, symbol_name_pattern);
return result;
}