diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 62903ff72..c5732b294 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -308,6 +308,38 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 pro return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon"); } +s32 PS4_SYSV_ABI sceKernelMapNamedSystemFlexibleMemory(void** addr_in_out, u64 len, s32 prot, + s32 flags, const char* name) { + LOG_INFO(Kernel_Vmm, "in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, name = '{}'", + fmt::ptr(*addr_in_out), len, prot, flags, name); + if (len == 0 || !Common::Is16KBAligned(len)) { + LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + if (name == nullptr) { + LOG_ERROR(Kernel_Vmm, "name is invalid!"); + return ORBIS_KERNEL_ERROR_EFAULT; + } + + if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) { + LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + + VAddr in_addr = reinterpret_cast(*addr_in_out); + if (in_addr == 0) { + in_addr = 0x880000000; + } + const auto mem_prot = static_cast(prot); + const auto map_flags = static_cast(flags); + auto* memory = Core::Memory::Instance(); + const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, + Core::VMAType::Stack, name); + LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr_in_out)); + return ret; +} + s32 PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) { auto* memory = Core::Memory::Instance(); return memory->QueryProtection(std::bit_cast(addr), start, end, prot); @@ -823,6 +855,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", sceKernelMmap); LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", sceKernelMunmap); LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", sceKernelMapNamedFlexibleMemory); + LIB_FUNCTION("kc+LEEIYakc", "libkernel", 1, "libkernel", sceKernelMapNamedSystemFlexibleMemory); LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", sceKernelAvailableFlexibleMemorySize); LIB_FUNCTION("aNz11fnnzi4", "libkernel_avlfmem", 1, "libkernel", sceKernelAvailableFlexibleMemorySize); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 8d7f9207e..d8153e535 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -69,6 +69,67 @@ void Linker::Execute(const std::vector& args) { Relocate(m.get()); } + // Before we can run guest code, we need to properly initialize the heap API and + // libSceLibcInternal. libSceLibcInternal's _malloc_init serves as an additional initialization + // function called by libkernel. + heap_api = new HeapAPI{}; + static PS4_SYSV_ABI void (*malloc_init)(); + + for (const auto& m : m_modules) { + const auto& mod = m.get(); + if (mod->name.contains("libSceLibcInternal.sprx")) { + // Found libSceLibcInternal, now search through function exports. + // Looking for _malloc_init to init libSceLibcInternal properly + // and for all the memory allocating functions, so we can initialize our heap API + for (const auto& sym : mod->export_sym.GetSymbols()) { + if (sym.nid_name.compare("_malloc_init") == 0) { + malloc_init = reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("malloc") == 0) { + heap_api->heap_malloc = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("free") == 0) { + heap_api->heap_free = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("calloc") == 0) { + heap_api->heap_calloc = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("realloc") == 0) { + heap_api->heap_realloc = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("memalign") == 0) { + heap_api->heap_memalign = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("posix_memalign") == 0) { + heap_api->heap_posix_memalign = + reinterpret_cast( + sym.virtual_address); + } + if (sym.nid_name.compare("reallocalign") == 0) { + heap_api->heap_reallocalign = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("malloc_stats") == 0) { + heap_api->heap_malloc_stats = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("malloc_stats_fast") == 0) { + heap_api->heap_malloc_stats_fast = + reinterpret_cast(sym.virtual_address); + } + if (sym.nid_name.compare("malloc_usable_size") == 0) { + heap_api->heap_malloc_usable_size = + reinterpret_cast(sym.virtual_address); + } + } + } + } + // Configure the direct and flexible memory regions. u64 fmem_size = ORBIS_FLEXIBLE_MEMORY_SIZE; bool use_extended_mem1 = true, use_extended_mem2 = true; @@ -112,6 +173,9 @@ void Linker::Execute(const std::vector& args) { LoadSharedLibraries(); + // Call _malloc_init + malloc_init(); + // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory. // Some games fail without accurately emulating this behavior. s64 phys_addr{};