From 4f49d60e4a204f333009debdbcaaa9fe33bd4536 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 15 Apr 2025 22:16:02 +0300 Subject: [PATCH] DynamicLoader/LibC: lazy malloc environ This allows DynamicLoader to just set the value of global environ symbol without libc needing to malloc it at startup --- userspace/libraries/LibC/stdlib.cpp | 43 ++++++++++++++--------- userspace/libraries/LibC/unistd.cpp | 25 ++----------- userspace/programs/DynamicLoader/main.cpp | 34 +++++++++++------- 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/userspace/libraries/LibC/stdlib.cpp b/userspace/libraries/LibC/stdlib.cpp index 34cbfead..a1bec184 100644 --- a/userspace/libraries/LibC/stdlib.cpp +++ b/userspace/libraries/LibC/stdlib.cpp @@ -14,11 +14,14 @@ #include #include #include +#include #include #include -extern "C" char** environ; +char** __environ; +weak_alias(__environ, environ); +static bool s_environ_malloced = false; extern "C" __attribute__((weak)) void _fini(); @@ -516,27 +519,35 @@ int putenv(char* string) return -1; } - if (!environ) + if (!s_environ_malloced) { - environ = (char**)malloc(sizeof(char*) * 2); - if (!environ) + size_t env_count = 0; + while (environ[env_count]) + env_count++; + + char** new_environ = static_cast(malloc((env_count + 1) * sizeof(char*))); + if (new_environ == nullptr) return -1; - environ[0] = string; - environ[1] = nullptr; - return 0; + for (size_t i = 0; i < env_count; i++) + { + const size_t bytes = strlen(environ[i]) + 1; + new_environ[i] = (char*)malloc(bytes); + memcpy(new_environ[i], environ[i], bytes); + } + new_environ[env_count] = nullptr; + + environ = new_environ; + s_environ_malloced = true; } - int cnt = 0; - for (int i = 0; string[i]; i++) - if (string[i] == '=') - cnt++; - if (cnt != 1) + const char* eq_addr = strchr(string, '='); + if (eq_addr == nullptr) { errno = EINVAL; return -1; } - int namelen = strchr(string, '=') - string; + size_t namelen = eq_addr - string; for (int i = 0; environ[i]; i++) { if (strncmp(environ[i], string, namelen + 1) == 0) @@ -547,15 +558,15 @@ int putenv(char* string) } } - int env_count = 0; + size_t env_count = 0; while (environ[env_count]) env_count++; - char** new_envp = (char**)malloc(sizeof(char*) * (env_count + 2)); + char** new_envp = static_cast(malloc(sizeof(char*) * (env_count + 2))); if (new_envp == nullptr) return -1; - for (int i = 0; i < env_count; i++) + for (size_t i = 0; i < env_count; i++) new_envp[i] = environ[i]; new_envp[env_count] = string; new_envp[env_count + 1] = nullptr; diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 9d03f09e..41779ec2 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -16,31 +16,12 @@ #include #include -char** __environ; -extern char** environ __attribute__((weak, alias("__environ"))); +extern "C" char** environ; extern "C" void _init_libc(char** _environ) { - static bool is_initialized = false; - if (is_initialized) - return; - is_initialized = true; - - if (!_environ) - return; - - size_t env_count = 0; - while (_environ[env_count]) - env_count++; - - environ = (char**)malloc(sizeof(char*) * env_count + 1); - for (size_t i = 0; i < env_count; i++) - { - size_t bytes = strlen(_environ[i]) + 1; - environ[i] = (char*)malloc(bytes); - memcpy(environ[i], _environ[i], bytes); - } - environ[env_count] = nullptr; + if (::environ == nullptr) + ::environ = environ; } void _exit(int status) diff --git a/userspace/programs/DynamicLoader/main.cpp b/userspace/programs/DynamicLoader/main.cpp index e6906007..55c589c8 100644 --- a/userspace/programs/DynamicLoader/main.cpp +++ b/userspace/programs/DynamicLoader/main.cpp @@ -857,16 +857,27 @@ static LoadedElf& load_elf(const char* path, int fd) return elf; } -static void call_init_libc(LoadedElf& elf, char** envp) +static void initialize_environ(char** envp) { - const auto* _init_libc = find_symbol(elf, "_init_libc"); - if (_init_libc == nullptr) + uintptr_t environ = SYM_NOT_FOUND; + for (size_t i = 0; i < s_loaded_file_count; i++) + { + const auto* match = find_symbol(s_loaded_files[i], "environ"); + if (match == nullptr) + continue; + if (environ == SYM_NOT_FOUND || ELF_ST_BIND(match->st_info) != STB_WEAK) + environ = s_loaded_files[i].base + match->st_value; + if (ELF_ST_BIND(match->st_info) != STB_WEAK) + break; + } + + if (environ == SYM_NOT_FOUND) return; - using _init_libc_t = void(*)(char**); - reinterpret_cast<_init_libc_t>(elf.base + _init_libc->st_value)(envp); + + *reinterpret_cast(environ) = envp; } -static void call_init_funcs(LoadedElf& elf, char** envp, bool skip) +static void call_init_funcs(LoadedElf& elf, bool is_main_elf) { if (elf.has_called_init) return; @@ -879,11 +890,12 @@ static void call_init_funcs(LoadedElf& elf, char** envp, bool skip) if (dynamic.d_tag == DT_NULL) break; if (dynamic.d_tag == DT_NEEDED) - call_init_funcs(*reinterpret_cast(dynamic.d_un.d_ptr), envp, false); + call_init_funcs(*reinterpret_cast(dynamic.d_un.d_ptr), false); } } - if (elf.has_called_init || skip) + // main executable calls its init functions in _start + if (elf.has_called_init || is_main_elf) return; using init_t = void(*)(); @@ -892,10 +904,7 @@ static void call_init_funcs(LoadedElf& elf, char** envp, bool skip) for (size_t i = 0; i < elf.init_arraysz / sizeof(init_t); i++) reinterpret_cast(elf.init_array)[i](); - if (strcmp(elf.path, "/usr/lib/libc.so") == 0) - call_init_libc(elf, envp); - elf.has_called_init = true; } extern "C" @@ -922,6 +931,7 @@ int _entry(int argc, char** argv, char** envp, int fd) fini_random(); relocate_elf(elf, true); - call_init_funcs(elf, envp, true); + initialize_environ(envp); + call_init_funcs(elf, true); call_entry_point(argc, argv, envp, elf.base + elf.file_header.e_entry); }