forked from Bananymous/banan-os
				
			LibC: Implement very hacky dlopen/dlsym/dclose
If ELF loading fails, this will just crash the program :D And there is no support for RTLD_LOCAL
This commit is contained in:
		
							parent
							
								
									83c0ef3514
								
							
						
					
					
						commit
						397219c22e
					
				|  | @ -1,23 +1,33 @@ | ||||||
| #include <BAN/Assert.h> |  | ||||||
| 
 |  | ||||||
| #include <dlfcn.h> | #include <dlfcn.h> | ||||||
| 
 | 
 | ||||||
| int dlclose(void*) | extern "C" int __dlclose(void*) __attribute__((weak)); | ||||||
|  | int dlclose(void* handle) | ||||||
| { | { | ||||||
| 	ASSERT_NOT_REACHED(); | 	if (&__dlclose == nullptr) [[unlikely]] | ||||||
|  | 		return -1; | ||||||
|  | 	return __dlclose(handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" char* __dlerror() __attribute__((weak)); | ||||||
| char* dlerror(void) | char* dlerror(void) | ||||||
| { | { | ||||||
| 	ASSERT_NOT_REACHED(); | 	if (&__dlerror == nullptr) [[unlikely]] | ||||||
|  | 		return const_cast<char*>("TODO: dlfcn functions with static linking"); | ||||||
|  | 	return __dlerror(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* dlopen(const char*, int) | extern "C" void* __dlopen(const char*, int) __attribute__((weak)); | ||||||
|  | void* dlopen(const char* file, int mode) | ||||||
| { | { | ||||||
| 	ASSERT_NOT_REACHED(); | 	if (&__dlopen == nullptr) [[unlikely]] | ||||||
|  | 		return nullptr; | ||||||
|  | 	return __dlopen(file, mode); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* dlsym(void* __restrict, const char* __restrict) | extern "C" void* __dlsym(void*, const char*) __attribute__((weak)); | ||||||
|  | void* dlsym(void* __restrict handle, const char* __restrict name) | ||||||
| { | { | ||||||
| 	ASSERT_NOT_REACHED(); | 	if (&__dlsym == nullptr) [[unlikely]] | ||||||
|  | 		return nullptr; | ||||||
|  | 	return __dlsym(handle, name); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include <LibELF/Types.h> | #include <LibELF/Types.h> | ||||||
| #include <LibELF/Values.h> | #include <LibELF/Values.h> | ||||||
| 
 | 
 | ||||||
|  | #include <dlfcn.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
|  | @ -421,6 +422,11 @@ static void handle_tls_relocation(const LoadedElf& elf, const RelocT& reloc) | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" int __dlclose(void* handle); | ||||||
|  | extern "C" char* __dlerror(void); | ||||||
|  | extern "C" void* __dlopen(const char* file, int mode); | ||||||
|  | extern "C" void* __dlsym(void* __restrict handle, const char* __restrict name); | ||||||
|  | 
 | ||||||
| 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) | ||||||
| { | { | ||||||
|  | @ -437,7 +443,16 @@ static uintptr_t handle_relocation(const LoadedElf& elf, const RelocT& reloc, bo | ||||||
| 		const auto& symbol = *reinterpret_cast<ElfNativeSymbol*>(elf.symtab + symbol_index * elf.syment); | 		const auto& symbol = *reinterpret_cast<ElfNativeSymbol*>(elf.symtab + symbol_index * elf.syment); | ||||||
| 		const char* symbol_name = reinterpret_cast<const char*>(elf.strtab + symbol.st_name); | 		const char* symbol_name = reinterpret_cast<const char*>(elf.strtab + symbol.st_name); | ||||||
| 
 | 
 | ||||||
| 		if (symbol.st_shndx && ELF_ST_BIND(symbol.st_info) != STB_WEAK) | 		if (false) {} | ||||||
|  | #define CHECK_SYM(sym) \ | ||||||
|  | 		else if (strcmp(symbol_name, #sym) == 0) \ | ||||||
|  | 			symbol_address = reinterpret_cast<uintptr_t>(&sym) | ||||||
|  | 		CHECK_SYM(__dlclose); | ||||||
|  | 		CHECK_SYM(__dlerror); | ||||||
|  | 		CHECK_SYM(__dlopen); | ||||||
|  | 		CHECK_SYM(__dlsym); | ||||||
|  | #undef CHECK_SYM | ||||||
|  | 		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; | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
|  | @ -697,6 +712,23 @@ uintptr_t resolve_symbol(const LoadedElf& elf, uintptr_t plt_entry) | ||||||
| 
 | 
 | ||||||
| static LoadedElf& load_elf(const char* path, int fd); | static LoadedElf& load_elf(const char* path, int fd); | ||||||
| 
 | 
 | ||||||
|  | static bool find_library(const char* library_name, char out[PATH_MAX]) | ||||||
|  | { | ||||||
|  | 	const char* library_dir = "/usr/lib/"; | ||||||
|  | 
 | ||||||
|  | 	char path_buffer[PATH_MAX]; | ||||||
|  | 	char* path_ptr = path_buffer; | ||||||
|  | 
 | ||||||
|  | 	if (library_name[0] != '/') | ||||||
|  | 		for (size_t i = 0; library_dir[i]; i++) | ||||||
|  | 			*path_ptr++ = library_dir[i]; | ||||||
|  | 	for (size_t i = 0; library_name[i]; i++) | ||||||
|  | 		*path_ptr++ = library_name[i]; | ||||||
|  | 	*path_ptr = '\0'; | ||||||
|  | 
 | ||||||
|  | 	return syscall(SYS_REALPATH, path_buffer, out) >= 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void handle_dynamic(LoadedElf& elf) | static void handle_dynamic(LoadedElf& elf) | ||||||
| { | { | ||||||
| 	uintptr_t pltgot = 0; | 	uintptr_t pltgot = 0; | ||||||
|  | @ -754,24 +786,13 @@ static void handle_dynamic(LoadedElf& elf) | ||||||
| 		if (dynamic.d_tag != DT_NEEDED) | 		if (dynamic.d_tag != DT_NEEDED) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		const char* library_dir = "/usr/lib/"; | 		const char* library_name = reinterpret_cast<const char*>(elf.strtab + dynamic.d_un.d_val); | ||||||
| 
 | 
 | ||||||
| 		char path_buffer[PATH_MAX]; | 		char path_buffer[PATH_MAX]; | ||||||
| 		char* path_ptr = path_buffer; | 		if (!find_library(library_name, path_buffer)) | ||||||
|  | 			print_error_and_exit("could not open shared object", 0); | ||||||
| 
 | 
 | ||||||
| 		const char* library_name = reinterpret_cast<const char*>(elf.strtab + dynamic.d_un.d_val); | 		const auto& loaded_elf = load_elf(path_buffer, -1); | ||||||
| 		if (library_name[0] != '/') |  | ||||||
| 			for (size_t i = 0; library_dir[i]; i++) |  | ||||||
| 				*path_ptr++ = library_dir[i]; |  | ||||||
| 		for (size_t i = 0; library_name[i]; i++) |  | ||||||
| 			*path_ptr++ = library_name[i]; |  | ||||||
| 		*path_ptr = '\0'; |  | ||||||
| 
 |  | ||||||
| 		char realpath[PATH_MAX]; |  | ||||||
| 		if (auto ret = syscall(SYS_REALPATH, path_buffer, realpath); ret < 0) |  | ||||||
| 			print_error_and_exit("realpath", ret); |  | ||||||
| 
 |  | ||||||
| 		const auto& loaded_elf = load_elf(realpath, -1); |  | ||||||
| 		dynamic.d_un.d_ptr = reinterpret_cast<uintptr_t>(&loaded_elf); | 		dynamic.d_un.d_ptr = reinterpret_cast<uintptr_t>(&loaded_elf); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1224,6 +1245,75 @@ static void call_init_funcs(LoadedElf& elf, bool is_main_elf) | ||||||
| 		reinterpret_cast<init_t*>(elf.init_array)[i](); | 		reinterpret_cast<init_t*>(elf.init_array)[i](); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int __dlclose(void* handle) | ||||||
|  | { | ||||||
|  | 	// TODO: maybe actually close handles? (not required by spec)
 | ||||||
|  | 	(void)handle; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const char* s_dlerror_string = nullptr; | ||||||
|  | 
 | ||||||
|  | char* __dlerror(void) | ||||||
|  | { | ||||||
|  | 	const char* result = s_dlerror_string; | ||||||
|  | 	s_dlerror_string = nullptr; | ||||||
|  | 	return const_cast<char*>(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* __dlopen(const char* file, int mode) | ||||||
|  | { | ||||||
|  | 	const bool lazy = !(mode & RTLD_NOW); | ||||||
|  | 
 | ||||||
|  | 	// FIXME: RTLD_{LOCAL,GLOBAL}
 | ||||||
|  | 
 | ||||||
|  | 	char path_buffer[PATH_MAX]; | ||||||
|  | 	if (!find_library(file, path_buffer)) | ||||||
|  | 	{ | ||||||
|  | 		s_dlerror_string = "Could not find file"; | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	init_random(); | ||||||
|  | 	auto& elf = load_elf(path_buffer, -1); | ||||||
|  | 	fini_random(); | ||||||
|  | 
 | ||||||
|  | 	if (!elf.is_relocating && !elf.is_calling_init) | ||||||
|  | 	{ | ||||||
|  | 		if (elf.tls_header.p_type == PT_TLS) | ||||||
|  | 		{ | ||||||
|  | 			s_dlerror_string = "TODO: __dlopen with TLS"; | ||||||
|  | 			return nullptr; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		relocate_elf(elf, lazy); | ||||||
|  | 		call_init_funcs(elf, false); | ||||||
|  | 		syscall(SYS_CLOSE, elf.fd); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &elf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* __dlsym(void* __restrict handle, const char* __restrict name) | ||||||
|  | { | ||||||
|  | 	if (handle == nullptr) | ||||||
|  | 	{ | ||||||
|  | 		for (size_t i = 0; i < s_loaded_file_count; i++) | ||||||
|  | 			if (auto* sym = __dlsym(&s_loaded_files[i], name)) | ||||||
|  | 				return sym; | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	auto& elf = *static_cast<LoadedElf*>(handle); | ||||||
|  | 
 | ||||||
|  | 	// FIXME: look in ELF's dependency tree
 | ||||||
|  | 	if (auto* match = find_symbol(elf, name)) | ||||||
|  | 		return reinterpret_cast<void*>(elf.base + match->st_value); | ||||||
|  | 
 | ||||||
|  | 	s_dlerror_string = "symbol not found"; | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 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