LibC+LibELF: Pass information from linker via magic lookup

This works by defining a set of weak symbols in dynamic linker whose
value would be provided by it. This has the same effect as preloading
library that magically knows right addresses of functions shared between
dynamic linker and LibC.

We were previously passing the same information by rewriting values
based on hardcoded library name, so the new approach seems a little
nicer to me.
This commit is contained in:
Dan Klishch 2024-01-20 16:20:42 -05:00 committed by Andrew Kaster
parent a17041fe7f
commit 982799f7a0
Notes: sideshowbarker 2024-07-17 16:23:06 +09:00
6 changed files with 59 additions and 71 deletions

View file

@ -11,10 +11,10 @@
#include <string.h>
// These are filled in by the dynamic loader.
DlCloseFunction __dlclose;
DlOpenFunction __dlopen;
DlSymFunction __dlsym;
DlAddrFunction __dladdr;
[[gnu::weak]] DlCloseFunction __dlclose;
[[gnu::weak]] DlOpenFunction __dlopen;
[[gnu::weak]] DlSymFunction __dlsym;
[[gnu::weak]] DlAddrFunction __dladdr;
// FIXME: use thread_local and a String once TLS works
#ifdef NO_TLS

View file

@ -17,8 +17,8 @@ int errno_storage;
#else
__thread int errno_storage;
#endif
char** environ;
bool __environ_is_malloced;
[[gnu::weak]] char** environ;
bool __environ_is_malloced = false;
bool __stdio_is_initialized;
void* __auxiliary_vector;

View file

