feat(libc): implement malloc_usable_size
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aac0f1b810
commit
859f2f3b92
|
|
@ -16,6 +16,7 @@ void* ApplicationHeapRealign_nid_no_patch(void* pointer, std::size_t bytes, std:
|
|||
void* ApplicationHeapCalloc_nid_no_patch(std::size_t count, std::size_t bytes);
|
||||
int ApplicationHeapPosixAlign_nid_no_patch(void** pointer, std::size_t alignment, std::size_t bytes);
|
||||
int ApplicationHeapStatsFast_nid_no_patch(void* stats);
|
||||
std::size_t ApplicationHeapUsableSize_nid_no_patch(void* pointer);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ namespace GuestHeap {
|
|||
|
||||
extern "C" {
|
||||
|
||||
std::size_t GuestHeapUsableSize_nid_postfix(const void* pointer);
|
||||
void* GuestHeapAllocate_nid_postfix(std::size_t bytes);
|
||||
void GuestHeapFree_nid_postfix(void* pointer);
|
||||
void* GuestHeapReallocate_nid_postfix(void* pointer, std::size_t bytes);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ using Realign = void* (APS5_VABI *)(void*, std::size_t, std::size_t);
|
|||
using PosixAlign = int (APS5_VABI *)(void**, std::size_t, std::size_t);
|
||||
using Initialize = void (APS5_VABI *)();
|
||||
using Stats = int (APS5_VABI *)(void*);
|
||||
using UsableSize = std::size_t (APS5_VABI *)(void*);
|
||||
|
||||
std::mutex heapMutex;
|
||||
std::array<void*, 10> heapApi{};
|
||||
|
|
@ -249,3 +250,20 @@ int ApplicationHeapStatsFast_nid_no_patch(void* stats) {
|
|||
CallbackScope scope;
|
||||
return statsFast(stats);
|
||||
}
|
||||
|
||||
std::size_t ApplicationHeapUsableSize_nid_no_patch(void* pointer) {
|
||||
UsableSize usableSize = nullptr;
|
||||
{
|
||||
std::lock_guard lock(heapMutex);
|
||||
if (heapFailure) std::rethrow_exception(heapFailure);
|
||||
if (heapFinalized) throw std::runtime_error("application heap: allocator has been finalized");
|
||||
if (heapApi[0] == nullptr) throw std::runtime_error("application heap: allocator API is not registered");
|
||||
if (heapApi != defaultApi()) {
|
||||
if (heapApi[9] == nullptr) throw std::runtime_error("malloc_usable_size: implemented only for an application allocator that provides it");
|
||||
std::memcpy(&usableSize, &heapApi[9], sizeof(usableSize));
|
||||
}
|
||||
}
|
||||
if (usableSize == nullptr) return GuestHeap::GuestHeapUsableSize_nid_postfix(pointer);
|
||||
CallbackScope scope;
|
||||
return usableSize(pointer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,10 @@ int APS5_VABI malloc_stats_fast_nid_postfix(void* stats) {
|
|||
return ApplicationHeapStatsFast_nid_no_patch(stats);
|
||||
}
|
||||
|
||||
size_t APS5_VABI malloc_usable_size_nid_postfix(void* pointer) {
|
||||
return ApplicationHeapUsableSize_nid_no_patch(pointer);
|
||||
}
|
||||
|
||||
void* APS5_VABI malloc_nid_postfix(size_t size) {
|
||||
return ApplicationHeapAllocate_nid_no_patch(size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,6 +263,12 @@ void* GuestHeapRealign_nid_postfix(void* pointer, std::size_t bytes, std::size_t
|
|||
return result;
|
||||
}
|
||||
|
||||
std::size_t GuestHeapUsableSize_nid_postfix(const void* pointer) {
|
||||
if (pointer == nullptr) return 0;
|
||||
GuestAllocations::Mutation mutation;
|
||||
return mutation.Find(pointer).bytes;
|
||||
}
|
||||
|
||||
void* GuestHeapAlign_nid_postfix(std::size_t alignment, std::size_t bytes) {
|
||||
GuestAllocations::Mutation mutation;
|
||||
return allocate(mutation, alignment, bytes);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
extern "C" {
|
||||
int APS5_VABI malloc_stats_fast_nid_postfix(void*);
|
||||
std::size_t APS5_VABI malloc_usable_size_nid_postfix(void*);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
@ -46,6 +47,15 @@ int APS5_VABI statsFast(void* stats) {
|
|||
return 0x2a;
|
||||
}
|
||||
|
||||
unsigned usableCalls = 0;
|
||||
void* lastUsable = nullptr;
|
||||
|
||||
std::size_t APS5_VABI usableSize(void* pointer) {
|
||||
++usableCalls;
|
||||
lastUsable = pointer;
|
||||
return 0x55;
|
||||
}
|
||||
|
||||
template<typename TValue, std::size_t TSize>
|
||||
void write(std::array<std::byte, TSize>& data, std::size_t offset, TValue value) {
|
||||
require(offset <= data.size() && sizeof(value) <= data.size() - offset);
|
||||
|
|
@ -75,19 +85,29 @@ int main(int argc, char** argv) {
|
|||
write(replacement, 0x48, &realign);
|
||||
write(replacement, 0x50, &posixAlign);
|
||||
write(replacement, 0x60, &statsFast);
|
||||
write(replacement, 0x68, &usableSize);
|
||||
if (argc > 1 && std::strcmp(argv[1], "default") == 0) {
|
||||
std::memset(replacement.data() + 0x20, 0, 10 * sizeof(void*));
|
||||
ApplicationHeapInitialize_nid_no_patch(process.data());
|
||||
require(ApplicationHeapAllocate_nid_no_patch(16) != nullptr);
|
||||
reject([&] { malloc_stats_fast_nid_postfix(stats.data()); });
|
||||
auto* small = ApplicationHeapAllocate_nid_no_patch(16);
|
||||
auto* large = ApplicationHeapAllocate_nid_no_patch(100000);
|
||||
require(malloc_usable_size_nid_postfix(small) == 16 && malloc_usable_size_nid_postfix(large) == 100000);
|
||||
require(malloc_usable_size_nid_postfix(nullptr) == 0 && usableCalls == 0);
|
||||
ApplicationHeapFree_nid_no_patch(small);
|
||||
ApplicationHeapFree_nid_no_patch(large);
|
||||
return 0;
|
||||
}
|
||||
if (argc > 1 && std::strcmp(argv[1], "missing") == 0) {
|
||||
write(replacement, 0x60, static_cast<void*>(nullptr));
|
||||
write(replacement, 0x68, static_cast<void*>(nullptr));
|
||||
ApplicationHeapInitialize_nid_no_patch(process.data());
|
||||
require(ApplicationHeapAllocate_nid_no_patch(16) == storage.data());
|
||||
reject([&] { malloc_stats_fast_nid_postfix(stats.data()); });
|
||||
require(statsCalls == 0);
|
||||
reject([&] { malloc_usable_size_nid_postfix(storage.data()); });
|
||||
reject([&] { malloc_usable_size_nid_postfix(nullptr); });
|
||||
require(statsCalls == 0 && usableCalls == 0);
|
||||
return 0;
|
||||
}
|
||||
ApplicationHeapInitialize_nid_no_patch(process.data());
|
||||
|
|
@ -99,4 +119,6 @@ int main(int argc, char** argv) {
|
|||
require(statsCalls == 3);
|
||||
require(malloc_stats_fast_nid_postfix(stats.data()) == 0x2a && statsCalls == 4);
|
||||
require(ApplicationHeapAllocate_nid_no_patch(16) == storage.data());
|
||||
require(malloc_usable_size_nid_postfix(storage.data()) == 0x55 && usableCalls == 1 && lastUsable == storage.data());
|
||||
require(malloc_usable_size_nid_postfix(nullptr) == 0x55 && usableCalls == 2 && lastUsable == nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [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
|
||||
- [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`
|
||||
- [_sceLibcInternalThreadAtexit, _sceLibcInternalThreadDtors](../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - FreeBSD `__cxa_thread_atexit` semantics assumed; a null or non-image destructor and an allocation failure throw
|
||||
- [_sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement](../../core/libs/prx/libkernel/System/src/Rtld.cpp) (libkernel) - the argument is the `dso` address a title's `libc.prx` passes to `__cxa_thread_atexit` (PPSA14632), which fails when Increment returns nonzero. As in the [fpPS4](https://github.com/red-prig/fpPS4) reimplementation, Increment takes a reference on the loaded image containing the address and Decrement releases one; the variants without the leading underscore are assumed to match
|
||||
- [sceImeGetPanelPositionAndForm](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the output is taken to be the `PositionAndForm` that `sceImeDialogGetPanelPositionAndForm` fills; it is never written, since no panel can be open. That a closed panel is reported before a null output is assumed by analogy with `sceImeSetCaret`, not confirmed on a PS5 title
|
||||
|
|
|
|||
Loading…
Reference in New Issue