feat(libkernel): implement sceKernelGetModuleInfoFromAddr on Windows
Read the module layout from the PE image that holds the address. A relinked image reports its .elf sections as segments and the eh_frame_hdr recorded in .ehmeta; any other image reports its sections with memory access and its .ehfram section. The eh_frame_hdr and eh_frame helpers are shared with the Linux path, and the guest module info test now also runs on Windows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
311c0493bd
commit
77ef75c1da
|
|
@ -90,6 +90,7 @@ if(BUILD_TESTING)
|
|||
COMMAND $<TARGET_FILE:nid_patcher> guest_module_fixture $<TARGET_FILE:guest_module_fixture>
|
||||
VERBATIM)
|
||||
add_dependencies(guest_module_fixture nid_patcher)
|
||||
configure_windows_unwind(guest_module_fixture)
|
||||
set_target_properties(guest_module_fixture PROPERTIES BUILD_WITH_INSTALL_RPATH ON INSTALL_RPATH "$ORIGIN")
|
||||
find_package(Python3 QUIET COMPONENTS Interpreter)
|
||||
if(Python3_Interpreter_FOUND AND NOT WIN32)
|
||||
|
|
@ -109,14 +110,17 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_dynamic_loader PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
if(NOT WIN32)
|
||||
add_test_executable(guest_module_info_tests ${LIBS_INCLUDE_DIR}/tests/GuestModuleInfo.cpp)
|
||||
target_include_directories(guest_module_info_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_module_info_tests PRIVATE libkernel libc)
|
||||
add_dependencies(guest_module_info_tests guest_module_fixture)
|
||||
add_test(NAME guest_module_info COMMAND guest_module_info_tests $<TARGET_FILE_NAME:guest_module_fixture>)
|
||||
set_tests_properties(guest_module_info PROPERTIES
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:guest_module_fixture>")
|
||||
add_test_executable(guest_module_info_tests ${LIBS_INCLUDE_DIR}/tests/GuestModuleInfo.cpp)
|
||||
target_include_directories(guest_module_info_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_module_info_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_module_info_tests)
|
||||
add_dependencies(guest_module_info_tests guest_module_fixture)
|
||||
add_test(NAME guest_module_info COMMAND guest_module_info_tests $<TARGET_FILE_NAME:guest_module_fixture>)
|
||||
set_tests_properties(guest_module_info PROPERTIES
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:guest_module_fixture>")
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_module_info PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_filesystem_tests ${LIBS_INCLUDE_DIR}/tests/GuestFilesystem.cpp)
|
||||
target_include_directories(guest_filesystem_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,13 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/KernelErrors.hpp"
|
||||
#ifndef _WIN32
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <filesystem>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -18,12 +24,104 @@ extern "C" std::int32_t ModuleIdForImage_nid_no_patch(const void* native);
|
|||
|
||||
namespace {
|
||||
|
||||
#ifndef _WIN32
|
||||
constexpr char GuestModuleSuffix[] = ".guest.prx";
|
||||
constexpr std::int32_t ProtRead = 1;
|
||||
constexpr std::int32_t ProtWrite = 2;
|
||||
constexpr std::int32_t ProtExecute = 4;
|
||||
|
||||
std::uint32_t ToU32(std::uint64_t value, const char* field) {
|
||||
if (value > std::numeric_limits<std::uint32_t>::max())
|
||||
throw std::runtime_error(std::string("sceKernelGetModuleInfoFromAddr: ") + field + " exceeds 32 bits");
|
||||
return static_cast<std::uint32_t>(value);
|
||||
}
|
||||
|
||||
std::string ModuleName(std::string fileName) {
|
||||
if (fileName.size() > sizeof(GuestModuleSuffix) - 1 && fileName.ends_with(GuestModuleSuffix))
|
||||
fileName.resize(fileName.size() - (sizeof(GuestModuleSuffix) - 1));
|
||||
return fileName;
|
||||
}
|
||||
|
||||
void SetName(ModuleInfoEx& info, const std::string& name) {
|
||||
if (name.size() >= sizeof(info.name))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: module name too long: " + name);
|
||||
std::memcpy(info.name, name.c_str(), name.size() + 1);
|
||||
}
|
||||
|
||||
std::uint64_t EncodedSize(std::uint8_t encoding) {
|
||||
switch (encoding & 0x0fu) {
|
||||
case 0x00: case 0x04: case 0x0c: return 8;
|
||||
case 0x02: case 0x0a: return 2;
|
||||
case 0x03: case 0x0b: return 4;
|
||||
default: throw std::runtime_error("sceKernelGetModuleInfoFromAddr: unsupported eh_frame_hdr encoding");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TContains>
|
||||
std::uintptr_t EncodedValue(const TContains& contains, std::uintptr_t field, std::uint8_t encoding) {
|
||||
const auto size = EncodedSize(encoding);
|
||||
if (!contains(field, size)) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame_hdr outside the image");
|
||||
const auto* bytes = reinterpret_cast<const void*>(field);
|
||||
std::int64_t value = 0;
|
||||
switch (encoding & 0x0fu) {
|
||||
case 0x02: { std::uint16_t data; std::memcpy(&data, bytes, sizeof(data)); value = data; break; }
|
||||
case 0x0a: { std::int16_t data; std::memcpy(&data, bytes, sizeof(data)); value = data; break; }
|
||||
case 0x03: { std::uint32_t data; std::memcpy(&data, bytes, sizeof(data)); value = data; break; }
|
||||
case 0x0b: { std::int32_t data; std::memcpy(&data, bytes, sizeof(data)); value = data; break; }
|
||||
default: std::memcpy(&value, bytes, sizeof(value)); break;
|
||||
}
|
||||
switch (encoding & 0x70u) {
|
||||
case 0x00: return static_cast<std::uintptr_t>(value);
|
||||
case 0x10: return field + static_cast<std::uintptr_t>(value);
|
||||
default: throw std::runtime_error("sceKernelGetModuleInfoFromAddr: unsupported eh_frame_hdr encoding");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TContains>
|
||||
std::uintptr_t EhFrameAddress(const TContains& contains, std::uintptr_t header) {
|
||||
if (!contains(header, 4)) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame_hdr outside the image");
|
||||
const auto* bytes = reinterpret_cast<const std::uint8_t*>(header);
|
||||
if (bytes[0] != 1) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: unsupported eh_frame_hdr version");
|
||||
return EncodedValue(contains, header + 4, bytes[1]);
|
||||
}
|
||||
|
||||
template<typename TContains>
|
||||
std::uint64_t EhFrameHeaderSize(const TContains& contains, std::uintptr_t header) {
|
||||
const auto* bytes = reinterpret_cast<const std::uint8_t*>(header);
|
||||
std::uint64_t size = 4 + EncodedSize(bytes[1]);
|
||||
if (bytes[2] == 0xff) return size;
|
||||
const auto count = static_cast<std::uint64_t>(EncodedValue(contains, header + size, bytes[2] & 0x0fu));
|
||||
size += EncodedSize(bytes[2]);
|
||||
if (bytes[3] == 0xff) return size;
|
||||
const auto entry = 2 * EncodedSize(bytes[3]);
|
||||
if (count > (std::numeric_limits<std::uint64_t>::max() - size) / entry || !contains(header, size + count * entry))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame_hdr table outside the image");
|
||||
return size + count * entry;
|
||||
}
|
||||
|
||||
template<typename TContains>
|
||||
std::uint64_t EhFrameSize(const TContains& contains, std::uintptr_t frame) {
|
||||
std::uintptr_t position = frame;
|
||||
for (;;) {
|
||||
if (!contains(position, 4))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame outside the image");
|
||||
std::uint32_t length;
|
||||
std::memcpy(&length, reinterpret_cast<const void*>(position), sizeof(length));
|
||||
position += 4;
|
||||
if (length == 0) return position - frame;
|
||||
std::uint64_t recordLength = length;
|
||||
if (length == 0xffffffffu) {
|
||||
if (!contains(position, 8))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame outside the image");
|
||||
std::memcpy(&recordLength, reinterpret_cast<const void*>(position), sizeof(recordLength));
|
||||
position += 8;
|
||||
}
|
||||
if (!contains(position, recordLength))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame record outside the image");
|
||||
position += recordLength;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
struct ImageSearch {
|
||||
std::uintptr_t address;
|
||||
ModuleInfoEx* info;
|
||||
|
|
@ -40,63 +138,6 @@ bool Contains(const dl_phdr_info& image, std::uintptr_t begin, std::uint64_t siz
|
|||
return false;
|
||||
}
|
||||
|
||||
std::uint32_t ToU32(std::uint64_t value, const char* field) {
|
||||
if (value > std::numeric_limits<std::uint32_t>::max())
|
||||
throw std::runtime_error(std::string("sceKernelGetModuleInfoFromAddr: ") + field + " exceeds 32 bits");
|
||||
return static_cast<std::uint32_t>(value);
|
||||
}
|
||||
|
||||
std::uintptr_t EhFrameAddress(const dl_phdr_info& image, std::uintptr_t header) {
|
||||
if (!Contains(image, header, 8))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame_hdr outside the image");
|
||||
const auto* bytes = reinterpret_cast<const std::uint8_t*>(header);
|
||||
if (bytes[0] != 1) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: unsupported eh_frame_hdr version");
|
||||
const std::uintptr_t field = header + 4;
|
||||
switch (bytes[1]) {
|
||||
case 0x1b: {
|
||||
std::int32_t offset;
|
||||
std::memcpy(&offset, bytes + 4, sizeof(offset));
|
||||
return field + static_cast<std::intptr_t>(offset);
|
||||
}
|
||||
case 0x03: {
|
||||
std::uint32_t value;
|
||||
std::memcpy(&value, bytes + 4, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
case 0x00: {
|
||||
std::uint64_t value;
|
||||
if (!Contains(image, header, 12))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame_hdr outside the image");
|
||||
std::memcpy(&value, bytes + 4, sizeof(value));
|
||||
return static_cast<std::uintptr_t>(value);
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: unsupported eh_frame_ptr encoding");
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t EhFrameSize(const dl_phdr_info& image, std::uintptr_t frame) {
|
||||
std::uintptr_t position = frame;
|
||||
for (;;) {
|
||||
if (!Contains(image, position, 4))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame outside the image");
|
||||
std::uint32_t length;
|
||||
std::memcpy(&length, reinterpret_cast<const void*>(position), sizeof(length));
|
||||
position += 4;
|
||||
if (length == 0) return position - frame;
|
||||
std::uint64_t recordLength = length;
|
||||
if (length == 0xffffffffu) {
|
||||
if (!Contains(image, position, 8))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame outside the image");
|
||||
std::memcpy(&recordLength, reinterpret_cast<const void*>(position), sizeof(recordLength));
|
||||
position += 8;
|
||||
}
|
||||
if (!Contains(image, position, recordLength))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: eh_frame record outside the image");
|
||||
position += recordLength;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ImageName(const dl_phdr_info& image) {
|
||||
std::string path = image.dlpi_name ? image.dlpi_name : "";
|
||||
if (path.empty()) {
|
||||
|
|
@ -105,17 +146,12 @@ std::string ImageName(const dl_phdr_info& image) {
|
|||
if (length < 0) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: cannot resolve the executable path");
|
||||
path.assign(executable, static_cast<std::size_t>(length));
|
||||
}
|
||||
std::string name = path.substr(path.find_last_of('/') + 1);
|
||||
if (name.size() > sizeof(GuestModuleSuffix) - 1 && name.ends_with(GuestModuleSuffix))
|
||||
name.resize(name.size() - (sizeof(GuestModuleSuffix) - 1));
|
||||
return name;
|
||||
return ModuleName(path.substr(path.find_last_of('/') + 1));
|
||||
}
|
||||
|
||||
void Fill(const dl_phdr_info& image, ModuleInfoEx& info) {
|
||||
const auto name = ImageName(image);
|
||||
if (name.size() >= sizeof(info.name))
|
||||
throw std::runtime_error("sceKernelGetModuleInfoFromAddr: module name too long: " + name);
|
||||
std::memcpy(info.name, name.c_str(), name.size() + 1);
|
||||
const auto contains = [&](std::uintptr_t begin, std::uint64_t size) { return Contains(image, begin, size); };
|
||||
SetName(info, ImageName(image));
|
||||
info.tls_index = ToU32(image.dlpi_tls_modid, "TLS module index");
|
||||
for (std::uint16_t i = 0; i < image.dlpi_phnum; ++i) {
|
||||
const auto& header = image.dlpi_phdr[i];
|
||||
|
|
@ -133,8 +169,8 @@ void Fill(const dl_phdr_info& image, ModuleInfoEx& info) {
|
|||
} else if (header.p_type == PT_GNU_EH_FRAME) {
|
||||
info.eh_frame_hdr_addr = address;
|
||||
info.eh_frame_hdr_size = ToU32(header.p_memsz, "eh_frame_hdr size");
|
||||
info.eh_frame_addr = EhFrameAddress(image, address);
|
||||
info.eh_frame_size = ToU32(EhFrameSize(image, info.eh_frame_addr), "eh_frame size");
|
||||
info.eh_frame_addr = EhFrameAddress(contains, address);
|
||||
info.eh_frame_size = ToU32(EhFrameSize(contains, info.eh_frame_addr), "eh_frame size");
|
||||
} else if (header.p_type == PT_DYNAMIC) {
|
||||
for (const auto* entry = reinterpret_cast<const ElfW(Dyn)*>(address); entry->d_tag != DT_NULL; ++entry) {
|
||||
if (entry->d_tag == DT_INIT) info.init_proc_addr = image.dlpi_addr + entry->d_un.d_ptr;
|
||||
|
|
@ -152,6 +188,76 @@ int FindImage(dl_phdr_info* image, std::size_t, void* data) {
|
|||
search.found = true;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
bool SectionNamed(const IMAGE_SECTION_HEADER& section, const char* name) {
|
||||
return std::strncmp(reinterpret_cast<const char*>(section.Name), name, IMAGE_SIZEOF_SHORT_NAME) == 0;
|
||||
}
|
||||
|
||||
std::int32_t SectionProtection(const IMAGE_SECTION_HEADER& section) {
|
||||
const auto characteristics = section.Characteristics;
|
||||
return ((characteristics & IMAGE_SCN_MEM_READ) ? ProtRead : 0) | ((characteristics & IMAGE_SCN_MEM_WRITE) ? ProtWrite : 0) | ((characteristics & IMAGE_SCN_MEM_EXECUTE) ? ProtExecute : 0);
|
||||
}
|
||||
|
||||
std::string ImageName(HMODULE module) {
|
||||
std::wstring path(MAX_PATH, L'\0');
|
||||
for (;;) {
|
||||
const auto length = GetModuleFileNameW(module, path.data(), static_cast<DWORD>(path.size()));
|
||||
if (length == 0) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: cannot resolve the module path");
|
||||
if (length < path.size()) {
|
||||
path.resize(length);
|
||||
break;
|
||||
}
|
||||
path.resize(path.size() * 2);
|
||||
}
|
||||
const auto fileName = std::filesystem::path(path).filename().u8string();
|
||||
return ModuleName(std::string(fileName.begin(), fileName.end()));
|
||||
}
|
||||
|
||||
bool Fill(std::uintptr_t address, ModuleInfoEx& info) {
|
||||
MEMORY_BASIC_INFORMATION memory{};
|
||||
if (VirtualQuery(reinterpret_cast<LPCVOID>(address), &memory, sizeof(memory)) != sizeof(memory) || memory.Type != MEM_IMAGE) return false;
|
||||
const auto base = reinterpret_cast<std::uintptr_t>(memory.AllocationBase);
|
||||
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false;
|
||||
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS64*>(base + static_cast<std::uintptr_t>(dos->e_lfanew));
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE || nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) return false;
|
||||
const std::uint64_t imageSize = nt->OptionalHeader.SizeOfImage;
|
||||
const auto contains = [&](std::uintptr_t begin, std::uint64_t size) { return begin >= base && begin - base <= imageSize && size <= imageSize - (begin - base); };
|
||||
SetName(info, ImageName(reinterpret_cast<HMODULE>(base)));
|
||||
const auto* sections = IMAGE_FIRST_SECTION(nt);
|
||||
const auto sectionCount = nt->FileHeader.NumberOfSections;
|
||||
bool relinked = false;
|
||||
for (unsigned i = 0; i < sectionCount; ++i) relinked = relinked || SectionNamed(sections[i], ".elf0");
|
||||
for (unsigned i = 0; i < sectionCount; ++i) {
|
||||
const auto& section = sections[i];
|
||||
const auto start = base + section.VirtualAddress;
|
||||
if (SectionNamed(section, ".ehmeta")) {
|
||||
if (!contains(start, 4)) throw std::runtime_error("sceKernelGetModuleInfoFromAddr: .ehmeta outside the image");
|
||||
std::uint32_t headerRva;
|
||||
std::memcpy(&headerRva, reinterpret_cast<const void*>(start), sizeof(headerRva));
|
||||
info.eh_frame_hdr_addr = base + headerRva;
|
||||
info.eh_frame_addr = EhFrameAddress(contains, info.eh_frame_hdr_addr);
|
||||
info.eh_frame_hdr_size = ToU32(EhFrameHeaderSize(contains, info.eh_frame_hdr_addr), "eh_frame_hdr size");
|
||||
info.eh_frame_size = ToU32(EhFrameSize(contains, info.eh_frame_addr), "eh_frame size");
|
||||
continue;
|
||||
}
|
||||
if (SectionNamed(section, ".ehfram") && info.eh_frame_addr == 0) {
|
||||
info.eh_frame_addr = start;
|
||||
info.eh_frame_size = ToU32(EhFrameSize(contains, start), "eh_frame size");
|
||||
continue;
|
||||
}
|
||||
if (relinked && std::strncmp(reinterpret_cast<const char*>(section.Name), ".elf", 4) != 0) continue;
|
||||
const auto prot = SectionProtection(section);
|
||||
if (prot == 0 || info.segment_count >= std::size(info.segments)) continue;
|
||||
auto& segment = info.segments[info.segment_count++];
|
||||
segment.address = start;
|
||||
segment.size = ToU32(section.Misc.VirtualSize, "segment size");
|
||||
segment.prot = prot;
|
||||
}
|
||||
info.id = ModuleIdForImage_nid_no_patch(reinterpret_cast<const void*>(base));
|
||||
info.ref_count = 1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -163,24 +269,22 @@ int APS5_VABI sceKernelGetModuleInfoFromAddr(std::uint64_t address, int flags, M
|
|||
if (flags != 2) throw std::invalid_argument("sceKernelGetModuleInfoFromAddr: unsupported flags " + std::to_string(flags));
|
||||
if (info->st_size != sizeof(ModuleInfoEx))
|
||||
throw std::invalid_argument("sceKernelGetModuleInfoFromAddr: unsupported st_size " + std::to_string(info->st_size));
|
||||
ModuleInfoEx result{};
|
||||
result.st_size = sizeof(ModuleInfoEx);
|
||||
#ifdef _WIN32
|
||||
(void)address;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!Fill(static_cast<std::uintptr_t>(address), result)) return SCE_KERNEL_ERROR_ESRCH;
|
||||
#else
|
||||
Dl_info symbol{};
|
||||
link_map* native = nullptr;
|
||||
if (!dladdr1(reinterpret_cast<const void*>(address), &symbol, reinterpret_cast<void**>(&native), RTLD_DL_LINKMAP) || !native)
|
||||
return SCE_KERNEL_ERROR_ESRCH;
|
||||
ModuleInfoEx result{};
|
||||
result.st_size = sizeof(ModuleInfoEx);
|
||||
ImageSearch search{static_cast<std::uintptr_t>(address), &result, false};
|
||||
dl_iterate_phdr(FindImage, &search);
|
||||
if (!search.found) return SCE_KERNEL_ERROR_ESRCH;
|
||||
result.id = ModuleIdForImage_nid_no_patch(native);
|
||||
#endif
|
||||
*info = result;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ int main(int argc, char** argv) {
|
|||
executable = (segment.prot & 5) == 5;
|
||||
}
|
||||
Require(executable);
|
||||
Require(info.eh_frame_hdr_addr != 0 && info.eh_frame_hdr_size != 0 && info.eh_frame_addr != 0 && info.eh_frame_size != 0);
|
||||
Require(info.eh_frame_addr != 0 && info.eh_frame_size != 0);
|
||||
#ifndef _WIN32
|
||||
Require(info.eh_frame_hdr_addr != 0 && info.eh_frame_hdr_size != 0);
|
||||
#endif
|
||||
Require(info.init_proc_addr == 0 || info.init_proc_addr >= info.segments[0].address);
|
||||
const auto self = Query(reinterpret_cast<const void*>(&Require), 0);
|
||||
Require(self.id != info.id && Query(reinterpret_cast<const void*>(&Query), 0).id == self.id);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceAgcDcbQueueEndOfShaderActionGetSize](../../core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp) (libSceAgc) - the builder is inline in the SDK and not exported; the size is assumed to be that of the RELEASE_MEM packet `sceAgcCbReleaseMem` writes (32 bytes), the same as `sceAgcAcbQueueEndOfShaderActionGetSize`, as in [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [REmu](https://github.com/xnetcat/REmu/blob/main/crates/remu-runtime/src/agc_dcb.rs)
|
||||
- [sceKernelInternalMemoryGetModuleSegmentInfo](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature
|
||||
- [sceKernelGetModuleInfoForUnwind](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - `eh_frame_size` is assumed to cover the records up to, not including, the zero 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; what the console reports is not known.
|
||||
- [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, as is only the full `st_size`. 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. Not implemented 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, as is only the full `st_size`. 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
|
||||
- [sceKernelReleaseFlexibleMemory](../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - unmaps the range like sceKernelMunmap, as fpPS4 and prosper do; whether the console keeps the virtual range reserved after releasing its pages is unverified
|
||||
- [Memory protection bits 0x100 and 0x200](../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - taken as audio coprocessor (ACP) read and write, as in the [KytyPS5](https://github.com/KytyPS5/KytyPS5) and [etaHEN](https://github.com/etaHEN/etaHEN) headers; the coprocessor work runs on the host CPU, so they grant host read and read/write access like the GPU bits
|
||||
- [sceKernelSyncOnAddressWait](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored
|
||||
|
|
|
|||
Loading…
Reference in New Issue