react-native: E/libc++abi: terminating with uncaught exception of type std::bad_cast: std::bad_cast

Description

As of version 0.62.0, there’s a conflict in libc++_shared.so between react-native and tesseract. This issue can be observed with any version of ScanbotSDK, however, it only shows up as of 0.62.0 react-native version.

I already faced SDK linking issues with this version of react-native when I first tried it out. Then found that an additional required step is now adding the following to settings.gradle:

include ':react-native-scanbot-sdk'
project(':react-native-scanbot-sdk').projectDir =
        new File(rootProject.projectDir, '../node_modules/react-native-scanbot-sdk/android/app')

But, as I am posting this, it’s still not enough. Workaround and details below.

React Native version:

System:
    OS: macOS 10.15.6
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 3.03 GB / 32.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 12.13.0 - ~/.nvm/versions/node/v12.13.0/bin/node
    npm: 6.12.0 - ~/.nvm/versions/node/v12.13.0/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
    Android SDK:
      API Levels: 26, 28, 29
      Build Tools: 27.0.3, 28.0.3, 29.0.2, 29.0.3
      System Images: android-28 | Google Play Intel x86 Atom
      Android NDK: 21.0.6113669
  IDEs:
    Android Studio: 4.0 AI-193.6911.18.40.6514223
    Xcode: 11.5/11E608c - /usr/bin/xcodebuild
  npmPackages:
    react: 16.11.0 => 16.11.0 
    react-native: 0.62.2 => 0.62.2 
  npmGlobalPackages:
    react-native: 0.61.4

Steps To Reproduce

  1. Clone https://github.com/doo/scanbot-sdk-example-react-native/tree/v4.1.0
  2. Change "react-native-scanbot-sdk": "4.1.0-rc2", back to "react-native-scanbot-sdk": "4.1.0-beta10",
  3. react-native run-android
  4. Press Scan MRZ on ID Card
  5. E/libc++abi: terminating with uncaught exception of type std::bad_cast: std::bad_cast

The same can be reproduced with a hello-world app created via npx react-native init AwesomeProject

Expected Results

Well, I’d expect the MRZ Scanner to start. As you can observe when you change "react-native-scanbot-sdk" back to "4.1.0-rc2", where I’ve implemented the workaround.

Or you can change "react-native" to anything below "0.62", and it will run as expected.

Snack, code example, screenshot, or link to a repository:

Instead of snacking, I’ll explain the process and workaround that I found.

First, the issue. When compiling our core with debug symbols attached, I can see that the bad_cast is, in fact, inside tesseract. In a relatively harmless piece of code, nonetheless:

const char* buffer = "NULL 0 NULL 0\n";
std::istringstream stream(buffer);
stream.imbue(std::locale::classic());
// 标 1 0,255,0,255,0,0,0,0,0,0 Han 68 0 68 标  # 标 [6807 ]x
stream >> std::setw(255) >> unichar >> std::hex >> properties >> std::dec;

If I move that same block back into our core, it passes through it with a breeze… but inside tesseract it crashes with the bad_cast exception inside c++ shared library. It cannot find a facet, that should be there in every case.

Well, at this point I was pretty dumbfounded, and cannot debug shared library without debug symbols and with optimizations.

Off to my next adventure: Building the standard library in debug mode. But of course, that cannot be done separately, so I had to download all of Google’s monorepo (60gb, woo) and compile the entirety android NDK from source in debug, so I could see what the hell was going on in locale.cpp.

That step was a bit easier than I thought. Just sprinkled some of the following around in different makefiles:

LOCAL_CFLAGS += -O0 -UNDEBUG -g
LOCAL_CPPFLAGS += -O0 -UNDEBUG -g

Now I have the debug build of libc++_shared.so, weighing just shy of 6mb (a bit less than 1mb in release), that I copied to my NDK directory: /Users/$USER/Library/Android/sdk/ndk/19.2.5345600/sources/cxx-stl/llvm-libc++/libs/arm64-v8a

I specifically used the same NDK react-native is compiled with, as described here. And I also built react-native from source, just so I could control every aspect of it and specify which NDK to use in all cases…

