Skip to content

fix(libc): return the calling thread from _Thrd_id as the console's libc.prx does - #9170

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
rooklz:fix/libc-thrd-id
Oct 25, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
rooklz:fix/libc-thrd-id

Conversation

@rooklz

@rooklz rooklz commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

_Thrd_id (np6xXcXEnXE) threw on main because its return value wasn't known. Two people have since read its body in the console's libc.prx, and both found the same thing:

Earlier Animal Well (PPSA22520, #966) reports stopped at _Thrd_id_nid_postfix not implemented; a retest on 2026-10-22 on unmodified main 7e2ba73e4c reaches gameplay without this change, so this PR no longer claims to unblock that title. It stays a fix for any title that calls the export, as PPSA23827 does.

MarcZX10 asked on #4164 for a focused follow-up once the libc export location was settled, which #8148 did. So _Thrd_id now returns ThreadForwards(__func__).self(), the same scePthreadSelf that libkernel registers and that _Thrd_current already returns. It hands back the full pointer-width value, as the console body does (RAX unchanged), so a caller that only reads EAX gets the low half either way. With no thread forwards registered it throws, like _Thrd_current.

The libc debt entry for std::_Pad, _Thrd_join, _Thrd_detach, _Thrd_id now says what _Thrd_id returns and where that comes from (a reading of the body, not a measurement) instead of why it throws.

Tested

  • @edubox found that title libc.prx builds forward the export the same way: in Cyberpunk 2077 (PPSA04029), np6xXcXEnXE at 0x4bb0 jumps through the GOT entry relocated to scePthreadSelf (aI+OeCz8xrQ), PPSA02530 has the same shape at 0x4e20, and the title's libREDGalaxy64.prx imports np6xXcXEnXE. They also rebased and verified the change on main f26434afa3 (Linux, GCC 16.2.1).

Ubuntu 24.04 x86-64 container (GCC 13.3, Release) under Rosetta on an Apple M5 Mac.

  • guest_libc_threads gets three checks: _Thrd_id() equals scePthreadSelf() with libkernel's forwards, returns the fake self result when the test registers its own forwards, and throws once they're unregistered.
  • With main's Threads.cpp, guest_libc_threads aborts with what(): _Thrd_id_nid_postfix not implemented. With this change it passes.
  • tools/progress.py --module libc: 1217/1225 before, 1218/1225 after.
  • check_conventions.py --base origin/main passes.

I don't have Animal Well, so I haven't run it. #8636 reports that on main "the title runs until _Thrd_id_nid_postfix not implemented", which is the throw this removes; what it hits next is unknown to me.

Hosted runs on my fork (GitHub runners), this branch ce50ab75a and its base e072e8840:

  • Ubuntu 24.04 x86-64, GCC 13.3, full build with the CI configure flags, tests on lavapipe: 1805/1809 with the change, 1806/1809 on the base. The three net_dontwait_blocking_* tests fail on both. agc_driver_sgpr_pattern_fill_keys failed on one side only; it is intermittent under parallel ctest (agc_driver_sgpr_pattern_fill_keys times out under parallel ctest load #8708).
  • Windows Server 2025, WinLibs GCC 15.2.0, full build: it builds, and guest_libc_threads passes (1/1; 1/1 on the base).

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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Progress report (2e22b67 → 0297073)

➖ System libraries: 48.8% (+0.0%, +1 implemented functions)

✅ 1 implemented
Library Name
libc _Thrd_id_nid_postfix

Changed libraries (implemented exports)

Library Before After Change
libc 1338/1348 (99.26%) 1339/1348 (99.33%) +0.07 pp

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 10615 docs/dev/debt/libc.md L171
PR 10648 docs/dev/debt/libc.md L171
PR 10749 docs/dev/debt/libc.md L171-172

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 1835 core/libs/prx/libc/src/Threads.cpp
PR 3965 docs/dev/debt/libc.md
PR 5021 core/libs/prx/libc/src/Threads.cpp
docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6713 docs/dev/debt/libc.md
PR 7081 docs/dev/debt/libc.md
PR 7201 core/libs/prx/libc/src/Threads.cpp
PR 7386 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7544 docs/dev/debt/libc.md
PR 7648 core/libs/prx/libc/src/Threads.cpp
docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8417 docs/dev/debt/libc.md
PR 9269 core/libs/tests/GuestLibcThreads.cpp
docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9447 core/libs/prx/libc/src/Threads.cpp
core/libs/tests/GuestLibcThreads.cpp
docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9648 docs/dev/debt/libc.md
PR 9669 docs/dev/debt/libc.md
PR 9784 docs/dev/debt/libc.md
PR 9863 docs/dev/debt/libc.md
PR 9873 docs/dev/debt/libc.md
PR 9929 docs/dev/debt/libc.md
PR 9973 docs/dev/debt/libc.md
PR 10405 docs/dev/debt/libc.md
PR 10460 docs/dev/debt/libc.md
PR 10786 core/libs/prx/libc/include/Api.hpp
docs/dev/debt/libc.md

@xXPBSXx

xXPBSXx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Windows data point for ce50ab75a merged onto main cecb540a6 (clean, git merge-tree rc 0): Windows 10.0.19045, MinGW-w64 GCC 15.2.0, Release. ctest -N -R '^guest_libc_threads$' lists exactly that one test; ctest --no-tests=error -R '^guest_libc_threads$' was run three times per tree.

  • With the PR: passes 3 of 3 (0.1-0.8 s).
  • Same tree with core/libs/prx/libc/src/Threads.cpp restored to main (the new test cases kept): fails 3 of 3, exit code 0xc0000409 (the test process aborts).
    This only shows that the new test separates the two trees on Windows; it does not measure what the console's _Thrd_id returns. Not tested: other platforms, a title run.
    AI-assisted (Claude Code).

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

The one-line forwarding implementation matches the two cited console readings, and the added test checks it with the registered and test-provided thread forwards. I did not find a defect in that forwarding path. Two coordination items and a test-context update are needed before approval.

Same export in #9416

The current overlap report identifies #9416 as defining the same _Thrd_id_nid_postfix export. I confirmed its current diff defines that symbol in libc/Export.cpp returning 1, while this PR defines it in Threads.cpp by forwarding scePthreadSelf(). These are competing implementations of the same export and cannot both be integrated as written.

Please coordinate with the #9416 author and maintainers on the single implementation and landing order, then update the overlap notes and Depends on line if the PRs are intentionally stacked. I’m recording the conflict, not presuming which PR should land.

Title bring-up record

The linked Animal Well discussion currently says there is no blocker and does not list this PR. A newer report in #8636 says the title reaches _Thrd_id_nid_postfix not implemented after #8636’s fix. Please update the discussion with that current test context, add this PR to its open-PR list, and state whether the stop is still reproducible. If it is no longer a blocker on current main, change the checklist’s Unblocks entry accordingly.

Current base and checks

This head is 601 commits behind current main (merge base e072e8840a). Please update it and rerun guest_libc_threads on the rebased head.

  • git diff --check: passed.
  • Current conventions, eligibility, intake, labels, and report jobs passed.
  • Register-context jobs passed on Ubuntu and macOS (Debug/Release); relinker jobs passed on macOS, Ubuntu, and Windows; relinker ASan and both write-watch jobs passed.
  • Linux and Windows product-test jobs were skipped. The local and fork test results in the PR description are author-reported on the older base; the full Linux run included one intermittent AGC test failure on the PR side, which the report identifies.

The forwarding implementation is supported by strong source evidence. Please reconcile the competing export, title record, and current-base validation before merge.

@krishalivaibhav

Copy link
Copy Markdown
Contributor

Code review: _Thrd_id forwarding to scePthreadSelf matches both cited readings of the console body (#4164, #8711), and returning the full void* keeps RAX unchanged, as the console does. For the overlap with #9416: that PR returns a constant 1 for the same export, which neither reading supports, so it seems simplest for #9416 to drop its _Thrd_id and leave the export to this PR. (Code review only; I don't own PPSA22520.)

@rooklz

rooklz commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (8b3e39832a): head eca9862e90, same diff.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@rooklz

rooklz commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (f6075c2f4a): head 64f3dc9c6e. The conflict was in docs/dev/debt/libc.md, where main added the num_get<char>::id entry on the line after this one; both entries are kept and the code diff is unchanged. guest_libc_threads passes on this head in a verify run on my fork (Ubuntu 24.04, GCC, Release); its two failures, agc_driver_queued_index_read_order and agc_driver_sgpr_pattern_fill_keys, fail the same way on main on that runner.

@xXPBSXx

xXPBSXx commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

The description already cites two readings of the export body; this adds the other side, how a title uses the value: a call-site data point for _Thrd_id, read in the disassembly of one title (PPSA23827, Ghidra 12.1.4 headless, statically, not run on a console).

  • The title calls _Thrd_id once, in the destructor of its thread wrapper object, without arguments, and compares the 64-bit return value for equality with the 64-bit thread id the wrapper stored when it created the thread. When they compare equal the title raises an error (a self-join check, inferred from the pattern); a stored id of 0 (not joinable) raises another one.

This agrees with _Thrd_id returning the pointer-wide scePthreadSelf value: the comparison only works if _Thrd_id returns the same value the creation call stored for that thread, and the title uses all 64 bits. It does not show which exact value the console's _Thrd_create stores.

Not shown by the call: the value width beyond what the comparison needs, other titles' use of _Thrd_id.

AI-assisted (Claude Code).

@rooklz

rooklz commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

@xXPBSXx thanks, that is the call-site side the description lacked: a title comparing all 64 bits of _Thrd_id() with the id its own _Thrd_create stored only works if both are the same pointer-wide value. The description's evidence list now cites it.

@adriadam10 adriadam10 self-assigned this Oct 23, 2026

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the implementation against the evidence summarized in the PR: the console export returns the result of scePthreadSelf unchanged, and _Thrd_current uses the same registered forward. The added test covers the registered kernel forward, a test double, and the missing-forward failure path. The author reports the focused Linux and Windows tests passing, with CI conventions, eligibility, labels, relinker matrix, register-context and write-watch jobs green. I found no blocking issue.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The forwarding implementation and its focused test remain supported by the cited console evidence. I need to correct my earlier approval because the live overlap report still lists open #9416 as defining _Thrd_id_nid_postfix, and this PR still says Depends on: none. The #9416 author has not agreed to drop or sequence its competing export, so the repository’s overlap rule is not satisfied. Please coordinate the single implementation/landing order with that author and update the dependency line (or wait for #9416 to close/remove the duplicate). I’ll re-review once that is resolved.

@rooklz

rooklz commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 the competing export in #9416 is a one-line placeholder, uint64_t _Thrd_id_nid_postfix() { return 1; }, in a PR about std::system_category and std::future_category; a constant 1 contradicts the console evidence here (the export returns scePthreadSelf() unchanged, and PPSA23827 compares all 64 bits of it with the id its _Thrd_create stored). I've asked @Delciob on #9416 to drop that function, which removes the overlap without a landing order; Depends on stays none unless they want to keep it, in which case I'll record the order here. I'm also rebasing this head onto current main and will post the rerun, and will refresh the PPSA22520 record on #966 as your earlier review asked.

@rooklz

rooklz commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Title record updated as asked: the 2026-10-22 retest on #966 (unmodified main 7e2ba73e4c) reaches Animal Well's gameplay without this change, so the _Thrd_id stop is not reproducible there on current main; the description's Unblocks is now none, the What section says so, and a status note with the PR is on #966 for its owner's table. Rebased onto current main (0884fab4b1): head ac34b9c3b1, same diff; the hosted guest_libc_threads rerun follows.

@github-actions github-actions Bot removed the unblocks title Names a title ID on the Unblocks line label Oct 23, 2026
@rooklz

rooklz commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

The hosted guest_libc_threads rerun on the rebased head ac34b9c3b1 passes: https://github.com/rooklz/AnyPS5/actions/runs/40041017594 (Ubuntu 24.04, GCC, Release, lavapipe, fails the job on any ctest failure); its two failures, agc_driver_queued_index_read_order and agc_driver_sgpr_pattern_fill_keys, are main's on that runner.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation and its test are fine: _Thrd_id forwards to scePthreadSelf like _Thrd_current, matching the cited readings, and guest_libc_threads fails on main. The merge queue refused it because of MarcZX10's change request, which is still unmet:

  • #9416 is open and still defines uint64_t _Thrd_id_nid_postfix() { return 1; } in core/libs/prx/libc/Export.cpp. Two definitions of the same export don't link, so whichever PR lands second breaks the build. The constant 1 isn't supported by either console reading or the PPSA23827 call site.
  • To unblock: #9416 drops that line (already asked there), or this PR lands after #9416 replaces it. If you want the second option, record it under Depends on and say so here. Once #9416 no longer defines _Thrd_id, this is ready to merge.

@rooklz

rooklz commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

@adriadam10 thanks. I'd rather not sequence after #9416: its _Thrd_id is a placeholder, and that PR also fails to link on Windows for unrelated duplicates (ycbuzi's run on it), so it may not land soon. Depends on stays none, and this waits for #9416 to drop the line; the request to its author is posted there. If a maintainer prefers, this can merge first and #9416 then removes its duplicate on rebase, as the overlap rule allows for the later PR.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@rooklz

rooklz commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Measured on a PS5: a homebrew payload called the console's _Thrd_id next to scePthreadSelf in the main thread and a second thread. It returned exactly scePthreadSelf() each time (0x8804ac020 in main, 0x8804db4a0 in the worker), which is what this PR does; I posted the output on #9416 and asked again for its constant-1 _Thrd_id to be dropped. @MarcZX10 @adriadam10 for the record, nothing on this side has changed; it is still waiting only on #9416.

@rooklz

rooklz commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. The new Api.hpp from #10706 declared _Thrd_id as returning int. The console returns the 64-bit scePthreadSelf() pointer (measured, above), so the declaration is now void*. guest_libc_threads passes. #9416 still conflicts with main and hasn't answered the two requests to drop its constant-1 _Thrd_id. @adriadam10 @MarcZX10: if you'd rather not wait, I'm happy for this to land first, and #9416 then drops its line when it rebases. Otherwise this stays as is until that's settled.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 25, 2026
@edubox

edubox commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

I rebased your commit onto current main f26434afa3 (the #10706 headers refactor) and verified it. It applied cleanly: #10706 had moved the declaration into Api.hpp, and this PR's own int -> void* change is exactly what the rebase needs. guest_libc_threads passes (Linux, GCC 16.2.1) and check_conventions is clean; the author and message are preserved.

Branch if useful: https://github.com/edubox/AnyPS5/tree/verify/9170-thrd-id (be0a4bfe95). Feel free to force-push it to your branch; I will not open a competing PR.

One direct data point from a title that imports the export: Cyberpunk 2077 (PPSA04029) has np6xXcXEnXE in its sce_module/libc.prx at 0x4bb0 (11 bytes) -> 0xcc700 -> GOT 0x113ae0, whose relocation is aI+OeCz8xrQ (scePthreadSelf); PPSA02530 has the same shape at 0x4e20 -> 0x116630. Its own modules/libREDGalaxy64.prx imports np6xXcXEnXE, and tools/import_audit.py reported it as a stub on main before this change.

@rooklz

rooklz commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

@edubox thank you, that's the most direct evidence yet: a title's own libc.prx forwarding _Thrd_id to scePthreadSelf, and a title module importing it. It's in the description now. My branch already carries the same rebase (the int to void* declaration change on top of #10706), so I'll keep it as is.

@adriadam10
adriadam10 dismissed MarcZX10’s stale review October 25, 2026 10:19

Dismissing a stale review: the requested changes were addressed after it (new head) and the merge queue blocks on it. Please request changes again if anything is still open.

@adriadam10
adriadam10 merged commit 0a77bbf into boykopovar:main Oct 25, 2026
19 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the diff, the test and the evidence: _Thrd_id returning the full-width scePthreadSelf() matches the console readings (#4164, #8711), the PPSA23827 call site, edubox's title libc.prx forwards and your PS5 measurement; the Api.hpp declaration is void* to match. guest_libc_threads fails on main (the export threw) and passes here for libkernel's forwards, test-provided forwards and unregistered forwards.

The Windows run's only failure was a 30 s timeout of guest_raise_in_system_call, unrelated to this diff; rerun. #9416 drops its duplicate _Thrd_id_nid_postfix when it rebases. Merged.

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.

6 participants