forked from Bananymous/banan-os
				
			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