Skip to content

feat(libc): provide the runtime scripting allocator hooks - #5092

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
MrMha:fix/scripting-heap-694
Oct 15, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
MrMha:fix/scripting-heap-694

Conversation

@MrMha

@MrMha MrMha commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

Add the runtime allocator hooks requested in #694: scriptingGetMem(alignment, size) calls the application allocator's aligned allocation, and scriptingFreeMem(pointer) frees through that same allocator. Alignments below 16 normalize to 16; larger non-powers of two return null. Null free is a no-op; allocation failure represented by std::bad_alloc returns null; other allocator errors propagate.

The signatures and source-based alignment contract remain documented in libc technical debt. NIDs: scriptingGetMem → ayuoL6Vjz2k; scriptingFreeMem → yV45DG6ei28. This supplies the hooks, but does not claim complete resolution of the title's process-scope lookup path.

Reapplied on main 2e5dd5aa144af1bf90a214a6341bf3e1b4e6d47a. Retained the newly merged fdopen debt entry and added the scripting entry beside it; production and test changes apply cleanly.

Tested

Fresh local checks on macOS 27.0.1 arm64, AppleClang 21, C++20, -Wall -Wextra -Werror:

  • Linked the actual updated production ApplicationHeap.cpp with a controlled registered allocator. Native and ASan/UBSan executions pass eight normalization/alignment cases, writable storage, matching/null free, three invalid alignments without callback invocation, null/bad_alloc allocation failures, and propagation of callback/misalignment errors.
  • Default guest-heap and process-parameter stand-ins abort if reached. This checks the registered custom-allocator path only, not the default guest arena or the full libc library.
  • The updated repository core/libs/tests/ApplicationHeap.cpp passes -fsyntax-only.
  • Negative control: the same harness fails to link with unchanged main's ApplicationHeap.cpp because both scripting hooks are absent.
  • The trusted main conventions checker accepts the rebased change, title and completed template; whitespace checks pass.

Earlier Linux full linked regression evidence from @coltken95 at old head a4e1104 remains in the discussion; it was not rerun on this new head. Full runtime build/CTest, patched-libc process-scope lookup and title execution remain unverified on this head and need the maintainer-dispatched full build.

Manual review of the bot-listed shared hunks found other libc debt entries, #4927's getcwd assertion, #6217's default-heap statistics wiring and #6044's test relocation/labels. None adds these scripting hooks. If #6044 lands first, preserve the scripting assertions at core/libs/tests/memory/ApplicationHeap.cpp. These are source comparisons, not combined runtime validation.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes (OpenAI Codex)

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Progress report (8dc92ae → aea1dd6)

➖ System libraries: 88.7% (+0.0%, +2 implemented functions)

✅ 2 implemented
Library Name
libc scriptingFreeMem_nid_postfix
libc scriptingGetMem_nid_postfix

@adriadam10

Copy link
Copy Markdown
Collaborator

Ready to merge from my side, but the PR description fails the conventions check:

  • [pr-template] Fill in the pull request template: unchecked: Based on current main; no other open PR implements the same functions (see outstanding work above)

Please fill those template lines in (only you can answer AI-assisted); it merges once they pass.

@coltken95

Copy link
Copy Markdown

