JetBrains / kotlin-native

Kotlin/Native infrastructure
Apache License 2.0
7.02k stars 566 forks source link

(Missing c++ linkerflag) +[BLEDeviceInfo isReachOS:]: unrecognized selector sent to class 0x1016a5818 #3851

Closed ArthurBrum closed 4 years ago

ArthurBrum commented 4 years ago

In my project, I'm using an Objective-C library through cinterop. The external libs are used to connect with bluetooth devices and has dependencies with CoreBluetooth, ExternalAccessory and other Apple frameworks. I'm not totally sure if I have all my targets and sources settings correctly, but you can check details about my build.gradle files here https://github.com/JetBrains/kotlin-native/issues/3827#issue-560593909 if someone think it could help...

But when I try to run a simple scan for devices, I get these errors on the logs from xcode:

objc[342]: Class _NSZombie_OS_dispatch_data is implemented in both ?? (0x280404840) and ?? (0x280409da0). One of the two will be used. Which one is undefined.
objc[342]: Class _NSZombie_OS_xpc_data is implemented in both ?? (0x280409e30) and ?? (0x280409e90). One of the two will be used. Which one is undefined.
objc[342]: Class _NSZombie_OS_xpc_uint64 is implemented in both ?? (0x280409ec0) and ?? (0x280404a50). One of the two will be used. Which one is undefined.
objc[342]: Class _NSZombie_OS_xpc_int64 is implemented in both ?? (0x280400d80) and ?? (0x280400ea0). One of the two will be used. Which one is undefined.
objc[342]: Class _NSZombie_OS_voucher is implemented in both ?? (0x28041d470) and ?? (0x28041eb80). One of the two will be used. Which one is undefined.
BTest : Got here!!  // Just a friendly print checkpoint
2020-02-10 17:05:58.070363-0300 iosApp[342:37453] +[BLEDeviceInfo isReachOS:]: unrecognized selector sent to class 0x1016a5818
2020-02-10 17:05:58.073366-0300 iosApp[342:37453] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[BLEDeviceInfo isReachOS:]: unrecognized selector sent to class 0x1016a5818'
*** First throw call stack:
(0x1fb43b180 0x1fa6139f8 0x1fb35ca08 0x1fb4409c8 0x1fb44265c 0x1015a74cc 0x200f55114 0x200f561f0 0x200f64438 0x1018736f4 0x101874c78 0x10187cbf4 0x10187d8ec 0x10187ca4c 0x10187d8b4 0x10188777c 0x1fb05a114 0x1fb05ccd4)
libc++abi.dylib: terminating with uncaught exception of type NSException

Maybe I have a smaller problem here with duplicated linked libraries, but I think the bigger one is that I cannot know which are the classes referenced by these errors. Is there a better way to debug this instead of using lldb? Can I export the references for the classes somehow?

ArthurBrum commented 4 years ago

Just in case, the code I'm trying to run is:

package com.fidential.project

import com.github.florent37.log.Logger
import platform.darwin.NSObject

fun makeBTest() = BTest()

actual class BTest {

    val TAG = "BTest"

    private val delegate = object : NSObject(), LibDeviceSearchListenerProtocol {

        override fun discoveredDevice(reader: LibDevice?) {
            Logger.d(TAG, reader?.name ?: "None found")
        }

        override fun discoveryComplete() {
            Logger.d(TAG, "discoveryComplete.")
        }
    }

    var deviceManager: LibDeviceManagerProtocol? = null

    actual fun startSearch() {
        this.deviceManager = Lib.getDeviceManager(LibDeviceTypeEnum.LibDeviceTypeValue01)
        this.deviceManager?.searchDevicesWithLowRSSI(-124, 100, this.delegate)
        Logger.d(TAG, "Got here!! \n")
    }
}

And I'm calling

        let a = BTestKt.makeBTest()
        a.startSearch()

in my iOS app. And about isReachOS, its not in my code neither in the headers that I have access of this Lib

artdfel commented 4 years ago

Hi! Have you tried this approach?

ArthurBrum commented 4 years ago

Well, I'm not experienced at using lldb, but how could I add breakpoints to my app files? I dont know the name of files generated. I tried:

