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
This commit is contained in:
Bananymous 2025-04-15 22:16:02 +03:00
parent 3721dadd72
commit 4f49d60e4a
3 changed files with 52 additions and 50 deletions

View File

@ -14,11 +14,14 @@
#include <strings.h> #include <strings.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/weak_alias.h>
#include <unistd.h> #include <unistd.h>
#include <icxxabi.h> #include <icxxabi.h>
extern "C" char** environ; char** __environ;
weak_alias(__environ, environ);
static bool s_environ_malloced = false;
extern "C" __attribute__((weak)) void _fini(); extern "C" __attribute__((weak)) void _fini();
@ -516,27 +519,35 @@ int putenv(char* string)
return -1; return -1;
} }
if (!environ) if (!s_environ_malloced)
{ {
environ = (char**)malloc(sizeof(char*) * 2); size_t env_count = 0;
if (!environ) while (environ[env_count])
env_count++;
char** new_environ = static_cast<char**>(malloc((env_count + 1) * sizeof(char*)));
if (new_environ == nullptr)
return -1; return -1;
environ[0] = string; for (size_t i = 0; i < env_count; i++)
environ[1] = nullptr; {
return 0; 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; const char* eq_addr = strchr(string, '=');
for (int i = 0; string[i]; i++) if (eq_addr == nullptr)
if (string[i] == '=')
cnt++;
if (cnt != 1)
{ {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
int namelen = strchr(string, '=') - string; size_t namelen = eq_addr - string;
for (int i = 0; environ[i]; i++) for (int i = 0; environ[i]; i++)
{ {
if (strncmp(environ[i], string, namelen + 1) == 0) 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]) while (environ[env_count])
env_count++; env_count++;
char** new_envp = (char**)malloc(sizeof(char*) * (env_count + 2)); char** new_envp = static_cast<char**>(malloc(sizeof(char*) * (env_count + 2)));
if (new_envp == nullptr) if (new_envp == nullptr)
return -1; 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[i] = environ[i];
new_envp[env_count] = string; new_envp[env_count] = string;
new_envp[env_count + 1] = nullptr; new_envp[env_count + 1] = nullptr;

View File

@ -16,31 +16,12 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
char** __environ; extern "C" char** environ;
extern char** environ __attribute__((weak, alias("__environ")));
extern "C" void _init_libc(char** _environ) extern "C" void _init_libc(char** _environ)
{ {
static bool is_initialized = false; if (::environ == nullptr)
if (is_initialized) ::environ = environ;
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;
} }
void _exit(int status) void _exit(int status)

View File

@ -857,16 +857,27 @@ static LoadedElf& load_elf(const char* path, int fd)
return elf; 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"); uintptr_t environ = SYM_NOT_FOUND;
if (_init_libc == nullptr) 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; return;
using _init_libc_t = void(*)(char**);
reinterpret_cast<_init_libc_t>(elf.base + _init_libc->st_value)(envp); *reinterpret_cast<char***>(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) if (elf.has_called_init)
return; return;
@ -879,11 +890,12 @@ static void call_init_funcs(LoadedElf& elf, char** envp, bool skip)
if (dynamic.d_tag == DT_NULL) if (dynamic.d_tag == DT_NULL)
break; break;
if (dynamic.d_tag == DT_NEEDED) if (dynamic.d_tag == DT_NEEDED)
call_init_funcs(*reinterpret_cast<LoadedElf*>(dynamic.d_un.d_ptr), envp, false); call_init_funcs(*reinterpret_cast<LoadedElf*>(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; return;
using init_t = void(*)(); 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++) for (size_t i = 0; i < elf.init_arraysz / sizeof(init_t); i++)
reinterpret_cast<init_t*>(elf.init_array)[i](); reinterpret_cast<init_t*>(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" extern "C"
@ -922,6 +931,7 @@ int _entry(int argc, char** argv, char** envp, int fd)
fini_random(); fini_random();
relocate_elf(elf, true); 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); call_entry_point(argc, argv, envp, elf.base + elf.file_header.e_entry);
} }