@ -12,7 +12,7 @@ extern "C" {
using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*);
using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*);
DlIteratePhdrFunction __dl_iterate_phdr;
[[gnu::weak]] DlIteratePhdrFunction __dl_iterate_phdr;
int dl_iterate_phdr(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data)
{

View file

@ -18,8 +18,8 @@
extern "C" {
extern uintptr_t __stack_chk_guard;
// Initialized in `initialize_libc` (we leave a placeholder value here before initialization).
__attribute__((used)) uintptr_t __stack_chk_guard = (uintptr_t)0xc6c7c8c9;
// Populated by DynamicLinker in shared executables.
[[gnu::weak]] uintptr_t __stack_chk_guard = (uintptr_t)0xc6c7c8c9;
__attribute__((noreturn)) void __stack_chk_fail()
{

View file

@ -345,7 +345,7 @@ static T c_str_to_floating_point(char const* str, char** endptr)
extern "C" {
void (*__call_fini_functions)();
[[gnu::weak]] void (*__call_fini_functions)();
void exit(int status)
{

View file

@ -13,6 +13,7 @@
#include <AK/HashTable.h>
#include <AK/LexicalPath.h>
#include <AK/Platform.h>
#include <AK/Random.h>
#include <AK/ScopeGuard.h>
#include <AK/Vector.h>
#include <Kernel/API/VirtualMemoryAnnotations.h>
@ -55,7 +56,6 @@ static size_t s_current_tls_offset = 0;
static size_t s_total_tls_size = 0;
static size_t s_allocated_tls_block_size = 0;
static char** s_envp = nullptr;
static LibCExitFunction s_libc_exit = nullptr;
static __pthread_mutex_t s_loader_lock = __PTHREAD_MUTEX_INITIALIZER;
static ByteString s_cwd;
@ -65,11 +65,32 @@ static StringView s_ld_library_path;
static StringView s_main_program_pledge_promises;
static ByteString s_loader_pledge_promises;
static Result<void, DlErrorMessage> __dlclose(void* handle);
static Result<void*, DlErrorMessage> __dlopen(char const* filename, int flags);
static Result<void*, DlErrorMessage> __dlsym(void* handle, char const* symbol_name);
static Result<void, DlErrorMessage> __dladdr(void const* addr, Dl_info* info);
static void __call_fini_functions();
class MagicWeakSymbol : public RefCounted<MagicWeakSymbol> {
AK_MAKE_NONCOPYABLE(MagicWeakSymbol);
AK_MAKE_NONMOVABLE(MagicWeakSymbol);
public:
template<typename T>
MagicWeakSymbol(unsigned int type, T value)
{
m_storage = reinterpret_cast<uintptr_t>(value);
m_lookup_result.size = 8;
m_lookup_result.type = type;
m_lookup_result.address = VirtualAddress { &m_storage };
m_lookup_result.bind = STB_GLOBAL;
}
auto lookup_result() const
{
return m_lookup_result;
}
private:
DynamicObject::SymbolLookupResult m_lookup_result;
uintptr_t m_storage;
};
static HashMap<StringView, NonnullRefPtr<MagicWeakSymbol>> s_magic_weak_symbols;
Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(StringView name)
{
@ -87,6 +108,10 @@ Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(
weak_result = res;
// We don't want to allow local symbols to be pulled in to other modules
}
if (auto magic_lookup = s_magic_weak_symbols.get(name); magic_lookup.has_value())
weak_result = (*magic_lookup)->lookup_result();
return weak_result;
}
@ -260,61 +285,7 @@ static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data)
static void initialize_libc(DynamicObject& libc)
{
// Traditionally, `_start` of the main program initializes libc.
// However, since some libs use malloc() and getenv() in global constructors,
// we have to initialize libc just after it is loaded.
// Also, we can't just mark `__libc_init` with "__attribute__((constructor))"
// because it uses getenv() internally, so `environ` has to be initialized before we call `__libc_init`.
auto res = libc.lookup_symbol("environ"sv);
VERIFY(res.has_value());
*((char***)res.value().address.as_ptr()) = s_envp;
// __stack_chk_guard should be initialized before anything significant (read: global constructors) is running.
// This is not done in __libc_init, as we definitely have to return from that, and it might affect Loader as well.
res = libc.lookup_symbol("__stack_chk_guard"sv);
VERIFY(res.has_value());
void* stack_guard = res.value().address.as_ptr();
arc4random_buf(stack_guard, sizeof(uintptr_t));
#ifdef AK_ARCH_64_BIT
// For 64-bit platforms we include an additional hardening: zero the first byte of the stack guard to avoid
// leaking or overwriting the stack guard with C-style string functions.
((char*)stack_guard)[0] = 0;
#endif
res = libc.lookup_symbol("__environ_is_malloced"sv);
VERIFY(res.has_value());
*((bool*)res.value().address.as_ptr()) = false;
res = libc.lookup_symbol("exit"sv);
VERIFY(res.has_value());
s_libc_exit = (LibCExitFunction)res.value().address.as_ptr();
res = libc.lookup_symbol("__dl_iterate_phdr"sv);
VERIFY(res.has_value());
*((DlIteratePhdrFunction*)res.value().address.as_ptr()) = __dl_iterate_phdr;
res = libc.lookup_symbol("__dlclose"sv);
VERIFY(res.has_value());
*((DlCloseFunction*)res.value().address.as_ptr()) = __dlclose;
res = libc.lookup_symbol("__dlopen"sv);
VERIFY(res.has_value());
*((DlOpenFunction*)res.value().address.as_ptr()) = __dlopen;
res = libc.lookup_symbol("__dlsym"sv);
VERIFY(res.has_value());
*((DlSymFunction*)res.value().address.as_ptr()) = __dlsym;
res = libc.lookup_symbol("__dladdr"sv);
VERIFY(res.has_value());
*((DlAddrFunction*)res.value().address.as_ptr()) = __dladdr;
res = libc.lookup_symbol("__call_fini_functions"sv);
VERIFY(res.has_value());
*((CallFiniFunctionsFunction*)res.value().address.as_ptr()) = __call_fini_functions;
res = libc.lookup_symbol("__libc_init"sv);
auto res = libc.lookup_symbol("__libc_init"sv);
VERIFY(res.has_value());
typedef void libc_init_func();
((libc_init_func*)res.value().address.as_ptr())();
@ -662,6 +633,23 @@ void ELF::DynamicLinker::linker_main(ByteString&& main_program_path, int main_pr
s_envp = envp;
uintptr_t stack_guard = get_random<uintptr_t>();
#ifdef AK_ARCH_64_BIT
// For 64-bit platforms we include an additional hardening: zero the first byte of the stack guard to avoid
// leaking or overwriting the stack guard with C-style string functions.
stack_guard &= ~0xffULL;
#endif
s_magic_weak_symbols.set("environ"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, s_envp));
s_magic_weak_symbols.set("__stack_chk_guard"sv, make_ref_counted<MagicWeakSymbol>(STT_OBJECT, stack_guard));
s_magic_weak_symbols.set("__call_fini_functions"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __call_fini_functions));
s_magic_weak_symbols.set("__dl_iterate_phdr"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dl_iterate_phdr));
s_magic_weak_symbols.set("__dlclose"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlclose));
s_magic_weak_symbols.set("__dlopen"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlopen));
s_magic_weak_symbols.set("__dlsym"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dlsym));
s_magic_weak_symbols.set("__dladdr"sv, make_ref_counted<MagicWeakSymbol>(STT_FUNC, __dladdr));
char* raw_current_directory = getcwd(nullptr, 0);
s_cwd = raw_current_directory;
free(raw_current_directory);