Repository navigation
Replace the port's weak symbols with PC_OVERRIDE and a pre-processing step - #58
Merged
Merged
Conversation
PC_OVERRIDE goes in front of each of the 323 definitions in port/src that replace one of ps2/src's: 312 functions, main and ten globals of the title sequence. port.h defines the macro as nothing, so nothing is compiled differently yet.
scripts/port/pc_override.py lists the names port/src's PC_OVERRIDE definitions link under and writes a copy of each ps2/src unit without its definitions of those names; the port compiles the copies. A replaced function is left as its declaration, a member function is removed and a variable becomes an extern declaration. ps2/src itself is not touched. Each name then has one definition on every platform, so the merged object, llvm-objcopy --weaken, -fsemantic-interposition, the interposition check and tools/weaken go. scripts/port/truncations.py reads each unit as ps2/src has it, so the replaced bodies stay in docs/port/truncations.md, and it refuses a build that is not a Debug one: without semantic interposition an optimised build inlines functions whose bodies the link then drops.
Adubbz
pushed a commit
that referenced
this pull request
Oct 6, 2026
… step (#58) * Tag port/src's replacements with PC_OVERRIDE PC_OVERRIDE goes in front of each of the 323 definitions in port/src that replace one of ps2/src's: 312 functions, main and ten globals of the title sequence. port.h defines the macro as nothing, so nothing is compiled differently yet. * Build the port from ps2/src without the definitions port/src overrides scripts/port/pc_override.py lists the names port/src's PC_OVERRIDE definitions link under and writes a copy of each ps2/src unit without its definitions of those names; the port compiles the copies. A replaced function is left as its declaration, a member function is removed and a variable becomes an extern declaration. ps2/src itself is not touched. Each name then has one definition on every platform, so the merged object, llvm-objcopy --weaken, -fsemantic-interposition, the interposition check and tools/weaken go. scripts/port/truncations.py reads each unit as ps2/src has it, so the replaced bodies stay in docs/port/truncations.md, and it refuses a build that is not a Debug one: without semantic interposition an optimised build inlines functions whose bodies the link then drops. * Describe PC_OVERRIDE in the port's documentation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the override mechanism you described: a
PC_OVERRIDEmacro on the port's side, and a pre-processing stage that takes the PS2 code out for what the port overrides, the same on every platform. It replaces the merged weak object,llvm-objcopy --weaken,-fsemantic-interposition, the interposition check andtools/weaken. Three commits on currentport.Nothing under
ps2/changes. PAL and NTSC builds of this branch and ofportare byte-identical, as they have to be.What it is
PC_OVERRIDEgoes in front of the 323 definitions inport/srcthat replace one ofps2/src's: 312 functions,mainand ten globals of the title sequence.port/include/port.hdefines it as nothing. The list is what a weak-symbol Linux build defined in both halves, so nothing is replaced that was not replaced before.scripts/port/pc_override.py(about 500 lines, no dependencies) has three steps.listreads the tags inport/srcand writes the name each tagged definition links under: a function's qualified name and parameter types, or the name alone for a variable and for a function with C linkage.stripwrites a copy of eachps2/srcunit into the build directory without its definitions of those names, and CMake compiles the copies. A function is left as its declaration, a member function is removed (an explicit specialization of one keeps its declaration), a variable becomes anexterndeclaration. Line numbers are kept and a#linedirective names the original, so diagnostics and debug information still point intops2/src.checkstops the build if a tag names nothingps2/srcdefines.inline, or Apple's linker drops it as dead code first). A tag whose signatureps2/srcdoes not have (after a rename or a changed parameter there, say) fails the check; without it both would link and the game would quietly go on calling the PS2 code. The port's CMake no longer looks upllvm-objcopy,llvm-nm,llvm-objdump,llvm-readobj,llvm-lipoor a relocatable linker.port/srcrebuilds that file and nothing ofps2/src, and adding a tag recompiles only the unit it reaches.scripts/port/truncations.pyreads each unit asps2/srchas it, so the replaced bodies stay indocs/port/truncations.md. It now refuses a build that is not Debug: without semantic interposition an optimised build inlines functions and the link drops their bodies, which the audit would count as dead.docs/PC.md("Howport/srctakes precedence" and the places that mentioned weak symbols) and the rule lines of the three plan documents.Behaviour that changes
Under weak symbols the title units' static constructors also ran over the port's ten replaced globals, as
docs/PC.mdnoted. They no longer do.CFireOmni's constructor callsrand(), so the random sequence of a run shifts once: a recorded input replay lands on a different random dungeon floor than before. That is the only difference in the replays I have: making the build construct those ten a second time reproduces the weak-symbol build's screenshots byte for byte.Optimised builds now inline and fold calls between
ps2/srcfunctions the port does not replace, which the interposition flags used to prevent. Debug builds are unaffected.Testing
Linux x86-64, clang 23:
ctest94 of 94 on RADV and on lavapipe.porthas 96; the two that go aretools/weaken's own.git clang-formatclean;truncations.py --checkpasses; Doxygen gives no new warning.PC portworkflow's Linux job as written, in anubuntu:26.04container with clang 22: 94 of 94 and the truncation audit passes.Flatpakworkflow as written, in Flathub's image: builds, anddarkcloudstarts and stops at the data check.Releasebuild: 94 of 94 on both drivers.InitCDFileunder#else, which the PAL build never compiles.PS2: PAL and NTSC, as above.
Windows: the native build from #51, rebased onto this, links with no multiple-definition flag and passes its suite in Debug and in an optimised build. That is the follow-up PR.
macOS is not built here; CI will be the first run. The macOS-only pieces that go are the Mach-O weakening,
-Xclang -fsemantic-interposition,-fno-inline-functions, and-fno-zero-initialized-in-bsson the four title units whose globals are replaced.Things you may want different
ps2/srcdoes (u_char, notunsigned char); a mismatch stops the build, at the check or at the link. A name a stub header renames (MainDrawtoDunMainDraw) is tagged under the new name..cppunits and handles a function with its body or a variable without constructor arguments. Where it cannot be sure, it stops the build rather than guess: a definition that holds part of an#ifblock or has a directive before its body, aninlinedefinition it cannot read (the link would not catch that one), a line continued by a backslash outside a directive, and a definition taken out of a branch the port does not compile. For that last one it reads#ifdef/#ifndefofPORTandPAL, which CMake passes, and assumes nothing inps2/srcredefines them. A function defined in a class body, a namespace or a header cannot be replaced; the check says so. Nothing inps2/srctoday trips any of this.port/src/linknames.cppstill has weak definitions. They are the port's own fallbacks for link names and have nothing to do withps2/src; I left them alone.