fix(core): lazy dlsym stub materialization, COW snapshots and deferred bootstrap logging#94
Merged
Merged
Conversation
…ent normalization Enforce transactional and thread-safe resolution for standalone ELF bootstrapper pipelines. - Implement a 4-tier additive fallback cascade (T0: runtime symbols, T1: import entries scan, T2: Aerolib mapping, T3: runtime slack-pool lazy stub allocation at 0x7000_0000_0000). - Fix UnmanagedCallersOnly CLR runtime crashes on second bootstrap by adding NormalizeKernelDynlibDlsymArguments to detect and swap mirrored (symbol, handle) register inputs via rigorous pointer bounds verification. - Protect failure paths via CompleteKernelDynlibDlsymFailure, cleanly zero-filling target outputAddress buffers and returns Rax = -1 with zero managed logging execution in hot native paths.
…ed bootstrap logging Follow-up to lazy stub pool copy-on-write publishing in TryGetOrCreateLazyImportStub. - Snapshot _importEntries in ProbeReturnRip before near-call and PLT import lookup loops to prevent torn iteration during concurrent array replacement. - Defer SHARPEMU_LOG_BOOTSTRAP output: hot path records raw register slots in a ring buffer under lock; TryReadAsciiZ and Console.Error run only after import handler completion via DrainDeferredBootstrapTraces. - Normalize bootstrap dynlib register order at DispatchImport gateway entry before any logging or trace reads, so swapped RDI/RSI on Import#2 cannot fault the native gateway when bootstrap tracing is enabled. - Resolve lazy stub pool bounds from the full SelfLoader-mapped import region via VirtualQuery instead of a hardcoded 4 KiB cap. - Use ConcurrentDictionary for runtime symbol registration during concurrent dlsym. - Emit distinct [LOADER][WARN] reasons when import stub region resolution fails versus lazy stub pool exhaustion.
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.
Standalone ELF loaders call sceKernelDlsym through the internal bootstrap bridge before system libraries are imported. Those binaries often have no import-table entry for sceKernelDlsym itself, so the second bootstrap dlsym used to fault inside the native import gateway. This change adds on-demand stub materialization for missing symbols and tightens diagnostics so bootstrap tracing does not disturb gateway execution.
Lazy dlsym resolution:
When DispatchKernelDynlibDlsym looks up a symbol, resolution now walks a fixed fallback chain: runtime symbols and aliases first, then existing import stubs (to avoid duplicates), then Aerolib (rejecting data symbols and re-checking earlier tiers). Only if all of that misses does the backend allocate a new import stub in the slack at the end of the already-mapped stub region at 0x7000_0000_0000 — the same layout SelfLoader uses. The host trampoline is patched first; _importEntries is replaced only after that succeeds, under lock, via copy-on-write.
Bootstrap register layout:
Some standalone loaders pass (symbol_ptr, handle, out) instead of the canonical (handle, symbol_ptr, out). NormalizeKernelDynlibDlsymArguments swaps RDI and RSI only when RSI looks like a small handle and RDI is a plausible guest pointer. Normal game paths are left alone. The swap now runs at the top of DispatchImport for bootstrap bridge calls, before any logging or trace code reads those registers.
Failure paths and materialization warnings:
On resolve failure the output buffer is zeroed and Rax is set to -1 without managed logging on the hot path. If lazy materialization cannot proceed, the backend emits a distinct [LOADER][WARN]: either the import stub region could not be resolved, or the lazy pool within that region is full.
Diagnostics follow-up:
ProbeReturnRip snapshots _importEntries before near-call and PLT loops so a concurrent lazy stub publish cannot cause torn iteration. SHARPEMU_LOG_BOOTSTRAP is deferred: the gateway only records raw register values in a small ring buffer; string reads and console output happen after the handler returns. Runtime symbol registration uses ConcurrentDictionary so concurrent dlsym from guest threads does not race the lazy writer.
Lazy pool sizing:
Pool bounds come from VirtualQuery over the full SelfLoader-mapped import region, not a fixed 4 KiB window. The primary path walks the same 64 candidate bases as SelfLoader; a fallback derives limits from existing stub addresses when VirtualQuery is unavailable.
Out of scope: post-bootstrap module loading (.sprx pipeline after dlsym bootstrap); VFS changes in PR #77; fallback coverage for stride-shifted stub regions without VirtualQuery (not used with PhysicalVirtualMemory today).
Files: DirectExecutionBackend.Imports.cs, DirectExecutionBackend.cs, DirectExecutionBackend.Diagnostics.cs