DynamicLoader: Add support for dladdr
This commit is contained in:
parent
36cb3d56fe
commit
ca9361abc1
|
@ -190,6 +190,19 @@ struct LoadedElf
|
||||||
bool is_relocating;
|
bool is_relocating;
|
||||||
|
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
struct LoadedPHDR
|
||||||
|
{
|
||||||
|
uintptr_t base;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
LoadedPHDR loaded_phdrs[16];
|
||||||
|
size_t loaded_phdr_count;
|
||||||
|
|
||||||
|
size_t real_symtab_size;
|
||||||
|
size_t real_symtab_entsize;
|
||||||
|
const uint8_t* real_symtab_addr;
|
||||||
|
const uint8_t* real_strtab_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static LoadedElf s_loaded_files[128];
|
static LoadedElf s_loaded_files[128];
|
||||||
|
@ -433,6 +446,7 @@ extern "C" int __dlclose(void* handle);
|
||||||
extern "C" char* __dlerror(void);
|
extern "C" char* __dlerror(void);
|
||||||
extern "C" void* __dlopen(const char* file, int mode);
|
extern "C" void* __dlopen(const char* file, int mode);
|
||||||
extern "C" void* __dlsym(void* __restrict handle, const char* __restrict name);
|
extern "C" void* __dlsym(void* __restrict handle, const char* __restrict name);
|
||||||
|
extern "C" int __dladdr(const void* addr, Dl_info_t* dlip);
|
||||||
|
|
||||||
template<typename RelocT> requires BAN::is_same_v<RelocT, ElfNativeRelocation> || BAN::is_same_v<RelocT, ElfNativeRelocationA>
|
template<typename RelocT> requires BAN::is_same_v<RelocT, ElfNativeRelocation> || BAN::is_same_v<RelocT, ElfNativeRelocationA>
|
||||||
static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bool resolve_symbols)
|
static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bool resolve_symbols)
|
||||||
|
@ -458,6 +472,7 @@ static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bo
|
||||||
CHECK_SYM(__dlerror);
|
CHECK_SYM(__dlerror);
|
||||||
CHECK_SYM(__dlopen);
|
CHECK_SYM(__dlopen);
|
||||||
CHECK_SYM(__dlsym);
|
CHECK_SYM(__dlsym);
|
||||||
|
CHECK_SYM(__dladdr);
|
||||||
#undef CHECK_SYM
|
#undef CHECK_SYM
|
||||||
else if (symbol.st_shndx && ELF_ST_BIND(symbol.st_info) != STB_WEAK)
|
else if (symbol.st_shndx && ELF_ST_BIND(symbol.st_info) != STB_WEAK)
|
||||||
symbol_address = elf.base + symbol.st_value;
|
symbol_address = elf.base + symbol.st_value;
|
||||||
|
@ -947,6 +962,64 @@ static void load_program_header(const ElfNativeProgramHeader& program_header, in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool read_section_header(const LoadedElf& elf, size_t index, ElfNativeSectionHeader& header)
|
||||||
|
{
|
||||||
|
if (index >= elf.file_header.e_shnum)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto& file_header = elf.file_header;
|
||||||
|
if (syscall(SYS_PREAD, elf.fd, &header, sizeof(header), file_header.e_shoff + index * file_header.e_shentsize) != sizeof(header))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t* mmap_section_header_data(const LoadedElf& elf, const ElfNativeSectionHeader& header)
|
||||||
|
{
|
||||||
|
const size_t offset_in_page = header.sh_offset % PAGE_SIZE;
|
||||||
|
const sys_mmap_t mmap_args {
|
||||||
|
.addr = nullptr,
|
||||||
|
.len = header.sh_size + offset_in_page,
|
||||||
|
.prot = PROT_READ,
|
||||||
|
.flags = MAP_SHARED,
|
||||||
|
.fildes = elf.fd,
|
||||||
|
.off = static_cast<off_t>(header.sh_offset - offset_in_page),
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t* mmap_ret = reinterpret_cast<const uint8_t*>(syscall(SYS_MMAP, &mmap_args));
|
||||||
|
if (mmap_ret == MAP_FAILED)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return mmap_ret + offset_in_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool load_symbol_table(LoadedElf& elf)
|
||||||
|
{
|
||||||
|
if (elf.file_header.e_shentsize < sizeof(ElfNativeSectionHeader))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < elf.file_header.e_shnum; i++)
|
||||||
|
{
|
||||||
|
ElfNativeSectionHeader symtab_header;
|
||||||
|
if (!read_section_header(elf, i, symtab_header))
|
||||||
|
return false;
|
||||||
|
if (symtab_header.sh_type != SHT_SYMTAB)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ElfNativeSectionHeader strtab_header;
|
||||||
|
if (!read_section_header(elf, symtab_header.sh_link, strtab_header))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
elf.real_symtab_entsize = symtab_header.sh_entsize;
|
||||||
|
elf.real_symtab_size = symtab_header.sh_size;
|
||||||
|
elf.real_symtab_addr = mmap_section_header_data(elf, symtab_header);
|
||||||
|
elf.real_strtab_addr = mmap_section_header_data(elf, strtab_header);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static LoadedElf& load_elf(const char* path, int fd)
|
static LoadedElf& load_elf(const char* path, int fd)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < s_loaded_file_count; i++)
|
for (size_t i = 0; i < s_loaded_file_count; i++)
|
||||||
|
@ -1021,8 +1094,13 @@ static LoadedElf& load_elf(const char* path, int fd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElfNativeProgramHeader tls_header {};
|
auto& elf = s_loaded_files[s_loaded_file_count++];
|
||||||
tls_header.p_type = PT_NULL;
|
elf.tls_header.p_type = PT_NULL;
|
||||||
|
elf.base = base;
|
||||||
|
elf.fd = fd;
|
||||||
|
elf.dynamics = nullptr;
|
||||||
|
memcpy(&elf.file_header, &file_header, sizeof(file_header));
|
||||||
|
strcpy(elf.path, path);
|
||||||
|
|
||||||
for (size_t i = 0; i < file_header.e_phnum; i++)
|
for (size_t i = 0; i < file_header.e_phnum; i++)
|
||||||
{
|
{
|
||||||
|
@ -1043,10 +1121,20 @@ static LoadedElf& load_elf(const char* path, int fd)
|
||||||
case PT_GNU_RELRO:
|
case PT_GNU_RELRO:
|
||||||
break;
|
break;
|
||||||
case PT_TLS:
|
case PT_TLS:
|
||||||
tls_header = program_header;
|
elf.tls_header = program_header;
|
||||||
break;
|
break;
|
||||||
case PT_LOAD:
|
case PT_LOAD:
|
||||||
|
if (elf.loaded_phdr_count >= sizeof(elf.loaded_phdrs) / sizeof(*elf.loaded_phdrs))
|
||||||
|
{
|
||||||
|
print(STDERR_FILENO, "file '");
|
||||||
|
print(STDERR_FILENO, elf.path);
|
||||||
|
print_error_and_exit("' has too many PT_LOAD headers", 0);
|
||||||
|
}
|
||||||
program_header.p_vaddr += base;
|
program_header.p_vaddr += base;
|
||||||
|
elf.loaded_phdrs[elf.loaded_phdr_count++] = {
|
||||||
|
.base = program_header.p_vaddr,
|
||||||
|
.size = program_header.p_memsz,
|
||||||
|
};
|
||||||
load_program_header(program_header, fd, needs_writable);
|
load_program_header(program_header, fd, needs_writable);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1056,13 +1144,6 @@ static LoadedElf& load_elf(const char* path, int fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& elf = s_loaded_files[s_loaded_file_count++];
|
|
||||||
elf.tls_header = tls_header;
|
|
||||||
elf.base = base;
|
|
||||||
elf.fd = fd;
|
|
||||||
elf.dynamics = nullptr;
|
|
||||||
memcpy(&elf.file_header, &file_header, sizeof(file_header));
|
|
||||||
strcpy(elf.path, path);
|
|
||||||
|
|
||||||
if (has_dynamic_pheader)
|
if (has_dynamic_pheader)
|
||||||
{
|
{
|
||||||
|
@ -1084,6 +1165,8 @@ static LoadedElf& load_elf(const char* path, int fd)
|
||||||
handle_dynamic(elf);
|
handle_dynamic(elf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load_symbol_table(elf);
|
||||||
|
|
||||||
return elf;
|
return elf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1419,6 +1502,88 @@ void* __dlsym(void* __restrict handle, const char* __restrict name)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool elf_contains_address(const LoadedElf& elf, const void* address)
|
||||||
|
{
|
||||||
|
const uintptr_t addr_uptr = reinterpret_cast<uintptr_t>(address);
|
||||||
|
for (size_t i = 0; i < elf.loaded_phdr_count; i++)
|
||||||
|
{
|
||||||
|
const auto& phdr = elf.loaded_phdrs[i];
|
||||||
|
if (phdr.base <= addr_uptr && addr_uptr < phdr.base + phdr.size)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FindSymbolResult
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
void* addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static FindSymbolResult find_symbol_containing(const LoadedElf& elf, const void* address)
|
||||||
|
{
|
||||||
|
const uintptr_t addr_uptr = reinterpret_cast<uintptr_t>(address);
|
||||||
|
|
||||||
|
const size_t symbol_count = reinterpret_cast<const uint32_t*>(elf.hash)[1];
|
||||||
|
for (size_t i = 1; i < symbol_count; i++)
|
||||||
|
{
|
||||||
|
const auto& symbol = *reinterpret_cast<const ElfNativeSymbol*>(elf.symtab + i * elf.syment);
|
||||||
|
const uintptr_t symbol_base = elf.base + symbol.st_value;
|
||||||
|
if (!(symbol_base <= addr_uptr && addr_uptr < symbol_base + symbol.st_size))
|
||||||
|
continue;
|
||||||
|
return {
|
||||||
|
.name = reinterpret_cast<const char*>(elf.strtab + symbol.st_name),
|
||||||
|
.addr = reinterpret_cast<void*>(symbol_base),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!elf.real_symtab_addr || !elf.real_strtab_addr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
for (size_t i = 1; i < elf.real_symtab_size / elf.real_symtab_entsize; i++)
|
||||||
|
{
|
||||||
|
const auto& symbol = *reinterpret_cast<const ElfNativeSymbol*>(elf.real_symtab_addr + i * elf.real_symtab_entsize);
|
||||||
|
const uintptr_t symbol_base = elf.base + symbol.st_value;
|
||||||
|
if (!(symbol_base <= addr_uptr && addr_uptr < symbol_base + symbol.st_size))
|
||||||
|
continue;
|
||||||
|
return {
|
||||||
|
.name = reinterpret_cast<const char*>(elf.real_strtab_addr + symbol.st_name),
|
||||||
|
.addr = reinterpret_cast<void*>(symbol_base),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int __dladdr(const void* addr, Dl_info_t* dlip)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < s_loaded_file_count; i++)
|
||||||
|
{
|
||||||
|
const auto& elf = s_loaded_files[i];
|
||||||
|
if (!elf_contains_address(elf, addr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dlip->dli_fname = elf.path;
|
||||||
|
dlip->dli_fbase = reinterpret_cast<void*>(elf.base);
|
||||||
|
|
||||||
|
if (const auto symbol = find_symbol_containing(elf, addr); symbol.addr && symbol.name)
|
||||||
|
{
|
||||||
|
dlip->dli_sname = symbol.name;
|
||||||
|
dlip->dli_saddr = symbol.addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dlip->dli_sname = nullptr;
|
||||||
|
dlip->dli_saddr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_dlerror_string = "address is not contained in any file";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static LibELF::AuxiliaryVector* find_auxv(char** envp)
|
static LibELF::AuxiliaryVector* find_auxv(char** envp)
|
||||||
{
|
{
|
||||||
if (envp == nullptr)
|
if (envp == nullptr)
|
||||||
|
|
Loading…
Reference in New Issue