And it works. No more crash. Cannot reproduce the issue with the debug build of libc++_shared.so. Well, what now?

So I included the debug build in our SDK and wrote a simple "postinstall" script that copies the binary to project/app/src/main/jniLibs and now the correct c++ standard library is included in the apk.

As you can see, that’s still a… well, relatively dirty workaround. Still not sure what exactly changed in react-native builds between versions 0.61 and 0.62 on that level. Anyone?

Thank you for coming to my TED talk

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 7
  • Comments: 38 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Hi,

In the past weeks, I tried to investigate the issue, and apparently, the only solution for it (unless Facebook solves the issue before) is to build your native code using the static version of libc++.

In addition, I kept looking for workarounds, and today I found a one, which some of you (including me) will call dirty or nasty - but it works. I am sharing it with you so can provide your opinions on it (and maybe use it too…), and MORE: explain why it works and whether it could affect RN’s functionality:

In the method onCreate() of the RN’s class MainApplication.java was changed so its first action is loading libc++_shared.so to the app’s memory before any other library RN’s code loads:

From:

@Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }

To:

@Override
  public void onCreate() {
    System.loadLibrary("c++_shared");
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }

The reason I took the liberty to make this change is that this class must change whenever a Native module is added to the RN class (See RN’s documentation https://reactnative.dev/docs/0.61/native-modules-android#register-the-module) …

In order to test the change, I used two examples:

  1. The sample code that fails, which is included in the issue’s description (with some minor adaptations…) - (Verified that the substring “NULL” from buffer is no successfully assigned to unichar)
const char* buffer = "NULL 0 NULL 0\n";
std::istringstream stream(buffer);
stream.imbue(std::locale::classic());
// 标 1 0,255,0,255,0,0,0,0,0,0 Han 68 0 68 标  # 标 [6807 ]x
stream >> std::setw(255) >> unichar >> std::hex >> properties >> std::dec;
  1. A conversion from a string to a number, which failed In my company’s code:
std::string in_str_ = "1234"; 
   double out_number_ = 0.0;
   std::istringstream iss(in_str_);
   iss >> out_number_;

Thanks Shay

Hi, Any expected date to fix this issue? How can we get the React-Native team to respond? The proposed “workarounds” are not acceptable. We need a fix!! This issue was opened on July 15 and I have yet to see any response from the react team…

This issue is preventing me upgrading from 0.61, and unfortunately none of the mentioned workarounds are of use as a 3rd party library is providing their own libc++_shared.so

I must say, having someone on GitHub tell me I’m a lifesaver is very humbling. Some day I’m going to tell my grandchildren about this.

Anyway, I also tried it out and, as @bibinprathap mentioned, this does indeed appear to be solved in v0.64.0. However, I’m reluctant to remove my workaround without a proper explanation of what the hell was going on.

Having the same issue when using the HERE map sdk.

I got this issue when implementing latest ZoomSDK. Please fix this.

@minhchienwikipedia I wouldn’t count on the RN team addressing this issue any time soon. Not because I do not have faith in them, but simply because it’s very difficult to even diagnose what exactly is causing this issue. My best guess is it’ll happen when they upgrade the NDK.

Anyway, everything I did, with the jnilibs directory and custom libc++_shared.so build, is openly available within the package: https://www.npmjs.com/package/react-native-scanbot-sdk 😄

Hi @Kudo,

Last week I found a workaround to the above failure - by explicitly loading libc++_shared.so before RN loads its own Native Shared Libraries. This change does not require any other change and was verified on different RN apps.

If the issue is still relevant for you, I will be more than happy to have your comments on it. Thanks Shay

@minhchienwikipedia I wouldn’t count on the RN team addressing this issue any time soon. Not because I do not have faith in them, but simply because it’s very difficult to even diagnose what exactly is causing this issue. My best guess is it’ll happen when they upgrade the NDK.

Anyway, everything I did, with the jnilibs directory and custom libc++_shared.so build, is openly available within the package: https://www.npmjs.com/package/react-native-scanbot-sdk 😄

I would just like to say you are a life saver!

@Nikituh I resolved this issue by custom libc++_shared.so, thank you so much 😄