objc[359]: Class _NSZombie_OS_dispatch_data is implemented in both ?? (0x281b2ea60) and ?? (0x281b52bb0). One of the two will be used. Which one is undefined.
objc[359]: Class _NSZombie_OS_xpc_data is implemented in both ?? (0x281b52d90) and ?? (0x281b2ee50). One of the two will be used. Which one is undefined.
BTest : Got here!! 
2020-02-11 08:38:12.755820-0300 iosApp[359:50145] +[BLEDeviceInfo isReachOS:]: unrecognized selector sent to class 0x100d19818
2020-02-11 08:38:12.763249-0300 iosApp[359:50145] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[BLEDeviceInfo isReachOS:]: unrecognized selector sent to class 0x100d19818'
*** First throw call stack:
(0x1fb43b180 0x1fa6139f8 0x1fb35ca08 0x1fb4409c8 0x1fb44265c 0x100c1b4cc 0x200f55114 0x200f561f0 0x200f64438 0x10097b6f4 0x10097cc78 0x100984bf4 0x1009858ec 0x100984a4c 0x1009858b4 0x10098f77c 0x1fb05a114 0x1fb05ccd4)
libc++abi.dylib: terminating with uncaught exception of type NSException

(lldb) b -a 0x100d19818
Breakpoint 1: where = BC_lib`BLEDeviceInfo, address = 0x0000000100d19818
(lldb) b -a 0x281b2ea60
Breakpoint 2: address = 0x0000000281b2ea60
(lldb) b -a 0x281b52bb0
Breakpoint 3: address = 0x0000000281b52bb0
(lldb) b -a 0x1fb43b180
Breakpoint 4: where = CoreFoundation`__exceptionPreprocess + 228, address = 0x00000001fb43b180
(lldb) b -a 0x1fa6139f8
Breakpoint 5: where = libobjc.A.dylib`objc_exception_throw + 56, address = 0x00000001fa6139f8
(lldb) b -a 0x1fb35ca08
Breakpoint 6: where = CoreFoundation`+[NSOrderedSet orderedSetWithSet:], address = 0x00000001fb35ca08
(lldb) b -a 0x1fb44265c
Breakpoint 7: where = CoreFoundation`_CF_forwarding_prep_0 + 92, address = 0x00000001fb44265c
(lldb) b -a 0x1fb4409c8 0x100c1b4cc 0x200f55114
Breakpoint 8: where = CoreFoundation`___forwarding___ + 1408, address = 0x00000001fb4409c8
(lldb) b -a 0x100c1b4cc
Breakpoint 9: where = BC_lib`-[BLEClientDriver centralManager:didDiscoverPeripheral:advertisementData:RSSI:] + 376, address = 0x0000000100c1b4cc
(lldb) b -a 0x200f55114
Breakpoint 10: where = CoreBluetooth`-[CBCentralManager handlePeripheralDiscovered:] + 1060, address = 0x0000000200f55114
(lldb) b -a 0x200f561f0
Breakpoint 11: where = CoreBluetooth`-[CBCentralManager handleMsg:args:] + 620, address = 0x0000000200f561f0
(lldb) b -a 0x200f64438
Breakpoint 12: where = CoreBluetooth`__30-[CBXpcConnection _handleMsg:]_block_invoke + 60, address = 0x0000000200f64438
(lldb) b -a 0x10097b6f4
Breakpoint 13: where = libdispatch.dylib`_dispatch_call_block_and_release + 24, address = 0x000000010097b6f4
(lldb) b -a 0x10097cc78
Breakpoint 14: where = libdispatch.dylib`_dispatch_client_callout + 16, address = 0x000000010097cc78
(lldb) b -a 0x100984bf4
Breakpoint 15: where = libdispatch.dylib`_dispatch_lane_serial_drain + 712, address = 0x0000000100984bf4
(lldb) b -a 0x1009858ec
Breakpoint 16: where = libdispatch.dylib`_dispatch_lane_invoke + 512, address = 0x00000001009858ec
(lldb) b -a 0x100984a4c
Breakpoint 17: where = libdispatch.dylib`_dispatch_lane_serial_drain + 288, address = 0x0000000100984a4c
(lldb) b -a 0x1009858b4
Breakpoint 18: where = libdispatch.dylib`_dispatch_lane_invoke + 456, address = 0x00000001009858b4
(lldb) b -a 0x10098f77c
Breakpoint 19: where = libdispatch.dylib`_dispatch_workloop_worker_thread + 1148, address = 0x000000010098f77c
(lldb) b -a 0x1fb05a114
Breakpoint 20: where = libsystem_pthread.dylib`_pthread_wqthread + 304, address = 0x00000001fb05a114
(lldb) b -a 0x1fb05ccd4
Breakpoint 21: where = libsystem_pthread.dylib`start_wqthread + 4, address = 0x00000001fb05ccd4
ArthurBrum commented 4 years ago

Hi! Have you tried this approach?

Hi! Could you give me instructions on how to compile my framework with debug flags? I'm running into this same issue in several parts of my code now and I really need to fix this :(

ArthurBrum commented 4 years ago

As I was searching this symbol "isReachOS" inside the other files, I found it inside the binaries of another framework that I thought was being correctly compiled and linked through cinterop... but it actually wasnt. It was generating just an empty kotlin file inside myProject/build/classes/.../myProject-cinterop-libName.klib-build

And the worst part is that this second framework is partly written in C++, so even if when I fixed the cinterop .def files, it could not add it in my kotlin multiplatform project... It kept giving me

> Task :BcLib:cinteropLibNameIos FAILED
Exception in thread "main" java.lang.Error: 
src/nativeInterop/frameworks/LibName.framework/Headers/LDInfoParser.h:190:1: error: unknown type name 'class'
        at org.jetbrains.kotlin.native.interop.indexer.UtilsKt.ensureNoCompileErrors(Utils.kt:146)
        at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.indexDeclarations(Indexer.kt:966)
        at org.jetbrains.kotlin.native.interop.indexer.IndexerKt.buildNativeIndexImpl(Indexer.kt:955)
        at org.jetbrains.kotlin.native.interop.indexer.NativeIndexKt.buildNativeIndex(NativeIndex.kt:91)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.processCLib(main.kt:221)
        at org.jetbrains.kotlin.native.interop.gen.jvm.MainKt.interop(main.kt:39)
        at org.jetbrains.kotlin.cli.utilities.InteropCompilerKt.invokeInterop(InteropCompiler.kt:71)
        at org.jetbrains.kotlin.cli.utilities.MainKt.main(main.kt:18)

Is it possible to solve this by importing one framework in my kotlin multiplatform project and this second one in xcode? I tried removing the second one from the gradle and it started asking for some symbols (that are from the removed framework). I tried some other ways, got to successful compilation, but crashed in runtime when launching from xcode). I don't need to call any function from this second lib, it just have to be there because this first ObjC framework is calling it under the hoods, so its probably just about linking it correctly... Does anyone has any idea? or just opinions if this could ever work??

Thanks in advance!

artdfel commented 4 years ago

Hello again, @ArthurBrum. Can you clarify a little, what is the current status of your problem? It seems like you found the root cause successfully, but cannot separate framework to be interop'ted from one that has to be attached to the Xcode project, am I right? If this is correct, then you're probably doing everything fine. The crash you experience now, is it the same as one from the topic start? What are those "other ways" you mention?

ArthurBrum commented 4 years ago

Hello again, @ArthurBrum. Can you clarify a little, what is the current status of your problem? It seems like you found the root cause successfully, but cannot separate framework to be interop'ted from one that has to be attached to the Xcode project, am I right? If this is correct, then you're probably doing everything fine. The crash you experience now, is it the same as one from the topic start?

Yes, you understood it correctly. The issue is still the same. I still can not make my lib use the function 'isReachOS' successfully.

(To be clear: this function, I don't need to call it directly, it's just being called under the hood... That is, I only call Lib_One directly in my project. Lib_One calls Lib_Two and Lib_Two has this isReachOS somewhere in its binaries, but not in its headers.)

What are those "other ways" you mention?

So, to explain my attempts better, I'll clarify the files I had for each situation:

Starting State

In here, I thought I was using the 2 frameworks correctly, but just Lib_One was generating a useful kotlin file from the cinterop'ing process. Lib_Two was generating an empty kotlin file inside myProject/build/classes/.../myProject-cinterop-libName.klib-build

Gradle File

(...)
fromPreset(iosTarget, 'ios') {
    binaries {
        framework('BC_lib'){
            linkerOpts "-Fsrc/nativeInterop/frameworks"
        }
    }
    compilations.main {
        cinterops {
            Lib_One {
                packageName 'com.fidential.bclib'
                compilerOpts '-Isrc/nativeInterop/frameworks/Lib_One.framework/Headers'
            }
            Lib_Two {
                packageName 'com.fidential.bclib'
                compilerOpts '-Isrc/nativeInterop/frameworks/Lib_Two.framework/Headers'
            }
        }
    }
}
(...)

Lib_One.def

language = Objective-C
package = com.fidential.bclib
headers = Lib_One.h
linkerOpts = -framework Lib_One -framework UIKit -framework CoreBluetooth

Lib_Two.def

language = Objective-C
package = com.fidential.bclib
headerFilter =  *.h
linkerOpts = -framework Lib_Two

Xcode

Just including my BC_lib.framework (that is always generated by my kmp project)

Attempt 1: Cinterop'ing the 2 frameworks correctly

As result of this first trial, I ran into the errors of my last comment error: unknown type name 'class'
I think it happened because cinterop currently doesnt support C++ headers

Lib_Two.def

language = Objective-C
package = com.fidential.bclib
headers =  several.h \
other.h \
header.h \
files.h
linkerOpts = -framework Lib_Two

No changes in other files

Attempt 2: Cinterop'ing just Lib_One

Gradle File

(...)
fromPreset(iosTarget, 'ios') {
    binaries {
        framework('BC_lib'){
            linkerOpts "-Fsrc/nativeInterop/frameworks"
        }
    }
    compilations.main {
        cinterops {
            Lib_One {
                packageName 'com.fidential.bclib'
                compilerOpts '-Isrc/nativeInterop/frameworks/Lib_One.framework/Headers'
            }
        }
    }
}
(...)

(same .def files from starting state)

As result, I got gradle compiling errors like:

> Task :BcLib:linkBC_libDebugFrameworkIos
Undefined symbols for architecture x86_64:

  "_OBJC_CLASS_$_LDBluetoothManager", referenced from:
      objc-class-ref in Lib_One(DeviceCode-DeviceManager.o)
  "_OBJC_CLASS_$_RDeviceInfo", referenced from:
      objc-class-ref in LibOne(NameReader.o)

 (...)

Which I know are symbols that exist in Lib_Two headers

Attempt 3: Trying to link the second framework without cinterop'ing it

(Just added -framework Lib_Two at the end of linkerOpts of Lib_One.def)

Gradle file just like Attempt 2

Lib_One.def

language = Objective-C
package = com.fidential.bclib
headers = Lib_One.h
linkerOpts = -framework Lib_One -framework UIKit -framework CoreBluetooth -framework Lib_Two

Here I got the same error saying isReachOS couldnt be found.

Attempt 4: Trying to add Lib_Two in xcode

No changes in files

In xcode, I drag & dropped the Lib_Two framework, and set it to embed & sign and other combinations of it

No success, same error as Attempt 3

ArthurBrum commented 4 years ago

Also, from the provider of the frameworks, I have some sample projects that includes both Lib_One and Lib_Two and another lib called libc++.tdb I dont know if it makes any difference, but I think I should mention it

artdfel commented 4 years ago

I suppose that libc++ should not affect your case. In the Xcode, are you adding this Lib_Two.framework to the result app target or the kotlin-produced framework? I think it should be in the framework build phases, like in this manual. Are you registered in the kotlinlang Slack? The community there is much more responsive, as we use GH mostly for reporting bugs.

ArthurBrum commented 4 years ago

Hey @artdfel! Thanks for helping me out! I finally managed to fix it. I just added some flags to my Lib_One.def like: linkerOpts = -all_load -ObjC -lc++ -framework Lib_One -framework Lib_Two

And it works!!! I wonder why it was needed, but found it with the help of a friend at work, he logged the ld command called by xcode in the sample projects and figure it out!

And also thanks for the advice, I'll try talking to the community through the kotlinlang slack then!!

artdfel commented 4 years ago

Nice to hear about it! About the Slack, you can find useful links here.