flutterust icon indicating copy to clipboard operation
flutterust copied to clipboard

IOS Release running error: Failed to lookup symbol

Open yiv opened this issue 4 years ago • 10 comments

When I compile and run in deubg mode on ios, everything works fine. But when I switch to release, I got the error like below:

2021-01-26 20:36:03.466780+0800 Runner[26430:9877639] Metal API Validation Enabled
2021-01-26 20:36:03.669728+0800 Runner[26430:9877805] [VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, store_dart_post_cobject): symbol not found)
#0      DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31)
#1      _store_dart_post_cobject (package:full_search/ffi.dart)
#2      _store_dart_post_cobject (package:full_search/ffi.dart)
#3      store_dart_post_cobject (package:full_search/ffi.dart:263)
#4      SearchEngine.setup (package:full_search/full_search.dart:12)
#5      _MyAppState.initSearchEngine (package:full_search_example/main.dart:38)
#6      _MyAppState.initState (package:full_search_example/main.dart:31)
#7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4822)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4659)
#9      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3625)
#10     Element.updateChild (package:flutter/src/widgets/framework.dart:3390)
#11     RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1208)
#12     RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:1179)
#13     RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:1121)
#14     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2731)
#15     RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:1120)
#16     WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:960)
#17     WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:941)
#18     _rootRun (dart:async/zone.dart:1178)
#19     _CustomZone.run (dart:async/zone.dart:1090)
#20     _CustomZone.runGuarded (dart:async/zone.dart:994)
#21     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1034)
#22     _rootRun (dart:async/zone.dart:1186)
#23     _CustomZone.run (dart:async/zone.dart:1090)
#24     _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1018)
#25     TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:399)
#26     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395)
#27     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426)
#28     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184)

yiv avatar Jan 26 '21 12:01 yiv

you need to include allo-isolate crate. or that means that Xcode tree-shaking process decided to strip your static library.

to solve this issue, there is a workaround this to force Xcode Link to your library. what should do is creating a no-op function let's call it link_me_please something like this

#[no_mangle]
pub extern "C" fn link_me_please() {}

and then in your package/ios add a dommy method in your class that calls this function. here is an example from this repo: https://github.com/shekohex/flutterust/blob/master/packages/adder_ffi/ios/Classes/SwiftAdderPlugin.swift#L13

note that your Makefile.toml the cargo make script should also copy the binding.h (the generated header file) to Classes folder .. which I guess is set to that by default.

shekohex avatar Jan 26 '21 13:01 shekohex

Right now, I did like this

https://github.com/shekohex/flutterust/blob/master/packages/scrap_ffi/ios/Classes/SwiftScrapPlugin.swift

public static func dummyMethodToEnforceBundling() {
    last_error_length()
  }

It works well in ios debug mode, async function works well too.

And I found a releated issue in another project. https://github.com/brickpop/flutter-rust-ffi/issues/7

I'm sorry to be late on this, but where the readme says add a dummy method in SwiftMylibPlugin.swift that uses at least one of the native functions, it should say add a dummy call for every single one of them.

For some unexplainable reason:

In Debug mode, XCode will bundle the entire library if at least one library function is used. In Release mode, XCode will do tree shaking, and only bundle the native functions and the callees that you explicitly reference I learnt it the hard way myself, I'm updating the Readme right now.

yiv avatar Jan 26 '21 14:01 yiv

And I'm confused. There is no store_dart_post_cobject function in binding.h, how can it be called? And it work fine in IOS debug mode.

yiv avatar Jan 26 '21 14:01 yiv

the store_dart_post_cobject is function exported from allo-isolate package. so one way to make it exported is to re-export it again or use something like this:

use allo_isolate::ffi;

#[no_mangle]
pub unsafe extern "C" fn store_dart_post_cobject(
    ptr: ffi::DartPostCObjectFnType,
) {
    allo_isolate::store_dart_post_cobject(ptr);
}

