Skip to content

Hook individual objects #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 78 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
f4287f3
add sample for segfault
steipete Jun 6, 2020
807a047
push changes for abort trap 6
steipete Jun 6, 2020
e8a63b8
Cursed first version
steipete Jun 6, 2020
333ba4c
cleanup
steipete Jun 6, 2020
4f85b46
Remove object retain to allow integer returns
steipete Jun 7, 2020
839a2e5
Add integer test case
steipete Jun 7, 2020
a053337
Remove zombies
steipete Jun 7, 2020
7d349a8
Add combined test
steipete Jun 7, 2020
463dc81
Add swiftlint rule
steipete Jun 7, 2020
1fb6c6c
Illegal Instruction 4
steipete Jun 7, 2020
faa3e53
Work around compiler crash
steipete Jun 7, 2020
55cc408
add object task
steipete Jun 7, 2020
d822fc0
Try to make watchOS compile
steipete Jun 7, 2020
e42f2ff
Large class refactor, rename task to hook
steipete Jun 7, 2020
5f0fe6f
remove swiftlint rule for Foundation
steipete Jun 7, 2020
e05016b
swiftlint
steipete Jun 7, 2020
c5cb344
Move types into method call
steipete Jun 7, 2020
eadb073
Add ITKAddSuperMethod and new generics
steipete Jun 8, 2020
0c5624c
Add more tests
steipete Jun 8, 2020
40e8f66
x64 works!
steipete Jun 8, 2020
c9fc2df
Add additional tests
steipete Jun 8, 2020
7888297
Fix memory leak
steipete Jun 8, 2020
17475fd
cleanup
steipete Jun 8, 2020
1357b8d
write arm64
steipete Jun 9, 2020
7a562e8
Use thread local storage
steipete Jun 9, 2020
5c6b75d
cleanup
steipete Jun 9, 2020
9993a61
add links to assembly
steipete Jun 9, 2020
9f345e3
Add test and stret handling
steipete Jun 9, 2020
10c4aa4
Document!
steipete Jun 9, 2020
ef4431f
Remove assembly based object hook
steipete Jun 9, 2020
db7b24d
add link to article
steipete Jun 10, 2020
5619117
header cleanup
steipete Jun 11, 2020
84a1923
Add floating point register protection
steipete Jun 11, 2020
aa86ace
tweak assembly
steipete Jun 11, 2020
430ea37
Prepare new test
steipete Jun 13, 2020
c0779aa
Add tests, remove KVO support
steipete Jun 14, 2020
d014741
lipstick
steipete Jun 14, 2020
53c9371
remove test host for CI
steipete Jun 14, 2020
3c7e36f
really remove test host
steipete Jun 14, 2020
5faede9
use error log
steipete Jun 14, 2020
1695fc7
Disable code signing
steipete Jun 14, 2020
9c9d0d0
todo
steipete Jun 14, 2020
ef94e2f
Warn instead of error for unknown architectures
steipete Jun 14, 2020
ab78db4
Touched by Xcode 11.6
steipete Jun 14, 2020
cd828b5
Addd ILP32 link
steipete Jun 14, 2020
0d20b61
disable code signing
steipete Jun 14, 2020
f960072
Support removal of multiple hooks
steipete Jun 14, 2020
df9ce33
fix jazzy
steipete Jun 14, 2020
562885f
Move error to own file
steipete Jun 14, 2020
505578f
Return self on apply
steipete Jun 14, 2020
524bf93
Support shorter syntax as NSObject category
steipete Jun 14, 2020
e3a954d
Use shorter syntax
steipete Jun 14, 2020
26721b6
Update readme
steipete Jun 14, 2020
304f8ad
Fix Linux
steipete Jun 14, 2020
33f0777
move file to source
steipete Jun 15, 2020
8e024eb
Remove interpose NSObject helper, add class-based version
steipete Jun 15, 2020
54f8686
refer to @_dynamicReplacement
steipete Jun 15, 2020
6f2f13f
Merge branch 'master' into peter/objecthooks
steipete Jun 15, 2020
a66718a
add improvement idea
steipete Jun 15, 2020
66b2224
use shorter hook syntax
steipete Jun 15, 2020
600c58b
Don't swiftlint analyze tests
steipete Jun 15, 2020
471adce
Move SuperBuilder to SwiftPM folder structure
steipete Jun 15, 2020
c73f3c8
Update swiftlint_analyze.yml
steipete Jun 15, 2020
ff9644c
Improve Linux Support
steipete Jun 15, 2020
9207555
Linux Support
steipete Jun 15, 2020
cb3e6b3
Swiftlint
steipete Jun 15, 2020
e55364a
remove unused decl rule
steipete Jun 15, 2020
dcb01ab
add object example
steipete Jun 15, 2020
748a544
Linux
steipete Jun 15, 2020
e994a63
Linux
steipete Jun 15, 2020
122d4df
Linux
steipete Jun 15, 2020
363429b
Linux
steipete Jun 15, 2020
b040f7f
equatable
steipete Jun 15, 2020
9965a84
add objc to error
steipete Jun 15, 2020
a953f70
Linux support
steipete Jun 15, 2020
e042d8f
linux
steipete Jun 15, 2020
a202a0f
linux
steipete Jun 15, 2020
b90bdcd
add docs
steipete Jun 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add ITKAddSuperMethod and new generics
  • Loading branch information
