johnno1962 / injectionforxcode

Runtime Code Injection for Objective-C & Swift
MIT License
6.55k stars 565 forks source link

Xcode 8 Support Plan #159

Closed johntmcintosh closed 7 years ago

johntmcintosh commented 7 years ago

Hi John,

What are you thinking in terms of "supporting" running in Xcode 8 in light of the new limitations around supported plugins? I wanted to see how far I could get, and was able to get an ObjC test project injecting successfully by doing the following:

  1. Based on the discussion from https://github.com/alcatraz/Alcatraz/issues/475, unsign Xcode-beta (noting the risks involved).
  2. Clone the injection repo rather than installing through Alcatraz
  3. Edit the InjectionPlugin and InjectionBundle project files to include my codesigning identity.
  4. Update the injectSource.pl script to apply codesigning for the simulator in addition to the device.
  5. Build injection to install the plugin

After doing these steps, I was able to successfully inject into the ObjC project from Xcode 8 beta. However, I have run into a few issues:

  1. My changes to injectSource.pl are very basic and only support Xcode 8. For example, attempting the codesign for the simulator fails when trying to inject from Xcode 7. (I suspect it wouldn't be too difficult to make this more robust, but it would take me a little while to figure out how to expose the right information to the script.)
  2. I've been unable to successfully inject into a fully Swift project using either Swift 2.3 or Swift 3 -- the two failed for different reasons. (I have not done any tests yet with mixed ObjC/Swift projects.)

Swift 2.3

When I attempt injection in the Swift 2.3 project from Xcode 8, the following is logged to the Xcode console:

2016-08-12 12:02:49.537 InjectionDemo[77493:11945605] Injection attempting connection to: 127.0.0.1:31442
2016-08-12 12:02:49.609 InjectionDemo[77493:11945605] Connected to "Injection" plugin, ready to load x86_64 code.

and the following is logged to the Injection Console:

Connection from: /Users/jtm/Library/Developer/CoreSimulator/Devices/BDD4BF54-DEDE-41E6-8E5A-A74C65B079FA/data/Containers/Data/Application/637BC6FF-1276-4010-B366-FAF39DE80A86 x86_64 (74)
buidRoot: /Users/jtm/Library/Developer/Xcode/DerivedData/InjectionDemo-dakcpbwejytdeneimwfpaodsppmz/Build
logDir: /Users/jtm/Library/Developer/Xcode/DerivedData/InjectionDemo-dakcpbwejytdeneimwfpaodsppmz/Logs/Build
Copying iOSBundleTemplate into project.
Migrating project parameters to bundle..
Inported setting IPHONEOS_DEPLOYMENT_TARGET = 9.3;
Inported setting SDKROOT = iphoneos;
Inported setting CLANG_ENABLE_OBJC_ARC = YES;
Inported setting CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
Inported setting CLANG_CXX_LIBRARY = "libc++";
2016-08-12 12:02:50.439 xcodebuild[77513:11945627] WARNING: Failed to load plugin at path: "/Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin", skipping. Error: Error Domain=NSCocoaErrorDomain Code=3587 "dlopen_preflight(/Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/MacOS/InjectionPlugin): no suitable image found.  Did find:
    /Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/MacOS/InjectionPlugin: cannot load image with wrong team ID in process using Library Validation" UserInfo=NSLocalizedFailureReason=The bundle is damaged or missing necessary resources., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSFilePath=/Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/MacOS/InjectionPlugin, NSDebugDescription=dlopen_preflight(/Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/MacOS/InjectionPlugin): no suitable image found.  Did find:
    /Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/MacOS/InjectionPlugin: cannot load image with wrong team ID in process using Library Validation, NSBundlePath=/Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin, NSLocalizedDescription=The bundle í ÞInjectionPluginí µ couldní Ût be loaded because it is damaged or missing necessary resources.
*** Could not locate compile command for /temp/InjectionDemo/InjectionDemo/ViewController.swift
If you have switched xcode versions, please cmd-shift-k to clean then rebuild the project so there is a complete build history logged and try again.
/Users/jtm/Library/Developer/Xcode/DerivedData/InjectionDemo-dakcpbwejytdeneimwfpaodsppmz/Logs/Build/DF2CCF34-B648-4138-A913-DC6F8C886256.xcactivitylog ***
 at /Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/Resources/common.pm line 57.
    main::error('Could not locate compile command for /temp/InjectionDemo/Inje...') called at /Users/jtm/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin/Contents/Resources/injectSource.pl line 252
*** Bundle build failed ***

The message cannot load image with wrong team ID in process using Library Validation looks suspicious, but best I can tell I do have the signing setup correctly, so there's a chance that's a false alarm and that the "real" error is Could not locate compile command for /temp/InjectionDemo/InjectionDemo/ViewController.swift.

Swift 3

If I take the same swift demo project and run the conversion from Swift 2.3 to Swift 3 and then re-run, the following is logged to the Xcode 8 console:

2016-08-12 12:19:13.265 InjectionDemo[78302:12026915] Injection attempting connection to: 127.0.0.1:31442
2016-08-12 12:19:13.301 InjectionDemo[78302:12026915] Connected to "Injection" plugin, ready to load x86_64 code.
Compiling /temp/InjectionDemo/InjectionDemo/ViewController.swift
objc[78302]: Class _TtC13InjectionDemo14ViewController is implemented in both /Users/jtm/Library/Developer/CoreSimulator/Devices/BDD4BF54-DEDE-41E6-8E5A-A74C65B079FA/data/Containers/Bundle/Application/80FFC61D-BC15-4245-A245-5FD0CED7E1E9/InjectionDemo.app/InjectionDemo (0x10b0042c0) and /temp/InjectionDemo/iOSInjectionProject/build/Debug-iphonesimulator/InjectionBundle2.bundle/InjectionBundle (0x11a62f350). One of the two will be used. Which one is undefined.
2016-08-12 12:19:13.827 InjectionDemo[78302:12025954] Ignore any warning, Swizzled InjectionDemo.ViewController 0x11a62f350 -> 0x10b0042c0

And then we hit EXC_BAD_ACCESS (code=1, address=0xffff...fffe9a0). The bad access appears to be in a function called ivar_getTypeEncodingSwift called from -[NSObject(BundleSweeper) bsweep].

Conclusion

So, with all that said... is this a route that's worth continuing to go down? Or should we consider moving to AppCode for continuing to use injection until we get enough "official" support in Xcode to be able to do this more properly?

(I apologize if this is really multiple separate issues. I'm not sure how much of what I'm running into is related to Xcode 8 plugin changes vs separate changes that need to be made to injection before Swift 2.3 or 3 are supported, unrelated to Xcode 8)

Thanks!

johnno1962 commented 7 years ago

Hi John, you’ve done well to get as far as you did getting injection working. There are a few issues with Xcode 8 most of which you’ve worked though:

You need to unsign Xcode and xcodebuild binaries. You need to codesign injection’s bundle and the on-demand loader for the simulator now. The bad access in ivar_getTypeEncodingSwift is to do with a new way Swift 3 represents types which affects the implementation of instance level “-injected" methods. Not sure what the issue is with Swift 2.3. I think I’ve been able to inject this end. Swift 3 certainly works.

I’m waiting until the smoke clears to decide what to do next. If Xcode continues to load plugins in it's final release version, I’ll release the new versions of the code I have offline in a branch and injection as is will have a future. If not I’ll need to come up with an out of process solution something like the “Injector” project perhaps using the “Smuggler” project if there is interest. Email me at injection at johnholdsworth.com and I can send you a snapshot of the new Xcode 8 code.

johntmcintosh commented 7 years ago

I'd certainly be interested in alternative solutions if things were to be more locked down in the final Xcode 8 release, but hopefully things will stay stable for now, and we'll get some additional official extension points before too long. We'll see...

johntmcintosh commented 7 years ago

@johnno1962 FYI - I confirmed this morning that the injection plugin (with both Swift 2.3 and Swift 3) is still working for me with the new beta 6.

johnno1962 commented 7 years ago

Happy to hear it!

timothycosta commented 7 years ago

Just commenting with exact, quick steps to get injection working with Xcode 8 and the simulator:

  1. Use https://github.com/fpg1503/MakeXcodeGr8Again to create a new unsigned version of Xcode 8
  2. Edit line 510 of injectSource.pl if ( $isDevice ) -> if ( 1 )
  3. Build injection to install
  4. Patch your project for injection
  5. Put your signing identity in identity.txt for all architectures including x86_64
johnno1962 commented 7 years ago

Thanks Timothy. I’m planning to release a version as soon as XCode 8 GM comes out with these changes if things keep working as they do now.