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.

9d2ea021919ff81e02d48530aae191bd?s=128

Boris Bรผgling

June 10, 2015
Tweet

Transcript

  1. SWIFT FUNTIME ! ALTCONF, JUNE 2015 BORIS BรœGLING - @NEONACHO

  2. None
  3. None
  4. CONTENTFUL

  5. None
  6. !

  7. None
  8. WHAT IS A SWIFT OBJECT EVEN?

  9. IT DEPENDS

  10. class MyObject : NSObject { }

  11. โ–ธ behaves like any old Objective-C object โ–ธ instance variables

    are properties โ–ธ fully interopable with ObjC
  12. import ObjectiveC.runtime !

  13. class MyObject { }

  14. โ–ธ has SwiftObject as superclass โ–ธ instance variables are ivars

    only โ–ธ ivars have no type encoding โ–ธ methods are not ObjC methods โ–ธ not interoperable with ObjC
  15. SWIFTOBJECT Ivar: magic {SwiftObject_s="isa"^v"refCount"q} Protocol: NSObject NSOBJECT Ivar: isa #

    Protocol: NSObject
  16. HOW DOES BRIDGING WORK, THEN?

  17. IT DOESN'T

  18. 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
  19. !

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

    type '@convention(c) (T) -> U' typealias foo = CFunctionPointer<() -> ()>
  21. None
  22. @convention(swift) Apply this attribute to the type of the function

    to indicate its calling conventions.
  23. 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
  24. OBJECTIVE-C BLOCKS SWIFT 1.X @objc_block ... SWIFT 2.X @convention(block)

  25. None
  26. Y DID WE ! THE OBJECTIVE-" RUNTIME? โ–ธ Dynamic Introspection

    ! โ–ธ Change Behaviour "# โ–ธ Analyse private API $
  27. None
  28. DYNAMIC INTROSPECTION

  29. 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)]))!) }
  30. IN PURE SWIFT => !

  31. 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 { [...] }
  32. 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 }
  33. 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)
  34. None
  35. CHANGE BEHAVIOUR

  36. 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) // โœ‹
  37. NSINVOCATION DOES NOT EXIST

  38. WHAT ABOUT PURE SWIFT?

  39. SWROUTE โ–ธ PoC of function hooking in Swift โ–ธ Uses

    rd_route, a Mach specific injection library for C
  40. #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; };
  41. 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; }
  42. None
  43. LET'S DO THAT IN SWIFT

  44. 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 [...]
  45. MEMORY LAYOUT โ–ธ 16 bytes => Swift object โ–ธ 8

    bytes => Pointer to _TF6memory3addFTSiSi_Si Function pointer !
  46. 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 }
  47. @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
  48. 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))
  49. $ xcrun swift -Onone hook.swift 6.0 7.0

  50. None
  51. CAN WE DO THIS THE OTHER WAY AROUND?

  52. void executeFunction(void(*f)(void)) { f(); } typealias CFunc = @convention(c) ()->()

    @asmname("executeFunction") func executeFunction(fp: CFunc)
  53. 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
  54. None
  55. ANALYSE PRIVATE API

  56. None
  57. class MyClass { var someVar = 1 func someFuncWithAReallyLongNameLol() {

    } }
  58. $ xcrun swiftc f.swift $ ./swift-dump.rb f // Code generated

    from `f` import Foundation class MyClass { var someVar: Int = 0 func someFuncWithAReallyLongNameLol() -> () {} }
  59. $ 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
  60. $ xcrun swift-demangle __TFC1f7MyClassg7someVarSi _TFC1f7MyClassg7someVarSi ---> f.MyClass.someVar.getter : Swift.Int

  61. None
  62. 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"))
  63. ! 1. While emitting IR SIL function @_TFE15PerformSelectorCSo8NSObject16performSelector2fS0_FV10ObjectiveC8SelectorCSo8NSString for 'performSelector2'

    at ./PerformSelector.swift:13:12 Abort trap: 6
  64. None
  65. ONLY WORKS WITH SWIFT 1.1 :( #!/usr/bin/env DEVELOPER_DIR=/Applications/Xcode-6.2.app/Contents/Developer xcrun swift

  66. $ ./PerformSelector.swift Fuck yeah, Swift

  67. None
  68. WHY func performSelector2 ? $ ./PerformSelector.swift Segmentation fault: 11

  69. None
  70. 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
  71. None
  72. WHAT HAVE WE LEARNED? โ–ธ import ObjectiveC.runtime โ˜บ โ–ธ Introspection

    somewhat exists " โ–ธ Changing behaviour is hard # โ–ธ Reverse engineering is still fine $
  73. THANK YOU!

  74. โ–ธ http://airspeedvelocity.net/ โ–ธ http://www.russbishop.net/swift-how-did-i-do-horrible-things โ–ธ https://github.com/mikeash/memorydumper โ–ธ https://github.com/rodionovd/SWRoute

  75. @NeoNacho boris@contentful.com http://buegling.com/talks http://www.contentful.com