feat(libc): report heap backtraces and heap errors requested by the guest heap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
28c9dae73d
commit
d3ef2b30b7
|
|
@ -567,6 +567,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(log_macros PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
|
||||
add_test_executable(guest_heap_report_for_game_tests ${LIBS_INCLUDE_DIR}/tests/GuestHeapReportForGame.cpp)
|
||||
target_include_directories(guest_heap_report_for_game_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_heap_report_for_game_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_heap_report_for_game_tests)
|
||||
add_test(NAME guest_heap_report_for_game COMMAND guest_heap_report_for_game_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_heap_report_for_game PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
|
|
@ -28,18 +29,14 @@ void LibcHeapTraceInfo_nid_no_patch(LibcHeapInfo* info) {
|
|||
info->mstate_table = mstateTable.data();
|
||||
}
|
||||
|
||||
// unknown signature
|
||||
void APS5_VABI sceLibcInternalBacktraceForGame_nid_postfix(const char* heapName) {
|
||||
(void)heapName;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
std::fprintf(stderr, "[%s] backtrace requested from guest 0x%llx\n", heapName ? heapName : "",
|
||||
static_cast<unsigned long long>(reinterpret_cast<std::uintptr_t>(__builtin_return_address(0))));
|
||||
}
|
||||
|
||||
// unknown signature
|
||||
void APS5_VABI sceLibcInternalHeapErrorReportForGame_nid_postfix(void* heap, void* block, std::uint32_t error) {
|
||||
(void)heap;
|
||||
(void)block;
|
||||
(void)error;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
std::fprintf(stderr, "[libc] heap error 0x%x: heap %p, block %p, reported from guest 0x%llx\n", error, heap, block,
|
||||
static_cast<unsigned long long>(reinterpret_cast<std::uintptr_t>(__builtin_return_address(0))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
void APS5_VABI sceLibcInternalBacktraceForGame_nid_postfix(const char* heapName);
|
||||
void APS5_VABI sceLibcInternalHeapErrorReportForGame_nid_postfix(void* heap, void* block, std::uint32_t error);
|
||||
}
|
||||
|
||||
static void Require(bool value, const char* message) {
|
||||
if (value) return;
|
||||
std::fprintf(stdout, "%s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int main() {
|
||||
const auto path = std::filesystem::temp_directory_path() / ("anyps5-heap-report-" + std::to_string(std::rand()) + ".txt");
|
||||
Require(std::freopen(path.string().c_str(), "w", stderr) != nullptr, "stderr redirection failed");
|
||||
|
||||
char heapName[] = "SceLibcHeap";
|
||||
sceLibcInternalBacktraceForGame_nid_postfix(heapName);
|
||||
sceLibcInternalBacktraceForGame_nid_postfix(nullptr);
|
||||
int heap = 0;
|
||||
int block = 0;
|
||||
sceLibcInternalHeapErrorReportForGame_nid_postfix(&heap, &block, 0x1234u);
|
||||
std::fflush(stderr);
|
||||
std::fclose(stderr);
|
||||
|
||||
std::ifstream file(path);
|
||||
std::stringstream text;
|
||||
text << file.rdbuf();
|
||||
file.close();
|
||||
std::filesystem::remove(path);
|
||||
const std::string report = text.str();
|
||||
Require(report.find("[SceLibcHeap] backtrace requested from guest 0x") != std::string::npos, "heap name not reported");
|
||||
Require(report.find("[] backtrace requested from guest 0x") != std::string::npos, "null heap name not reported");
|
||||
Require(report.find("[libc] heap error 0x1234: heap ") != std::string::npos, "heap error not reported");
|
||||
Require(std::string(heapName) == "SceLibcHeap", "heap name modified");
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
- [nearbyintf](../../../core/libs/prx/libc/src/Math.cpp) (libc, NID `c+4r-T-tEIc`) uses the host rounding mode, not the console's; console rounding mode state remains unverified.
|
||||
- [_Thrd_sleep](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `jfRI3snge3o`) follows the Dinkumware threads library shipped with [Visual C++ 2013](https://github.com/adityaramesh/concurrency_runtime/tree/master/VS2013_SP2/crt/src/thr) (`xtime.c`) and C11 Annex K, without console verification. It reads a 16-byte xtime (64-bit seconds and nanoseconds) on the guest `CLOCK_REALTIME`, normalizes the nanoseconds and sleeps through `nanosleep`, retrying on `EINTR`, so it is a cancellation point; a null xtime or one whose normalization overflows throws. The console's `_Thrd_sleep` (disassembled from a title's `libc.prx`) also takes an optional `xtime*` of remaining time and returns `int`; here the sleep is never cut short, so it returns 0 and leaves the remaining time untouched.
|
||||
- [Guest arena](../../../core/libs/prx/libc/src/GuestArena.cpp) (libc) - the application map area is assumed to end at 0xFC_0000_0000, as `SCE_KERNEL_APP_MAP_AREA_END_ADDR` does on the PS4. On Windows, host allocations made in the arena before libc loads stay, and a fixed guest mapping over one throws
|
||||
- [sceLibcInternalBacktraceForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature
|
||||
- [sceLibcInternalHeapErrorReportForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature
|
||||
- [sceLibcInternalBacktraceForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - `(const char* heapName)` and its return follow the `libc.prx` ASTRO BOT (PPSA21564 01.007.000) bundles: its four calls (0x6801, 0x7F46, 0xE75E, 0xEE02) are in the `malloc_stats` and `sceLibcMspaceMallocStats` paths, each after a `[%s] ...` line printed with the same heap name buffer, and the caller ignores the result and continues. The console's backtrace output is not reproduced: the heap name and the guest return address are written to the host standard error
|
||||
- [sceLibcInternalHeapErrorReportForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - `(void* heap, void* block, uint32 error)` follows the same `libc.prx` (call at 0xF634): it passes the heap, the block and the nonzero result of its block check, and then calls a function that never returns, so the report only has to return. The meaning of the error values is unknown; the three values and the guest return address are written to the host standard error
|
||||
- [malloc_usable_size](../../../core/libs/prx/libc/src/ApplicationHeap.cpp) (libc) - with the default allocator it returns the size the block was requested with (the range registered for it), not the rounded-up chunk size Sony's allocator reports; both satisfy "at least the requested size", and bytes past the request would not be write-watched. With an application allocator it calls entry 9 of the replacement table (`malloc_usable_size` in KytyPS5's `MallocReplace` order) and throws when the table leaves it null, like `malloc_stats_fast`
|
||||
- [reallocf](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libSceLibcInternal, implemented in libc) - follows FreeBSD 11 `lib/libc/stdlib/reallocf.c` on top of this libc's `realloc`, not measured on the console: size 0 frees the block and returns null as `realloc` does here, so the block is not freed twice; when `realloc` fails because the application allocator returns null or the default heap rejects the size, the block is freed through the same allocator, `errno` is `ENOMEM` and null is returned. Other failures (an exhausted guest arena, an unknown pointer) throw, as they do for `realloc`
|
||||
- [acosl](../../../core/libs/prx/libc/src/Math.cpp) (libc) - computed by the host's math library, like the other libc math functions here, not by the console's (FreeBSD msun ld80 code in Sony's `libc.prx`): results can differ from the console's in the last bit, and on Linux glibc sets `errno` to `EDOM` for `acosl` outside [-1, 1] where msun only raises the floating-point exception. `long double` is the x87 80-bit format, as Sony's `frexpl` uses it (`fldt 0x8(%rsp)`, result in `st(0)`)
|
||||
|
|
|
|||
Loading…
Reference in New Issue