diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index f42d73844f..3ff9119b7b 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -31,7 +31,11 @@ #include "SoftCPU.h" #include #include +#include #include +#include +#include +#include #include #include #include @@ -55,7 +59,7 @@ # pragma GCC optimize("O3") #endif -//#define DEBUG_SPAM +// #define DEBUG_SPAM namespace UserspaceEmulator { @@ -70,20 +74,45 @@ Emulator& Emulator::the() return *s_the; } -Emulator::Emulator(const Vector& arguments, const Vector& environment, NonnullRefPtr elf) - : m_elf(move(elf)) +Emulator::Emulator(const String& executable_path, const Vector& arguments, const Vector& environment) + : m_executable_path(executable_path) + , m_arguments(arguments) + , m_environment(environment) , m_mmu(*this) , m_cpu(*this) { m_malloc_tracer = make(*this); ASSERT(!s_the); s_the = this; - setup_stack(arguments, environment); + // setup_stack(arguments, environment); register_signal_handlers(); setup_signal_trampoline(); } -void Emulator::setup_stack(const Vector& arguments, const Vector& environment) +Vector Emulator::generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, String executable_path, int executable_fd) const +{ + // FIXME: This is not fully compatible with the auxiliary vector the kernel generates, this is just the bare + // minimum to get the loader going. + Vector auxv; + // PHDR/EXECFD + // PH* + auxv.append({ AuxiliaryValue::PageSize, PAGE_SIZE }); + auxv.append({ AuxiliaryValue::BaseAddress, (void*)load_base }); + + auxv.append({ AuxiliaryValue::Entry, (void*)entry_eip }); + + // FIXME: Don't hard code this? We might support other platforms later.. (e.g. x86_64) + auxv.append({ AuxiliaryValue::Platform, "i386" }); + + auxv.append({ AuxiliaryValue::ExecFilename, executable_path }); + + auxv.append({ AuxiliaryValue::ExecFileDescriptor, executable_fd }); + + auxv.append({ AuxiliaryValue::Null, 0L }); + return auxv; +} + +void Emulator::setup_stack(Vector aux_vector) { auto stack_region = make(stack_location, stack_size); stack_region->set_stack(true); @@ -92,18 +121,30 @@ void Emulator::setup_stack(const Vector& arguments, const Vector Vector argv_entries; - for (auto& argument : arguments) { + for (auto& argument : m_arguments) { m_cpu.push_string(argument.characters()); argv_entries.append(m_cpu.esp().value()); } Vector env_entries; - for (auto& variable : environment) { + for (auto& variable : m_environment) { m_cpu.push_string(variable.characters()); env_entries.append(m_cpu.esp().value()); } + for (auto& auxv : aux_vector) { + if (!auxv.optional_string.is_empty()) { + m_cpu.push_string(auxv.optional_string.characters()); + auxv.auxv.a_un.a_ptr = (void*)m_cpu.esp().value(); + } + } + + for (ssize_t i = aux_vector.size() - 1; i >= 0; --i) { + auto& value = aux_vector[i].auxv; + m_cpu.push_buffer((const u8*)&value, sizeof(value)); + } + m_cpu.push32(shadow_wrap_as_initialized(0)); // char** envp = { envv_entries..., nullptr } for (ssize_t i = env_entries.size() - 1; i >= 0; --i) m_cpu.push32(shadow_wrap_as_initialized(env_entries[i])); @@ -125,54 +166,70 @@ void Emulator::setup_stack(const Vector& arguments, const Vector bool Emulator::load_elf() { - m_elf->image().for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) { + MappedFile mapped_executable(m_executable_path); + if (!mapped_executable.is_valid()) { + reportln("Unable to map {}", m_executable_path); + return false; + } + + ELF::Image executable_elf((const u8*)mapped_executable.data(), mapped_executable.size()); + + if (!executable_elf.is_dynamic()) { + // FIXME: Support static objects + ASSERT_NOT_REACHED(); + } + + String interpreter_path; + if (!ELF::validate_program_headers(*(Elf32_Ehdr*)mapped_executable.data(), mapped_executable.size(), (u8*)mapped_executable.data(), mapped_executable.size(), &interpreter_path)) { + reportln("failed to validate ELF file"); + return false; + } + + ASSERT(!interpreter_path.is_null()); + dbgln("interpreter: {}", interpreter_path); + + auto interpreter_file = make(interpreter_path); + ASSERT(interpreter_file->is_valid()); + ELF::Image interpreter_image((const u8*)interpreter_file->data(), interpreter_file->size()); + + constexpr FlatPtr interpreter_load_offset = 0x08000000; + interpreter_image.for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) { + // Loader is not allowed to have its own TLS regions + ASSERT(program_header.type() != PT_TLS); + if (program_header.type() == PT_LOAD) { - auto region = make(program_header.vaddr().get(), program_header.size_in_memory()); + auto region = make(program_header.vaddr().offset(interpreter_load_offset).get(), program_header.size_in_memory()); if (program_header.is_executable() && !program_header.is_writable()) region->set_text(true); memcpy(region->data(), program_header.raw_data(), program_header.size_in_image()); memset(region->shadow_data(), 0x01, program_header.size_in_memory()); + if (program_header.is_executable()) { + m_loader_text_base = region->base(); + m_loader_text_size = region->size(); + } mmu().add_region(move(region)); return; } - if (program_header.type() == PT_TLS) { - auto tcb_region = make(0x20000000, program_header.size_in_memory()); - memcpy(tcb_region->data(), program_header.raw_data(), program_header.size_in_image()); - memset(tcb_region->shadow_data(), 0x01, program_header.size_in_memory()); - - auto tls_region = make(0, 4); - tls_region->write32(0, shadow_wrap_as_initialized(tcb_region->base() + program_header.size_in_memory())); - memset(tls_region->shadow_data(), 0x01, 4); - - mmu().add_region(move(tcb_region)); - mmu().set_tls_region(move(tls_region)); - return; - } }); - m_cpu.set_eip(m_elf->image().entry().get()); + auto entry_point = interpreter_image.entry().offset(interpreter_load_offset).get(); + m_cpu.set_eip(entry_point); - auto malloc_symbol = m_elf->find_demangled_function("malloc"); - auto free_symbol = m_elf->find_demangled_function("free"); - auto realloc_symbol = m_elf->find_demangled_function("realloc"); - auto malloc_size_symbol = m_elf->find_demangled_function("malloc_size"); + // executable_fd will be used by the loader + int executable_fd = open(m_executable_path.characters(), O_RDONLY); + if (executable_fd < 0) + return false; - m_malloc_symbol_start = malloc_symbol.value().value(); - m_malloc_symbol_end = m_malloc_symbol_start + malloc_symbol.value().size(); - m_free_symbol_start = free_symbol.value().value(); - m_free_symbol_end = m_free_symbol_start + free_symbol.value().size(); - m_realloc_symbol_start = realloc_symbol.value().value(); - m_realloc_symbol_end = m_realloc_symbol_start + realloc_symbol.value().size(); - m_malloc_size_symbol_start = malloc_size_symbol.value().value(); - m_malloc_size_symbol_end = m_malloc_size_symbol_start + malloc_size_symbol.value().size(); + auto aux_vector = generate_auxiliary_vector(interpreter_load_offset, entry_point, m_executable_path, executable_fd); + setup_stack(move(aux_vector)); - m_debug_info = make(m_elf); return true; } int Emulator::exec() { - X86::ELFSymbolProvider symbol_provider(*m_elf); + // X86::ELFSymbolProvider symbol_provider(*m_elf); + X86::ELFSymbolProvider* symbol_provider = nullptr; bool trace = false; @@ -182,7 +239,7 @@ int Emulator::exec() auto insn = X86::Instruction::from_stream(m_cpu, true, true); if (trace) - outln("{:p} \033[33;1m{}\033[0m", m_cpu.base_eip(), insn.to_string(m_cpu.base_eip(), &symbol_provider)); + outln("{:p} \033[33;1m{}\033[0m", m_cpu.base_eip(), insn.to_string(m_cpu.base_eip(), symbol_provider)); (m_cpu.*insn.handler())(insn); @@ -217,16 +274,58 @@ Vector Emulator::raw_backtrace() return backtrace; } +const MmapRegion* Emulator::find_text_region(FlatPtr address) +{ + const MmapRegion* matching_region = nullptr; + mmu().for_each_region([&](auto& region) { + if (!region.is_mmap()) + return IterationDecision::Continue; + const auto& mmap_region = static_cast(region); + if (!(mmap_region.is_executable() && address >= mmap_region.base() && address < mmap_region.base() + mmap_region.size())) + return IterationDecision::Continue; + matching_region = &mmap_region; + return IterationDecision::Break; + }); + return matching_region; +} + +String Emulator::create_backtrace_line(FlatPtr address) +{ + String minimal = String::format("=={%d}== %p", getpid(), address); + const auto* region = find_text_region(address); + if (!region) + return minimal; + auto separator_index = region->name().index_of(":"); + if (!separator_index.has_value()) + return minimal; + + String lib_name = region->name().substring(0, separator_index.value()); + String lib_path = lib_name; + if (region->name().contains(".so")) + lib_path = String::format("/usr/lib/%s", lib_path.characters()); + + auto mapped_file = make(lib_path); + if (!mapped_file->is_valid()) + return minimal; + + auto loader = ELF::Loader::create((const u8*)mapped_file->data(), mapped_file->size()); + String symbol = loader->symbolicate(address - region->base()); + + auto line_without_source_info = String::format("=={%d}== %p [%s]: %s", getpid(), address, lib_name.characters(), symbol.characters()); + auto debug_info = make(loader); + + auto source_position = debug_info->get_source_position(address - region->base()); + if (source_position.has_value()) + return String::format("=={%d}== %p [%s]: %s (\033[34;1m%s\033[0m:%u)", getpid(), address, lib_name.characters(), symbol.characters(), LexicalPath(source_position.value().file_path).basename().characters(), source_position.value().line_number); + else { + return line_without_source_info; + } +} + void Emulator::dump_backtrace(const Vector& backtrace) { for (auto& address : backtrace) { - u32 offset = 0; - String symbol = m_elf->symbolicate(address, &offset); - auto source_position = m_debug_info->get_source_position(address); - if (source_position.has_value()) - reportln("=={}== {:p} {} (\033[34;1m{}\033[0m:{})", getpid(), address, symbol, LexicalPath(source_position.value().file_path).basename(), source_position.value().line_number); - else - reportln("=={}== {:p} {} +{:x}", getpid(), address, symbol, offset); + reportln("{}", create_backtrace_line(address)); } } @@ -406,6 +505,8 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) return virt$clock_nanosleep(arg1); case SC_readlink: return virt$readlink(arg1); + case SC_allocate_tls: + return virt$allocate_tls(arg1); default: reportln("\n=={}== \033[31;1mUnimplemented syscall: {}\033[0m, {:p}", getpid(), Syscall::to_string((Syscall::Function)function), function); dump_backtrace(); @@ -870,8 +971,6 @@ u32 Emulator::virt$mmap(u32 params_addr) Syscall::SC_mmap_params params; mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - ASSERT(params.addr == 0); - u32 final_size = round_up_to_power_of_two(params.size, PAGE_SIZE); u32 final_address = allocate_vm(final_size, params.alignment); if (params.addr != 0) { @@ -886,14 +985,18 @@ u32 Emulator::virt$mmap(u32 params_addr) if (params.flags & MAP_ANONYMOUS) mmu().add_region(MmapRegion::create_anonymous(final_address, final_size, params.prot)); else { - dbgln("chars: {:p}, len: {}", params.name.characters, params.name.length); String name_str; if (params.name.characters) { auto name = ByteBuffer::create_uninitialized(params.name.length); mmu().copy_from_vm(name.data(), (FlatPtr)params.name.characters, params.name.length); name_str = { name.data(), name.size() }; } - mmu().add_region(MmapRegion::create_file_backed(final_address, final_size, params.prot, params.flags, params.fd, params.offset, name_str)); + auto region = MmapRegion::create_file_backed(final_address, final_size, params.prot, params.flags, params.fd, params.offset, name_str); + if (region->name() == "libc.so: .text (Emulated)") { + bool rc = find_malloc_symbols(*region); + ASSERT(rc); + } + mmu().add_region(move(region)); } return final_address; @@ -1567,4 +1670,46 @@ int Emulator::virt$readlink(FlatPtr params_addr) return rc; } +u32 Emulator::virt$allocate_tls(size_t size) +{ + // TODO: Why is this needed? without this, the loader overflows the bounds of the TLS region. + constexpr size_t TLS_SIZE_HACK = 8; + auto tcb_region = make(0x20000000, size + TLS_SIZE_HACK); + bzero(tcb_region->data(), size); + memset(tcb_region->shadow_data(), 0x01, size); + + auto tls_region = make(0, 4); + tls_region->write32(0, shadow_wrap_as_initialized(tcb_region->base() + (u32)size)); + memset(tls_region->shadow_data(), 0x01, 4); + + u32 tls_base = tcb_region->base(); + mmu().add_region(move(tcb_region)); + mmu().set_tls_region(move(tls_region)); + return tls_base; +} + +bool Emulator::find_malloc_symbols(const MmapRegion& libc_text) +{ + auto mapped_file = make("/usr/lib/libc.so"); + if (!mapped_file->is_valid()) + return {}; + + ELF::Image image((const u8*)mapped_file->data(), mapped_file->size()); + auto malloc_symbol = image.find_demangled_function("malloc"); + auto free_symbol = image.find_demangled_function("free"); + auto realloc_symbol = image.find_demangled_function("realloc"); + auto malloc_size_symbol = image.find_demangled_function("malloc_size"); + if (!malloc_symbol.has_value() || !free_symbol.has_value() || !realloc_symbol.has_value() || !malloc_size_symbol.has_value()) + return false; + + m_malloc_symbol_start = malloc_symbol.value().value() + libc_text.base(); + m_malloc_symbol_end = m_malloc_symbol_start + malloc_symbol.value().size(); + m_free_symbol_start = free_symbol.value().value() + libc_text.base(); + m_free_symbol_end = m_free_symbol_start + free_symbol.value().size(); + m_realloc_symbol_start = realloc_symbol.value().value() + libc_text.base(); + m_realloc_symbol_end = m_realloc_symbol_start + realloc_symbol.value().size(); + m_malloc_size_symbol_start = malloc_size_symbol.value().value() + libc_text.base(); + m_malloc_size_symbol_end = m_malloc_size_symbol_start + malloc_size_symbol.value().size(); + return true; +} } diff --git a/DevTools/UserspaceEmulator/Emulator.h b/DevTools/UserspaceEmulator/Emulator.h index 764fb68bc1..8696ee4a32 100644 --- a/DevTools/UserspaceEmulator/Emulator.h +++ b/DevTools/UserspaceEmulator/Emulator.h @@ -32,6 +32,7 @@ #include "SoftMMU.h" #include #include +#include #include #include #include @@ -45,7 +46,7 @@ class Emulator { public: static Emulator& the(); - Emulator(const Vector& arguments, const Vector& environment, NonnullRefPtr); + Emulator(const String& executable_path, const Vector& arguments, const Vector& environment); bool load_elf(); void dump_backtrace(); @@ -60,19 +61,22 @@ public: MallocTracer* malloc_tracer() { return m_malloc_tracer; } bool is_in_malloc_or_free() const; + bool is_in_loader_code() const; void did_receive_signal(int signum) { m_pending_signals |= (1 << signum); } private: - NonnullRefPtr m_elf; - OwnPtr m_debug_info; + const String m_executable_path; + const Vector m_arguments; + const Vector m_environment; SoftMMU m_mmu; SoftCPU m_cpu; OwnPtr m_malloc_tracer; - void setup_stack(const Vector& arguments, const Vector& environment); + void setup_stack(Vector); + Vector generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, String executable_path, int executable_fd) const; void register_signal_handlers(); void setup_signal_trampoline(); @@ -158,10 +162,14 @@ private: pid_t virt$setsid(); int virt$watch_file(FlatPtr, size_t); int virt$readlink(FlatPtr); + u32 virt$allocate_tls(size_t); FlatPtr allocate_vm(size_t size, size_t alignment); + bool find_malloc_symbols(const MmapRegion& libc_text); void dispatch_one_pending_signal(); + const MmapRegion* find_text_region(FlatPtr address); + String create_backtrace_line(FlatPtr address); bool m_shutdown { false }; int m_exit_status { 0 }; @@ -186,6 +194,8 @@ private: SignalHandlerInfo m_signal_handler[NSIG]; FlatPtr m_signal_trampoline { 0 }; + Optional m_loader_text_base; + Optional m_loader_text_size; }; ALWAYS_INLINE bool Emulator::is_in_malloc_or_free() const @@ -196,4 +206,11 @@ ALWAYS_INLINE bool Emulator::is_in_malloc_or_free() const || (m_cpu.base_eip() >= m_malloc_size_symbol_start && m_cpu.base_eip() < m_malloc_size_symbol_end); } +ALWAYS_INLINE bool Emulator::is_in_loader_code() const +{ + if (!m_loader_text_base.has_value() || !m_loader_text_size.has_value()) + return false; + return (m_cpu.base_eip() >= m_loader_text_base.value() && m_cpu.base_eip() < m_loader_text_base.value() + m_loader_text_size.value()); +} + } diff --git a/DevTools/UserspaceEmulator/MallocTracer.cpp b/DevTools/UserspaceEmulator/MallocTracer.cpp index 1a9b1d4077..98e9c934c7 100644 --- a/DevTools/UserspaceEmulator/MallocTracer.cpp +++ b/DevTools/UserspaceEmulator/MallocTracer.cpp @@ -58,6 +58,8 @@ inline void MallocTracer::for_each_mallocation(Callback callback) const void MallocTracer::target_did_malloc(Badge, FlatPtr address, size_t size) { + if (m_emulator.is_in_loader_code()) + return; auto* region = m_emulator.mmu().find_region({ 0x20, address }); ASSERT(region); ASSERT(region->is_mmap()); @@ -109,6 +111,7 @@ ALWAYS_INLINE size_t MallocRegionMetadata::chunk_index_for_address(FlatPtr addre return 0; } auto chunk_offset = address - (this->address + sizeof(ChunkedBlock)); + ASSERT(this->chunk_size); return chunk_offset / this->chunk_size; } @@ -116,6 +119,8 @@ void MallocTracer::target_did_free(Badge, FlatPtr address) { if (!address) return; + if (m_emulator.is_in_loader_code()) + return; if (auto* mallocation = find_mallocation(address)) { if (mallocation->freed) { @@ -136,6 +141,8 @@ void MallocTracer::target_did_free(Badge, FlatPtr address) void MallocTracer::target_did_realloc(Badge, FlatPtr address, size_t size) { + if (m_emulator.is_in_loader_code()) + return; auto* region = m_emulator.mmu().find_region({ 0x20, address }); ASSERT(region); ASSERT(region->is_mmap()); @@ -201,8 +208,13 @@ void MallocTracer::audit_read(const Region& region, FlatPtr address, size_t size if (!m_auditing_enabled) return; - if (m_emulator.is_in_malloc_or_free()) + if (m_emulator.is_in_malloc_or_free()) { return; + } + + if (m_emulator.is_in_loader_code()) { + return; + } auto* mallocation = find_mallocation(region, address); @@ -246,6 +258,10 @@ void MallocTracer::audit_write(const Region& region, FlatPtr address, size_t siz if (m_emulator.is_in_malloc_or_free()) return; + if (m_emulator.is_in_loader_code()) { + return; + } + auto* mallocation = find_mallocation(region, address); if (!mallocation) { reportln("\n=={}== \033[31;1mHeap buffer overflow\033[0m, invalid {}-byte write at address {:p}", getpid(), size, address); @@ -315,6 +331,8 @@ bool MallocTracer::is_reachable(const Mallocation& mallocation) const return IterationDecision::Continue; if (region.is_text()) return IterationDecision::Continue; + if (!region.is_readable()) + return IterationDecision::Continue; // Skip malloc blocks if (region.is_mmap() && static_cast(region).is_malloc_block()) return IterationDecision::Continue; diff --git a/DevTools/UserspaceEmulator/MmapRegion.cpp b/DevTools/UserspaceEmulator/MmapRegion.cpp index c0ce837c38..9e5eda4e56 100644 --- a/DevTools/UserspaceEmulator/MmapRegion.cpp +++ b/DevTools/UserspaceEmulator/MmapRegion.cpp @@ -44,7 +44,6 @@ NonnullOwnPtr MmapRegion::create_file_backed(u32 base, u32 size, u32 auto region = adopt_own(*new MmapRegion(base, size, prot)); region->m_file_backed = true; if (!name.is_empty()) { - dbgln("name is not empty"); name = String::format("%s (Emulated)", name.characters()); region->m_name = name; } diff --git a/DevTools/UserspaceEmulator/MmapRegion.h b/DevTools/UserspaceEmulator/MmapRegion.h index a94077fec7..51c2a8e370 100644 --- a/DevTools/UserspaceEmulator/MmapRegion.h +++ b/DevTools/UserspaceEmulator/MmapRegion.h @@ -61,6 +61,8 @@ public: MallocRegionMetadata* malloc_metadata() { return m_malloc_metadata; } void set_malloc_metadata(Badge, NonnullOwnPtr metadata) { m_malloc_metadata = move(metadata); } + const String& name() const { return m_name; } + private: MmapRegion(u32 base, u32 size, int prot); virtual bool is_mmap() const override { return true; } diff --git a/DevTools/UserspaceEmulator/SoftCPU.cpp b/DevTools/UserspaceEmulator/SoftCPU.cpp index cec716739b..280c38b35f 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.cpp +++ b/DevTools/UserspaceEmulator/SoftCPU.cpp @@ -229,6 +229,13 @@ void SoftCPU::push_string(const StringView& string) m_emulator.mmu().write8({ 0x20, esp().value() + string.length() }, shadow_wrap_as_initialized((u8)'\0')); } +void SoftCPU::push_buffer(const u8* data, size_t size) +{ + set_esp({ esp().value() - size, esp().shadow() }); + warn_if_uninitialized(esp(), "push_buffer"); + m_emulator.mmu().copy_to_vm(esp().value(), data, size); +} + void SoftCPU::push32(ValueWithShadow value) { set_esp({ esp().value() - sizeof(u32), esp().shadow() }); diff --git a/DevTools/UserspaceEmulator/SoftCPU.h b/DevTools/UserspaceEmulator/SoftCPU.h index 7a29cb30f4..89ece8976d 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.h +++ b/DevTools/UserspaceEmulator/SoftCPU.h @@ -93,6 +93,7 @@ public: ValueWithShadow pop16(); void push_string(const StringView&); + void push_buffer(const u8* data, size_t); u16 segment(X86::SegmentRegister seg) const { return m_segment[(int)seg]; } u16& segment(X86::SegmentRegister seg) { return m_segment[(int)seg]; } diff --git a/DevTools/UserspaceEmulator/main.cpp b/DevTools/UserspaceEmulator/main.cpp index 85b53753a0..ec6c320d94 100644 --- a/DevTools/UserspaceEmulator/main.cpp +++ b/DevTools/UserspaceEmulator/main.cpp @@ -50,14 +50,6 @@ int main(int argc, char** argv, char** env) auto executable_path = Core::find_executable_in_path(command[0]); - MappedFile mapped_file(executable_path); - if (!mapped_file.is_valid()) { - reportln("Unable to map {}", executable_path); - return 1; - } - - auto elf = ELF::Loader::create((const u8*)mapped_file.data(), mapped_file.size()); - Vector arguments; for (auto arg : command) { arguments.append(arg); @@ -69,7 +61,7 @@ int main(int argc, char** argv, char** env) } // FIXME: It might be nice to tear down the emulator properly. - auto& emulator = *new UserspaceEmulator::Emulator(arguments, environment, move(elf)); + auto& emulator = *new UserspaceEmulator::Emulator(executable_path, arguments, environment); if (!emulator.load_elf()) return 1; diff --git a/Userland/DynamicLoader/main.cpp b/Userland/DynamicLoader/main.cpp index c8a9904d62..ce40482591 100644 --- a/Userland/DynamicLoader/main.cpp +++ b/Userland/DynamicLoader/main.cpp @@ -254,6 +254,8 @@ static FlatPtr loader_main(auxv_t* auxvp) } } ASSERT(main_program_fd >= 0); + ASSERT(!main_program_name.is_null()); + dbgln("loading: {}", main_program_name); map_library(main_program_name, main_program_fd); map_dependencies(main_program_name);