Skip to content

fix: make sure native libs are loaded for RN >= 0.80#315

Open
marcinszalski-callstack wants to merge 3 commits intocallstack:mainfrom
marcinszalski-callstack:feature/283_bug_report_sigsegv_in_hostplatformviewprops_during_fabriccomponentdescriptor_registration_in_brownfield_apps_with_large_dex_counts
Open

fix: make sure native libs are loaded for RN >= 0.80#315
marcinszalski-callstack wants to merge 3 commits intocallstack:mainfrom
marcinszalski-callstack:feature/283_bug_report_sigsegv_in_hostplatformviewprops_during_fabriccomponentdescriptor_registration_in_brownfield_apps_with_large_dex_counts

Conversation

@marcinszalski-callstack
Copy link
Copy Markdown
Contributor

@marcinszalski-callstack marcinszalski-callstack commented May 7, 2026

Summary

loadNativeLibs() in ReactNativeBrownfield.kt is a no-op for RN >= 0.80. All ReactNativeHostManager templates call loadReactNative(application) BEFORE ReactNativeBrownfield.initialize(), so when done through the recommended path the libs load. But loadNativeLibs() — called inside every initialize() overload — silently skips for RN >= 0.80. Any consumer using ReactNativeBrownfield.initialize() directly — with a custom ReactHost, custom packages list, or custom options — skipped native lib loading entirely. Without JNI_OnLoad firing, registerComponentDescriptorsFromEntryPoint and the DefaultTurboModuleManagerDelegate module providers were never assigned, leading to the SIGSEGV during Fabric initialization.

ddc011c — moved SoLoader.init() from preloadReactNative() into initialize(application, rnHost, ...). Before this, SoLoader.init() was called inside preloadReactNative after creating the React context. It was consolidated into the initialize entry point.
9908b68 — added the RN_THRESHOLD_VERSION = "0.80.0" gate and removed SoLoader.init() + load() for RN ≥ 0.80, leaving those calls only for < 0.80.

ReactNativeBrownfield.initialize() itself had no fallback for the RN ≥ 0.80 case. If anything bypasses ReactNativeHostManager — a custom ReactHost, a consumer calling initialize() directly, or the generated ReactNativeApplicationEntryPoint being unavailable — native libs are never loaded. That's the bug this fix addresses.

Test plan

To genuinely reproduce the original crash we'd need one of:

  • the version of the AAR from before 59f472b consumed by the reporter's enterprise app
  • an APK that deliberately ships two conflicting versions of libreact_codegen_rnscreens.so

Neither is practical to set up cleanly. The SoLoader.init() not yet called crash the GH issue reporter produced is sufficient proof that the same class of bug exists — the fix prevents both the Java exception AND the conditions that led to the original native crash.

cause: loadNativeLibs() in ReactNativeBrownfield.kt is a no-op for RN >= 0.80.
All ReactNativeHostManager templates call loadReactNative(application) BEFORE
ReactNativeBrownfield.initialize(), so when done through the recommended path the libs load. But
loadNativeLibs() — called inside every initialize() overload — silently skips for RN >= 0.80. Any
consumer using ReactNativeBrownfield.initialize() directly — with a custom ReactHost, custom
packages list, or custom options — skipped native lib loading entirely. Without JNI_OnLoad firing,
registerComponentDescriptorsFromEntryPoint and the DefaultTurboModuleManagerDelegate module
providers were never assigned, leading to the SIGSEGV during Fabric initialization.
ddc011c — moved SoLoader.init() from preloadReactNative() into
initialize(application, rnHost, ...). Before this, SoLoader.init() was called inside
preloadReactNative after creating the React context. It was consolidated into the initialize entry
point.
9908b68 — added the RN_THRESHOLD_VERSION = "0.80.0" gate and removed SoLoader.init() + load() for
RN ≥ 0.80, leaving those calls only for < 0.80.
ReactNativeBrownfield.initialize() itself had no fallback for the RN ≥ 0.80 case. If anything
bypasses ReactNativeHostManager — a custom ReactHost, a consumer calling initialize() directly, or
the generated ReactNativeApplicationEntryPoint being unavailable — native libs are never loaded.
That's the bug this fix addresses.
Copy link
Copy Markdown
Member

@hurali97 hurali97 left a comment

Choose a reason for hiding this comment

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

If I understand correctly, the crash happens because the user does not call the loadReactNative in some integrations, so we are saying that we will do it for them if they do not follow the docs?

Since we are now using Reflection, we will need to ask the User to add this ReactNativeApplicationEntryPoint to their consumer-rules - so this is one extra step for them and we need to document it, see here

Also, let's get rid of RN <=80 support, only keep 80+ changes.

Copy link
Copy Markdown
Collaborator

@artus9033 artus9033 left a comment

Choose a reason for hiding this comment

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

LGTM after 1 comment, great job!

Comment on lines +65 to +70
Class.forName("com.facebook.react.ReactNativeApplicationEntryPoint")
val loadReactNativeMethod = reactNativeApplicationEntryPointClazz.getMethod(
"loadReactNative",
android.content.Context::class.java
)
loadReactNativeMethod.invoke(null, application.applicationContext)
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.

As we discussed, let's try to check which versions of RN expose this API and instead of reflection, try checking the version

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I also think we should drop RN <=80 support now, no point in maintaining a version outside of release support window.

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.

Agreed, if this is only for <= 0.80, I think we can drop it. Our compat matrix states >=0.83 for Brownfield v3.

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.

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.

3 participants