steipete committed Jun 8, 2020
commit eadb0734a479f8eec6e0fca087eede84cf827658
97 changes: 85 additions & 12 deletions InterposeKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,21 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "78C39D762482CC7D00B46395"
BuildableName = "InterposeTests.xctest"
BlueprintName = "InterposeTests"
BuildableName = "InterposeKitTests.xctest"
BlueprintName = "InterposeKitTests"
ReferencedContainer = "container:InterposeKit.xcodeproj">
</BuildableReference>
</TestableReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "78C39D762482CC7D00B46395"
BuildableName = "InterposeTests.xctest"
BlueprintName = "InterposeTests"
BuildableName = "InterposeKitTests.xctest"
BlueprintName = "InterposeKitTests"
ReferencedContainer = "container:InterposeKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
Expand Down Expand Up @@ -52,8 +52,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "78C39D762482CC7D00B46395"
BuildableName = "InterposeTests.xctest"
BlueprintName = "InterposeTests"
BuildableName = "InterposeKitTests.xctest"
BlueprintName = "InterposeKitTests"
ReferencedContainer = "container:InterposeKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
Expand Down
30 changes: 5 additions & 25 deletions InterposeTestHost/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
//
// AppDelegate.swift
// InterposeTestHost
//
// Created by Peter Steinberger on 07.06.20.
// Copyright © 2020 PSPDFKit GmbH. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {


var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()!
(window!.rootViewController as? UINavigationController)?.topViewController?.title = "Test Host"
window!.makeKeyAndVisible()
return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}


}

38 changes: 29 additions & 9 deletions InterposeTestHost/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="1tn-ac-EBX">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="Trk-wk-4or">
<objects>
<navigationController id="1tn-ac-EBX" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="1vj-TX-zC1">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="6lZ-01-xub" kind="relationship" relationship="rootViewController" id="wKN-DH-MvS"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TgX-rg-aXD" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="801" y="133"/>
</scene>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<scene sceneID="Opn-j2-Tea">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<viewController id="6lZ-01-xub" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="31n-bO-IqB">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="DIt-AO-3qQ"/>
</view>
<navigationItem key="navigationItem" id="9eb-Ye-odT"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="Yxf-nQ-wKc" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="800" y="777"/>
</scene>
</scenes>
</document>
19 changes: 0 additions & 19 deletions InterposeTestHost/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,6 @@
<string>public.app-category.developer-tools</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand Down
53 changes: 0 additions & 53 deletions InterposeTestHost/SceneDelegate.swift

This file was deleted.

17 changes: 5 additions & 12 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.0

import PackageDescription

Expand All @@ -11,18 +11,11 @@ let package = Package(
.watchOS(.v5)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "InterposeKit",
targets: ["InterposeKit"]),
.library(name: "InterposeKit",targets: ["InterposeKit"]),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "InterposeKit"),
.testTarget(
name: "InterposeKitTests",
dependencies: ["InterposeKit"]),
.target(name: "ITKAddSuperMethod"),
.target(name: "InterposeKit", dependencies: ["ITKAddSuperMethod"]),
.testTarget(name: "InterposeKitTests", dependencies: ["InterposeKit"]),
]
)
20 changes: 20 additions & 0 deletions Sources/ITKAddSuperMethod/ITKAddSuperMethod.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// ITKAddSuperMethod.h
// InterposeKit
//
// Created by Peter Steinberger on 08.06.20.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
Adds an empty super implementation instance method to klass.
If a method already exists, this will return NO.

@note This uses inline assembly to forward the parameters to objc_msgSendSuper.
*/
BOOL IKTAddSuperImplementationToClass(Class klass, SEL selector);

NS_ASSUME_NONNULL_END
124 changes: 124 additions & 0 deletions Sources/ITKAddSuperMethod/ITKAddSuperMethod.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// ITKAddSuperMethod.m
// InterposeKit
//
// Created by Peter Steinberger on 08.06.20.
// Copyright © 2020 PSPDFKit GmbH. All rights reserved.
//

#import "ITKAddSuperMethod.h"

@import ObjectiveC.message;
@import ObjectiveC.runtime;

