Commit Graph
46 Commits
Author SHA1 Message Date
Muhammad Wasif Ali WasifandClaude Opus 5.5 7d9a308044 fix(nid): reject ELF exports that patch to different symbols under one name
Two defined ELF exports that are renamed to the same name, such as foo
and foo_nid_disambig1, were both written as one name and the patcher
exited 0, leaving a lookup that can reach only one of them. The PE and
Mach-O back ends already reject this.

The ELF patcher now remembers the address (st_value and st_shndx) of
each patched export name and throws "duplicate patched export name"
only when the name comes from a symbol at a different address. Aliases
of one address are kept: the Linux libc defines __cxa_throw and
__cxa_throw_nid_no_patch_cut as aliases of __cxa_throw_nid_postfix, so
both become __cxa_throw, and any lookup of that name resolves to the
same code.

The Linux libc also exported __gxx_personality_v0 twice at different
addresses: the forwarding function in Personality.cpp and the
__gxx_personality_v0_nid_no_patch_cut alias of the guest personality.
Drop that alias, so the name keeps only the forwarding function, as
in the PE build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 00:08:27 +05:00
Adrià c14fd87044 Merge pull request #4721 from fdfawef324/build/stable-import-libraries
build(libs): keep import libraries stable so a source edit relinks one library
2026-10-14 19:08:35 +02:00
fdfawef324andClaude Opus 5.5 2251649a7f build(libs): keep import libraries stable so a source edit relinks one library
Every library under core/libs/prx links the libc and libkernel import
libraries, and every NID patch step depended on the unpatched libc.prx.
GNU ld 2.46 rewrites the import library on every link with new archive
timestamps (the member headers and the symbol map header), so the restat
on the link edge never pruned anything: one libc or libkernel .cpp edit
relinked all 124 libraries and re-patched all of them.

On MinGW the shared library link now writes the import library to
<implib>.new with SOURCE_DATE_EPOCH=0 (ld then writes zero member
timestamps), makes the symbol map deterministic with ranlib -D and copies
the result over the import library only when its bytes differ
(cmake -E copy_if_different). The link edge keeps restat, so a link that
keeps the exports leaves the import library untouched and prunes every
dependent link. The SOURCE_DATE_EPOCH also zeroes the export directory
TimeDateStamp of the DLL; the COFF header TimeDateStamp that the unwinder
keys on is unchanged.

The NID patch steps only use the names libc exports. nid_patcher
--write-export-list writes those names, normalized and sorted, to
libc.exports and leaves the file untouched when they did not change; the
patch steps read it with --preserve-export-list and depend on it instead
of libc.prx, on every platform. A patched library is byte-identical to
one patched against libc.prx.

Measured on Windows (GCC 15.2, -j 4, loaded machine) after touching
libc/src/Strings.cpp: with CMAKE_LINK_DEPENDS_NO_SHARED off, 124 links and
124 patches in 74.9 s before, 1 compile, 1 link, the export list and the
libc patch in 4.3 s after; with it on, 124 patches in 7.8 s before and
the same four steps in 5.3 s after. Touching libkernel/Time/Time.cpp
runs 123 links and 123 patches (89.1 s) before and one compile, link and
patch (3.4 s) after. Removing a libc export still relinks and re-patches
all 124 libraries.

nid_patcher_macho covers --write-export-list (sorted normalized names, no
rewrite when unchanged, rewrite when changed) and --preserve-export-list
(the same exports kept as with --preserve-exports; malformed lists and
two references refused). It fails on main, where the option is unknown.

