Skip to content

OSDynLoad_Acquire: make HLE-registered libraries acquirable - #2046

Closed
mirzaaamerali wants to merge 1 commit into
cemu-project:mainfrom
mirzaaamerali:hle-acquire-fix
Closed

OSDynLoad_Acquire: make HLE-registered libraries acquirable#2046
mirzaaamerali wants to merge 1 commit into
cemu-project:mainfrom
mirzaaamerali:hle-acquire-fix

Conversation

@mirzaaamerali

Copy link
Copy Markdown

Problem

osLib_registerHLEFunction is DLLEXPORTed so that external code can register HLE
functions under a library name. But guest code can never obtain a handle to such a
library. RPLLoader_GetHandleByModuleName (src/Cafe/OS/RPL/rpl.cpp) rejects any
module that is neither a Cafe OS module nor backed by an .rpl file:

if (!dep->isCafeOSModule && !dep->rplLoaderContext)
    return RPL_INVALID_HANDLE; // module not found

A library that exists purely as HLE registrations is exactly that case: it has no
.rpl file and is not a built-in Cafe OS module. So OSDynLoad_Acquire("my_hle_lib")
always fails, and every function registered through the exported API is unreachable
from the guest.

RPLLoader_FindModuleOrHLEExport already resolves these exports via
rpl_mapHLEImport — only the handle lookup was missing.

Why this is easy to miss

The failure is invisible in release builds. coreinit_DynLoad.cpp reports it with
cemuLog_logDebug, which is compiled out, so OSDynLoad_Acquire returns
"module not found" with no diagnostic anywhere. Behaviour is identical on 1.27.1 and
2.6.

Change

  • Add osLib_hasLibrary(const char*) — true if any HLE function is registered under
    that library name.
  • Accept such modules in RPLLoader_GetHandleByModuleName.
  • Promote the OSDynLoad_Acquire failure from cemuLog_logDebug to cemuLog_log.

The logging change is separable — happy to drop it if you would rather that stay
debug-only. It is included because the silence is what made this take so long to
identify.

Validation

Tested with an injection-based mod that registers its interceptor library through
osLib_registerHLEFunction:

  • On stock 2.6 and 1.27.1 the acquire fails for every such library, and the mod's
    hooks never resolve.
  • With this change the acquire succeeds. The call site retries on failure and caches
    the pointer only on success, so the failure log lines stop at the moment the library
    becomes reachable while the caller keeps polling at roughly 480 calls/sec.
  • Guest→HLE dispatch confirmed directly: recompiled guest code
    (PPCRecompiler_getJumpTableBase) was observed dispatching into a function
    registered by the injected library.

Built with this repository's own build.yml (Windows x64).

osLib_registerHLEFunction is DLLEXPORT'd so external code (mods,
injected libraries) can register HLE functions, but
RPLLoader_GetHandleByModuleName returns RPL_INVALID_HANDLE for any
module that is neither a Cafe OS module nor backed by an .rpl file --
which is exactly what a purely HLE-registered library is.
OSDynLoad_Acquire on such a library therefore always fails, and does so
silently in release builds because the failure log is cemuLog_logDebug.

Add osLib_hasLibrary() and accept a module in the handle lookup when it
has HLE registrations, so DLLEXPORT-registered libraries are reachable
from guest code. Also promote the OSDynLoad_Acquire failure log to a
release-visible cemuLog_log.
@Exzap

Exzap commented Sep 1, 2026

Copy link
Copy Markdown
Member

osLib_registerHLEFunction is an undocumented internal function that is only exported for historic reasons (originally used by Cemuhook). There are a few other unofficial plugins that rely on it so we kept it around for those. But it is by no means to be confused with an official hooking/modding API and we do not make guarantees that it remains stable. For example osLib_registerHLEFunction is not intended to be able to register new libraries and we are actively moving away from supporting "ghost RPL" HLE modules that don't actually exist in memory since it is a hack.

Unless you have a good reason to, you should just add the functions to coreinit(.rpl). And to signal if your Cemu-side hook is loaded you can just set a byte somewhere in PPC memory and read that out.

Also for future reference, we do not accept AI generated PRs. See our LLM contribution guidelines

@Exzap Exzap closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants