dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
105 stars 36 forks source link

Symbol lookup for plugin fails on iOS with static linkage #902

Open mityax opened 2 years ago

mityax commented 2 years ago

In order to use a Google MLKit plugin in my Flutter App on iOS, I had to set :linkage => :static in my Podfile. After doing so, the webcrypto plugin (which worked fine before) fails to lookup native boringssl symbols at runtime (it uses DynamicLibrary.executable() here).

Stack trace (in case it helps):

flutter: Unsupported operation: package:webcrypto cannot be used from scripts or `flutter test` unless `flutter pub run webcrypto:setup` has been run for the current root project.
flutter: #0      lookup.<anonymous closure> (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:47:5)
flutter: dart-lang/ffi#1      lookup (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:53:2)
flutter: dart-lang/ffi#2      lookup (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart)
flutter: dart-lang/ffi#3      _cachedLookup (package:webcrypto/src/boringssl/lookup/lookup.dart:26:21)
flutter: dart-lang/ffi#4      _cachedLookup (package:webcrypto/src/boringssl/lookup/lookup.dart)
flutter: dart-lang/ffi#5      ssl (package:webcrypto/src/boringssl/lookup/lookup.dart:29:44)
flutter: dart-lang/ffi#6      ssl (package:webcrypto/src/boringssl/lookup/lookup.dart)
flutter: dart-lang/ffi#7      _aesCbcEncryptOrDecrypt (package:webcrypto/src/impl_ffi/impl_ffi.aescbc.dart:41:52)

So the original exception seems to appear here.

I already tried setting the Strip-Style to "Non-Global Symbols" in Xcode, as suggested in the docs, but it doesn't make a difference.

Note: I also created an issue in the webcrypto plugin itself, but I'm not sure where it rather belongs and it currently blocks the release of our application, which is why I need to find a way to solve it fast. Hopefully here it's visible to more experts on this (I'm a noob with anything related to ffi on ios).

I'd really appreciate any help on this, as I'm completely stuck here. Please let me know if I need to provide more information.

dcharkes commented 2 years ago

I can reproduce your issue by changing flutter_app/ios/Podfile:

target 'Runner' do
  use_frameworks! :linkage => :static
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

For a FFI plugin to support static linking, I need to meddle with linker flags to avoid stripping symbols. For example in mylib_staticlib/ios/mylib_staticlib.podspec:

  s.pod_target_xcconfig = {
    'DEFINES_MODULE' => 'YES',
    'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
    "OTHER_LDFLAGS[sdk=iphoneos*]" => "-force_load $(PODS_TARGET_SRCROOT)/Frameworks/mylib_staticlib.xcframework/ios-arm64_armv7/libmylib_staticlib.a",
    "OTHER_LDFLAGS[sdk=iphonesimulator*]" => "-force_load $(PODS_TARGET_SRCROOT)/Frameworks/mylib_staticlib.xcframework/ios-arm64_x86_64-simulator/libmylib_staticlib.a",
  }
  s.vendored_frameworks = 'Frameworks/mylib_staticlib.xcframework'

However, when building from source, we just depend on s.source_files in mylib_source/ios/mylib_source.podspec:

  s.source_files = 'Classes/**/*'
  s.dependency 'Flutter'
  s.platform = :ios, '10.0'

  s.pod_target_xcconfig = {
    'DEFINES_MODULE' => 'YES',
    'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386'
  }

And the lookup can be done in DynamicLibrary.open('$_libName.framework/$_libName') rather than DynamicLibrary.executable() which can fail when there's duplicate symbols in different frameworks.

This is the setup used in package:webcrypto but it still uses DynamicLibrary.executable(). If it would have been using DynamicLibrary.open('$_libName.framework/$_libName') that step would fail instead of trying to lookup the symbol.

If we were to add those linker flags, the linking would fail without :linkage => :static. So, I'm not sure which route to go here. We would need to investigate how to make cocoapods packages that work both with static and dynamic linking and when used with static linking we want to ensure the symbols are not stripped out.

(As a side note, we would in Dart code also need to fallback to DynamicLibrary.executable().)