Sources: SOURCE_DATE_EPOCH, https://reproducible-builds.org/specs/source-date-epoch/;
ranlib -D and ld --out-implib, GNU binutils documentation; ninja restat,
https://ninja-build.org/manual.html#ref_rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:43:05 -04:00
Dean 75ceccd5db Merge pull request #4615 from MiniRML/fix/nid-elf-table-layout
fix(nid): validate ELF table layouts before patching
2026-10-14 15:38:11 +13:00
Adrià 6629c09392 Merge pull request #4619 from MiniRML/fix/nid-version-requirement-chains
fix(nid): bound version requirement chains to their section
2026-10-13 17:31:25 +02:00
Adrià 9c4686321b Merge pull request #4604 from MiniRML/fix/nid-gnu-hash-geometry
fix(nid): validate GNU hash geometry before rewriting
2026-10-13 17:26:25 +02:00
Adrià 284cb63dc9 Merge pull request #4601 from MiniRML/fix/nid-pe-export-references
fix(nid): resolve PE reference exports through their ordinals
2026-10-13 17:26:06 +02:00
Adrià 382c451acf Merge pull request #4564 from MiniRML/fix/nid-windows-unicode-paths
fix(nid): preserve Unicode paths on Windows
2026-10-13 17:19:00 +02:00
Adrià 72a8e67122 Merge pull request #4621 from MiniRML/fix/nid-elf-shared-string-roles
fix(nid): preserve each ELF string consumer independently
2026-10-13 16:59:13 +02:00
MiniRML 89d89ac048 fix(nid): preserve each ELF string consumer independently 2026-10-12 09:53:17 -07:00
MiniRML f25eba673d fix(nid): bound version requirement chains to their section 2026-10-12 09:51:20 -07:00
MiniRML abe8fb8193 fix(nid): validate ELF table layouts before patching 2026-10-12 09:45:30 -07:00
MiniRML bb443667ec fix(nid): validate GNU hash geometry before rewriting 2026-10-12 09:39:28 -07:00
MiniRML 6e0f942f0f fix(nid): resolve PE reference exports through their ordinals 2026-10-12 09:36:42 -07:00
MiniRML bf6a054c52 fix(nid): preserve Unicode paths on Windows 2026-10-12 08:43:02 -07:00
MiniRML ca67860168 fix(nid): validate buffer ranges and string terminators 2026-10-12 08:28:52 -07:00
mugurc f3c514744b feat(nid): patch NIDs in Mach-O libraries
nid_patcher turns the _nid_postfix names of a library into NIDs for ELF
and PE. A library built on macOS is a Mach-O dylib, whose exports are in
a dyld export trie and whose imports are in the chained fixups, so it
gets its own patcher.

- The export trie is read, every exported C name (without Mach-O's
  leading underscore) is resolved to its NID, and the trie is rebuilt the
  way ld64 lays it out: the offsets are uleb128, so the layout is repeated
  until it is stable. A trie that no longer fits is appended to
  __LINKEDIT, which has to end the file, and a signed image is refused.
- Weak definitions that are not marked _nid_postfix or _nid_no_patch keep
  their names: dyld coalesces those across images by name.
- In the chained imports, names bound to a .prx dylib that are guest
  facing (a _nid_postfix name or an sce prefix) are patched in place; a
  name that would grow, or that shares storage with another import, is an
  error. Images that use bind opcodes instead of chained fixups are
  refused.
- ExportExclusions reads the exports of a reference Mach-O, so the
  exclusion list works for libraries built on macOS as well.
2026-10-09 15:40:29 +03:00
MGXAeandarena-agent 049539ac3d fix(nid): reject libraries that define symbol versions
The patcher rewrites .dynstr in place and reorders .dynsym for
.gnu.hash, but .gnu.version_d kept pointing at name offsets that the
rewrite had recycled: a library carrying symbol version definitions
came out with renamed or blanked version names while its symbols
still advertised a version.

No library in the build defines symbol versions, so fail on
.gnu.version_d during the section scan and leave the input untouched
instead of carrying version remapping that nothing exercises. The
test covers the rejection and that a library without version
definitions is still patched.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
2026-10-08 03:04:07 +00:00
alfalfa-starandClaude Opus 5.5 1d9d5ab491 fix(libs): keep a $ORIGIN runpath in the patched prx libraries
The NID patcher rebuilds .dynstr keeping only the DT_NEEDED and
DT_SONAME strings, so DT_RUNPATH kept a stale offset and the patched
libraries shipped with an empty runpath. A library could then load only
those sibling prx that the executable already loads itself:
libSceVideoOut.prx needs libSceKeyboard.prx, which a title that does
not import libSceKeyboard never loads, so it fails at startup.

