dart-archive / ffi

Utilities for working with Foreign Function Interface (FFI) code
https://pub.dev/packages/ffi
BSD 3-Clause "New" or "Revised" License
156 stars 32 forks source link

FFI bug on iOS: Symbol not found at runtime with IPA #51

Closed kakyoism closed 4 years ago

kakyoism commented 4 years ago

My app, using FFI linked C++ code as backend, runs fine on the iOS with Release dev build distributed from Xcode directly.

Problem

But with the IPA created from the Release Archive, I get runtime errors

[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, NativeInit): symbol not found)
#0      DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31)
dart-lang/ffi#1      ffiInit (package:myapp/myapp.dart:25)
dart-lang/ffi#2      ffiInit (package:myapp/myapp.dart)
dart-lang/ffi#3      MyAppBackend.init (package:myapp_example/backend.dart:99)
<asynchronous suspension>
dart-lang/ffi#4      MyAppBackend.asyncInit (package:myapp_example/backend.dart:64)
<asynchronous suspension>

The missing symbol should be part of the CPP files added to the Runner Xcode project. And those files build and run fine with the Release build. So I expect the IPA would behave exactly the same as the Release build.

What am I missing?

Environment

The IPA is created from the Xcode standard procedure as a Development distribution, running Xcode 11.6.

