Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift funtime 🎉

Swift funtime 🎉

A look at accomplishing the things we loved in the Objective-C runtime with Swift. The talk was given at AltConf 2015.

Boris Bügling

June 10, 2015
Tweet

More Decks by Boris Bügling

Other Decks in Programming

Transcript

  1. !

  2. ▸ behaves like any old Objective-C object ▸ instance variables

    are properties ▸ fully interopable with ObjC
  3. ▸ has SwiftObject as superclass ▸ instance variables are ivars

    only ▸ ivars have no type encoding ▸ methods are not ObjC methods ▸ not interoperable with ObjC
  4. func info<T>(x: T) { print("\(x) is a \(_stdlib_getDemangledTypeName(x))") } let

    array = [0, 1, 2] // 'as AnyObject' => ! info(array) // is a Swift.Array import Foundation let objc_array: AnyObject = [0, 1, 2] as AnyObject info(objc_array) // is a Swift._NSSwiftArrayImpl // comparing different array types => compiler error as well //let equal = objc_array == array
  5. !

  6. SWIFT 2.0 ./foo.swift:3:17: error: 'CFunctionPointer' is unavailable: use a function

    type '@convention(c) (T) -> U' typealias foo = CFunctionPointer<() -> ()>
  7. C FUNCTION POINTERS SWIFT 1.X CFunctionPointer<(UnsafeMutablePointer<Void>, Float) -> Int>.self SWIFT

    2.X typealias CFunction = @convention(c) (UnsafeMutablePointer<Void>, Float) -> Int CFunction.self
  8. Y DID WE ! THE OBJECTIVE-" RUNTIME? ▸ Dynamic Introspection

    ! ▸ Change Behaviour "# ▸ Analyse private API $
  9. var propertyCount : UInt32 = 0 var properties : UnsafeMutablePointer<objc_property_t>

    = class_copyPropertyList(myClass, &propertyCount) for i in 0..<propertyCount { print("Property: " + String.fromCString(property_getName(properties[Int(i)]))!) }
  10. THERE IS HOPE // Excerpt from the standard library ///

    How children of this value should be presented in the IDE. enum MirrorDisposition { case Struct case Class case Enum [...] } /// A protocol that provides a reflection interface to an underlying value. protocol MirrorType { [...] }
  11. infix operator --> {} func --> (instance: Any, key: String)

    -> Any? { let mirror = reflect(instance) for index in 0 ..< mirror.count { let (childKey, childMirror) = mirror[index] if childKey == key { return childMirror.value } } return nil }
  12. struct MyPoint { let x: Float let y: Float }

    let point = MyPoint(x: 1, y: 2) print(point --> "x") // Optional(1.0) print(point --> "y") // Optional(2.0)
  13. let myString = "foobar" as NSString print(myString.description) // foobar let

    myBlock : @convention(block) (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in "✋" } let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) let method = class_getInstanceMethod(myString.dynamicType, "description") method_setImplementation(method, myIMP) print(myString.description) // ✋
  14. SWROUTE ▸ PoC of function hooking in Swift ▸ Uses

    rd_route, a Mach specific injection library for C
  15. #include <stdint.h> #define kObjectFieldOffset sizeof(uintptr_t) struct swift_func_object { uintptr_t *original_type_ptr;

    #if defined(__x86_64__) uintptr_t *unknown0; #else uintptr_t *unknown0, *unknown1; #endif uintptr_t function_address; uintptr_t *self; };
  16. uintptr_t _rd_get_func_impl(void *func) { struct swift_func_object *obj = (struct swift_func_object

    *) *(uintptr_t *)(func + kObjectFieldOffset); return obj->function_address; }
  17. MEMORY LAYOUT ▸ 8 bytes => Pointer to _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ▸

    8 bytes => Pointer to struct _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> partial apply forwarder for reabstraction thunk helper [...]
  18. MEMORY LAYOUT ▸ 16 bytes => Swift object ▸ 8

    bytes => Pointer to _TF6memory3addFTSiSi_Si Function pointer !
  19. struct f_trampoline { var trampoline_ptr: COpaquePointer var function_obj_ptr: UnsafeMutablePointer<function_obj> }

    struct function_obj { var some_ptr_0: COpaquePointer var some_ptr_1: COpaquePointer var function_ptr: COpaquePointer }
  20. @asmname("floor") func my_floor(dbl: Double) -> Double print(my_floor(6.7)) let handle =

    dlopen(nil, RTLD_NOW) let pointer = COpaquePointer(dlsym(handle, "ceil")) typealias FunctionType = (Double) -> Double
  21. struct f_trampoline { [...] } struct function_obj { [...] }

    let orig = unsafeBitCast(my_floor, f_trampoline.self) let new = f_trampoline(prototype: orig, new_fp: pointer) let my_ceil = unsafeBitCast(new, FunctionType.self) print(my_ceil(6.7))
  22. void executeFunction(void(*f)(void)) { f(); } typealias CFunc = @convention(c) ()->()

    @asmname("executeFunction") func executeFunction(fp: CFunc)
  23. func greeting() { print("Hello from Swift") } typealias CFunc =

    @convention(c) ()->() let t = unsafeBitCast(greeting, f_trampoline.self) let fp = unsafeBitCast(t.function_obj_ptr.memory.function_ptr, CFunc.self) executeFunction(fp) Hello from Swift Program ended with exit code: 0
  24. $ xcrun swiftc f.swift $ ./swift-dump.rb f // Code generated

    from `f` import Foundation class MyClass { var someVar: Int = 0 func someFuncWithAReallyLongNameLol() -> () {} }
  25. $ xcrun nm -g f|grep TFC 0000000100000c50 T __TFC1f7MyClass30someFuncWithAReallyLongNameLolfS0_FT_T_ 0000000100000d30

    T __TFC1f7MyClassCfMS0_FT_S0_ 0000000100000c70 T __TFC1f7MyClassD 0000000100000d10 T __TFC1f7MyClasscfMS0_FT_S0_ 0000000100000c60 T __TFC1f7MyClassd 0000000100000ca0 T __TFC1f7MyClassg7someVarSi 0000000100000ce0 T __TFC1f7MyClassm7someVarSi 0000000100000cc0 T __TFC1f7MyClasss7someVarSi
  26. performSelector? import Foundation import ObjectiveC.runtime @asmname("objc_msgSend") func sendPerformSelector(NSObject, Selector, Selector)

    -> NSString extension NSObject { public func performSelector2(selector : Selector) -> NSString { return sendPerformSelector(self, "performSelector:", selector) } } let string : NSString = "Fuck yeah, Swift!" println(string.performSelector2("description"))
  27. HOW ARE EMOJI FORMED? $ echo 'class ! {}'|xcrun swiftc

    -emit-library -o test - $ nm -g test ... 0000000000000db0 T __TFC4testX4ypIhD ... $ xcrun swift-demangle __TFC4testX4ypIhD _TFC4testX4ypIhD ---> test.!.__deallocating_deinit X4 ypIh ~ xn--yp8h
  28. WHAT HAVE WE LEARNED? ▸ import ObjectiveC.runtime ☺ ▸ Introspection

    somewhat exists " ▸ Changing behaviour is hard # ▸ Reverse engineering is still fine $