The patcher now keeps DT_RPATH and DT_RUNPATH strings, and the prx
libraries are linked with a $ORIGIN runpath instead of the build
directory, so they find each other wherever they are copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:20:14 +02:00
Adrià FranchandClaude eb85dc7362 fix(nid): share string suffixes when rebuilding .dynstr
The linker stores a dynamic string that is the suffix of another once
(for example "cos" inside "acos"). The patcher wrote every string
separately, so a library whose renamed exports do not shrink the table
enough failed with "rebuilt .dynstr exceeds original section size".
Strings are now sorted by their reversed text and a string that ends
another one points into it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 20:09:41 +02:00
ownerandClaude Sonnet 5 63209b584f fix: correct sscanf %x signedness and avoid a substr copy
libSceSysmodule/Export.cpp parsed /proc/self/maps device major/minor
numbers into signed int with a %x format, which is undefined behaviour
per the C standard even though it happened to work on this target;
switched to unsigned int to match the format specifier.

NidPatcherUtils.hpp's StripNidPostfix built a truncated copy with
substr(0, n) where the prefix is already the string itself; resize(n)
does the same truncation without the extra allocation and copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 14:11:50 -06:00
boykopovar a43f73163d feat(libs): no patch already exported from libc.prx symbols 2026-09-22 18:21:08 +03:00
boykopovar 1e4740ed4b fix(nid): no patch sdl exports for nid 2026-09-14 10:52:04 +03:00
boykopovar 08b1968b15 fix(libs): repack pe export names contiguously instead of edata no free space ex 2026-09-11 08:38:42 +03:00
boykopovar a1116fe948 feat(libc): windows way for ex handling (wip) 2026-09-11 08:17:12 +03:00
boykopovar 24620106c5 fix(nid): sort pe exports and preserve ordinal mappings 2026-09-09 08:46:43 +03:00
boykopovar a369e0126c feat(libs): nid patching for non-std imports from prx 2026-09-09 08:12:27 +03:00
boykopovar 14f8c16ac4 fix(libs): add _nid_no_patch_cut postfix and global cxa/unwind exports 2026-09-07 10:56:34 +03:00
boykopovar 53b20aec77 refactor(nid): separate nid resolving logic 2026-09-07 07:52:09 +03:00
boykopovar b000880913 chore(nid): restore skip nid_no_patch 2026-09-06 15:06:59 +03:00
boykopovar 64ed717e77 fix(nid): no skip symbols without nid_postfix 2026-09-06 14:27:22 +03:00
boykopovar 58aef08fe1 feat(nid): add nid_no_patch rule 2026-09-06 13:49:24 +03:00
boykopovar 228d95be52 feat(nid): check exported symbols dup 2026-09-06 13:30:17 +03:00
boykopovar b1f51a3e4b feat(nid): remap dynamic needed offsets 2026-08-22 14:11:42 +03:00
boykopovar e838a2d9df fix(nid): rebuild ghu hash for patched nid 2026-08-22 11:47:41 +03:00
boykopovar b49279e832 fix(nid): update nid computing algorithm 2026-08-22 10:39:19 +03:00
boykopovar 54e66624ec fix(libs): fix nid compute algorithm - no lib name 2026-08-21 17:19:22 +03:00
boykopovar f23cc495f4 fix(nid): rebuild dynstr instead of in-place patch to avoid suffix-merge corruption 2026-08-21 16:24:16 +03:00
boykopovar 6dfd5a9296 refactor(nid): rename spec cpp + move utils 2026-08-21 15:16:25 +03:00
boykopovar c6550929e9 refactor(nid): windows and linux pathing separating via interface 2026-08-21 15:00:55 +03:00
boykopovar 55a120e318 fix(nid): detect linker suffix-merged symbol names postfixes 2026-08-21 14:45:07 +03:00
boykopovar ca37e667f9 fix(nid): patch linux nid in place + detailed overflow msg 2026-08-21 14:13:58 +03:00
boykopovar 486bb51167 fix(nid): exclude undefined dynamic symbols from elf nid patching 2026-08-21 00:32:00 +03:00
boykopovar 8627076df5 fix(libs): drop std name collisions via nid postfix + remove custom entry point 2026-08-21 00:26:40 +03:00
boykopovar ca38455917 fix(nid): grow export names in place within existing section padding for pe and elf 2026-08-21 00:15:21 +03:00
boykopovar 9e21a054e0 feat(libs): add rpx library build as libs target 2026-08-20 17:06:41 +03:00