$ flutter doctor -v
[✓] Flutter (Channel dev, 1.21.0-1.0.pre, on Mac OS X 10.15.5 19F101, locale en-CN)
    • Flutter version 1.21.0-1.0.pre at /Applications/flutter
    • Framework revision f25bd9c55c (2 weeks ago), 2020-07-14 20:26:01 -0400
    • Engine revision 99c2b3a245
    • Dart version 2.9.0 (build 2.9.0-21.0.dev 20bf2fcf56)
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
    • Android SDK at /Applications/Android/sdk
    • Platform android-30, build-tools 30.0.0
    • ANDROID_HOME = /Applications/Android/sdk
    • ANDROID_SDK_ROOT = /Applications/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.6, Build version 11E708
    • CocoaPods version 1.9.1

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 47.1.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] VS Code (version 1.46.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.12.1

[✓] Connected device (2 available)
    • ONEPLUS A6000 (mobile)   • <my uuid> • android-arm64 • Android 10 (API 29)
    • MyiPhone (mobile) • <my uuid> • ios           • iOS 13.5.1

• No issues found!
kakyoism commented 4 years ago

create_flutter_ffi_plugin.sh.zip I reproduced this with a very simple plugin project based on the official native_add example.

Repro

You should see the homepage showing the device name and version plus the native_add example: 1+2==3

Now you should see a blank screen.

Observation

The code runs fine on the device with Xcode directly, but can't enter homepage

The device console shows the following log related to the Runner:

    default 20:42:04.072195+0800    kernel  memorystatus: set assertion priority(10) target Runner:270
    error   20:42:04.124898+0800    kernel  Sandbox: Runner(270) deny(1) sysctl-read kern.bootargs
connection  default 20:42:04.203428+0800    Runner  Initializing connection
process default 20:42:04.204375+0800    Runner  Removing all cached process handles
connection  default 20:42:04.204895+0800    Runner  Sending handshake request attempt dart-lang/ffi#1 to server
connection  default 20:42:04.204987+0800    Runner  Creating connection to com.apple.runningboard
Common  default 20:42:04.207922+0800    Runner  FBSWorkspace connecting to endpoint : <private>
default default 20:42:04.213118+0800    backboardd  Connection added: IOHIDEventSystemConnection uuid:A226279B-013F-46B6-B4C9-7B366BB8590D pid:270 process:Runner type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 364 attributes:{
    HighFrequency = 0;
    bundleID = "com.example.nativeAddExample";
    pid = 270;
} state:0x0 events:0 mask:0x0
Common  default 20:42:04.230750+0800    Runner  FBSWorkspace registering source: <private>
Common  default 20:42:04.230873+0800    Runner  FBSWorkspace connected to endpoint : <private>
general default 20:42:04.230905+0800    Runner  Added observer for process assertions expiration warning: <_RBSExpirationWarningAssertion: 0x28150f0c0; identifier: com.apple.runningboardservices.processExpirationWarningForHandle; reason: observation; valid: YES>
    default 20:42:04.260195+0800    Runner  kExcludedFromBackupXattrName set on path: <private>
AXSupportCommon default 20:42:04.260272+0800    Runner  Retrieving resting unlock: 0
Default default 20:42:04.262358+0800    Runner  Registering for test daemon availability notify post.
Default default 20:42:04.263381+0800    Runner  notify_get_state check indicated test daemon not ready.
    error   20:42:04.266933+0800    kernel  Sandbox: Runner(270) deny(2) file-test-existence /private/etc/.mdns_debug
connection  default 20:42:04.292603+0800    Runner  Handshake succeeded
connection  default 20:42:04.292682+0800    Runner  Identity resolved as application<com.example.nativeAddExample>
    error   20:42:04.349930+0800    kernel  Sandbox: Runner(270) deny(1) sysctl-read machdep.cpu.brand_string
    default 20:42:04.583446+0800    Runner  HTHangEventCreate: HangTracing is disabled. Not creating a new event.
Common  default 20:42:04.585776+0800    Runner  FBSWorkspace already connected to endpoint : <private>
    default 20:42:04.591634+0800    Runner  flutter:
    default 20:42:04.609826+0800    Runner  flutter: Another exception was thrown: Instance of 'DiagnosticsProperty<void>'
client  default 20:42:04.627820+0800    Runner  Received configuration update from daemon (initial)
default default 20:42:18.973432+0800    backboardd  Connection removed: IOHIDEventSystemConnection uuid:A226279B-013F-46B6-B4C9-7B366BB8590D pid:270 process:Runner type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 364 attributes:{
    HighFrequency = 0;
    bundleID = "com.example.nativeAddExample";
    pid = 270;
} state:0x1 events:0 mask:0x0
connection  default 20:42:21.008084+0800    Runner  Initializing connection
process default 20:42:21.008176+0800    Runner  Removing all cached process handles
default default 20:42:21.008470+0800    backboardd  Connection added: IOHIDEventSystemConnection uuid:788A6D31-C6AA-4A99-A9DF-0BD3B12C618E pid:278 process:Runner type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 364 attributes:{
    HighFrequency = 0;
    bundleID = "com.example.nativeAddExample";
    pid = 278;
} state:0x0 events:0 mask:0x0
Common  default 20:42:21.012182+0800    Runner  FBSWorkspace connecting to endpoint : <private>
Common  default 20:42:21.012331+0800    Runner  FBSWorkspace registering source: <private>
Common  default 20:42:21.012409+0800    Runner  FBSWorkspace connected to endpoint : <private>
general default 20:42:21.012547+0800    Runner  Added observer for process assertions expiration warning: <_RBSExpirationWarningAssertion: 0x281a578c0; identifier: com.apple.runningboardservices.processExpirationWarningForHandle; reason: observation; valid: YES>
AXSupportCommon default 20:42:21.037445+0800    Runner  Retrieving resting unlock: 0
    error   20:42:21.043662+0800    kernel  Sandbox: Runner(278) deny(1) sysctl-read kern.bootargs
    error   20:42:21.043730+0800    kernel  Sandbox: Runner(278) deny(2) file-test-existence /private/etc/.mdns_debug
Default default 20:42:21.059825+0800    Runner  Registering for test daemon availability notify post.
Default default 20:42:21.059911+0800    Runner  notify_get_state check indicated test daemon not ready.
connection  default 20:42:21.060075+0800    Runner  Sending handshake request attempt dart-lang/ffi#1 to server
connection  default 20:42:21.079293+0800    Runner  Creating connection to com.apple.runningboard
    default 20:42:21.099663+0800    kernel  memorystatus: set assertion priority(10) target Runner:278
connection  default 20:42:21.116539+0800    Runner  Handshake succeeded
connection  default 20:42:21.116590+0800    Runner  Identity resolved as application<com.example.nativeAddExample>
Common  default 20:42:21.116694+0800    Runner  FBSWorkspace already connected to endpoint : <private>
    error   20:42:21.120500+0800    kernel  Sandbox: Runner(278) deny(1) sysctl-read machdep.cpu.brand_string
    default 20:42:21.294773+0800    Runner  HTHangEventCreate: HangTracing is disabled. Not creating a new event.
    default 20:42:21.296731+0800    Runner  flutter:
    default 20:42:21.304259+0800    Runner  flutter: Another exception was thrown: Instance of 'DiagnosticsProperty<void>'
client  default 20:43:21.231815+0800    Runner  Received configuration update from daemon (initial)

Comparison

I created a default flutter project using Android Studio, repeated the Repro steps, and found that the IPA works as expected.

Thoughts

So this is probably not a regular Dart-land problem, and I start to suspect that this is a flutter/Android Studio bug over FFI.

kakyoism commented 4 years ago

Cross-reference: https://github.com/flutter/flutter/issues/62666

@dcharkes

kakyoism commented 4 years ago

This problem is blocking us from doing internal QA. Could anyone share an idea, please?

kakyoism commented 4 years ago

So using sentry, I've got the same error report from the simple native_add example with the development IPA.

ArgumentError native_add.dart in nativeAdd
Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, native_add): symbol not found)
kakyoism commented 4 years ago

Finally found a workaround in this issue . Credits goes to @vladimirdjokic76.

Quoting it here:

In Xcode

Select Target Runner -> Build Settings -> Strip Style -> change from "All Symbols" to "Non-Global Symbols"

The cause of this problem was discussed in these issues

For now, I'm no longer blocked. But this knowledge is essential and should definitely go into the FFI / C Interop documentation.