Tested on Linux x86-64 (Release build, CTest), with a4e1104 merged into main 9d0a41d:

  • With the PR: application_heap_replacement, application_heap_invalid_metadata, application_heap_default and application_heap_exit_order all pass.
  • With main's libc sources the test executable doesn't build (the hooks don't exist there), so none of the four can run.

AI-assisted: yes (Claude Code)

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3987 docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 5835 docs/dev/debt/libc.md
PR 6044 core/libs/tests/ApplicationHeap.cpp
PR 6222 docs/dev/debt/libc.md
PR 6315 docs/dev/debt/libc.md
PR 6318 docs/dev/debt/libc.md

Use the registered application allocator and the source-backed alignment/size signature. Add callback ownership and failure checks; record unmeasured console behavior.
@MrMha
MrMha force-pushed the fix/scripting-heap-694 branch from a4e1104 to aea1dd6 Compare October 15, 2026 13:21
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 15, 2026
@MrMha

MrMha commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main 2e5dd5aa144af1bf90a214a6341bf3e1b4e6d47a as aea1dd601afd98233c22eb45131a80306b00976b. The documentation conflict is resolved by retaining both the merged fdopen note and the scripting-hook note. The PR description/template now reflects this head and the current test limitations.

Fresh macOS 27.0.1 arm64 / AppleClang 21 / C++20 checks: the actual production ApplicationHeap.cpp linked to the controlled registered-allocator harness below passes natively and under ASan/UBSan. It checks eight normalization/alignment values, writable storage, matching/null free, three invalid alignments without calling the allocator, null and bad_alloc failures, and propagation of callback/misalignment errors. The updated repository ApplicationHeap.cpp test also passes -fsyntax-only. With unchanged main's production file, the same harness fails to link because both hooks are absent. Trusted main conventions and whitespace checks pass.

The aborting stand-ins below deliberately exclude default guest-heap/process-parameter execution: this is not a full libc or patched-export lookup test. The rebased head still needs the linked application_heap_* runtime tests through the maintainer-dispatched full Build workflow before claiming full runtime validation. The older Linux linked-test result in this discussion is historical, not a fresh result on this head.

Manually inspected the bot-listed shared hunks: the debt changes are other entries; #4927 changes getcwd expectations, #6217 wires default-heap statistics, and #6044 relocates/labels tests. None implements the scripting hooks. Preserve our assertions at the moved memory test path if #6044 lands first. No combined-runtime validation is claimed.

Reproduce the focused custom-allocator check

Save the following as allocator_harness.cpp outside the repository. From this checkout on macOS, build with AppleClang 21 (add the SDK libc++ include path if the local toolchain requires it):

clang++ -std=c++20 -Wall -Wextra -Werror -Icore/libs core/libs/prx/libc/src/ApplicationHeap.cpp /path/to/allocator_harness.cpp -o /tmp/allocator-check
/tmp/allocator-check
clang++ -std=c++20 -Wall -Wextra -Werror -fsanitize=address,undefined -fno-omit-frame-pointer -Icore/libs core/libs/prx/libc/src/ApplicationHeap.cpp /path/to/allocator_harness.cpp -o /tmp/allocator-sanitized
/tmp/allocator-sanitized
#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libc/include/GuestHeap.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <new>
#include <stdexcept>
extern "C" void* scriptingGetMem_nid_postfix(std::size_t, std::size_t);
extern "C" void scriptingFreeMem_nid_postfix(void*);
extern "C" const void* ApplicationProcessParameters_nid_no_patch() { std::abort(); }
namespace GuestHeap {
extern "C" void* GuestHeapAllocate_nid_postfix(std::size_t) { std::abort(); }
extern "C" void GuestHeapFree_nid_postfix(void*) { std::abort(); }
extern "C" void* GuestHeapReallocate_nid_postfix(void*, std::size_t) { std::abort(); }
extern "C" void* GuestHeapAlign_nid_postfix(std::size_t, std::size_t) { std::abort(); }
extern "C" void* GuestHeapRealign_nid_postfix(void*, std::size_t, std::size_t) { std::abort(); }
extern "C" std::size_t GuestHeapUsableSize_nid_postfix(const void*) { std::abort(); }
}
namespace {
alignas(4096) std::array<unsigned char,8192> storage{};
std::size_t seenAlignment=0, seenSize=0;
unsigned calls=0, frees=0;
int failure=0;
void require(bool value) { if (!value) throw std::runtime_error("allocator assertion failed"); }
void* align(std::size_t alignment, std::size_t size) {
 ++calls; seenAlignment=alignment; seenSize=size;
 if(failure==1) return nullptr;
 if(failure==2) throw std::bad_alloc();
 if(failure==3) throw std::runtime_error("callback failure");
 if(failure==4) return storage.data()+1;
 return storage.data();
}
void* allocate(std::size_t bytes) { return align(16,bytes); }
void release(void* pointer) { require(pointer==storage.data()); ++frees; }
void* zeroAllocate(std::size_t count,std::size_t bytes) { return allocate(count*bytes); }
void* resize(void*,std::size_t bytes) { return allocate(bytes); }
void* realign(void*,std::size_t bytes,std::size_t alignment) { return align(alignment,bytes); }
int posix(void** pointer,std::size_t alignment,std::size_t bytes) { *pointer=align(alignment,bytes);return 0; }
}
int main() {
 scriptingFreeMem_nid_postfix(nullptr);
 const std::array<void*,10> api{reinterpret_cast<void*>(&allocate),reinterpret_cast<void*>(&release),reinterpret_cast<void*>(&zeroAllocate),reinterpret_cast<void*>(&resize),reinterpret_cast<void*>(&align),reinterpret_cast<void*>(&realign),reinterpret_cast<void*>(&posix)};
 ApplicationHeapRegister_nid_no_patch(api.data());
 for(const std::size_t alignment: {0,1,3,15,16,32,64,256}) {
  auto* pointer=static_cast<unsigned char*>(scriptingGetMem_nid_postfix(alignment,37));
  require(pointer==storage.data() && seenAlignment==(alignment<16?16:alignment) && seenSize==37);
  std::memset(pointer,0xa5,37); scriptingFreeMem_nid_postfix(pointer);
 }
 require(frees==8);
 auto before=calls;
 for(const std::size_t alignment: {24,48,1280}) require(scriptingGetMem_nid_postfix(alignment,37)==nullptr);
 require(calls==before);
 scriptingFreeMem_nid_postfix(nullptr);require(frees==8);
 for(failure=1;failure<=2;++failure) require(scriptingGetMem_nid_postfix(16,37)==nullptr);
 for(failure=3;failure<=4;++failure) {
  bool threw=false; try { scriptingGetMem_nid_postfix(16,37); } catch(const std::runtime_error&) { threw=true; }
  require(threw);
 }
 std::cout << "8 normalization/alignment cases, 3 invalid alignments, matching/null release, null/bad_alloc failure and 2 propagated errors pass\n";
}

AI-assisted: yes (OpenAI Codex).

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked on the current head: build + ctest of core/libs (105/105, incl. the eight application_heap_* tests), and the new assertions fail to link without the change. scriptingGetMem matches KytyPS5's KernelApplicationHeapGetMem (alignment < 16 raised to 16, non-power-of-two returns null) and routes through the registered application allocator; scriptingFreeMem handles null. The unverified signature of the free hook is recorded in the libc debt file. Merging once the full Build is green.

@coltken95

Copy link
Copy Markdown

Tested on Linux x86-64 (Release build, CTest), with aea1dd6 merged into main 44df216. This replaces my earlier result at a4e1104:

  • With the PR: application_heap_replacement, application_heap_invalid_metadata, application_heap_default, application_heap_exit_order and application_heap_lazy pass.
  • With main's libc/src/ApplicationHeap.cpp: application_heap_tests doesn't link (undefined reference to 'scriptingGetMem_nid_postfix' and 'scriptingFreeMem_nid_postfix'), since main doesn't provide the hooks.

AI-assisted: yes (Claude Code)

@adriadam10
adriadam10 merged commit 7663bc6 into boykopovar:main Oct 15, 2026
14 of 15 checks passed
@milescoviello

Copy link
Copy Markdown
Contributor

Heads-up @MrMha: since this merged, Metal Gear Solid Master Collection (PPSA18194) aborts 0 s in on main. The title ships its own libc.prx, which has already initialized its allocator. IL2CPP's first scriptingGetMem call then makes the application heap's lazy initialization run the title's allocator initializer a second time, and Unity's dlmalloc aborts on its next free. #6842 keeps the hooks but serves them from libc's own guest heap. The details and runs are there.

AI-assisted: yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants