From ea03a22c7f61c8174fecf2a6359759783c092b4e Mon Sep 17 00:00:00 2001 From: jagrat Singh Date: Mon, 12 Oct 2026 19:50:04 +0530 Subject: [PATCH] feat(libkernel): implement sceKernelInternalMemoryGetModuleSegmentInfo --- core/libs/SceTypes.hpp | 6 ++++++ core/libs/prx/libkernel/Module/src/Module.cpp | 7 ------- core/libs/prx/libkernel/Module/src/ModuleInfo.cpp | 10 ++++++++++ core/libs/tests/GuestModuleInfo.cpp | 5 +++++ docs/dev/debt/libkernel.md | 2 +- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 95c4f4207..5a9df3031 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -280,6 +280,12 @@ struct ModuleInfoForUnwind { std::uint64_t seg0_size; }; +struct InternalSegmentInfo { + std::uint64_t address; + std::uint64_t size; +}; +static_assert(sizeof(InternalSegmentInfo) == 0x10); + struct MallocReplace { std::uint64_t size = sizeof(MallocReplace); void* malloc_initialize = nullptr; diff --git a/core/libs/prx/libkernel/Module/src/Module.cpp b/core/libs/prx/libkernel/Module/src/Module.cpp index f1d39a1ec..d196d8613 100644 --- a/core/libs/prx/libkernel/Module/src/Module.cpp +++ b/core/libs/prx/libkernel/Module/src/Module.cpp @@ -241,11 +241,4 @@ int APS5_VABI __elf_phdr_match_addr_nid_postfix(dl_phdr_info* phdrInfo, void* ad return 0; } -// unknown signature -std::int32_t APS5_VABI sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(void* result) { - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/prx/libkernel/Module/src/ModuleInfo.cpp b/core/libs/prx/libkernel/Module/src/ModuleInfo.cpp index 6357a1c29..63451dbca 100644 --- a/core/libs/prx/libkernel/Module/src/ModuleInfo.cpp +++ b/core/libs/prx/libkernel/Module/src/ModuleInfo.cpp @@ -447,6 +447,16 @@ std::int32_t APS5_VABI sceKernelGetLibkernelTextLocation_nid_postfix(void** addr return 0; } +std::int32_t APS5_VABI sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(InternalSegmentInfo* result) { + if (!result) throw std::invalid_argument("sceKernelInternalMemoryGetModuleSegmentInfo: result is null"); + void* address = nullptr; + std::size_t size = 0; + const auto status = sceKernelGetLibkernelTextLocation_nid_postfix(&address, &size); + if (status != 0) return status; + *result = {reinterpret_cast(address), size}; + return 0; +} + std::uint64_t GuestImageSegmentBytes_nid_no_patch() { return GuestSegmentBytes(); } diff --git a/core/libs/tests/GuestModuleInfo.cpp b/core/libs/tests/GuestModuleInfo.cpp index edeeb42af..6c70763d2 100644 --- a/core/libs/tests/GuestModuleInfo.cpp +++ b/core/libs/tests/GuestModuleInfo.cpp @@ -19,6 +19,7 @@ int APS5_VABI sceKernelGetModuleInfo(KernelModule, ModuleInfo*); int APS5_VABI sceKernelGetModuleList2(KernelModule*, std::size_t, std::size_t*); int APS5_VABI sceKernelGetModuleInfo2(KernelModule, ModuleInfo*); std::int32_t APS5_VABI sceKernelGetLibkernelTextLocation_nid_postfix(void**, std::size_t*); +std::int32_t APS5_VABI sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(InternalSegmentInfo*); } static void Require(bool value) { if (!value) std::abort(); } template @@ -107,6 +108,10 @@ int main(int argc, char** argv) { Require(textSize == program.segments[0].size); Require(ThrowsInvalidArgument([&] { sceKernelGetLibkernelTextLocation_nid_postfix(nullptr, &textSize); })); Require(ThrowsInvalidArgument([&] { sceKernelGetLibkernelTextLocation_nid_postfix(&textAddress, nullptr); })); + InternalSegmentInfo segment{}; + Require(sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(&segment) == 0); + Require(segment.address == program.segments[0].address && segment.size == program.segments[0].size); + Require(ThrowsInvalidArgument([] { sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(nullptr); })); for (const std::uint64_t size : {0x158, 0x1a8, 0x1b0, 0x200, 0}) { ModuleInfo sized{}; sized.st_size = size; diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index b4dec2907..acb54d7c2 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -32,7 +32,7 @@ - [AMM PRT ranges](../../../core/libs/prx/libkernel/Apr/src/Apr.cpp) (libSceAmpr) - signatures, checks and their order taken from ampr_emu, where `MapAsPrt` is a map with the PRT flag and protection 0 and `AllocatePaForPrt` a memory type and protection change with mask 1019, both checking the buffer memory after its size; the effects are inferred from the names, not confirmed on a title. Unbacked PRT pages are zero-filled pages with GPU read access, so CPU reads see zeros instead of faulting and GPU writes fault instead of being dropped. `AllocatePaForPrt` backs each unbacked page with a pool page and sets the type and protection of backed ones; `RemapIntoPrt` moves a fully mapped range into a PRT range, its opcode argument (1011 when 0, as in ampr_emu) is recorded and ignored; `UnmapToPrt` returns the pages to the pool and zero-fills them again. A plain map, unmap or remap over a PRT range ends it; `AllocatePaForPrt`, `RemapIntoPrt` or `UnmapToPrt` outside a PRT range throws - [AMM kernel write commands](../../../core/libs/prx/libkernel/Apr/src/Apr.cpp) (libkernel) - signatures of `sceKernelWriteModifyMtypeProtectCommand` and `sceKernelWriteRemapIntoPrtCommand` taken from [ampr_emu's libkernel hook targets](https://github.com/drakmor/ampr_emu/blob/cfa85df379f6eeeb165d7badf9b648e266fe77b7/include/ampr_libkernel_hook_targets.h), which forward to the real kernel; the measure mode (`dst` null reports the size, `outSize` null skips the report) follows ampr_emu's measure-then-record calls, not confirmed on a title. The range, protection and mask checks mirror the `libSceAmpr` siblings, and the recorded bytes match them exactly so the submitted buffer executes on the existing paths - [sceCoredumpWriteUserString](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) and [sceSaveDataGetConvertProgress](../../../core/libs/prx/libSceSaveData.native/Export.cpp) - imported by PPSA16064, not called during its start-up and added without an implementation; unknown signatures -- [sceKernelInternalMemoryGetModuleSegmentInfo](../../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature +- [sceKernelInternalMemoryGetModuleSegmentInfo](../../../core/libs/prx/libkernel/Module/src/ModuleInfo.cpp) (libkernel) - the `(InternalSegmentInfo* result)` signature, the 16-byte `{address, size}` result and the choice of the main program's first segment follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_libkernel.pas#L343-L355) only, which fills it through its `sceKernelGetLibkernelTextLocation` from the first segment of `eboot.bin`; whether the console reports that segment, the text of libkernel itself or the calling module's is unknown. The segment is the first one `sceKernelGetModuleInfo` reports for the main program, and 0 is returned. A null result throws. 44 audited titles that bundle their own `libc.prx` import it; no call has been observed - [sceKernelGetModuleInfoForUnwind](../../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - on Linux the tables come from `sceKernelGetModuleInfoFromAddr`, whose `eh_frame_size` includes the zero terminator, as measured on firmware 13.60 (reported in [#2419](https://github.com/boykopovar/AnyPS5/issues/2419), not reproduced here); on Windows it is assumed to stop before the terminator. LLVM libunwind's `CFI_Parser::findFDE` stops at the terminator or the length, whichever comes first, so it finds the same FDEs either way. Only the main executable and `.guest.prx` images report tables; other host images report none, as on Windows - [sceKernelGetModuleInfoFromAddr](../../../core/libs/prx/libkernel/Module/src/ModuleInfo.cpp) (libkernel) - the 0x1A8-byte info layout follows the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation; a title's `libc.prx` reads only `id` (PPSA14632). The meaning of `flags` is unknown; only 2 is accepted. The input `st_size` is ignored, because PPSA21703's `libc.prx` passes an uninitialized 0x1A8-byte buffer; the full size is written back. The id is the `sceKernelLoadStartModule` handle of an image opened through the loader and otherwise a stable id from the same range. The name is the host file name without `.guest.prx`. An image with more than 4 load segments (the converted executable has 7) reports its first 4. `tls_offset` is 0 and `ref_count` is 1, as the host loader exposes neither. On Windows the module is the PE image holding the address: a relinked image reports its `.elf` sections as segments and the eh_frame_hdr recorded in `.ehmeta`, any other image its first 4 sections with memory access and the `.ehfram` section without an eh_frame_hdr; the TLS fields and `init_proc_addr`/`fini_proc_addr` are 0 - [sceKernelGetModuleList](../../../core/libs/prx/libkernel/Module/src/ModuleInfo.cpp) (libkernel) - lists the main program and every loaded `.prx` image in load order; host runtime libraries are left out. The handles are the ids `sceKernelGetModuleInfoFromAddr` reports, not the console's. The `(int32_t* handles, size_t count, size_t* actual)` signature and returning `ENOMEM` without writing `actual` after filling the first `count` handles when more images are loaded follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/kernel/process.cpp#L244-L264); whether the console writes any handles in that case is unknown