NS_ASSUME_NONNULL_BEGIN

void msgSendSuperTrampoline(void);

#if defined(__arm64__)

__attribute__((__naked__))
void msgSendSuperTrampoline(void) {
asm volatile (
" sub sp, sp, #48 ; =48 \n\t"
" stp x29, x30, [sp, #32] ; 16-byte Folded Spill \n\t"
" add x29, sp, #32 ; =32 \n\t"
" stur x0, [x29, #-8] \n\t"
" str x1, [sp, #16] \n\t"
" ldur x8, [x29, #-8] \n\t"
" str x8, [sp] \n\t"
" ldur x0, [x29, #-8] \n\t"
" bl _objc_opt_class \n\t"
" str x0, [sp, #8] \n\t"
" ldr x1, [sp, #16] \n\t"
" mov x0, sp \n\t"
" bl _objc_msgSendSuper \n\t"
" mov x29, x29 ; marker for objc_retainAutoreleaseReturnValue \n\t"
" ldp x29, x30, [sp, #32] ; 16-byte Folded Reload \n\t"
" add sp, sp, #48 ; =48 \n\t"
" ret \n\t"
: : : "x0", "x1");
}

#elif defined(__x86_64__)

__attribute__((__naked__))
void msgSendSuperTrampoline(void) {
asm volatile (
"pushq %%rbp # push frame pointer \n\t"
"movq %%rsp, %%rbp # set stack to frame pointer \n\t"
"subq $32, %%rsp # reserve 32 byte on the stack (need 16 byte alignment) \n\t"
"movq %%rdi, -8(%%rbp) # copy self to stack[1] \n\t"
"movq %%rsi, -16(%%rbp) # copy _cmd to stack[2] \n\t"
"movq -8(%%rbp), %%rax # load self to rax \n\t"
"movq %%rax, -32(%%rbp) # store self to stack[4] \n\t"
"movq -8(%%rbp), %%rdi # load self to rdi-first parameter \n\t"
"callq _objc_opt_class # call objc_opt_class(self) \n\t"
"#movq %%rax, %%rdi # move result to rdi-first parameter \n\t"
"#callq _class_getSuperclass # call class_getSuperclass(self) \n\t"
"movq %%rax, -24(%%rbp) # move result to stack[3] \n\t"
"movq -16(%%rbp), %%rsi # copy _cmd to #rsi \n\t"
"xorl %%ecx, %%ecx # nill out rcx? \n\t"
"leaq -32(%%rbp), %%rdi # load address of objc_super struct to rdi-first param \n\t"
"movb %%cl, %%al # nill ot rax/rcx? \n\t"
"callq _objc_msgSendSuper # regular call \n\t"
"movq %%rax, %%rdi \n\t"
"addq $32, %%rsp # remove 32 byte from stack \n\t"
"popq %%rbp # pop frame pointer \n\t"
"retq\n\r"
: : : "rsi", "rdi");
}

#endif

typedef NS_ENUM(NSInteger, DispatchMode) {
DispatchMode_Normal,
DispatchMode_Stret,
};

static DispatchMode IKTGetDispatchMode(const char * typeEncoding) {
DispatchMode dispatchMode = DispatchMode_Normal;
#if defined (__arm64__)
// ARM64 doesn't use stret dispatch
#elif defined (__x86_64__)
// On x86-64, stret dispatch is used whenever return type doesn't fit into two registers
NSUInteger returnTypeActualSize = 0;
NSGetSizeAndAlignment(typeEncoding, &returnTypeActualSize, NULL);
dispatchMode = returnTypeActualSize > (sizeof(void *) * 2) ? DispatchMode_Stret : DispatchMode_Normal;
#else
#error - Unknown architecture
#endif
return dispatchMode;
}

BOOL IKTAddSuperImplementationToClass(Class klass, SEL selector) {
Class originalClass = klass;

Class superClass = class_getSuperclass(originalClass);
if (superClass == nil) {
return NO;
}
Method method = class_getInstanceMethod(superClass, selector);
if (method == NULL) {
[NSException raise:NSInternalInconsistencyException
format:@"No dynamically dispatched method with selector %@ is available on any of the superclasses of %@",
NSStringFromSelector(selector), NSStringFromClass(originalClass)];
return NO;
}
const char *typeEncoding = method_getTypeEncoding(method);
// Need to write asm for x64
__unused DispatchMode dispatchMode = IKTGetDispatchMode(typeEncoding);
BOOL methodAdded = class_addMethod(klass,
selector,
msgSendSuperTrampoline,
typeEncoding);
if (!methodAdded) {
NSLog(@"Failed to add method for selector %@ to class %@",
NSStringFromSelector(selector),
NSStringFromClass(klass));
}

return methodAdded;
}

NS_ASSUME_NONNULL_END
Loading