shekohex avatar Jan 26 '21 16:01 shekohex

Also worth mentioning this too: https://flutter.dev/docs/development/platform-integration/c-interop#ios-symbols-stripped

shekohex avatar Jan 26 '21 17:01 shekohex

iOS symbols stripped
When creating a release archive (IPA) the symbols are stripped by Xcode.

In Xcode, go to Target Runner > Build Settings > Strip Style.
Change from All Symbols to Non-Global Symbols.

After did like above.

This time, I am trying to build and run flutterust project directly in IOS release mode. Also facing the same error.

2021-01-27 10:22:42.684201+0800 Runner[27043:10106244] Metal API Validation Enabled
2021-01-27 10:22:43.004687+0800 Runner[27043:10106378] flutter: Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, store_dart_post_cobject): symbol not found)
2021-01-27 10:22:43.008625+0800 Runner[27043:10106378] flutter: #0      DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31)
2021-01-27 10:22:43.008682+0800 Runner[27043:10106378] flutter: #1      _store_dart_post_cobject (package:scrap/ffi.dart)
2021-01-27 10:22:43.008705+0800 Runner[27043:10106378] flutter: #2      _store_dart_post_cobject (package:scrap/ffi.dart)
2021-01-27 10:22:43.008723+0800 Runner[27043:10106378] flutter: #3      store_dart_post_cobject (package:scrap/ffi.dart:63)
2021-01-27 10:22:43.008741+0800 Runner[27043:10106378] flutter: #4      Scrap.setup (package:scrap/scrap.dart:10)
2021-01-27 10:22:43.008760+0800 Runner[27043:10106378] flutter: #5      _MyHomePageState.initState (package:flutterust/main.dart:37)
2021-01-27 10:22:43.008776+0800 Runner[27043:10106378] flutter: #6      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4822)

yiv avatar Jan 27 '21 02:01 yiv

Unfortunately, I don't have access to a macOS Machine right now to do tests and try to fix this issue, but feel free if you got it to work correctly to Open a PR for README to add steps to fix this issue. Thanks :)

shekohex avatar Jan 27 '21 14:01 shekohex

You can pull this project from me and it has been resolved. https://github.com/Aaron009/flutter-rust-ffi

the reason: https://github.com/brickpop/flutter-rust-ffi/pull/23

Aaron009 avatar Feb 11 '21 20:02 Aaron009

I separated a plugin for just the basics like lazy initialization of the RUNTIME and linking of the store_dart_post_cobject symbol. In the Swift plugin header, I have added a declaration

In packages/runtime_ffi/ios/Classes/RuntimePlugin.h:

#import <Flutter/Flutter.h>

void* store_dart_post_cobject(void*);

@interface RuntimePlugin : NSObject<FlutterPlugin>
@end

In a static function that I don't call, I call all of the methods that I would like linked. In packages/runtime_ffi/ios/SwiftRuntimePlugin.swift:

import Flutter
import UIKit

public class SwiftRuntimePlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "runtime", binaryMessenger: registrar.messenger())
    let instance = SwiftRuntimePlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    result("iOS " + UIDevice.current.systemVersion)
  }

  public static func dummyMethodToEnforceBundling() {
    store_dart_post_cobject(nil)
    runtime_setup()
    last_error_length()
    error_message_utf8(nil, 0)
  }
}

This is all with Non-Global Symbols set for iOS Screen Shot 2022-02-08 at 3 12 48 PM

I believe that any other symbols that could have this issue would be in Rust code that you add. Those symbols should be trivial to find and add to dummyMethodToEnforceBundling for each package plugin.

trueb2 avatar Feb 08 '22 21:02 trueb2

I found that even though this allows the build to succeed, this is not a fix. The POST_COBJECT is never stored correctly and is always None during post calls even after setup.

trueb2 avatar Feb 09 '22 15:02 trueb2