Help debugging a runtime crash, please?

I have a question about a Swift runtime crash I am seeing. It may be a bona fide problem with the Swift runtime, but it just as well might be an error in my code. I don't know where else to ask for help, and I don't think this issue is quite appropriate for the bug reporter.

I have come up with what looks like a workable method for loading in-process plugins in Swift. It involves annotating a method with the (currently technically private) @_cdecl attribute, in order to provide a function I can load and call using dlopen/dlsym. You can view the full code on my GitHub repo here. On my machine, it loads the plugin, calls the exported method, and marshals the object back using Unmanaged<T> and a pointer. However, when I try to call a method on the marshaled object, I get a segmentation fault within the swift_unknownObjectRetain method. I do not know enough about the Swift runtime internals to pin this down to a specific source line, as all I can see in the Xcode debugger is a disassembly.

If someone could please lend me a hand and help me figure out what's going wrong, I would be very appreciative. Thanks!

Have you tried running with Address Sanitizer? It sounds like you might have an overrelease somewhere in your code that goes back and forth through Unmanaged.

I ran with the Address Sanitizer; it made no difference. I also tried changing the bridgeTransfer function to use Unmanaged<T>.takeUnretainedValue() (deliberately leaking memory for testing purposes); that made no difference either. Furthermore, after discovering that @_cdecl apparently allows a return value of type AnyObject? directly, I also tried getting rid of the Unmanaged marshaling and returning my object directly. I still get the crash inside swift_unknownObjectRetain.

I think this problem may have something to do with the Swift runtime in the main binary not knowing about the type metadata in the plugin binary, but I have no evidence to support this theory. I checked the Swift runtime sources, and it appears to be able to load the type metadata automatically as a side-effect of dlopen().

What's the backtrace? You can try the bt command from the debugger. You ought to be able to work your way up to a line in your own code.

The backtrace is as follows:

thread #1, queue = '', stop reason = EXC_BAD_ACCESS (code=1, address=0x1004f22f8)
  frame #0: 0x00007fff65180aa0 libswiftCore.dylib`swift_unknownObjectRetain + 32
* frame #1: 0x0000000100001616 swift-reflection`main() at main.swift:49:13
  frame #2: 0x0000000100000d34 swift-reflection`main at main.swift:52:1
  frame #3: 0x00007fff659f52f5 libdyld.dylib`start + 1

Unfortunately, it doesn't help me very much. Frame #1 above is the following line from my main binary:

exit(Int32(plugin.main(arguments: CommandLine.arguments)))

Xcode does not show me any intermediary steps between the call to plugin.main() and the frame with the crash. Is there any setting I might change to cause Xcode to do so? Doing an instruction-level step-into moves me through multiple levels of indirection involving dyld_stub_binder. Every time this code moves to a new frame, the old frame is overwritten. I really don't know what else to try.

That at least probably indicates that the corruption is happening before you call into the library. Are the library and plugin built with debug info enabled and optimizations disabled? If the plugin is getting moved around from its build directory, it may also be getting separated from its dSYM bundle with the debug info in it. Double-checking those settings, and making sure the debugger is finding debug info for all your libraries, might help, because you ought to have something more than assembly language to step through.

I figured it out! The problem was the Swift package I used was calling dlclose() on the plugin module handle after the call into the @_cdecl function, but before I called into the object returned from that function. As a result, the memory containing the type metadata was deallocated, and any attempt to use it resulted in a segfault. Once I instructed the library to not call dlclose() (at all), the problem went away. Thanks for your help nonetheless!

Ah! Yeah, it is not safe to dlclose a dynamic library with Swift (or ObjC, for that matter) in it.

Terms of Service

Privacy Policy

Cookie Policy