├── .gitignore ├── Notes ├── AnalysedBinaries │ ├── MixinTestClassMethodCall │ └── MixinTestMethodCalls ├── DetailedNotes.md ├── MethodCalls.md └── NOTES.md ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── SwiftMixin │ ├── Disassembler.swift │ ├── MachO.swift │ └── Mixin.swift └── SwiftMixinC │ ├── include │ └── swift-mixin.h │ └── swift-mixin.c └── Tests ├── LinuxMain.swift └── SwiftMixinTests ├── Functions.swift ├── SwiftMixinTests.swift ├── Types ├── Number.swift ├── ThreeNumbers.swift └── TwoNumbers.swift └── XCTestManifests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /Notes/AnalysedBinaries/MixinTestClassMethodCall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackotter/swift-mixin/0d1bbc14de8347e9d032139e3c1baea76842d3f1/Notes/AnalysedBinaries/MixinTestClassMethodCall -------------------------------------------------------------------------------- /Notes/AnalysedBinaries/MixinTestMethodCalls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackotter/swift-mixin/0d1bbc14de8347e9d032139e3c1baea76842d3f1/Notes/AnalysedBinaries/MixinTestMethodCalls -------------------------------------------------------------------------------- /Notes/DetailedNotes.md: -------------------------------------------------------------------------------- 1 | # Path of RCX 2 | 3 | - from `qword ptr [rbp - 0xb8]` 4 | - from `qword ptr [rbp - 0xa0]` 5 | - from `qword ptr [rbp - 0x98]` 6 | - from `qword ptr [rbp - 0x80]` 7 | - from `lea [rip + 0x302]` 8 | 9 | # Path of RDI 10 | 11 | - from `qword ptr [r13 + 0x10]` 12 | - `r13` is from `rsi` 13 | - `rsi` is from `qword ptr [r13 + 0x18]` 14 | - `r13` is from `qword ptr [rbp - 0xb0]` 15 | - `qword ptr [rbp - 0xb0]` is from `rax` 16 | - `rax` is from `qword ptr [rbp - 0xa8]` 17 | - `qword ptr [rbp - 0xa8]` is from `rcx` 18 | - `rcx` is from `qword ptr [rbp - 0x90]` 19 | - `qword ptr [rbp - 0x90]` is from `rax` 20 | - `rax` is from `qword ptr [rbp - 0x88]` 21 | - `qword ptr [rbp - 0x88]` is from `rax` 22 | 23 | 24 | # 0x1000035a8 : structure of memory at r13 25 | 26 | + 0x10: pointer to `MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out ())` <- `rbp - 0x78` 27 | + 0x18: pointer to memory { <- `rbp - 0x70` 28 | + 0x10: `function2` 29 | } 30 | 31 | # Struct methods 32 | 33 | 34 | 35 | 36 | - first `call rax` 37 | - 0x000000010000aec0: partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 38 | - 0x10000ae20: reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 39 | - 0x000000010000aa30: partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) to @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) at 40 | - 0x10000a980: reabstraction thunk helper from @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) to @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) at 41 | - 0x10000a8e0: implicit closure #1 in main() 42 | - Returns a `partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at ` in rax and an allocated object in rdx 43 | 44 | 45 | - second `call rax` 46 | - 0x10000af10: partial apply for thunk for @escaping @callee_guaranteed () -> (@out Int) 47 | - 0x1000077a0: reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 48 | - 0x000000010000af60: partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 49 | - 0x10000aa50: reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 50 | - 0x000000010000afa0: partial apply forwarder for implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.TwoNumbers) -> () -> Swift.Int in MixinTest.main() -> () at 51 | - 0x10000a950: implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.TwoNumbers) -> () -> Swift.Int in MixinTest.main() -> () at main.swift 52 | - 0x100009090: MixinTest.TwoNumbers.sum() -> Swift.Int at TestFunctions.swift 53 | 54 | 55 | 56 | 57 | - first 58 | - 0x000000010000ae60 59 | - 0x10000adc0 60 | - 0x000000010000a8e0 61 | - 0x10000a830 62 | - 0x000000010000a790 63 | - second 64 | - 0x000000010000aeb0 65 | - 0x1000075e0 66 | - 0x000000010000af40 67 | - 0x10000a900 68 | - 0x000000010000af80 69 | - 0x10000a800 70 | - actual function: 0x100008ed0 71 | 72 | - first 73 | - 0x000000010000ae60 74 | - 0x10000adc0 75 | - 0x10000ad70 76 | - 0x10000a830 77 | - 0x000000010000acd0 78 | - second 79 | - 0x000000010000aeb0 80 | - 0x1000075e0 81 | - 0x10000af40 82 | - 0x10000a900 83 | - 0x10000aef0 84 | - 0x10000ad90 85 | - actual function: 0x100008ed0 86 | 87 | 88 | 89 | (lldb) d -s 0x10000ad90 90 | MixinTest`implicit closure #4 in implicit closure #3 in main(): 91 | 0x10000ad90 <+0>: push rbp 92 | 0x10000ad91 <+1>: mov rbp, rsp 93 | 0x10000ad94 <+4>: sub rsp, 0x10 94 | -> 0x10000ad98 <+8>: xorps xmm0, xmm0 95 | 0x10000ad9b <+11>: movaps xmmword ptr [rbp - 0x10], xmm0 96 | 0x10000ad9f <+15>: mov qword ptr [rbp - 0x10], rdi 97 | 0x10000ada3 <+19>: mov qword ptr [rbp - 0x8], rsi 98 | 0x10000ada7 <+23>: call 0x100008ed0 ; MixinTest.TwoNumbers.sum() -> Swift.Int at TestFunctions.swift:41 99 | 0x10000adac <+28>: add rsp, 0x10 100 | 101 | (lldb) d -s 0x10000aef0 102 | MixinTest`partial apply for implicit closure #4 in implicit closure #3 in main(): 103 | 0x10000aef0 <+0>: push rbp 104 | 0x10000aef1 <+1>: mov rbp, rsp 105 | 0x10000aef4 <+4>: mov rdi, qword ptr [r13 + 0x10] 106 | 0x10000aef8 <+8>: mov rsi, qword ptr [r13 + 0x18] 107 | 0x10000aefc <+12>: pop rbp 108 | 0x10000aefd <+13>: jmp 0x10000ad90 ; implicit closure #4 () -> Swift.Int in implicit closure #3 (MixinTest.TwoNumbers) -> () -> Swift.Int in MixinTest.main() -> () at main.swift 109 | 0x10000af02 <+18>: nop word ptr cs:[rax + rax] 110 | 0x10000af0c <+28>: nop dword ptr [rax] 111 | 112 | (lldb) d -s 0x10000a900 113 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@unowned Int): 114 | 0x10000a900 <+0>: push rbp 115 | 0x10000a901 <+1>: mov rbp, rsp 116 | 0x10000a904 <+4>: push r13 117 | 0x10000a906 <+6>: push rax 118 | 0x10000a907 <+7>: mov r13, rsi 119 | 0x10000a90a <+10>: mov qword ptr [rbp - 0x10], rax 120 | 0x10000a90e <+14>: call rdi 121 | 0x10000a910 <+16>: mov rcx, qword ptr [rbp - 0x10] 122 | 0x10000a914 <+20>: mov qword ptr [rcx], rax 123 | 0x10000a917 <+23>: add rsp, 0x8 124 | 0x10000a91b <+27>: pop r13 125 | 0x10000a91d <+29>: pop rbp 126 | 0x10000a91e <+30>: ret 127 | 0x10000a91f <+31>: nop 128 | 129 | (lldb) d -s 0x10000af40 130 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@unowned Int): 131 | 0x10000af40 <+0>: push rbp 132 | 0x10000af41 <+1>: mov rbp, rsp 133 | 0x10000af44 <+4>: mov rdi, qword ptr [r13 + 0x10] 134 | 0x10000af48 <+8>: mov rsi, qword ptr [r13 + 0x18] 135 | 0x10000af4c <+12>: call 0x10000a900 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 136 | 0x10000af51 <+17>: pop rbp 137 | 0x10000af52 <+18>: ret 138 | 0x10000af53 <+19>: nop word ptr cs:[rax + rax] 139 | 0x10000af5d <+29>: nop dword ptr [rax] 140 | 141 | (lldb) d -s 0x1000075e0 142 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@out Int): 143 | 0x1000075e0 <+0>: push rbp 144 | 0x1000075e1 <+1>: mov rbp, rsp 145 | 0x1000075e4 <+4>: push r13 146 | 0x1000075e6 <+6>: push rax 147 | 0x1000075e7 <+7>: lea rax, [rbp - 0x10] 148 | 0x1000075eb <+11>: mov r13, rsi 149 | 0x1000075ee <+14>: call rdi 150 | 0x1000075f0 <+16>: mov rax, qword ptr [rbp - 0x10] 151 | 0x1000075f4 <+20>: add rsp, 0x8 152 | 0x1000075f8 <+24>: pop r13 153 | 0x1000075fa <+26>: pop rbp 154 | 0x1000075fb <+27>: ret 155 | 0x1000075fc <+28>: nop dword ptr [rax] 156 | 157 | (lldb) d -s 0x000000010000aeb0 158 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out Int): 159 | 0x10000aeb0 <+0>: push rbp 160 | 0x10000aeb1 <+1>: mov rbp, rsp 161 | 0x10000aeb4 <+4>: mov rdi, qword ptr [r13 + 0x10] 162 | 0x10000aeb8 <+8>: mov rsi, qword ptr [r13 + 0x18] 163 | 0x10000aebc <+12>: pop rbp 164 | 0x10000aebd <+13>: jmp 0x1000075e0 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 165 | 0x10000aec2 <+18>: nop word ptr cs:[rax + rax] 166 | 0x10000aecc <+28>: nop dword ptr [rax] 167 | (lldb) second 168 | error: 'second' is not a valid command. 169 | 170 | 171 | (lldb) first 172 | error: 'first' is not a valid command. 173 | (lldb) d -s 0x10000a790 174 | MixinTest`implicit closure #1 in main(): 175 | 0x10000a790 <+0>: push rbp 176 | 0x10000a791 <+1>: mov rbp, rsp 177 | 0x10000a794 <+4>: sub rsp, 0x30 178 | 0x10000a798 <+8>: xorps xmm0, xmm0 179 | 0x10000a79b <+11>: movaps xmmword ptr [rbp - 0x10], xmm0 180 | 0x10000a79f <+15>: mov qword ptr [rbp - 0x10], rdi 181 | 0x10000a7a3 <+19>: mov qword ptr [rbp - 0x8], rsi 182 | 0x10000a7a7 <+23>: lea rax, [rip + 0x6332] ; type metadata for MixinTest.TwoNumbers + 520 183 | 0x10000a7ae <+30>: mov ecx, 0x20 184 | 0x10000a7b3 <+35>: mov edx, 0x7 185 | 0x10000a7b8 <+40>: mov qword ptr [rbp - 0x18], rdi 186 | 0x10000a7bc <+44>: mov rdi, rax 187 | 0x10000a7bf <+47>: mov qword ptr [rbp - 0x20], rsi 188 | 0x10000a7c3 <+51>: mov rsi, rcx 189 | 0x10000a7c6 <+54>: call 0x10000e51c ; symbol stub for: swift_allocObject 190 | 0x10000a7cb <+59>: mov rcx, qword ptr [rbp - 0x18] 191 | 0x10000a7cf <+63>: mov qword ptr [rax + 0x10], rcx 192 | 0x10000a7d3 <+67>: mov rdx, qword ptr [rbp - 0x20] 193 | 0x10000a7d7 <+71>: mov qword ptr [rax + 0x18], rdx 194 | 0x10000a7db <+75>: lea rsi, [rip + 0x79e] ; partial apply forwarder for implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.TwoNumbers) -> () -> Swift.Int in MixinTest.main() -> () at 195 | 0x10000a7e2 <+82>: mov qword ptr [rbp - 0x28], rax 196 | 0x10000a7e6 <+86>: mov rax, rsi 197 | 0x10000a7e9 <+89>: mov rdx, qword ptr [rbp - 0x28] 198 | 0x10000a7ed <+93>: add rsp, 0x30 199 | 0x10000a7f1 <+97>: pop rbp 200 | 0x10000a7f2 <+98>: ret 201 | 202 | (lldb) d -s 0x10000a830 203 | MixinTest`thunk for @escaping @callee_guaranteed (@unowned TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Int)): 204 | 0x10000a830 <+0>: push rbp 205 | 0x10000a831 <+1>: mov rbp, rsp 206 | 0x10000a834 <+4>: push r13 207 | 0x10000a836 <+6>: sub rsp, 0x28 208 | 0x10000a83a <+10>: mov rcx, qword ptr [rdi] 209 | 0x10000a83d <+13>: mov rdi, qword ptr [rdi + 0x8] 210 | 0x10000a841 <+17>: mov qword ptr [rbp - 0x10], rdi 211 | 0x10000a845 <+21>: mov rdi, rcx 212 | 0x10000a848 <+24>: mov rcx, qword ptr [rbp - 0x10] 213 | 0x10000a84c <+28>: mov qword ptr [rbp - 0x18], rsi 214 | 0x10000a850 <+32>: mov rsi, rcx 215 | 0x10000a853 <+35>: mov r13, rdx 216 | 0x10000a856 <+38>: mov rdx, qword ptr [rbp - 0x18] 217 | 0x10000a85a <+42>: mov qword ptr [rbp - 0x20], rax 218 | 0x10000a85e <+46>: call rdx 219 | 0x10000a860 <+48>: lea rdi, [rip + 0x6251] ; type metadata for MixinTest.TwoNumbers + 480 220 | 0x10000a867 <+55>: mov esi, 0x20 221 | 0x10000a86c <+60>: mov ecx, 0x7 222 | 0x10000a871 <+65>: mov qword ptr [rbp - 0x28], rdx 223 | 0x10000a875 <+69>: mov rdx, rcx 224 | 0x10000a878 <+72>: mov qword ptr [rbp - 0x30], rax 225 | 0x10000a87c <+76>: call 0x10000e51c ; symbol stub for: swift_allocObject 226 | 0x10000a881 <+81>: mov rcx, qword ptr [rbp - 0x30] 227 | 0x10000a885 <+85>: mov qword ptr [rax + 0x10], rcx 228 | 0x10000a889 <+89>: mov rdx, qword ptr [rbp - 0x28] 229 | 0x10000a88d <+93>: mov qword ptr [rax + 0x18], rdx 230 | 0x10000a891 <+97>: lea rsi, [rip + 0x6a8] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 231 | 0x10000a898 <+104>: mov rdi, qword ptr [rbp - 0x20] 232 | 0x10000a89c <+108>: mov qword ptr [rdi], rsi 233 | 0x10000a89f <+111>: mov qword ptr [rdi + 0x8], rax 234 | 0x10000a8a3 <+115>: add rsp, 0x28 235 | 0x10000a8a7 <+119>: pop r13 236 | 0x10000a8a9 <+121>: pop rbp 237 | 0x10000a8aa <+122>: ret 238 | 0x10000a8ab <+123>: nop dword ptr [rax + rax] 239 | 240 | (lldb) d -s 0x10000ad70 241 | MixinTest`thunk for @escaping @callee_guaranteed (@unowned TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Int))partial apply: 242 | 0x10000ad70 <+0>: push rbp 243 | 0x10000ad71 <+1>: mov rbp, rsp 244 | 0x10000ad74 <+4>: mov rsi, qword ptr [r13 + 0x10] 245 | 0x10000ad78 <+8>: mov rdx, qword ptr [r13 + 0x18] 246 | 0x10000ad7c <+12>: call 0x10000a830 ; reabstraction thunk helper from @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) to @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) at 247 | 0x10000ad81 <+17>: pop rbp 248 | 0x10000ad82 <+18>: ret 249 | 0x10000ad83 <+19>: nop word ptr cs:[rax + rax] 250 | 0x10000ad8d <+29>: nop dword ptr [rax] 251 | 252 | (lldb) d -s 0x10000adc0 253 | MixinTest`thunk for @escaping @callee_guaranteed (@in_guaranteed TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ): 254 | 0x10000adc0 <+0>: push rbp 255 | 0x10000adc1 <+1>: mov rbp, rsp 256 | 0x10000adc4 <+4>: push r13 257 | 0x10000adc6 <+6>: sub rsp, 0x38 258 | 0x10000adca <+10>: mov qword ptr [rbp - 0x18], rdi 259 | 0x10000adce <+14>: mov qword ptr [rbp - 0x10], rsi 260 | 0x10000add2 <+18>: lea rax, [rbp - 0x28] 261 | 0x10000add6 <+22>: lea rdi, [rbp - 0x18] 262 | 0x10000adda <+26>: mov r13, rcx 263 | 0x10000addd <+29>: call rdx 264 | 0x10000addf <+31>: mov rax, qword ptr [rbp - 0x28] 265 | 0x10000ade3 <+35>: mov rcx, qword ptr [rbp - 0x20] 266 | 0x10000ade7 <+39>: lea rdi, [rip + 0x5c7a] ; type metadata for MixinTest.TwoNumbers + 400 267 | 0x10000adee <+46>: mov esi, 0x20 268 | 0x10000adf3 <+51>: mov edx, 0x7 269 | 0x10000adf8 <+56>: mov qword ptr [rbp - 0x30], rax 270 | 0x10000adfc <+60>: mov qword ptr [rbp - 0x38], rcx 271 | 0x10000ae00 <+64>: call 0x10000e51c ; symbol stub for: swift_allocObject 272 | 0x10000ae05 <+69>: mov rcx, qword ptr [rbp - 0x30] 273 | 0x10000ae09 <+73>: mov qword ptr [rax + 0x10], rcx 274 | 0x10000ae0d <+77>: mov rcx, qword ptr [rbp - 0x38] 275 | 0x10000ae11 <+81>: mov qword ptr [rax + 0x18], rcx 276 | 0x10000ae15 <+85>: lea rcx, [rip + 0x94] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 277 | 0x10000ae1c <+92>: mov qword ptr [rbp - 0x40], rax 278 | 0x10000ae20 <+96>: mov rax, rcx 279 | 0x10000ae23 <+99>: mov rdx, qword ptr [rbp - 0x40] 280 | 0x10000ae27 <+103>: add rsp, 0x38 281 | 0x10000ae2b <+107>: pop r13 282 | 0x10000ae2d <+109>: pop rbp 283 | 0x10000ae2e <+110>: ret 284 | 285 | (lldb) d -s 0x000000010000ae60 286 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed (@in_guaranteed TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ): 287 | 0x10000ae60 <+0>: push rbp 288 | 0x10000ae61 <+1>: mov rbp, rsp 289 | 0x10000ae64 <+4>: mov rdx, qword ptr [r13 + 0x10] 290 | 0x10000ae68 <+8>: mov rcx, qword ptr [r13 + 0x18] 291 | 0x10000ae6c <+12>: pop rbp 292 | 0x10000ae6d <+13>: jmp 0x10000adc0 ; reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 293 | 0x10000ae72 <+18>: nop word ptr cs:[rax + rax] 294 | 0x10000ae7c <+28>: nop dword ptr [rax] 295 | 296 | 297 | (lldb) d -n runSum 298 | MixinTest`runSum(_:): 299 | 0x10000a920 <+0>: push rbp 300 | 0x10000a921 <+1>: mov rbp, rsp 301 | 0x10000a924 <+4>: push r13 302 | 0x10000a926 <+6>: sub rsp, 0x148 303 | 0x10000a92d <+13>: mov qword ptr [rbp - 0x18], 0x0 304 | 0x10000a935 <+21>: xorps xmm0, xmm0 305 | 0x10000a938 <+24>: movaps xmmword ptr [rbp - 0x40], xmm0 306 | 0x10000a93c <+28>: mov qword ptr [rbp - 0x48], 0x0 307 | 0x10000a944 <+36>: movaps xmmword ptr [rbp - 0x60], xmm0 308 | 0x10000a948 <+40>: mov qword ptr [rbp - 0x10], rsi 309 | 0x10000a94c <+44>: mov rax, qword ptr [rsi - 0x8] 310 | 0x10000a950 <+48>: mov rcx, qword ptr [rax + 0x40] 311 | 0x10000a954 <+52>: add rcx, 0xf 312 | 0x10000a958 <+56>: and rcx, -0x10 313 | 0x10000a95c <+60>: mov rdx, rsp 314 | 0x10000a95f <+63>: sub rdx, rcx 315 | 0x10000a962 <+66>: mov rsp, rdx 316 | 0x10000a965 <+69>: mov qword ptr [rbp - 0x18], rdi 317 | 0x10000a969 <+73>: mov qword ptr [rbp - 0x70], rdi 318 | 0x10000a96d <+77>: mov rdi, rdx 319 | 0x10000a970 <+80>: mov rcx, qword ptr [rbp - 0x70] 320 | 0x10000a974 <+84>: mov qword ptr [rbp - 0x78], rsi 321 | 0x10000a978 <+88>: mov rsi, rcx 322 | 0x10000a97b <+91>: mov r8, qword ptr [rbp - 0x78] 323 | 0x10000a97f <+95>: mov qword ptr [rbp - 0x80], rdx 324 | 0x10000a983 <+99>: mov rdx, r8 325 | 0x10000a986 <+102>: call qword ptr [rax + 0x10] 326 | 0x10000a989 <+105>: lea rcx, [rbp - 0x28] 327 | 0x10000a98d <+109>: lea rdi, [rip + 0x64fc] ; demangling cache variable for type metadata for (MixinTest.TwoNumbers) -> () -> Swift.Int 328 | 0x10000a994 <+116>: mov qword ptr [rbp - 0x88], rax 329 | 0x10000a99b <+123>: mov qword ptr [rbp - 0x90], rcx 330 | 0x10000a9a2 <+130>: call 0x100007890 ; __swift_instantiateConcreteTypeFromMangledName at 331 | 0x10000a9a7 <+135>: mov rdi, qword ptr [rbp - 0x90] 332 | 0x10000a9ae <+142>: mov rsi, qword ptr [rbp - 0x80] 333 | 0x10000a9b2 <+146>: mov rdx, qword ptr [rbp - 0x78] 334 | 0x10000a9b6 <+150>: mov rcx, rax 335 | 0x10000a9b9 <+153>: mov r8d, 0x6 336 | 0x10000a9bf <+159>: call 0x10000e534 ; symbol stub for: swift_dynamicCast 337 | 0x10000a9c4 <+164>: test al, 0x1 338 | 0x10000a9c6 <+166>: jne 0x10000a9ca ; <+170> at main.swift 339 | 0x10000a9c8 <+168>: jmp 0x10000aa2f ; <+271> at main.swift 340 | 0x10000a9ca <+170>: lea rax, [rip + 0x605f] ; type metadata for MixinTest.TwoNumbers + 344 341 | 0x10000a9d1 <+177>: add rax, 0x10 342 | 0x10000a9d7 <+183>: mov rcx, qword ptr [rbp - 0x28] 343 | 0x10000a9db <+187>: mov rdx, qword ptr [rbp - 0x20] 344 | 0x10000a9df <+191>: mov rdi, rax 345 | 0x10000a9e2 <+194>: mov esi, 0x20 346 | 0x10000a9e7 <+199>: mov eax, 0x7 347 | 0x10000a9ec <+204>: mov qword ptr [rbp - 0x98], rdx 348 | 0x10000a9f3 <+211>: mov rdx, rax 349 | 0x10000a9f6 <+214>: mov qword ptr [rbp - 0xa0], rcx 350 | 0x10000a9fd <+221>: call 0x10000e51c ; symbol stub for: swift_allocObject 351 | 0x10000aa02 <+226>: mov rcx, qword ptr [rbp - 0xa0] 352 | 0x10000aa09 <+233>: mov qword ptr [rax + 0x10], rcx 353 | 0x10000aa0d <+237>: mov rcx, qword ptr [rbp - 0x98] 354 | 0x10000aa14 <+244>: mov qword ptr [rax + 0x18], rcx 355 | 0x10000aa18 <+248>: lea rcx, [rip + 0x441] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.TwoNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@unowned MixinTest.TwoNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 356 | 0x10000aa1f <+255>: mov qword ptr [rbp - 0xa8], rcx 357 | 0x10000aa26 <+262>: mov qword ptr [rbp - 0xb0], rax 358 | 0x10000aa2d <+269>: jmp 0x10000aa44 ; <+292> at main.swift 359 | 0x10000aa2f <+271>: xor eax, eax 360 | 0x10000aa31 <+273>: mov ecx, eax 361 | 0x10000aa33 <+275>: mov rdx, rcx 362 | 0x10000aa36 <+278>: mov qword ptr [rbp - 0xa8], rdx 363 | 0x10000aa3d <+285>: mov qword ptr [rbp - 0xb0], rcx 364 | 0x10000aa44 <+292>: mov rax, qword ptr [rbp - 0xb0] 365 | 0x10000aa4b <+299>: mov rcx, qword ptr [rbp - 0xa8] 366 | 0x10000aa52 <+306>: cmp rcx, 0x0 367 | 0x10000aa56 <+310>: mov qword ptr [rbp - 0xb8], rax 368 | 0x10000aa5d <+317>: mov qword ptr [rbp - 0xc0], rcx 369 | 0x10000aa64 <+324>: je 0x10000aa84 ; <+356> at main.swift:82:30 370 | 0x10000aa66 <+326>: mov rax, qword ptr [rbp - 0xc0] 371 | 0x10000aa6d <+333>: mov rcx, qword ptr [rbp - 0xb8] 372 | 0x10000aa74 <+340>: mov qword ptr [rbp - 0xc8], rax 373 | 0x10000aa7b <+347>: mov qword ptr [rbp - 0xd0], rcx 374 | 0x10000aa82 <+354>: jmp 0x10000aa89 ; <+361> at main.swift 375 | 0x10000aa84 <+356>: jmp 0x10000acc0 ; <+928> at main.swift:86:1 376 | 0x10000aa89 <+361>: mov rax, qword ptr [rbp - 0xd0] 377 | 0x10000aa90 <+368>: mov rcx, qword ptr [rbp - 0xc8] 378 | 0x10000aa97 <+375>: mov qword ptr [rbp - 0x40], rcx 379 | 0x10000aa9b <+379>: mov qword ptr [rbp - 0x38], rax 380 | 0x10000aa9f <+383>: mov rdi, rax 381 | 0x10000aaa2 <+386>: mov qword ptr [rbp - 0xd8], rax 382 | 0x10000aaa9 <+393>: mov qword ptr [rbp - 0xe0], rcx 383 | 0x10000aab0 <+400>: call 0x10000e576 ; symbol stub for: swift_retain 384 | 0x10000aab5 <+405>: mov edi, 0x2 385 | 0x10000aaba <+410>: mov esi, 0x3 386 | 0x10000aabf <+415>: mov qword ptr [rbp - 0xe8], rax 387 | 0x10000aac6 <+422>: call 0x100008e90 ; MixinTest.TwoNumbers.init(a: Swift.Int, b: Swift.Int) -> MixinTest.TwoNumbers at TestFunctions.swift:36 388 | 0x10000aacb <+427>: mov rdi, rax 389 | 0x10000aace <+430>: mov rsi, rdx 390 | 0x10000aad1 <+433>: mov r13, qword ptr [rbp - 0xd8] 391 | 0x10000aad8 <+440>: mov rax, qword ptr [rbp - 0xe0] 392 | 0x10000aadf <+447>: call rax 393 | 0x10000aae1 <+449>: mov r13, rdx 394 | 0x10000aae4 <+452>: mov qword ptr [rbp - 0xf0], rdx 395 | 0x10000aaeb <+459>: call rax 396 | 0x10000aaed <+461>: mov qword ptr [rbp - 0x48], rax 397 | 0x10000aaf1 <+465>: mov rdi, qword ptr [rbp - 0xf0] 398 | 0x10000aaf8 <+472>: mov qword ptr [rbp - 0xf8], rax 399 | 0x10000aaff <+479>: call 0x10000e570 ; symbol stub for: swift_release 400 | 0x10000ab04 <+484>: mov rdi, qword ptr [rbp - 0xd8] 401 | 0x10000ab0b <+491>: call 0x10000e570 ; symbol stub for: swift_release 402 | 0x10000ab10 <+496>: mov rax, qword ptr [rip + 0x5641] ; (void *)0x00007fff81589a28: type metadata for Any 403 | 0x10000ab17 <+503>: add rax, 0x8 404 | 0x10000ab1d <+509>: mov edi, 0x1 405 | 0x10000ab22 <+514>: mov rsi, rax 406 | 0x10000ab25 <+517>: call 0x10000e378 ; symbol stub for: Swift._allocateUninitializedArray(Builtin.Word) -> (Swift.Array, Builtin.RawPointer) 407 | 0x10000ab2a <+522>: mov edi, 0x5 408 | 0x10000ab2f <+527>: mov esi, 0x1 409 | 0x10000ab34 <+532>: mov qword ptr [rbp - 0x100], rax 410 | 0x10000ab3b <+539>: mov qword ptr [rbp - 0x108], rdx 411 | 0x10000ab42 <+546>: call 0x10000e372 ; symbol stub for: Swift.DefaultStringInterpolation.init(literalCapacity: Swift.Int, interpolationCount: Swift.Int) -> Swift.DefaultStringInterpolation 412 | 0x10000ab47 <+551>: mov qword ptr [rbp - 0x60], rax 413 | 0x10000ab4b <+555>: mov qword ptr [rbp - 0x58], rdx 414 | 0x10000ab4f <+559>: lea rdi, [rip + 0x451c] ; "sum: " 415 | 0x10000ab56 <+566>: mov esi, 0x5 416 | 0x10000ab5b <+571>: mov edx, 0x1 417 | 0x10000ab60 <+576>: call 0x10000e300 ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 418 | 0x10000ab65 <+581>: mov rdi, rax 419 | 0x10000ab68 <+584>: mov rsi, rdx 420 | 0x10000ab6b <+587>: lea r13, [rbp - 0x60] 421 | 0x10000ab6f <+591>: mov qword ptr [rbp - 0x110], rdx 422 | 0x10000ab76 <+598>: call 0x10000e36c ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 423 | 0x10000ab7b <+603>: mov rdi, qword ptr [rbp - 0x110] 424 | 0x10000ab82 <+610>: call 0x10000e522 ; symbol stub for: swift_bridgeObjectRelease 425 | 0x10000ab87 <+615>: mov rsi, qword ptr [rip + 0x551a] ; (void *)0x00007fff81581c80: type metadata for Swift.Int 426 | 0x10000ab8e <+622>: mov rdx, qword ptr [rip + 0x553b] ; (void *)0x00007fff81579cb0: protocol witness table for Swift.Int : Swift.CustomStringConvertible in Swift 427 | 0x10000ab95 <+629>: mov rax, qword ptr [rbp - 0xf8] 428 | 0x10000ab9c <+636>: mov qword ptr [rbp - 0x68], rax 429 | 0x10000aba0 <+640>: lea rcx, [rbp - 0x68] 430 | 0x10000aba4 <+644>: mov rdi, rcx 431 | 0x10000aba7 <+647>: lea r13, [rbp - 0x60] 432 | 0x10000abab <+651>: call 0x10000e366 ; symbol stub for: Swift.DefaultStringInterpolation.appendInterpolation(A) -> () 433 | 0x10000abb0 <+656>: xor r8d, r8d 434 | 0x10000abb3 <+659>: mov esi, r8d 435 | 0x10000abb6 <+662>: lea rdi, [rip + 0x41bb] ; "" 436 | 0x10000abbd <+669>: mov edx, 0x1 437 | 0x10000abc2 <+674>: call 0x10000e300 ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 438 | 0x10000abc7 <+679>: mov rdi, rax 439 | 0x10000abca <+682>: mov rsi, rdx 440 | 0x10000abcd <+685>: lea r13, [rbp - 0x60] 441 | 0x10000abd1 <+689>: mov qword ptr [rbp - 0x118], rdx 442 | 0x10000abd8 <+696>: call 0x10000e36c ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 443 | 0x10000abdd <+701>: mov rdi, qword ptr [rbp - 0x118] 444 | 0x10000abe4 <+708>: call 0x10000e522 ; symbol stub for: swift_bridgeObjectRelease 445 | 0x10000abe9 <+713>: mov rdi, qword ptr [rbp - 0x60] 446 | 0x10000abed <+717>: mov rax, qword ptr [rbp - 0x58] 447 | 0x10000abf1 <+721>: mov qword ptr [rbp - 0x120], rdi 448 | 0x10000abf8 <+728>: mov rdi, rax 449 | 0x10000abfb <+731>: mov qword ptr [rbp - 0x128], rax 450 | 0x10000ac02 <+738>: call 0x10000e528 ; symbol stub for: swift_bridgeObjectRetain 451 | 0x10000ac07 <+743>: lea rdi, [rbp - 0x60] 452 | 0x10000ac0b <+747>: mov qword ptr [rbp - 0x130], rax 453 | 0x10000ac12 <+754>: call 0x100007ac0 ; outlined destroy of Swift.DefaultStringInterpolation at 454 | 0x10000ac17 <+759>: mov rdi, qword ptr [rbp - 0x120] 455 | 0x10000ac1e <+766>: mov rsi, qword ptr [rbp - 0x128] 456 | 0x10000ac25 <+773>: mov qword ptr [rbp - 0x138], rax 457 | 0x10000ac2c <+780>: call 0x10000e2fa ; symbol stub for: Swift.String.init(stringInterpolation: Swift.DefaultStringInterpolation) -> Swift.String 458 | 0x10000ac31 <+785>: mov rcx, qword ptr [rip + 0x5448] ; (void *)0x00007fff8157fe00: type metadata for Swift.String 459 | 0x10000ac38 <+792>: mov rsi, qword ptr [rbp - 0x108] 460 | 0x10000ac3f <+799>: mov qword ptr [rsi + 0x18], rcx 461 | 0x10000ac43 <+803>: mov qword ptr [rsi], rax 462 | 0x10000ac46 <+806>: mov qword ptr [rsi + 0x8], rdx 463 | 0x10000ac4a <+810>: call 0x100005d60 ; default argument 1 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 464 | 0x10000ac4f <+815>: mov qword ptr [rbp - 0x140], rax 465 | 0x10000ac56 <+822>: mov qword ptr [rbp - 0x148], rdx 466 | 0x10000ac5d <+829>: call 0x100005d80 ; default argument 2 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 467 | 0x10000ac62 <+834>: mov rdi, qword ptr [rbp - 0x100] 468 | 0x10000ac69 <+841>: mov rsi, qword ptr [rbp - 0x140] 469 | 0x10000ac70 <+848>: mov rcx, qword ptr [rbp - 0x148] 470 | 0x10000ac77 <+855>: mov qword ptr [rbp - 0x150], rdx 471 | 0x10000ac7e <+862>: mov rdx, rcx 472 | 0x10000ac81 <+865>: mov rcx, rax 473 | 0x10000ac84 <+868>: mov r8, qword ptr [rbp - 0x150] 474 | 0x10000ac8b <+875>: call 0x10000e396 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () 475 | 0x10000ac90 <+880>: mov rdi, qword ptr [rbp - 0x150] 476 | 0x10000ac97 <+887>: call 0x10000e522 ; symbol stub for: swift_bridgeObjectRelease 477 | 0x10000ac9c <+892>: mov rdi, qword ptr [rbp - 0x148] 478 | 0x10000aca3 <+899>: call 0x10000e522 ; symbol stub for: swift_bridgeObjectRelease 479 | 0x10000aca8 <+904>: mov rdi, qword ptr [rbp - 0x100] 480 | 0x10000acaf <+911>: call 0x10000e522 ; symbol stub for: swift_bridgeObjectRelease 481 | 0x10000acb4 <+916>: mov rdi, qword ptr [rbp - 0xd8] 482 | 0x10000acbb <+923>: call 0x10000e570 ; symbol stub for: swift_release 483 | 0x10000acc0 <+928>: lea rsp, [rbp - 0x8] 484 | 0x10000acc4 <+932>: pop r13 485 | 0x10000acc6 <+934>: pop rbp 486 | 0x10000acc7 <+935>: ret 487 | 488 | (lldb) 489 | 490 | 491 | 492 | # Class methods 493 | 494 | [r13 + 0x18] + 0x18 is the address of the allocated class. 495 | [[class address] + 0xb8] 496 | 497 | dynamic methods don't work because of ARC getting messed up by starting halfway through, it's easier just to use the disassembler to find the result of the lea that loads the partial apply forwarder 498 | 499 | The first of these nested implicit closures is for a method on the class. The second is for a method from an extension of the class. 500 | 501 | ``` 502 | (lldb) d -a 0x10000c4f0 503 | MixinTest`implicit closure #2 in implicit closure #1 in main(): 504 | 0x10000c4f0 <+0>: push rbp 505 | 0x10000c4f1 <+1>: mov rbp, rsp 506 | 0x10000c4f4 <+4>: push r13 507 | 0x10000c4f6 <+6>: push rax 508 | 0x10000c4f7 <+7>: mov qword ptr [rbp - 0x10], 0x0 509 | 0x10000c4ff <+15>: mov qword ptr [rbp - 0x10], rdi 510 | 0x10000c503 <+19>: mov rax, qword ptr [rdi] 511 | 0x10000c506 <+22>: mov rax, qword ptr [rax + 0xb8] 512 | 0x10000c50d <+29>: mov r13, rdi 513 | 0x10000c510 <+32>: call rax 514 | 0x10000c512 <+34>: add rsp, 0x8 515 | 0x10000c516 <+38>: pop r13 516 | 0x10000c518 <+40>: pop rbp 517 | 0x10000c519 <+41>: ret 518 | 519 | (lldb) d -a 0x10000c670 520 | MixinTest`implicit closure #4 in implicit closure #3 in main(): 521 | 0x10000c670 <+0>: push rbp 522 | 0x10000c671 <+1>: mov rbp, rsp 523 | 0x10000c674 <+4>: push r13 524 | 0x10000c676 <+6>: push rax 525 | 0x10000c677 <+7>: mov qword ptr [rbp - 0x10], 0x0 526 | 0x10000c67f <+15>: mov qword ptr [rbp - 0x10], rdi 527 | 0x10000c683 <+19>: mov r13, rdi 528 | 0x10000c686 <+22>: call 0x10000b5c0 ; MixinTest.ThreeNumbers.product() -> Swift.Int at main.swift:59 529 | 0x10000c68b <+27>: add rsp, 0x8 530 | 0x10000c68f <+31>: pop r13 531 | 0x10000c691 <+33>: pop rbp 532 | 0x10000c692 <+34>: ret 533 | ``` 534 | 535 | # Static struct methods 536 | 537 | call [[r13 + 0x18] + 0x10] -------------------------------------------------------------------------------- /Notes/MethodCalls.md: -------------------------------------------------------------------------------- 1 | # Struct 2 | 3 | ./AnalysedBinaries/MixinTestMethodCalls 4 | 5 | ## Calling sum normally 6 | 7 | - 0x10000ad50 8 | 9 | - 0x10000af20 10 | - 0x10000adc0 11 | - 0x100009250*: the actual function 12 | 13 | ## Calling sum from a casted generic - 1 14 | 15 | - 0x10000ae90* 16 | - 0x10000adf0* 17 | - 0x10000a7e0 18 | - 0x10000a730* 19 | - 0x10000a690 20 | 21 | - 0x10000aee0* 22 | - 0x1000075d0* 23 | - 0x10000afb0* 24 | - 0x10000a800* 25 | - 0x10000aff0 <- we have the address of this 26 | - 0x10000a700 27 | - 0x100009250*: the actual function 28 | 29 | ## Calling sum from a casted generic - 2 30 | 31 | - 0x10000ae90* 32 | - 0x10000adf0* 33 | - 0x10000ad00 34 | - 0x10000a730* 35 | - 0x10000ac60 -> returns 0x10000af60 36 | 37 | - 0x10000aee0* 38 | - 0x1000075d0* 39 | - 0x10000afb0* 40 | - 0x10000a800* 41 | - 0x10000af60 <- we have the address of this 42 | - 0x10000ad20 43 | - 0x100009250*: the actual function 44 | 45 | * means a shared closure/thunk/function call/jmp 46 | 47 | We have the address of the second last thunk before the actual function. The next two thunks both do hardcoded jumps and calls so we need to extract those address. 48 | 49 | ## Way to extract actual function address 50 | 51 | ```swift 52 | func getMethodAddress(_ method: T) { 53 | // stuff 54 | } 55 | ``` 56 | 57 | When this function is called as follows `TwoNumbers.sum` we get some metadata about some partial application closure in rdi. There is an address at rdi + 0x08. And at that address + 0x10 is 0x10000ac60 (see 'Calling sum from a casted generic - 2' for the functions called). Calling this function gives us the address to the thunk at 0x10000af60. The plan now is; 58 | 59 | 1. Iteratively search through the thunk at 0x10000af60 and look for a `pop rbp` instruction followed by a `jmp`. 60 | 2. Make a parser that can extract the address of the next thunk from the `jmp` instruction. 61 | 3. Iteratively search through the next thunk (0x10000ad20) for a `ret` instruction, then skip backwards 10 bytes to get to the `call`. 62 | 4. Make a parser that can extract the address of the actual function from the `call` instruction. 63 | 64 | 65 | # Classes 66 | 67 | ## First time 68 | 69 | - 0x10000cc70 70 | - 0x10000cba0 71 | - 0x10000c6d0 72 | - 0x10000c630 73 | - 0x10000c5c0 74 | - 0x10000cce0 75 | - 0x10000cc90 76 | - 0x10000cd40 77 | - 0x10000b790 78 | - 0x10000cd60 79 | - 0x10000c600 80 | - 0x100008d60 81 | 82 | ## Another time 83 | 84 | - 0x10000cca0 85 | - 0x10000cbd0 86 | - 0x10000c7c0 87 | - 0x10000c720 88 | - 0x10000c6b0 -> returns 0x10000cd80 89 | - 0x10000cd10 90 | - 0x10000ccc0 91 | - 0x10000cd60 92 | - 0x10000b9b0 93 | - 0x10000cd80 94 | - 0x10000c6f0 95 | - 0x100008f80 <- the actual function `ThreeNumbers.sum` 96 | 97 | 0x0000000100c523c0 98 | 99 | ``` 100 | (lldb) d -a 0x100008f80 101 | MixinTest`ThreeNumbers.sum(): 102 | 0x100008f80 <+0>: push rbp 103 | 0x100008f81 <+1>: mov rbp, rsp 104 | 0x100008f84 <+4>: push r13 105 | 0x100008f86 <+6>: sub rsp, 0x28 106 | 0x100008f8a <+10>: xor esi, esi 107 | 0x100008f8c <+12>: lea rax, [rbp - 0x10] 108 | 0x100008f90 <+16>: mov rdi, rax 109 | 0x100008f93 <+19>: mov edx, 0x8 110 | 0x100008f98 <+24>: mov qword ptr [rbp - 0x18], r13 111 | 0x100008f9c <+28>: call 0x10000e6fa ; symbol stub for: memset 112 | 0x100008fa1 <+33>: mov rax, qword ptr [rbp - 0x18] 113 | 0x100008fa5 <+37>: mov qword ptr [rbp - 0x10], rax 114 | 0x100008fa9 <+41>: mov rcx, qword ptr [rax] 115 | 0x100008fac <+44>: mov r13, rax 116 | 0x100008faf <+47>: call qword ptr [rcx + 0x68] 117 | 0x100008fb2 <+50>: mov rcx, qword ptr [rbp - 0x18] 118 | 0x100008fb6 <+54>: mov rdx, qword ptr [rcx] 119 | 0x100008fb9 <+57>: mov r13, rcx 120 | 0x100008fbc <+60>: mov qword ptr [rbp - 0x20], rax 121 | 0x100008fc0 <+64>: call qword ptr [rdx + 0x80] 122 | 0x100008fc6 <+70>: mov rcx, qword ptr [rbp - 0x20] 123 | 0x100008fca <+74>: add rcx, rax 124 | 0x100008fcd <+77>: seto r8b 125 | 0x100008fd1 <+81>: test r8b, 0x1 126 | 0x100008fd5 <+85>: mov qword ptr [rbp - 0x28], rcx 127 | 0x100008fd9 <+89>: jne 0x10000900a ; <+138> [inlined] Swift runtime failure: arithmetic overflow at TestFunctions.swift:71 128 | 0x100008fdb <+91>: mov rax, qword ptr [rbp - 0x18] 129 | 0x100008fdf <+95>: mov rcx, qword ptr [rax] 130 | 0x100008fe2 <+98>: mov r13, rax 131 | 0x100008fe5 <+101>: call qword ptr [rcx + 0x98] 132 | 0x100008feb <+107>: mov rcx, qword ptr [rbp - 0x28] 133 | 0x100008fef <+111>: add rcx, rax 134 | 0x100008ff2 <+114>: seto dl 135 | 0x100008ff5 <+117>: test dl, 0x1 136 | 0x100008ff8 <+120>: mov qword ptr [rbp - 0x30], rcx 137 | 0x100008ffc <+124>: jne 0x10000900c ; <+140> [inlined] Swift runtime failure: arithmetic overflow at TestFunctions.swift:71 138 | 0x100008ffe <+126>: mov rax, qword ptr [rbp - 0x30] 139 | 0x100009002 <+130>: add rsp, 0x28 140 | 0x100009006 <+134>: pop r13 141 | 0x100009008 <+136>: pop rbp 142 | 0x100009009 <+137>: ret 143 | 0x10000900a <+138>: ud2 144 | 0x10000900c <+140>: ud2 145 | 146 | (lldb) d -a 0x10000c6f0 147 | MixinTest`implicit closure #2 in implicit closure #1 in main(): 148 | 0x10000c6f0 <+0>: push rbp 149 | 0x10000c6f1 <+1>: mov rbp, rsp 150 | 0x10000c6f4 <+4>: push r13 151 | 0x10000c6f6 <+6>: push rax 152 | 0x10000c6f7 <+7>: mov qword ptr [rbp - 0x10], 0x0 153 | 0x10000c6ff <+15>: mov qword ptr [rbp - 0x10], rdi 154 | 0x10000c703 <+19>: mov rax, qword ptr [rdi] 155 | 0x10000c706 <+22>: mov rax, qword ptr [rax + 0xb8] 156 | 0x10000c70d <+29>: mov r13, rdi 157 | 0x10000c710 <+32>: call rax 158 | 0x10000c712 <+34>: add rsp, 0x8 159 | 0x10000c716 <+38>: pop r13 160 | 0x10000c718 <+40>: pop rbp 161 | 0x10000c719 <+41>: ret 162 | 163 | (lldb) d -a 0x10000cd80 164 | MixinTest`partial apply for implicit closure #2 in implicit closure #1 in main(): 165 | 0x10000cd80 <+0>: push rbp 166 | 0x10000cd81 <+1>: mov rbp, rsp 167 | 0x10000cd84 <+4>: mov rdi, r13 168 | 0x10000cd87 <+7>: pop rbp 169 | 0x10000cd88 <+8>: jmp 0x10000c6f0 ; implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.ThreeNumbers) -> () -> Swift.Int in MixinTest.main() -> () at main.swift 170 | 171 | (lldb) d -a 0x10000b9b0 172 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@unowned Int): 173 | 0x10000b9b0 <+0>: push rbp 174 | 0x10000b9b1 <+1>: mov rbp, rsp 175 | 0x10000b9b4 <+4>: push r13 176 | 0x10000b9b6 <+6>: push rax 177 | 0x10000b9b7 <+7>: mov r13, rsi 178 | 0x10000b9ba <+10>: mov qword ptr [rbp - 0x10], rax 179 | 0x10000b9be <+14>: call rdi 180 | 0x10000b9c0 <+16>: mov rcx, qword ptr [rbp - 0x10] 181 | 0x10000b9c4 <+20>: mov qword ptr [rcx], rax 182 | 0x10000b9c7 <+23>: add rsp, 0x8 183 | 0x10000b9cb <+27>: pop r13 184 | 0x10000b9cd <+29>: pop rbp 185 | 0x10000b9ce <+30>: ret 186 | 187 | (lldb) d -a 0x10000cd60 188 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@unowned Int): 189 | 0x10000cd60 <+0>: push rbp 190 | 0x10000cd61 <+1>: mov rbp, rsp 191 | 0x10000cd64 <+4>: mov rdi, qword ptr [r13 + 0x10] 192 | 0x10000cd68 <+8>: mov rsi, qword ptr [r13 + 0x18] 193 | 0x10000cd6c <+12>: call 0x10000b9b0 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 194 | 0x10000cd71 <+17>: pop rbp 195 | 0x10000cd72 <+18>: ret 196 | 197 | (lldb) d -a 0x10000ccc0 198 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@out Int): 199 | 0x10000ccc0 <+0>: push rbp 200 | 0x10000ccc1 <+1>: mov rbp, rsp 201 | 0x10000ccc4 <+4>: push r13 202 | 0x10000ccc6 <+6>: push rax 203 | 0x10000ccc7 <+7>: lea rax, [rbp - 0x10] 204 | 0x10000cccb <+11>: mov r13, rsi 205 | 0x10000ccce <+14>: call rdi 206 | 0x10000ccd0 <+16>: mov rax, qword ptr [rbp - 0x10] 207 | 0x10000ccd4 <+20>: add rsp, 0x8 208 | 0x10000ccd8 <+24>: pop r13 209 | 0x10000ccda <+26>: pop rbp 210 | 0x10000ccdb <+27>: ret 211 | 212 | (lldb) d -a 0x10000cd10 213 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out Int): 214 | 0x10000cd10 <+0>: push rbp 215 | 0x10000cd11 <+1>: mov rbp, rsp 216 | 0x10000cd14 <+4>: mov rdi, qword ptr [r13 + 0x10] 217 | 0x10000cd18 <+8>: mov rsi, qword ptr [r13 + 0x18] 218 | 0x10000cd1c <+12>: pop rbp 219 | 0x10000cd1d <+13>: jmp 0x10000ccc0 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 220 | (lldb) 221 | ``` 222 | 223 | ``` 224 | (lldb) d -a 0x10000c6b0 225 | MixinTest`implicit closure #1 in main(): 226 | 0x10000c6b0 <+0>: push rbp 227 | 0x10000c6b1 <+1>: mov rbp, rsp 228 | 0x10000c6b4 <+4>: sub rsp, 0x20 229 | 0x10000c6b8 <+8>: mov qword ptr [rbp - 0x8], 0x0 230 | 0x10000c6c0 <+16>: mov qword ptr [rbp - 0x8], rdi 231 | 0x10000c6c4 <+20>: mov qword ptr [rbp - 0x10], rdi 232 | 0x10000c6c8 <+24>: call 0x10000e78a ; symbol stub for: swift_retain 233 | 0x10000c6cd <+29>: lea rcx, [rip + 0x6ac] ; partial apply forwarder for implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.ThreeNumbers) -> () -> Swift.Int in MixinTest.main() -> () at 234 | 0x10000c6d4 <+36>: mov qword ptr [rbp - 0x18], rax 235 | 0x10000c6d8 <+40>: mov rax, rcx 236 | 0x10000c6db <+43>: mov rdx, qword ptr [rbp - 0x10] 237 | 0x10000c6df <+47>: add rsp, 0x20 238 | 0x10000c6e3 <+51>: pop rbp 239 | 0x10000c6e4 <+52>: ret 240 | 241 | (lldb) d -a 0x10000c720 242 | MixinTest`thunk for @escaping @callee_guaranteed (@guaranteed ThreeNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Int)): 243 | 0x10000c720 <+0>: push rbp 244 | 0x10000c721 <+1>: mov rbp, rsp 245 | 0x10000c724 <+4>: push r13 246 | 0x10000c726 <+6>: sub rsp, 0x18 247 | 0x10000c72a <+10>: mov rdi, qword ptr [rdi] 248 | 0x10000c72d <+13>: mov r13, rdx 249 | 0x10000c730 <+16>: mov qword ptr [rbp - 0x10], rax 250 | 0x10000c734 <+20>: call rsi 251 | 0x10000c736 <+22>: lea rdi, [rip + 0x408b] ; type metadata for MixinTest.Numbers + 672 252 | 0x10000c73d <+29>: mov esi, 0x20 253 | 0x10000c742 <+34>: mov ecx, 0x7 254 | 0x10000c747 <+39>: mov qword ptr [rbp - 0x18], rdx 255 | 0x10000c74b <+43>: mov rdx, rcx 256 | 0x10000c74e <+46>: mov qword ptr [rbp - 0x20], rax 257 | 0x10000c752 <+50>: call 0x10000e724 ; symbol stub for: swift_allocObject 258 | 0x10000c757 <+55>: mov rcx, qword ptr [rbp - 0x20] 259 | 0x10000c75b <+59>: mov qword ptr [rax + 0x10], rcx 260 | 0x10000c75f <+63>: mov rdx, qword ptr [rbp - 0x18] 261 | 0x10000c763 <+67>: mov qword ptr [rax + 0x18], rdx 262 | 0x10000c767 <+71>: lea rsi, [rip + 0x5f2] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 263 | 0x10000c76e <+78>: mov rdi, qword ptr [rbp - 0x10] 264 | 0x10000c772 <+82>: mov qword ptr [rdi], rsi 265 | 0x10000c775 <+85>: mov qword ptr [rdi + 0x8], rax 266 | 0x10000c779 <+89>: add rsp, 0x18 267 | 0x10000c77d <+93>: pop r13 268 | 0x10000c77f <+95>: pop rbp 269 | 0x10000c780 <+96>: ret 270 | 271 | (lldb) d -a 0x10000c7c0 272 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed (@guaranteed ThreeNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Int)): 273 | 0x10000c7c0 <+0>: push rbp 274 | 0x10000c7c1 <+1>: mov rbp, rsp 275 | 0x10000c7c4 <+4>: mov rsi, qword ptr [r13 + 0x10] 276 | 0x10000c7c8 <+8>: mov rdx, qword ptr [r13 + 0x18] 277 | 0x10000c7cc <+12>: call 0x10000c720 ; reabstraction thunk helper from @escaping @callee_guaranteed (@guaranteed MixinTest.ThreeNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) to @escaping @callee_guaranteed (@in_guaranteed MixinTest.ThreeNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) at 278 | 0x10000c7d1 <+17>: pop rbp 279 | 0x10000c7d2 <+18>: ret 280 | 281 | (lldb) d -a 0x10000cbd0 282 | MixinTest`thunk for @escaping @callee_guaranteed (@in_guaranteed ThreeNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ): 283 | 0x10000cbd0 <+0>: push rbp 284 | 0x10000cbd1 <+1>: mov rbp, rsp 285 | 0x10000cbd4 <+4>: push r13 286 | 0x10000cbd6 <+6>: sub rsp, 0x58 287 | 0x10000cbda <+10>: mov qword ptr [rbp - 0x28], rdi 288 | 0x10000cbde <+14>: mov qword ptr [rbp - 0x30], rdx 289 | 0x10000cbe2 <+18>: mov qword ptr [rbp - 0x38], rsi 290 | 0x10000cbe6 <+22>: call 0x10000e78a ; symbol stub for: swift_retain 291 | 0x10000cbeb <+27>: mov rcx, qword ptr [rbp - 0x28] 292 | 0x10000cbef <+31>: mov qword ptr [rbp - 0x10], rcx 293 | 0x10000cbf3 <+35>: lea rdx, [rbp - 0x20] 294 | 0x10000cbf7 <+39>: lea rdi, [rbp - 0x10] 295 | 0x10000cbfb <+43>: mov qword ptr [rbp - 0x40], rax 296 | 0x10000cbff <+47>: mov rax, rdx 297 | 0x10000cc02 <+50>: mov r13, qword ptr [rbp - 0x30] 298 | 0x10000cc06 <+54>: mov rdx, qword ptr [rbp - 0x38] 299 | 0x10000cc0a <+58>: call rdx 300 | 0x10000cc0c <+60>: mov rax, qword ptr [rbp - 0x20] 301 | 0x10000cc10 <+64>: mov rcx, qword ptr [rbp - 0x18] 302 | 0x10000cc14 <+68>: lea rdi, [rip + 0x3b85] ; type metadata for MixinTest.Numbers + 632 303 | 0x10000cc1b <+75>: mov esi, 0x20 304 | 0x10000cc20 <+80>: mov edx, 0x7 305 | 0x10000cc25 <+85>: mov qword ptr [rbp - 0x48], rax 306 | 0x10000cc29 <+89>: mov qword ptr [rbp - 0x50], rcx 307 | 0x10000cc2d <+93>: call 0x10000e724 ; symbol stub for: swift_allocObject 308 | 0x10000cc32 <+98>: mov rcx, qword ptr [rbp - 0x48] 309 | 0x10000cc36 <+102>: mov qword ptr [rax + 0x10], rcx 310 | 0x10000cc3a <+106>: mov rcx, qword ptr [rbp - 0x50] 311 | 0x10000cc3e <+110>: mov qword ptr [rax + 0x18], rcx 312 | 0x10000cc42 <+114>: mov rdi, qword ptr [rbp - 0x10] 313 | 0x10000cc46 <+118>: mov qword ptr [rbp - 0x58], rax 314 | 0x10000cc4a <+122>: call 0x10000e784 ; symbol stub for: swift_release 315 | 0x10000cc4f <+127>: lea rax, [rip + 0xba] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 316 | 0x10000cc56 <+134>: mov rdx, qword ptr [rbp - 0x58] 317 | 0x10000cc5a <+138>: add rsp, 0x58 318 | 0x10000cc5e <+142>: pop r13 319 | 0x10000cc60 <+144>: pop rbp 320 | 0x10000cc61 <+145>: ret 321 | 322 | (lldb) d -a 0x10000cca0 323 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed (@in_guaranteed ThreeNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ): 324 | 0x10000cca0 <+0>: push rbp 325 | 0x10000cca1 <+1>: mov rbp, rsp 326 | 0x10000cca4 <+4>: mov rsi, qword ptr [r13 + 0x10] 327 | 0x10000cca8 <+8>: mov rdx, qword ptr [r13 + 0x18] 328 | 0x10000ccac <+12>: pop rbp 329 | 0x10000ccad <+13>: jmp 0x10000cbd0 ; reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.ThreeNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@guaranteed MixinTest.ThreeNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 330 | (lldb) 331 | ``` 332 | 333 | ``` 334 | MixinTest`runSum(_:): 335 | 0x10000c7e0 <+0>: push rbp 336 | 0x10000c7e1 <+1>: mov rbp, rsp 337 | 0x10000c7e4 <+4>: push r13 338 | 0x10000c7e6 <+6>: sub rsp, 0x158 339 | 0x10000c7ed <+13>: mov qword ptr [rbp - 0x18], 0x0 340 | 0x10000c7f5 <+21>: xorps xmm0, xmm0 341 | 0x10000c7f8 <+24>: movaps xmmword ptr [rbp - 0x40], xmm0 342 | 0x10000c7fc <+28>: mov qword ptr [rbp - 0x48], 0x0 343 | 0x10000c804 <+36>: mov qword ptr [rbp - 0x50], 0x0 344 | 0x10000c80c <+44>: movaps xmmword ptr [rbp - 0x60], xmm0 345 | 0x10000c810 <+48>: mov qword ptr [rbp - 0x10], rsi 346 | 0x10000c814 <+52>: mov rax, qword ptr [rsi - 0x8] 347 | 0x10000c818 <+56>: mov rcx, qword ptr [rax + 0x40] 348 | 0x10000c81c <+60>: add rcx, 0xf 349 | 0x10000c820 <+64>: and rcx, -0x10 350 | 0x10000c824 <+68>: mov rdx, rsp 351 | 0x10000c827 <+71>: sub rdx, rcx 352 | 0x10000c82a <+74>: mov rsp, rdx 353 | 0x10000c82d <+77>: mov qword ptr [rbp - 0x18], rdi 354 | 0x10000c831 <+81>: mov qword ptr [rbp - 0x70], rdi 355 | 0x10000c835 <+85>: mov rdi, rdx 356 | 0x10000c838 <+88>: mov rcx, qword ptr [rbp - 0x70] 357 | 0x10000c83c <+92>: mov qword ptr [rbp - 0x78], rsi 358 | 0x10000c840 <+96>: mov rsi, rcx 359 | 0x10000c843 <+99>: mov r8, qword ptr [rbp - 0x78] 360 | 0x10000c847 <+103>: mov qword ptr [rbp - 0x80], rdx 361 | 0x10000c84b <+107>: mov rdx, r8 362 | 0x10000c84e <+110>: call qword ptr [rax + 0x10] 363 | 0x10000c851 <+113>: lea rcx, [rbp - 0x28] 364 | 0x10000c855 <+117>: lea rdi, [rip + 0x7cfc] ; demangling cache variable for type metadata for (MixinTest.ThreeNumbers) -> () -> Swift.Int 365 | 0x10000c85c <+124>: mov qword ptr [rbp - 0x88], rax 366 | 0x10000c863 <+131>: mov qword ptr [rbp - 0x90], rcx 367 | 0x10000c86a <+138>: call 0x100005ea0 ; __swift_instantiateConcreteTypeFromMangledName at 368 | 0x10000c86f <+143>: mov rdi, qword ptr [rbp - 0x90] 369 | 0x10000c876 <+150>: mov rsi, qword ptr [rbp - 0x80] 370 | 0x10000c87a <+154>: mov rdx, qword ptr [rbp - 0x78] 371 | 0x10000c87e <+158>: mov rcx, rax 372 | 0x10000c881 <+161>: mov r8d, 0x6 373 | 0x10000c887 <+167>: call 0x10000e748 ; symbol stub for: swift_dynamicCast 374 | 0x10000c88c <+172>: test al, 0x1 375 | 0x10000c88e <+174>: jne 0x10000c892 ; <+178> at main.swift 376 | 0x10000c890 <+176>: jmp 0x10000c8f7 ; <+279> at main.swift 377 | 0x10000c892 <+178>: lea rax, [rip + 0x3ecf] ; type metadata for MixinTest.Numbers + 576 378 | 0x10000c899 <+185>: add rax, 0x10 379 | 0x10000c89f <+191>: mov rcx, qword ptr [rbp - 0x28] 380 | 0x10000c8a3 <+195>: mov rdx, qword ptr [rbp - 0x20] 381 | 0x10000c8a7 <+199>: mov rdi, rax 382 | 0x10000c8aa <+202>: mov esi, 0x20 383 | 0x10000c8af <+207>: mov eax, 0x7 384 | 0x10000c8b4 <+212>: mov qword ptr [rbp - 0x98], rdx 385 | 0x10000c8bb <+219>: mov rdx, rax 386 | 0x10000c8be <+222>: mov qword ptr [rbp - 0xa0], rcx 387 | 0x10000c8c5 <+229>: call 0x10000e724 ; symbol stub for: swift_allocObject 388 | 0x10000c8ca <+234>: mov rcx, qword ptr [rbp - 0xa0] 389 | 0x10000c8d1 <+241>: mov qword ptr [rax + 0x10], rcx 390 | 0x10000c8d5 <+245>: mov rcx, qword ptr [rbp - 0x98] 391 | 0x10000c8dc <+252>: mov qword ptr [rax + 0x18], rcx 392 | 0x10000c8e0 <+256>: lea rcx, [rip + 0x3b9] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed (@in_guaranteed MixinTest.ThreeNumbers) -> (@out @escaping @callee_guaranteed @substituted () -> (@out A) for ) to @escaping @callee_guaranteed (@guaranteed MixinTest.ThreeNumbers) -> (@owned @escaping @callee_guaranteed () -> (@unowned Swift.Int)) at 393 | 0x10000c8e7 <+263>: mov qword ptr [rbp - 0xa8], rcx 394 | 0x10000c8ee <+270>: mov qword ptr [rbp - 0xb0], rax 395 | 0x10000c8f5 <+277>: jmp 0x10000c90c ; <+300> at main.swift 396 | 0x10000c8f7 <+279>: xor eax, eax 397 | 0x10000c8f9 <+281>: mov ecx, eax 398 | 0x10000c8fb <+283>: mov rdx, rcx 399 | 0x10000c8fe <+286>: mov qword ptr [rbp - 0xa8], rdx 400 | 0x10000c905 <+293>: mov qword ptr [rbp - 0xb0], rcx 401 | 0x10000c90c <+300>: mov rax, qword ptr [rbp - 0xb0] 402 | 0x10000c913 <+307>: mov rcx, qword ptr [rbp - 0xa8] 403 | 0x10000c91a <+314>: cmp rcx, 0x0 404 | 0x10000c91e <+318>: mov qword ptr [rbp - 0xb8], rax 405 | 0x10000c925 <+325>: mov qword ptr [rbp - 0xc0], rcx 406 | 0x10000c92c <+332>: je 0x10000c94c ; <+364> at main.swift:112:30 407 | 0x10000c92e <+334>: mov rax, qword ptr [rbp - 0xc0] 408 | 0x10000c935 <+341>: mov rcx, qword ptr [rbp - 0xb8] 409 | 0x10000c93c <+348>: mov qword ptr [rbp - 0xc8], rax 410 | 0x10000c943 <+355>: mov qword ptr [rbp - 0xd0], rcx 411 | 0x10000c94a <+362>: jmp 0x10000c951 ; <+369> at main.swift 412 | 0x10000c94c <+364>: jmp 0x10000cbbf ; <+991> at main.swift:119:1 413 | 0x10000c951 <+369>: mov rax, qword ptr [rbp - 0xd0] 414 | 0x10000c958 <+376>: mov rcx, qword ptr [rbp - 0xc8] 415 | 0x10000c95f <+383>: xor edx, edx 416 | 0x10000c961 <+385>: mov edi, edx 417 | 0x10000c963 <+387>: mov qword ptr [rbp - 0x40], rcx 418 | 0x10000c967 <+391>: mov qword ptr [rbp - 0x38], rax 419 | 0x10000c96b <+395>: mov qword ptr [rbp - 0xd8], rax 420 | 0x10000c972 <+402>: mov qword ptr [rbp - 0xe0], rcx 421 | 0x10000c979 <+409>: call 0x100009520 ; type metadata accessor for MixinTest.ThreeNumbers at 422 | 0x10000c97e <+414>: mov edi, 0x1 423 | 0x10000c983 <+419>: mov esi, 0x2 424 | 0x10000c988 <+424>: mov ecx, 0x4 425 | 0x10000c98d <+429>: mov qword ptr [rbp - 0xe8], rdx 426 | 0x10000c994 <+436>: mov rdx, rcx 427 | 0x10000c997 <+439>: mov r13, rax 428 | 0x10000c99a <+442>: call 0x100008de0 ; MixinTest.ThreeNumbers.__allocating_init(a: Swift.Int, b: Swift.Int, c: Swift.Int) -> MixinTest.ThreeNumbers at TestFunctions.swift:64 429 | 0x10000c99f <+447>: mov qword ptr [rbp - 0x48], rax 430 | 0x10000c9a3 <+451>: mov rdi, qword ptr [rbp - 0xd8] 431 | 0x10000c9aa <+458>: mov qword ptr [rbp - 0xf0], rax 432 | 0x10000c9b1 <+465>: call 0x10000e78a ; symbol stub for: swift_retain 433 | 0x10000c9b6 <+470>: mov rdi, qword ptr [rbp - 0xf0] 434 | 0x10000c9bd <+477>: mov r13, qword ptr [rbp - 0xd8] 435 | 0x10000c9c4 <+484>: mov rcx, qword ptr [rbp - 0xe0] 436 | 0x10000c9cb <+491>: mov qword ptr [rbp - 0xf8], rax 437 | 0x10000c9d2 <+498>: call rcx 438 | 0x10000c9d4 <+500>: mov r13, rdx 439 | 0x10000c9d7 <+503>: mov qword ptr [rbp - 0x100], rdx 440 | 0x10000c9de <+510>: call rax 441 | 0x10000c9e0 <+512>: mov qword ptr [rbp - 0x50], rax 442 | 0x10000c9e4 <+516>: mov rdi, qword ptr [rbp - 0x100] 443 | 0x10000c9eb <+523>: mov qword ptr [rbp - 0x108], rax 444 | 0x10000c9f2 <+530>: call 0x10000e784 ; symbol stub for: swift_release 445 | 0x10000c9f7 <+535>: mov rdi, qword ptr [rbp - 0xd8] 446 | 0x10000c9fe <+542>: call 0x10000e784 ; symbol stub for: swift_release 447 | 0x10000ca03 <+547>: mov rax, qword ptr [rip + 0x3786] ; (void *)0x00007fff81589a28: type metadata for Any 448 | 0x10000ca0a <+554>: add rax, 0x8 449 | 0x10000ca10 <+560>: mov edi, 0x1 450 | 0x10000ca15 <+565>: mov rsi, rax 451 | 0x10000ca18 <+568>: call 0x10000e6ca ; symbol stub for: Swift._allocateUninitializedArray(Builtin.Word) -> (Swift.Array, Builtin.RawPointer) 452 | 0x10000ca1d <+573>: mov edi, 0x5 453 | 0x10000ca22 <+578>: mov esi, 0x1 454 | 0x10000ca27 <+583>: mov qword ptr [rbp - 0x110], rax 455 | 0x10000ca2e <+590>: mov qword ptr [rbp - 0x118], rdx 456 | 0x10000ca35 <+597>: call 0x10000e6c4 ; symbol stub for: Swift.DefaultStringInterpolation.init(literalCapacity: Swift.Int, interpolationCount: Swift.Int) -> Swift.DefaultStringInterpolation 457 | 0x10000ca3a <+602>: mov qword ptr [rbp - 0x60], rax 458 | 0x10000ca3e <+606>: mov qword ptr [rbp - 0x58], rdx 459 | 0x10000ca42 <+610>: lea rdi, [rip + 0x2f50] ; "sum: " 460 | 0x10000ca49 <+617>: mov esi, 0x5 461 | 0x10000ca4e <+622>: mov edx, 0x1 462 | 0x10000ca53 <+627>: call 0x10000e652 ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 463 | 0x10000ca58 <+632>: mov rdi, rax 464 | 0x10000ca5b <+635>: mov rsi, rdx 465 | 0x10000ca5e <+638>: lea r13, [rbp - 0x60] 466 | 0x10000ca62 <+642>: mov qword ptr [rbp - 0x120], rdx 467 | 0x10000ca69 <+649>: call 0x10000e6be ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 468 | 0x10000ca6e <+654>: mov rdi, qword ptr [rbp - 0x120] 469 | 0x10000ca75 <+661>: call 0x10000e730 ; symbol stub for: swift_bridgeObjectRelease 470 | 0x10000ca7a <+666>: mov rsi, qword ptr [rip + 0x362f] ; (void *)0x00007fff81581c80: type metadata for Swift.Int 471 | 0x10000ca81 <+673>: mov rdx, qword ptr [rip + 0x3650] ; (void *)0x00007fff81579cb0: protocol witness table for Swift.Int : Swift.CustomStringConvertible in Swift 472 | 0x10000ca88 <+680>: mov rax, qword ptr [rbp - 0x108] 473 | 0x10000ca8f <+687>: mov qword ptr [rbp - 0x68], rax 474 | 0x10000ca93 <+691>: lea rcx, [rbp - 0x68] 475 | 0x10000ca97 <+695>: mov rdi, rcx 476 | 0x10000ca9a <+698>: lea r13, [rbp - 0x60] 477 | 0x10000ca9e <+702>: call 0x10000e6b8 ; symbol stub for: Swift.DefaultStringInterpolation.appendInterpolation(A) -> () 478 | 0x10000caa3 <+707>: xor r8d, r8d 479 | 0x10000caa6 <+710>: mov esi, r8d 480 | 0x10000caa9 <+713>: lea rdi, [rip + 0x2c18] ; "" 481 | 0x10000cab0 <+720>: mov edx, 0x1 482 | 0x10000cab5 <+725>: call 0x10000e652 ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 483 | 0x10000caba <+730>: mov rdi, rax 484 | 0x10000cabd <+733>: mov rsi, rdx 485 | 0x10000cac0 <+736>: lea r13, [rbp - 0x60] 486 | 0x10000cac4 <+740>: mov qword ptr [rbp - 0x128], rdx 487 | 0x10000cacb <+747>: call 0x10000e6be ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 488 | 0x10000cad0 <+752>: mov rdi, qword ptr [rbp - 0x128] 489 | 0x10000cad7 <+759>: call 0x10000e730 ; symbol stub for: swift_bridgeObjectRelease 490 | 0x10000cadc <+764>: mov rdi, qword ptr [rbp - 0x60] 491 | 0x10000cae0 <+768>: mov rax, qword ptr [rbp - 0x58] 492 | 0x10000cae4 <+772>: mov qword ptr [rbp - 0x130], rdi 493 | 0x10000caeb <+779>: mov rdi, rax 494 | 0x10000caee <+782>: mov qword ptr [rbp - 0x138], rax 495 | 0x10000caf5 <+789>: call 0x10000e736 ; symbol stub for: swift_bridgeObjectRetain 496 | 0x10000cafa <+794>: lea rdi, [rbp - 0x60] 497 | 0x10000cafe <+798>: mov qword ptr [rbp - 0x140], rax 498 | 0x10000cb05 <+805>: call 0x10000c360 ; outlined destroy of Swift.DefaultStringInterpolation at 499 | 0x10000cb0a <+810>: mov rdi, qword ptr [rbp - 0x130] 500 | 0x10000cb11 <+817>: mov rsi, qword ptr [rbp - 0x138] 501 | 0x10000cb18 <+824>: mov qword ptr [rbp - 0x148], rax 502 | 0x10000cb1f <+831>: call 0x10000e64c ; symbol stub for: Swift.String.init(stringInterpolation: Swift.DefaultStringInterpolation) -> Swift.String 503 | 0x10000cb24 <+836>: mov rcx, qword ptr [rip + 0x3565] ; (void *)0x00007fff8157fe00: type metadata for Swift.String 504 | 0x10000cb2b <+843>: mov rsi, qword ptr [rbp - 0x118] 505 | 0x10000cb32 <+850>: mov qword ptr [rsi + 0x18], rcx 506 | 0x10000cb36 <+854>: mov qword ptr [rsi], rax 507 | 0x10000cb39 <+857>: mov qword ptr [rsi + 0x8], rdx 508 | 0x10000cb3d <+861>: call 0x100005760 ; default argument 1 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 509 | 0x10000cb42 <+866>: mov qword ptr [rbp - 0x150], rax 510 | 0x10000cb49 <+873>: mov qword ptr [rbp - 0x158], rdx 511 | 0x10000cb50 <+880>: call 0x100005780 ; default argument 2 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 512 | 0x10000cb55 <+885>: mov rdi, qword ptr [rbp - 0x110] 513 | 0x10000cb5c <+892>: mov rsi, qword ptr [rbp - 0x150] 514 | 0x10000cb63 <+899>: mov rcx, qword ptr [rbp - 0x158] 515 | 0x10000cb6a <+906>: mov qword ptr [rbp - 0x160], rdx 516 | 0x10000cb71 <+913>: mov rdx, rcx 517 | 0x10000cb74 <+916>: mov rcx, rax 518 | 0x10000cb77 <+919>: mov r8, qword ptr [rbp - 0x160] 519 | 0x10000cb7e <+926>: call 0x10000e6e8 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () 520 | 0x10000cb83 <+931>: mov rdi, qword ptr [rbp - 0x160] 521 | 0x10000cb8a <+938>: call 0x10000e730 ; symbol stub for: swift_bridgeObjectRelease 522 | 0x10000cb8f <+943>: mov rdi, qword ptr [rbp - 0x158] 523 | 0x10000cb96 <+950>: call 0x10000e730 ; symbol stub for: swift_bridgeObjectRelease 524 | 0x10000cb9b <+955>: mov rdi, qword ptr [rbp - 0x110] 525 | 0x10000cba2 <+962>: call 0x10000e730 ; symbol stub for: swift_bridgeObjectRelease 526 | 0x10000cba7 <+967>: mov rdi, qword ptr [rbp - 0xf0] 527 | 0x10000cbae <+974>: call 0x10000e784 ; symbol stub for: swift_release 528 | 0x10000cbb3 <+979>: mov rdi, qword ptr [rbp - 0xd8] 529 | 0x10000cbba <+986>: call 0x10000e784 ; symbol stub for: swift_release 530 | 0x10000cbbf <+991>: lea rsp, [rbp - 0x8] 531 | 0x10000cbc3 <+995>: pop r13 532 | 0x10000cbc5 <+997>: pop rbp 533 | 0x10000cbc6 <+998>: ret 534 | ``` 535 | 536 | # Static struct methods 537 | 538 | - 0x100010b80 539 | - 0x100010b30 540 | - 0x100010620 541 | - 0x10000f3b0 542 | - 0x100010bd0 543 | - 0x100010640 544 | - 0x10000bb50 <- actual function 545 | 546 | ``` 547 | (lldb) d -a 0x100010640 548 | MixinTest`implicit closure #2 in implicit closure #1 in main(): 549 | 0x100010640 <+0>: push rbp 550 | 0x100010641 <+1>: mov rbp, rsp 551 | 0x100010644 <+4>: call 0x10000bb50 ; static MixinTest.TwoNumbers.getNice() -> Swift.Int at TestFunctions.swift:54 552 | 0x100010649 <+9>: pop rbp 553 | 0x10001064a <+10>: ret 554 | 555 | (lldb) d -a 0x100010bd0 556 | MixinTest`partial apply for implicit closure #2 in implicit closure #1 in main(): 557 | 0x100010bd0 <+0>: push rbp 558 | 0x100010bd1 <+1>: mov rbp, rsp 559 | 0x100010bd4 <+4>: pop rbp 560 | 0x100010bd5 <+5>: jmp 0x100010640 ; implicit closure #2 () -> Swift.Int in implicit closure #1 (MixinTest.TwoNumbers.Type) -> () -> Swift.Int in MixinTest.main() -> () at main.swift 561 | 562 | (lldb) d -a 0x10000f3b0 563 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@unowned Int): 564 | 0x10000f3b0 <+0>: push rbp 565 | 0x10000f3b1 <+1>: mov rbp, rsp 566 | 0x10000f3b4 <+4>: push r13 567 | 0x10000f3b6 <+6>: push rax 568 | 0x10000f3b7 <+7>: mov r13, rsi 569 | 0x10000f3ba <+10>: mov qword ptr [rbp - 0x10], rax 570 | 0x10000f3be <+14>: call rdi 571 | 0x10000f3c0 <+16>: mov rcx, qword ptr [rbp - 0x10] 572 | 0x10000f3c4 <+20>: mov qword ptr [rcx], rax 573 | 0x10000f3c7 <+23>: add rsp, 0x8 574 | 0x10000f3cb <+27>: pop r13 575 | 0x10000f3cd <+29>: pop rbp 576 | 0x10000f3ce <+30>: ret 577 | 578 | (lldb) d -a 0x100010620 579 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@unowned Int): 580 | 0x100010620 <+0>: push rbp 581 | 0x100010621 <+1>: mov rbp, rsp 582 | 0x100010624 <+4>: mov rdi, qword ptr [r13 + 0x10] 583 | 0x100010628 <+8>: mov rsi, qword ptr [r13 + 0x18] 584 | 0x10001062c <+12>: call 0x10000f3b0 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@unowned Swift.Int) to @escaping @callee_guaranteed () -> (@out Swift.Int) at 585 | 0x100010631 <+17>: pop rbp 586 | 0x100010632 <+18>: ret 587 | 588 | (lldb) d -a 0x100010b30 589 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@out Int): 590 | 0x100010b30 <+0>: push rbp 591 | 0x100010b31 <+1>: mov rbp, rsp 592 | 0x100010b34 <+4>: push r13 593 | 0x100010b36 <+6>: push rax 594 | 0x100010b37 <+7>: lea rax, [rbp - 0x10] 595 | 0x100010b3b <+11>: mov r13, rsi 596 | 0x100010b3e <+14>: call rdi 597 | 0x100010b40 <+16>: mov rax, qword ptr [rbp - 0x10] 598 | 0x100010b44 <+20>: add rsp, 0x8 599 | 0x100010b48 <+24>: pop r13 600 | 0x100010b4a <+26>: pop rbp 601 | 0x100010b4b <+27>: ret 602 | 603 | (lldb) d -a 0x100010b80 604 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out Int): 605 | 0x100010b80 <+0>: push rbp 606 | 0x100010b81 <+1>: mov rbp, rsp 607 | 0x100010b84 <+4>: mov rdi, qword ptr [r13 + 0x10] 608 | 0x100010b88 <+8>: mov rsi, qword ptr [r13 + 0x18] 609 | 0x100010b8c <+12>: pop rbp 610 | 0x100010b8d <+13>: jmp 0x100010b30 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 611 | (lldb) 612 | ``` 613 | 614 | ``` 615 | (lldb) d -n runStatic 616 | MixinTest`runStatic(_:): 617 | 0x100010650 <+0>: push rbp 618 | 0x100010651 <+1>: mov rbp, rsp 619 | 0x100010654 <+4>: push r13 620 | 0x100010656 <+6>: sub rsp, 0x128 621 | 0x10001065d <+13>: mov qword ptr [rbp - 0x18], 0x0 622 | 0x100010665 <+21>: xorps xmm0, xmm0 623 | 0x100010668 <+24>: movaps xmmword ptr [rbp - 0x40], xmm0 624 | 0x10001066c <+28>: movaps xmmword ptr [rbp - 0x50], xmm0 625 | 0x100010670 <+32>: mov qword ptr [rbp - 0x10], rsi 626 | 0x100010674 <+36>: mov rax, qword ptr [rsi - 0x8] 627 | 0x100010678 <+40>: mov rcx, qword ptr [rax + 0x40] 628 | 0x10001067c <+44>: add rcx, 0xf 629 | 0x100010680 <+48>: and rcx, -0x10 630 | 0x100010684 <+52>: mov rdx, rsp 631 | 0x100010687 <+55>: sub rdx, rcx 632 | 0x10001068a <+58>: mov rsp, rdx 633 | 0x10001068d <+61>: mov qword ptr [rbp - 0x18], rdi 634 | 0x100010691 <+65>: mov qword ptr [rbp - 0x60], rdi 635 | 0x100010695 <+69>: mov rdi, rdx 636 | 0x100010698 <+72>: mov rcx, qword ptr [rbp - 0x60] 637 | 0x10001069c <+76>: mov qword ptr [rbp - 0x68], rsi 638 | 0x1000106a0 <+80>: mov rsi, rcx 639 | 0x1000106a3 <+83>: mov r8, qword ptr [rbp - 0x68] 640 | 0x1000106a7 <+87>: mov qword ptr [rbp - 0x70], rdx 641 | 0x1000106ab <+91>: mov rdx, r8 642 | 0x1000106ae <+94>: call qword ptr [rax + 0x10] 643 | 0x1000106b1 <+97>: lea rcx, [rbp - 0x28] 644 | 0x1000106b5 <+101>: lea rdi, [rip + 0x7ef4] ; demangling cache variable for type metadata for () -> Swift.Int 645 | 0x1000106bc <+108>: mov qword ptr [rbp - 0x78], rax 646 | 0x1000106c0 <+112>: mov qword ptr [rbp - 0x80], rcx 647 | 0x1000106c4 <+116>: call 0x100006c10 ; __swift_instantiateConcreteTypeFromMangledName at 648 | 0x1000106c9 <+121>: mov rdi, qword ptr [rbp - 0x80] 649 | 0x1000106cd <+125>: mov rsi, qword ptr [rbp - 0x70] 650 | 0x1000106d1 <+129>: mov rdx, qword ptr [rbp - 0x68] 651 | 0x1000106d5 <+133>: mov rcx, rax 652 | 0x1000106d8 <+136>: mov r8d, 0x6 653 | 0x1000106de <+142>: call 0x100012606 ; symbol stub for: swift_dynamicCast 654 | 0x1000106e3 <+147>: test al, 0x1 655 | 0x1000106e5 <+149>: jne 0x1000106e9 ; <+153> at main.swift 656 | 0x1000106e7 <+151>: jmp 0x10001074e ; <+254> at main.swift 657 | 0x1000106e9 <+153>: lea rax, [rip + 0x41c8] ; type metadata for MixinTest.Numbers + 856 658 | 0x1000106f0 <+160>: add rax, 0x10 659 | 0x1000106f6 <+166>: mov rcx, qword ptr [rbp - 0x28] 660 | 0x1000106fa <+170>: mov rdx, qword ptr [rbp - 0x20] 661 | 0x1000106fe <+174>: mov rdi, rax 662 | 0x100010701 <+177>: mov esi, 0x20 663 | 0x100010706 <+182>: mov eax, 0x7 664 | 0x10001070b <+187>: mov qword ptr [rbp - 0x88], rdx 665 | 0x100010712 <+194>: mov rdx, rax 666 | 0x100010715 <+197>: mov qword ptr [rbp - 0x90], rcx 667 | 0x10001071c <+204>: call 0x1000125e2 ; symbol stub for: swift_allocObject 668 | 0x100010721 <+209>: mov rcx, qword ptr [rbp - 0x90] 669 | 0x100010728 <+216>: mov qword ptr [rax + 0x10], rcx 670 | 0x10001072c <+220>: mov rcx, qword ptr [rbp - 0x88] 671 | 0x100010733 <+227>: mov qword ptr [rax + 0x18], rcx 672 | 0x100010737 <+231>: lea rcx, [rip + 0x442] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at 673 | 0x10001073e <+238>: mov qword ptr [rbp - 0x98], rcx 674 | 0x100010745 <+245>: mov qword ptr [rbp - 0xa0], rax 675 | 0x10001074c <+252>: jmp 0x100010763 ; <+275> at main.swift 676 | 0x10001074e <+254>: xor eax, eax 677 | 0x100010750 <+256>: mov ecx, eax 678 | 0x100010752 <+258>: mov rdx, rcx 679 | 0x100010755 <+261>: mov qword ptr [rbp - 0x98], rdx 680 | 0x10001075c <+268>: mov qword ptr [rbp - 0xa0], rcx 681 | 0x100010763 <+275>: mov rax, qword ptr [rbp - 0xa0] 682 | 0x10001076a <+282>: mov rcx, qword ptr [rbp - 0x98] 683 | 0x100010771 <+289>: cmp rcx, 0x0 684 | 0x100010775 <+293>: mov qword ptr [rbp - 0xa8], rax 685 | 0x10001077c <+300>: mov qword ptr [rbp - 0xb0], rcx 686 | 0x100010783 <+307>: je 0x1000107a3 ; <+339> at main.swift:109:38 687 | 0x100010785 <+309>: mov rax, qword ptr [rbp - 0xb0] 688 | 0x10001078c <+316>: mov rcx, qword ptr [rbp - 0xa8] 689 | 0x100010793 <+323>: mov qword ptr [rbp - 0xb8], rax 690 | 0x10001079a <+330>: mov qword ptr [rbp - 0xc0], rcx 691 | 0x1000107a1 <+337>: jmp 0x1000107a8 ; <+344> at main.swift 692 | 0x1000107a3 <+339>: jmp 0x1000109a5 ; <+853> at main.swift:112:1 693 | 0x1000107a8 <+344>: mov rax, qword ptr [rbp - 0xc0] 694 | 0x1000107af <+351>: mov rcx, qword ptr [rbp - 0xb8] 695 | 0x1000107b6 <+358>: mov rdx, qword ptr [rip + 0x3a0b] ; (void *)0x00007fff81589a28: type metadata for Any 696 | 0x1000107bd <+365>: add rdx, 0x8 697 | 0x1000107c4 <+372>: mov qword ptr [rbp - 0x40], rcx 698 | 0x1000107c8 <+376>: mov qword ptr [rbp - 0x38], rax 699 | 0x1000107cc <+380>: mov edi, 0x1 700 | 0x1000107d1 <+385>: mov rsi, rdx 701 | 0x1000107d4 <+388>: mov qword ptr [rbp - 0xc8], rax 702 | 0x1000107db <+395>: mov qword ptr [rbp - 0xd0], rcx 703 | 0x1000107e2 <+402>: call 0x100012582 ; symbol stub for: Swift._allocateUninitializedArray(Builtin.Word) -> (Swift.Array, Builtin.RawPointer) 704 | 0x1000107e7 <+407>: mov edi, 0x8 705 | 0x1000107ec <+412>: mov esi, 0x1 706 | 0x1000107f1 <+417>: mov qword ptr [rbp - 0xd8], rax 707 | 0x1000107f8 <+424>: mov qword ptr [rbp - 0xe0], rdx 708 | 0x1000107ff <+431>: call 0x10001257c ; symbol stub for: Swift.DefaultStringInterpolation.init(literalCapacity: Swift.Int, interpolationCount: Swift.Int) -> Swift.DefaultStringInterpolation 709 | 0x100010804 <+436>: mov qword ptr [rbp - 0x50], rax 710 | 0x100010808 <+440>: mov qword ptr [rbp - 0x48], rdx 711 | 0x10001080c <+444>: lea rdi, [rip + 0x30d6] ; "result: " 712 | 0x100010813 <+451>: mov esi, 0x8 713 | 0x100010818 <+456>: mov edx, 0x1 714 | 0x10001081d <+461>: call 0x1000124fe ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 715 | 0x100010822 <+466>: mov rdi, rax 716 | 0x100010825 <+469>: mov rsi, rdx 717 | 0x100010828 <+472>: lea r13, [rbp - 0x50] 718 | 0x10001082c <+476>: mov qword ptr [rbp - 0xe8], rdx 719 | 0x100010833 <+483>: call 0x100012576 ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 720 | 0x100010838 <+488>: mov rdi, qword ptr [rbp - 0xe8] 721 | 0x10001083f <+495>: call 0x1000125ee ; symbol stub for: swift_bridgeObjectRelease 722 | 0x100010844 <+500>: mov rdi, qword ptr [rbp - 0xc8] 723 | 0x10001084b <+507>: call 0x100012648 ; symbol stub for: swift_retain 724 | 0x100010850 <+512>: mov r13, qword ptr [rbp - 0xc8] 725 | 0x100010857 <+519>: mov rcx, qword ptr [rbp - 0xd0] 726 | 0x10001085e <+526>: mov qword ptr [rbp - 0xf0], rax 727 | 0x100010865 <+533>: call rcx 728 | 0x100010867 <+535>: mov rsi, qword ptr [rip + 0x384a] ; (void *)0x00007fff81581c80: type metadata for Swift.Int 729 | 0x10001086e <+542>: mov rdx, qword ptr [rip + 0x386b] ; (void *)0x00007fff81579cb0: protocol witness table for Swift.Int : Swift.CustomStringConvertible in Swift 730 | 0x100010875 <+549>: mov qword ptr [rbp - 0x58], rax 731 | 0x100010879 <+553>: lea rax, [rbp - 0x58] 732 | 0x10001087d <+557>: mov rdi, rax 733 | 0x100010880 <+560>: lea r13, [rbp - 0x50] 734 | 0x100010884 <+564>: call 0x100012570 ; symbol stub for: Swift.DefaultStringInterpolation.appendInterpolation(A) -> () 735 | 0x100010889 <+569>: mov rdi, qword ptr [rbp - 0xc8] 736 | 0x100010890 <+576>: call 0x100012642 ; symbol stub for: swift_release 737 | 0x100010895 <+581>: xor r8d, r8d 738 | 0x100010898 <+584>: mov esi, r8d 739 | 0x10001089b <+587>: lea rdi, [rip + 0x2d16] ; "" 740 | 0x1000108a2 <+594>: mov edx, 0x1 741 | 0x1000108a7 <+599>: call 0x1000124fe ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 742 | 0x1000108ac <+604>: mov rdi, rax 743 | 0x1000108af <+607>: mov rsi, rdx 744 | 0x1000108b2 <+610>: lea r13, [rbp - 0x50] 745 | 0x1000108b6 <+614>: mov qword ptr [rbp - 0xf8], rdx 746 | 0x1000108bd <+621>: call 0x100012576 ; symbol stub for: Swift.DefaultStringInterpolation.appendLiteral(Swift.String) -> () 747 | 0x1000108c2 <+626>: mov rdi, qword ptr [rbp - 0xf8] 748 | 0x1000108c9 <+633>: call 0x1000125ee ; symbol stub for: swift_bridgeObjectRelease 749 | 0x1000108ce <+638>: mov rdi, qword ptr [rbp - 0x50] 750 | 0x1000108d2 <+642>: mov rax, qword ptr [rbp - 0x48] 751 | 0x1000108d6 <+646>: mov qword ptr [rbp - 0x100], rdi 752 | 0x1000108dd <+653>: mov rdi, rax 753 | 0x1000108e0 <+656>: mov qword ptr [rbp - 0x108], rax 754 | 0x1000108e7 <+663>: call 0x1000125f4 ; symbol stub for: swift_bridgeObjectRetain 755 | 0x1000108ec <+668>: lea rdi, [rbp - 0x50] 756 | 0x1000108f0 <+672>: mov qword ptr [rbp - 0x110], rax 757 | 0x1000108f7 <+679>: call 0x100010140 ; outlined destroy of Swift.DefaultStringInterpolation at 758 | 0x1000108fc <+684>: mov rdi, qword ptr [rbp - 0x100] 759 | 0x100010903 <+691>: mov rsi, qword ptr [rbp - 0x108] 760 | 0x10001090a <+698>: mov qword ptr [rbp - 0x118], rax 761 | 0x100010911 <+705>: call 0x1000124f8 ; symbol stub for: Swift.String.init(stringInterpolation: Swift.DefaultStringInterpolation) -> Swift.String 762 | 0x100010916 <+710>: mov rcx, qword ptr [rip + 0x377b] ; (void *)0x00007fff8157fe00: type metadata for Swift.String 763 | 0x10001091d <+717>: mov rsi, qword ptr [rbp - 0xe0] 764 | 0x100010924 <+724>: mov qword ptr [rsi + 0x18], rcx 765 | 0x100010928 <+728>: mov qword ptr [rsi], rax 766 | 0x10001092b <+731>: mov qword ptr [rsi + 0x8], rdx 767 | 0x10001092f <+735>: call 0x1000064d0 ; default argument 1 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 768 | 0x100010934 <+740>: mov qword ptr [rbp - 0x120], rax 769 | 0x10001093b <+747>: mov qword ptr [rbp - 0x128], rdx 770 | 0x100010942 <+754>: call 0x1000064f0 ; default argument 2 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at 771 | 0x100010947 <+759>: mov rdi, qword ptr [rbp - 0xd8] 772 | 0x10001094e <+766>: mov rsi, qword ptr [rbp - 0x120] 773 | 0x100010955 <+773>: mov rcx, qword ptr [rbp - 0x128] 774 | 0x10001095c <+780>: mov qword ptr [rbp - 0x130], rdx 775 | 0x100010963 <+787>: mov rdx, rcx 776 | 0x100010966 <+790>: mov rcx, rax 777 | 0x100010969 <+793>: mov r8, qword ptr [rbp - 0x130] 778 | 0x100010970 <+800>: call 0x1000125a0 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () 779 | 0x100010975 <+805>: mov rdi, qword ptr [rbp - 0x130] 780 | 0x10001097c <+812>: call 0x1000125ee ; symbol stub for: swift_bridgeObjectRelease 781 | 0x100010981 <+817>: mov rdi, qword ptr [rbp - 0x128] 782 | 0x100010988 <+824>: call 0x1000125ee ; symbol stub for: swift_bridgeObjectRelease 783 | 0x10001098d <+829>: mov rdi, qword ptr [rbp - 0xd8] 784 | 0x100010994 <+836>: call 0x1000125ee ; symbol stub for: swift_bridgeObjectRelease 785 | 0x100010999 <+841>: mov rdi, qword ptr [rbp - 0xc8] 786 | 0x1000109a0 <+848>: call 0x100012642 ; symbol stub for: swift_release 787 | 0x1000109a5 <+853>: lea rsp, [rbp - 0x8] 788 | 0x1000109a9 <+857>: pop r13 789 | 0x1000109ab <+859>: pop rbp 790 | 0x1000109ac <+860>: ret 791 | ``` -------------------------------------------------------------------------------- /Notes/NOTES.md: -------------------------------------------------------------------------------- 1 | # Some notes for myself on how functions and memory work in Swift 2 | 3 | ## Function as argument 4 | 5 | In the following, `function` is a pointer straight to the function that can be called like in c.. 6 | 7 | ```swift 8 | func runFunction(_ function: () -> ()) { 9 | function() 10 | } 11 | ``` 12 | 13 | The following runs a () -> () function passed as a generic. This can likely be modified to find the address of the function passed in. 14 | 15 | ```swift 16 | func runGenericThatIsFunction(_ function: T) { 17 | if let function = function as? () -> () { 18 | function() 19 | } 20 | } 21 | ``` 22 | 23 | Below is the assembly for the function above. We should be able to figure out how the function is passed in by analysing what the function does. 24 | The `call rcx` instruction corresponds to `function()`. We just need to figure out where `rcx` is from 25 | 26 | ``` 27 | MixinTest`runGenericThatIsFunction(_:): 28 | 0x100003440 <+0>: push rbp 29 | 0x100003441 <+1>: mov rbp, rsp 30 | 0x100003444 <+4>: push r13 31 | 0x100003446 <+6>: sub rsp, 0xb8 32 | 0x10000344d <+13>: mov qword ptr [rbp - 0x18], 0x0 33 | 0x100003455 <+21>: xorps xmm0, xmm0 34 | 0x100003458 <+24>: movaps xmmword ptr [rbp - 0x40], xmm0 35 | 0x10000345c <+28>: mov qword ptr [rbp - 0x10], rsi 36 | 0x100003460 <+32>: mov rax, qword ptr [rsi - 0x8] 37 | 0x100003464 <+36>: mov rcx, qword ptr [rax + 0x40] 38 | 0x100003468 <+40>: add rcx, 0xf 39 | 0x10000346c <+44>: and rcx, -0x10 40 | 0x100003470 <+48>: mov rdx, rsp 41 | 0x100003473 <+51>: sub rdx, rcx 42 | 0x100003476 <+54>: mov rsp, rdx 43 | 0x100003479 <+57>: mov qword ptr [rbp - 0x18], rdi 44 | 0x10000347d <+61>: mov qword ptr [rbp - 0x48], rdi 45 | 0x100003481 <+65>: mov rdi, rdx 46 | 0x100003484 <+68>: mov rcx, qword ptr [rbp - 0x48] 47 | 0x100003488 <+72>: mov qword ptr [rbp - 0x50], rsi 48 | 0x10000348c <+76>: mov rsi, rcx 49 | 0x10000348f <+79>: mov r8, qword ptr [rbp - 0x50] 50 | 0x100003493 <+83>: mov qword ptr [rbp - 0x58], rdx 51 | 0x100003497 <+87>: mov rdx, r8 52 | 0x10000349a <+90>: call qword ptr [rax + 0x10] 53 | 0x10000349d <+93>: lea rcx, [rbp - 0x28] 54 | 0x1000034a1 <+97>: lea rdi, [rip + 0x5298] ; demangling cache variable for type metadata for () -> () 55 | 0x1000034a8 <+104>: mov qword ptr [rbp - 0x60], rax 56 | 0x1000034ac <+108>: mov qword ptr [rbp - 0x68], rcx 57 | 0x1000034b0 <+112>: call 0x100003170 ; __swift_instantiateConcreteTypeFromMangledName at 58 | 0x1000034b5 <+117>: mov rdi, qword ptr [rbp - 0x68] 59 | 0x1000034b9 <+121>: mov rsi, qword ptr [rbp - 0x58] 60 | 0x1000034bd <+125>: mov rdx, qword ptr [rbp - 0x50] 61 | 0x1000034c1 <+129>: mov rcx, rax 62 | 0x1000034c4 <+132>: mov r8d, 0x6 63 | 0x1000034ca <+138>: call 0x1000072e4 ; symbol stub for: swift_dynamicCast 64 | 0x1000034cf <+143>: test al, 0x1 65 | 0x1000034d1 <+145>: jne 0x1000034d5 ; <+149> at main.swift 66 | 0x1000034d3 <+147>: jmp 0x10000352b ; <+235> at main.swift 67 | 0x1000034d5 <+149>: lea rax, [rip + 0x4f3c] 68 | 0x1000034dc <+156>: add rax, 0x10 69 | 0x1000034e2 <+162>: mov rcx, qword ptr [rbp - 0x28] 70 | 0x1000034e6 <+166>: mov rdx, qword ptr [rbp - 0x20] 71 | 0x1000034ea <+170>: mov rdi, rax 72 | 0x1000034ed <+173>: mov esi, 0x20 73 | 0x1000034f2 <+178>: mov eax, 0x7 74 | 0x1000034f7 <+183>: mov qword ptr [rbp - 0x70], rdx 75 | 0x1000034fb <+187>: mov rdx, rax 76 | 0x1000034fe <+190>: mov qword ptr [rbp - 0x78], rcx 77 | 0x100003502 <+194>: call 0x1000072cc ; symbol stub for: swift_allocObject 78 | 0x100003507 <+199>: mov rcx, qword ptr [rbp - 0x78] 79 | 0x10000350b <+203>: mov qword ptr [rax + 0x10], rcx 80 | 0x10000350f <+207>: mov rcx, qword ptr [rbp - 0x70] 81 | 0x100003513 <+211>: mov qword ptr [rax + 0x18], rcx 82 | 0x100003517 <+215>: lea rcx, [rip + 0x302] ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out ()) to @escaping @callee_guaranteed () -> () at 83 | 0x10000351e <+222>: mov qword ptr [rbp - 0x80], rcx 84 | 0x100003522 <+226>: mov qword ptr [rbp - 0x88], rax 85 | 0x100003529 <+233>: jmp 0x10000353d ; <+253> at main.swift 86 | 0x10000352b <+235>: xor eax, eax 87 | 0x10000352d <+237>: mov ecx, eax 88 | 0x10000352f <+239>: mov rdx, rcx 89 | 0x100003532 <+242>: mov qword ptr [rbp - 0x80], rdx 90 | 0x100003536 <+246>: mov qword ptr [rbp - 0x88], rcx 91 | 0x10000353d <+253>: mov rax, qword ptr [rbp - 0x88] 92 | 0x100003544 <+260>: mov rcx, qword ptr [rbp - 0x80] 93 | 0x100003548 <+264>: cmp rcx, 0x0 94 | 0x10000354c <+268>: mov qword ptr [rbp - 0x90], rax 95 | 0x100003553 <+275>: mov qword ptr [rbp - 0x98], rcx 96 | 0x10000355a <+282>: je 0x10000357a ; <+314> at main.swift:61:30 97 | 0x10000355c <+284>: mov rax, qword ptr [rbp - 0x98] 98 | 0x100003563 <+291>: mov rcx, qword ptr [rbp - 0x90] 99 | 0x10000356a <+298>: mov qword ptr [rbp - 0xa0], rax 100 | 0x100003571 <+305>: mov qword ptr [rbp - 0xa8], rcx 101 | 0x100003578 <+312>: jmp 0x10000357c ; <+316> at main.swift 102 | 0x10000357a <+314>: jmp 0x1000035d7 ; <+407> at main.swift:64:1 103 | 0x10000357c <+316>: mov rax, qword ptr [rbp - 0xa8] 104 | 0x100003583 <+323>: mov rcx, qword ptr [rbp - 0xa0] 105 | 0x10000358a <+330>: mov qword ptr [rbp - 0x40], rcx 106 | 0x10000358e <+334>: mov qword ptr [rbp - 0x38], rax 107 | -> 0x100003592 <+338>: mov rdi, rax 108 | 0x100003595 <+341>: mov qword ptr [rbp - 0xb0], rax 109 | 0x10000359c <+348>: mov qword ptr [rbp - 0xb8], rcx 110 | 0x1000035a3 <+355>: call 0x10000730e ; symbol stub for: swift_retain 111 | 0x1000035a8 <+360>: mov r13, qword ptr [rbp - 0xb0] 112 | 0x1000035af <+367>: mov rcx, qword ptr [rbp - 0xb8] 113 | 0x1000035b6 <+374>: mov qword ptr [rbp - 0xc0], rax 114 | 0x1000035bd <+381>: call rcx 115 | 0x1000035bf <+383>: mov rdi, qword ptr [rbp - 0xb0] 116 | 0x1000035c6 <+390>: call 0x100007308 ; symbol stub for: swift_release 117 | 0x1000035cb <+395>: mov rdi, qword ptr [rbp - 0xb0] 118 | 0x1000035d2 <+402>: call 0x100007308 ; symbol stub for: swift_release 119 | 0x1000035d7 <+407>: lea rsp, [rbp - 0x8] 120 | 0x1000035db <+411>: pop r13 121 | 0x1000035dd <+413>: pop rbp 122 | 0x1000035de <+414>: ret 123 | 0x1000035df <+415>: nop 124 | ``` 125 | 126 | At `call rcx`, `rcx` contains a pointer to a weird reabstraction function. The disassembly of which is below; 127 | 128 | ``` 129 | MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out ()): 130 | 0x100003820 <+0>: push rbp 131 | 0x100003821 <+1>: mov rbp, rsp 132 | 0x100003824 <+4>: mov rdi, qword ptr [r13 + 0x10] 133 | 0x100003828 <+8>: mov rsi, qword ptr [r13 + 0x18] 134 | 0x10000382c <+12>: pop rbp 135 | 0x10000382d <+13>: jmp 0x1000037d0 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out ()) to @escaping @callee_guaranteed () -> () at 136 | 0x100003832 <+18>: nop word ptr cs:[rax + rax] 137 | 0x10000383c <+28>: nop dword ptr [rax] 138 | ``` 139 | 140 | The important line is the `jmp` instruction which jumps into the following function; 141 | 142 | ``` 143 | MixinTest`thunk for @escaping @callee_guaranteed () -> (@out ()): 144 | 0x1000037d0 <+0>: push rbp 145 | 0x1000037d1 <+1>: mov rbp, rsp 146 | 0x1000037d4 <+4>: push r13 147 | 0x1000037d6 <+6>: push rax 148 | 0x1000037d7 <+7>: mov r13, rsi 149 | 0x1000037da <+10>: call rdi 150 | 0x1000037dc <+12>: add rsp, 0x8 151 | 0x1000037e0 <+16>: pop r13 152 | 0x1000037e2 <+18>: pop rbp 153 | 0x1000037e3 <+19>: ret 154 | 0x1000037e4 <+20>: nop word ptr cs:[rax + rax] 155 | 0x1000037ee <+30>: nop 156 | ``` 157 | 158 | With some initial dynamic analysis we can see it calls another function (dynamic this time). 159 | 160 | ``` 161 | MixinTest`thunk for @escaping @callee_guaranteed () -> ()partial apply: 162 | -> 0x100003420 <+0>: push rbp 163 | 0x100003421 <+1>: mov rbp, rsp 164 | 0x100003424 <+4>: mov rdi, qword ptr [r13 + 0x10] 165 | 0x100003428 <+8>: mov rsi, qword ptr [r13 + 0x18] 166 | 0x10000342c <+12>: call 0x100003100 ; reabstraction thunk helper from @escaping @callee_guaranteed () -> () to @escaping @callee_guaranteed () -> (@out ()) at 167 | 0x100003431 <+17>: pop rbp 168 | 0x100003432 <+18>: ret 169 | 0x100003433 <+19>: nop word ptr cs:[rax + rax] 170 | 0x10000343d <+29>: nop dword ptr [rax] 171 | ``` 172 | 173 | The function called by the above code (disassembly below) calls whatever is in `rdi` which happens to finally be `function2`! Now we just need to figure out where that came from. 174 | 175 | ``` 176 | MixinTest`thunk for @escaping @callee_guaranteed () -> (): 177 | -> 0x100003100 <+0>: push rbp 178 | 0x100003101 <+1>: mov rbp, rsp 179 | 0x100003104 <+4>: push r13 180 | 0x100003106 <+6>: push rax 181 | 0x100003107 <+7>: mov r13, rsi 182 | 0x10000310a <+10>: call rdi 183 | 0x10000310c <+12>: add rsp, 0x8 184 | 0x100003110 <+16>: pop r13 185 | 0x100003112 <+18>: pop rbp 186 | 0x100003113 <+19>: ret 187 | 0x100003114 <+20>: nop word ptr cs:[rax + rax] 188 | 0x10000311e <+30>: nop 189 | ``` 190 | 191 | Between 0x100003502 and 0x1000035bd in runGenericThatIsFunction(_:) the code is filling out a struct with the following layout; 192 | 193 | { 194 | \+ 0x10: pointer to `MixinTest`partial apply for thunk for @escaping @callee_guaranteed () -> (@out ())` 195 | \+ 0x18: pointer to memory { 196 | \+ 0x10: `function2` 197 | } 198 | } 199 | 200 | At an offset of 0x10 there is some sort of odd function that doesn't seem to do anything important (at least for a `() -> ()` type function). At an offset of 0x18 there is a pointer to another piece of memory. This other piece of memory contains a pointer to `function2` at an offset of 0x10. Somehow we need to find out where this comes from. 201 | 202 | It seems as if `rdi` is a 'value witness table'. 203 | 204 | I have found where in the argument the function pointer is. The function `getFunctionAddress(_:)` gets passed a pointer to a `GenericFunction` (these are just structures for what I know so far). That struct contains a pointer to a thunk and what I'm assuming is some kind of metadata struct. The third member of the metadata struct (assuming each takes up 64 bits) is a pointer to the function passed in. 205 | 206 | ```swift 207 | struct GenericFunction { 208 | var thunk: UInt64 209 | var metadata: UnsafePointer 210 | } 211 | 212 | struct FunctionMetadata { 213 | var arg1: UInt64 214 | var arg2: UInt64 215 | var functionPointer: UInt64 216 | } 217 | 218 | func getFunctionAddress(_ function: T) -> UInt64 { 219 | var functionAddress: UInt64 = 0 220 | withUnsafePointer(to: function, { pointer in 221 | pointer.withMemoryRebound(to: GenericFunction.self, capacity: 1, { pointer in 222 | functionAddress = pointer.pointee.metadata.pointee.functionPointer 223 | }) 224 | }) 225 | return functionAddress 226 | } 227 | ``` 228 | 229 | mprotect'ing function memory pages to add write gives permission denied. MachO maxprot for __text segment was changed to 0x5 at some point, patch it to be 0x7 (with a command and also on the fly). 230 | 231 | All normal functions work now. Methods of structs and classes do not. Consider the following struct; 232 | 233 | ```swift 234 | struct TwoNumbers { 235 | var a: Int 236 | var b: Int 237 | 238 | init(a: Int, b: Int) { 239 | self.a = a 240 | self.b = b 241 | } 242 | 243 | func sum() -> Int { 244 | return a + b 245 | } 246 | 247 | func product() -> Int { 248 | return a * b 249 | } 250 | } 251 | ``` 252 | 253 | Passing `TwoNumbers.sum` to a function taking a generic type passes some multiply nested tuple kind of deal. Then the call to `TwoNumber.sum` is actually hardcoded into a thunk or reabstraction handler or something. Could possibly get the address of the thunk with the call hardcoded in and then iterate through the instructions and find the address passed to call. 254 | 255 | The signature of `TwoNumber.sum` is `(TwoNumbers) -> () -> Int`. Replacing an explicit function of that type works fine, but a new thunk is generated each time a struct method is passed to a function. 256 | 257 | 258 | 259 | ## First `call rax` 260 | 261 | rdi is result of TwoNumbers.init 262 | 263 | context+0x10 is next thunk 264 | context+0x18 is context for next thunk 265 | 266 | 0x10000ad70 267 | 268 | rdi is a TwoNumbers 269 | rsi is context+0x10 (pointer to the thunk after next) 270 | rdx is context+0x18 (context for thunk after next) 271 | 272 | 0x10000a830 273 | 274 | rdi is a 275 | rsi is b 276 | rdx is next thunk 277 | 278 | 0x10000acd0 279 | 280 | ## Second `call rax` 281 | 282 | 0x10000aeb0 283 | 284 | rdi is context + 0x10 (pointer to the thunk after next) 285 | rsi is context + 0x18 (context for thunk after next) 286 | 287 | 0x1000075e0 288 | 289 | rdi is pointer to rdi 290 | rax is pointer to rbp - 0x28 291 | 292 | 0x10000af40 293 | 294 | rdi is context + 0x10 (pointer to the thunk after next) 295 | rsi is context + 0x18 (context for the thunk after next containing TwoNumbers at +0x10) 296 | 297 | 0x10000aef0 298 | 299 | rdi is context + 0x10 (a) 300 | rsi is context + 0x18 (b) 301 | 302 | 303 | 304 | Getting address to TwoNumbers.sum from context passed to second `call rax`; 305 | 306 | 1. get thunk at [r13+0x18]+0x10 307 | 2. find jmp instruction and get address it jumps to 308 | 3. parse the function it jumps into and find the call instruction, the address passed to that is TwoNumbers.sum 309 | 310 | Need to find the address of 0x10000a790 because it returns the address of the function at [r13+0x18]+0x10 as passed to the second `call rax`. Can then maybe find the address of the function and run it from c to get the address of the thunk and then continue from step 2 above. The address can be found passed to the function at [rdi+0x08]+0x10 311 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Capstone", 6 | "repositoryURL": "https://github.com/zydeco/capstone-swift", 7 | "state": { 8 | "branch": "v4", 9 | "revision": "8a809abaa5233e4f43e34a20ffd1c2c5fa902b50", 10 | "version": null 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SwiftMixin", 7 | products: [ 8 | .library(name: "SwiftMixin", targets: ["SwiftMixin"]) 9 | ], 10 | dependencies: [ 11 | .package( 12 | name:"Capstone", 13 | url: "https://github.com/zydeco/capstone-swift", 14 | .branch("v4")) 15 | ], 16 | targets: [ 17 | .target( 18 | name: "SwiftMixin", 19 | dependencies: ["SwiftMixinC", "Capstone"]), 20 | .target( 21 | name: "SwiftMixinC", 22 | dependencies: []), 23 | .testTarget( 24 | name: "SwiftMixinTests", 25 | dependencies: ["SwiftMixin"]), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftMixin 2 | 3 | > Disclaimer: This package is very young and due to its nature, a small change to the swift compiler could brick this package. E.g. class method replacement got broken by Swift 5.6 4 | 5 | ## Quick Overview 6 | 7 | SwiftMixin provides all of the functionality required to overwrite functions and methods at runtime. It also allows you to create backups of functions before you overwrite them so that you can still use the original function. This package was made for a Swift plugin system ([Delta Plugin API](https://github.com/thegail/DeltaPluginAPI)), but was abandoned because it was decided that it was better if plugins were restricted to only using the public API so that are more stable. 8 | 9 | Only x86_64 is supported and ARM64 support probably won't be added any time soon. 10 | 11 | ## Install 12 | 13 | Using this library in your project requires that you have capstone installed on your system. Capstone can be installed using homebrew with the following command; 14 | 15 | ```sh 16 | brew install capstone 17 | ``` 18 | 19 | The next few steps depend on what sort of project you have. Installation is different for Swift Package Manager projects and Xcode projects. 20 | 21 | ### Install - Swift Package Manager 22 | 23 | 1. Add this package as a dependency in your `Package.swift`. 24 | 25 | Below is an example `Package.swift` with SwiftMixin as a dependency; 26 | 27 | ```swift 28 | // swift-tools-version:5.3 29 | 30 | import PackageDescription 31 | 32 | let package = Package( 33 | name: "MixinHelloWorld", 34 | dependencies: [ 35 | .package( 36 | name: "SwiftMixin", 37 | url: "https://github.com/stackotter/swift-mixin", 38 | .branch("main")) 39 | ], 40 | targets: [ 41 | .target( 42 | name: "MixinHelloWorld", 43 | dependencies: ["SwiftMixin"]), 44 | .testTarget( 45 | name: "MixinHelloWorldTests", 46 | dependencies: ["MixinHelloWorld"]), 47 | ] 48 | ) 49 | ``` 50 | 51 | 2. Optional: Run `swift package generate-xcodeproj` because using an xcodeproj makes your life easier later on. 52 | 53 | ### Install - Xcode Project 54 | 55 | 1. Navigate to `File > Swift Packages > Add Package Dependency...`. 56 | 2. Enter `https://github.com/stackotter/swift-mixin` as the url. 57 | 3. On the next screen choose branch rule and leave the default value (it should be 'main'). 58 | 4. Click next. Once it finishes loading, choose your package in the `Add to Target` column and click done. 59 | 60 | ## Final Setup 61 | 62 | In more recent versions of macOS, Apple changed the default maxProt level of the text segment of MachO executables to be 5 (it used to be 7). In short; we need to change this value back to 7 otherwise we can't write to the memory that contains functions. I don't know which macOS version the change was made in so it's safest just to do the following steps anyway. 63 | 64 | ### Final Setup - Projects with an xcodeproj 65 | 66 | Add a run script phase to your project containing the following; 67 | 68 | ```sh 69 | printf '\x07' | dd of=${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_PATH} bs=1 seek=160 count=1 conv=notrunc 70 | ``` 71 | 72 | This will patch the binary correctly everytime you build your project. 73 | 74 | ### Final Setup - Projects without an xcodeproj 75 | 76 | If you have a swift package manager project and don't use a .xcodeproj then there are two options (both are not that good, it's not too late to run `swift package generate-xcodeproj`). 77 | 78 | Option 1: Run `printf '\x07' | dd of=./path/to/compiled/binary bs=1 seek=160 count=1 conv=notrunc` everytime you build your project (an example build and run script is listed below). 79 | 80 | RunDebug.sh 81 | ```sh 82 | swift build 83 | printf '\x07' | dd of=.build/debug/[PRODUCT_NAME] bs=1 seek=160 count=1 conv=notrunc 84 | ./.build/debug/[PRODUCT_NAME] 85 | ``` 86 | 87 | Option 2: Or, each time you want to build and run your project; First build and run it (you will get an error), and then run it again and it should work. Your executable will automatically patch itself, but it requires a restart of the program for the changes to take effect. This autopatching requires that your program calls `Mixin.setup()` when it starts up. 88 | 89 | ## Example Program 90 | 91 | ```swift 92 | import SwiftMixin 93 | 94 | func replaceMe() { 95 | print("Please replace me!") 96 | } 97 | 98 | func replacement() { 99 | print("Hello from the replacement!") 100 | } 101 | 102 | do { 103 | // Check that max prot is set correctly 104 | try Mixin.setup() 105 | // Create a backup of `replaceMe` so that we can still use it later 106 | let replaceMe_Backup = try Mixin.duplicateFunction(replaceMe) 107 | // Replace `replaceMe` with `replacement` 108 | try Mixin.replaceFunction(replaceMe, with: replacement) 109 | 110 | // Run `replaceMe` (should actually run `replacement` now) 111 | print("Replaced `replaceMe()`: ", terminator: "") 112 | replaceMe() 113 | 114 | // Run the backup 115 | print("Backup `replaceMe_Backup()`: ", terminator: "") 116 | replaceMe_Backup() 117 | } catch { 118 | print("There was an error: \(error)") 119 | } 120 | ``` 121 | 122 | ## The Basics 123 | 124 | Due to differences in compilation, different types of functions/methods are treated differently so it is important to make a few clear distinctions; 125 | 126 | 1. A function is **NOT** attached to any struct, enum or class. 127 | 2. A method **IS** attached to a struct, enum or class. 128 | 3. Class methods and struct methods act differently under the hood. 129 | 4. Struct methods and enum methods work the same under the hood. 130 | 5. Static functions work the same way under the hood for structs, enums and classes. 131 | 132 | SwiftMixin provides two main lines of functionality. Replacing functions/methods and 'duplicating' functions/methods. Duplicating does not duplicate the entire function it just duplicates the part that SwiftMixin replaces when told to replace a function. This is enough to allow calling the original function even after it is replaced. 133 | 134 | ### Setting up a mixin environment 135 | 136 | Each time your app starts it should check that it's memory protection bit it correctly patched. This sounds scary but SwiftMixin makes it easy. Just add the following line to your app's startup; 137 | 138 | ```swift 139 | try Mixin.setup() 140 | ``` 141 | 142 | This will automatically check your executable's text segment's maximum protection level (should be 7 but is 5 by default). If the protection level is not set correctly the executable will patch itself and `Mixin.setup()` will throw an error. The next time the executable is run it should work properly. 143 | 144 | ### Working With Functions 145 | 146 | Let's consider the following two functions; 147 | 148 | ```swift 149 | func sum(a: Int, b: Int) -> Int { 150 | return a + b 151 | } 152 | 153 | func product(a: Int, b: Int) -> Int { 154 | return a * b 155 | } 156 | ``` 157 | 158 | If we want to replace `sum` with `product` we can use the following line of code. Xcode will try to autocomplete these to calls to sum and product but make sure you are just passing the function as if it were a variable. 159 | 160 | ```swift 161 | try Mixin.replaceFunction(sum, with: product) 162 | ``` 163 | 164 | Now when we rum `sum(a: 2, b: 3)` we will get 6 instead of 5. 165 | 166 | Now consider the following function; 167 | 168 | ```swift 169 | func sumPlusOne(a: Int, b: Int) -> Int { 170 | return sum(a: a, b: b) + 1 171 | } 172 | ``` 173 | 174 | We really don't like repeating code (let's just ignore that using `sum` is longer than `a + b`), but what happens now if we try to replace `sum` with `sumPlusOne`. Well, we'll cause an infinite loop, and although that sounds fun, it's not very useful. What we need to do is create a copy of `sum` and use that instead. Replace your declaration of `sumPlusOne` with the following; 175 | 176 | ```swift 177 | let sum_Original = try Mixin.duplicateFunction(sum) 178 | 179 | func sumPlusOne(a: Int, b: Int) -> Int { 180 | return sum_Original(a, b) + 1 181 | } 182 | ``` 183 | 184 | Notice that sum_Original does not have any parameter labels, this is just how SwiftMixin has to work when duplicating functions. Now when we replace `sum` with `sumPlusOne`, `sum(a: 4, b: 5)` will return 10 (yeah, I know, it's very useful). 185 | 186 | ### Working With Structs, Enums and Classes (excluding static methods) 187 | 188 | In the `Working With Functions` section I explained the basics. I'll start going a bit faster now. 189 | 190 | Please note: replacement methods must be on the same struct, class or enum as the method to replace. This is achieved using extensions (because If you can edit the actual struct, enum or class definition then there is probably a better solution than mixins. 191 | 192 | Also, to back up methods we create a dummy method usually named `methodName_Original` and then overwrite it with a copy of the function we are backing up. This allows us to call the original method from our replacement or even elsewhere in our code. 193 | 194 | Using SwiftMixin is pretty similar for structs, enums and classes but there are some subtle differences. 195 | 196 | ### Struct Methods 197 | 198 | Consider the following struct; 199 | 200 | ```swift 201 | /// A simple struct for testing replacements and backups on. 202 | struct TwoNumbers { 203 | var a: Int 204 | var b: Int 205 | 206 | /// A simple method. 207 | func sum() -> Int { 208 | return a + b 209 | } 210 | } 211 | 212 | // MARK: Adding some simple replacement methods and backup dummies. 213 | extension TwoNumbers { 214 | /// A simple replacement for `sum`. 215 | func difference() -> Int { 216 | return a - b 217 | } 218 | 219 | /// A dummy to backup `sum` to. 220 | func sum_Original() -> Int { 221 | return sum() // dummy 222 | } 223 | } 224 | ``` 225 | 226 | Notice how the replacement and backup are added in an extension, this is likely how you'll want to replace methods because it you can just edit the source code then you don't need to use this package. To replace `TwoNumbers.sum` with `TwoNumbers.difference` run the following line; 227 | 228 | ```swift 229 | try Mixin.replaceStructMethod(TwoNumbers.sum, with: TwoNumbers.difference) 230 | ``` 231 | 232 | To create a backup of `TwoNumbers.sum` we'll overwrite `TwoNumbers.sum_Original` to be a backup; 233 | 234 | ```swift 235 | try Mixin.backupStructMethod(TwoNumbers.sum, to: TwoNumbers.sum_Original) 236 | ``` 237 | 238 | Pretty straightforward right? 239 | 240 | ### Enum Methods 241 | 242 | Pretty much the same as struct methods just replace Struct with Enum; 243 | 244 | ```swift 245 | enum FlightId { 246 | // ... 247 | 248 | func toInt() -> Int { 249 | // ... 250 | } 251 | } 252 | 253 | extension FlightId { 254 | func toIntTimesTen() -> Int { 255 | return toInt_Original() * 10 256 | } 257 | 258 | func toInt_Original() -> Int { 259 | fatalError("Don't forget to backup toInt") 260 | } 261 | } 262 | 263 | // Backing up a method 264 | try Mixin.backupEnumMethod(FlightId.toInt, to: FlightId.toInt_Original) 265 | // Replacing toInt with toIntTimesTen 266 | try Mixin.replaceEnumMethod(FlightId.toInt, with: FlightId.toIntTimesTen) 267 | ``` 268 | 269 | ### Class Methods 270 | 271 | The only difference from structs and enums is that you need to also pass the metatype of the class that you're doing stuff on because of how class methods work. For example; 272 | 273 | ```swift 274 | class ThreeNumbers { 275 | var a: Int 276 | var b: Int 277 | var c: Int 278 | 279 | /// A simple member-wise initializer. 280 | init(a: Int, b: Int, c: Int) { 281 | self.a = a 282 | self.b = b 283 | self.c = c 284 | } 285 | 286 | /// A simple instance method. 287 | func sum() -> Int { 288 | return a + b + c 289 | } 290 | } 291 | 292 | extension ThreeNumbers { 293 | /// A dummy to backup `sum` to. 294 | func sum_Original() -> Int { 295 | fatalError("someone forgot to backup ThreeNumbers.sum") 296 | } 297 | 298 | /// A method to replace `sum` with. 299 | func product() -> Int { 300 | return a + b + c 301 | } 302 | } 303 | 304 | // Backing up `sum` to `sum_Original` 305 | try Mixin.backupClassMethod(ThreeNumbers.sum, to: ThreeNumbers.sum_Original, on: ThreeNumbers.self) 306 | // Replacing `sum` with `product` 307 | try Mixin.replaceClassMethod(ThreeNumbers.sum, with: ThreeNumbers.product, on: ThreeNumbers.self) 308 | ``` 309 | 310 | ### Static Methods 311 | 312 | Static methods are the same for structs, enums and classes. 313 | 314 | Here's an example of backing up and replacing a static method on a struct; 315 | 316 | ```swift 317 | struct HandyNumbers { 318 | // A simple static method 319 | static func getPalindrome() -> Int { 320 | return 121 321 | } 322 | } 323 | 324 | extension HandyNumbers { 325 | /// A simple static method to replace `getPalindrome` with (it's also a palindrome!) 326 | static func getEvil() -> Int { 327 | return 666 328 | } 329 | 330 | static func getPalindrome_Original() -> Int { 331 | fatalError("You forgot to unstack the dishwasher") 332 | } 333 | } 334 | 335 | // Backing up `getPalindrome` to `getPalindrome_Original` 336 | try Mixin.backupStaticMethod(HandyNumbers.getPalindrome, to: HandyNumbers.getPalindrome_Original) 337 | // Replacing `getPalindrome` with `getEvil` 338 | try Mixin.replaceStaticMethod(HandyNumbers.getPalindrome, with: HandyNumbers.getEvil) 339 | ``` 340 | 341 | ## Limitations 342 | 343 | 1. Replacing initializers doesn't work yet. 344 | 2. Replacing getters and setters is also not supported yet, I have some ideas for approaching them but I will not be working on this again for a little while. 345 | 3. If the Swift compiler changes too much, this breaks. 346 | 4. No ARM support 347 | 5. No 32-bit support (but that's legacy and macOS refuses to run 32-bit apps anyway now) 348 | -------------------------------------------------------------------------------- /Sources/SwiftMixin/Disassembler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Disassembler.swift 3 | // SwiftMixin 4 | // 5 | // Created by Rohan van Klinken on 24/6/21. 6 | // 7 | 8 | import Foundation 9 | import Capstone 10 | 11 | enum DisassemblerError: LocalizedError { 12 | case pointerError 13 | case noInstruction 14 | } 15 | 16 | struct Disassembler { 17 | static let maxInstructionLength = 15 18 | 19 | private let capstone: Capstone 20 | var address: UInt 21 | 22 | var offset: UInt = 0 23 | 24 | init(forFunctionAt address: UInt) throws { 25 | self.address = address 26 | capstone = try Capstone(arch: .x86, mode: Mode.bits.b64) 27 | try capstone.set(option: .detail(value: true)) 28 | } 29 | 30 | mutating func next() throws -> T { 31 | guard let pointer = UnsafeRawPointer(bitPattern: address + offset) else { 32 | throw DisassemblerError.pointerError 33 | } 34 | 35 | let data = Data(bytes: pointer, count: Self.maxInstructionLength) 36 | let instructions: [T] = try capstone.disassemble(code: data, address: UInt64(address + offset), count: 1) 37 | 38 | guard let instruction = instructions.first else { 39 | throw DisassemblerError.noInstruction 40 | } 41 | 42 | offset += UInt(instruction.size) 43 | return instruction 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/SwiftMixin/MachO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachO.swift 3 | // SwiftMixin 4 | // 5 | // Created by Rohan van Klinken on 24/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum MachOError: LocalizedError { 11 | case getExecutablePathFailure 12 | } 13 | 14 | struct MachO { 15 | fileprivate init() { } 16 | 17 | static func getExecutablePath() throws -> URL { 18 | guard let path = Bundle.main.executablePath else { 19 | print("Failed to get executable path (required to check maxProt of __TEXT segment") 20 | throw MachOError.getExecutablePathFailure 21 | } 22 | return URL(fileURLWithPath: path) 23 | } 24 | 25 | static func getMaxProt() throws -> UInt8 { 26 | let executable = try getExecutablePath() 27 | return try getMaxProt(of: executable) 28 | } 29 | 30 | static func setMaxProt(to newMaxProt: UInt8) throws { 31 | let executable = try getExecutablePath() 32 | try setMaxProt(of: executable, to: newMaxProt) 33 | } 34 | 35 | static func getMaxProt(of executable: URL) throws -> UInt8 { 36 | let machoData = try Data(contentsOf: executable) 37 | return machoData[0xa0] 38 | } 39 | 40 | static func setMaxProt(of executable: URL, to newMaxProt: UInt8) throws { 41 | var machoData = try Data(contentsOf: executable) 42 | machoData[0xa0] = newMaxProt 43 | try machoData.write(to: executable) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/SwiftMixin/Mixin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mixin.swift 3 | // SwiftMixin 4 | // 5 | // Created by Rohan van Klinken on 21/6/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftMixinC 10 | import Capstone 11 | 12 | enum MixinError: LocalizedError { 13 | case invalidFunction 14 | case addWritePermissions 15 | case removeWritePermissions 16 | case incorrectMaxProtButFixed 17 | case failedToCreateDisassembler 18 | case createPointerFailed 19 | case disassembleInstructionFailed 20 | case invalidJump 21 | case invalidCall 22 | } 23 | 24 | // TODO: overwrite functions from swift instead of c if possible. 25 | // TODO: reduce the amount of c code. 26 | // TODO: get duplicating a function working. 27 | 28 | public struct Mixin { 29 | private struct GenericFunction { 30 | var thunk: UInt 31 | var metadata: UnsafePointer 32 | } 33 | 34 | private struct FunctionMetadata { 35 | var arg1: UInt 36 | var arg2: UInt 37 | var functionPointer: UInt 38 | } 39 | 40 | private static let maxInstructionSize = 15 41 | private static let thunkSize = 5 42 | 43 | fileprivate init() { } 44 | 45 | /// Sets up and checks the mixin environment for this executable. 46 | public static func setup() throws { 47 | // Executable must have the correct max prot level set to allow write and execute for function memory 48 | if try MachO.getMaxProt() != 0x07 { 49 | print("Setting max prot of executable. Restart executable for changes to take effect") 50 | try MachO.setMaxProt(to: 0x7) 51 | throw MixinError.incorrectMaxProtButFixed 52 | } 53 | } 54 | 55 | /// For a function this returns the address of the function. 56 | /// For a method it returns the address of the partially applied method. 57 | private static func implicitClosure(of function: T) -> UInt { 58 | var address: UInt = 0 59 | withUnsafePointer(to: function, { pointer in 60 | pointer.withMemoryRebound(to: GenericFunction.self, capacity: 1, { pointer in 61 | address = pointer.pointee.metadata.pointee.functionPointer 62 | }) 63 | }) 64 | return address 65 | } 66 | 67 | private static func typeSignature(of variable: T.Type) -> String { 68 | return String(describing: T.self) 69 | } 70 | 71 | /// Checks if a value is a function. Relies on only function type signatures containing arrows (`->`). 72 | private static func isFunction(_ potentialFunction: T) -> Bool { 73 | // check the function's type's string representation for '->' 74 | let signature = typeSignature(of: T.self) 75 | return signature.split(separator: " ").contains("->") 76 | } 77 | 78 | /// Returns the address of the given function. 79 | public static func getFunctionAddress(of function: T) -> UInt { 80 | return implicitClosure(of: function) 81 | } 82 | 83 | /// Replaces a function with another function. Does NOT work for struct/class methods 84 | public static func replaceFunction(_ function: T, with replacement: T) throws { 85 | guard isFunction(T.self) else { 86 | throw MixinError.invalidFunction 87 | } 88 | 89 | let functionAddress = implicitClosure(of: function) 90 | let replacementAddress = implicitClosure(of: replacement) 91 | let result = overwrite_function(functionAddress, replacementAddress) 92 | 93 | switch result { 94 | case -1: 95 | print("Failed to add write permissions to function memory") 96 | throw MixinError.addWritePermissions 97 | case -2: 98 | print("Failed to remove write permissions from function memory (non-fatal)") 99 | throw MixinError.removeWritePermissions 100 | default: 101 | return 102 | } 103 | } 104 | 105 | /// Returns the address of a struct method. 106 | public static func getStructMethodAddress(_ method: T) throws -> UInt { 107 | let implicitClosureAddress = implicitClosure(of: method) 108 | let nestedImplicitClosureAddress = run_void_to_uint64_function(implicitClosureAddress) 109 | let methodAddress = try getStructMethodAddress(fromNestedImplicitClosureAt: nestedImplicitClosureAddress) 110 | return methodAddress 111 | } 112 | 113 | private static func getStructMethodAddress(fromNestedImplicitClosureAt nestedImplicitClosureAddress: UInt) throws -> UInt { 114 | // TODO: fix variable naming to be more correct 115 | // Find the address that the partial apply forwarder jumps to (the address of the next implicit closure) 116 | var disassembler = try Disassembler(forFunctionAt: nestedImplicitClosureAddress) 117 | var partiallyApplied: UInt = 0 118 | while true { 119 | let instruction: X86Instruction = try disassembler.next() 120 | if instruction.isIn(group: .jump) { 121 | let hexString = instruction.operandsString.dropFirst(2) 122 | guard let address = UInt(hexString, radix: 16) else { 123 | print(instruction.description) 124 | throw MixinError.invalidJump 125 | } 126 | partiallyApplied = address 127 | break 128 | } 129 | } 130 | 131 | // Find the address that the closure calls (the address of the actual function) 132 | disassembler = try Disassembler(forFunctionAt: partiallyApplied) 133 | var methodAddress: UInt = 0 134 | while true { 135 | let instruction: X86Instruction = try disassembler.next() 136 | if instruction.isIn(group: .call) { 137 | let hexString = instruction.operandsString.dropFirst(2) 138 | guard let address = UInt(hexString, radix: 16) else { 139 | print(instruction.description) 140 | throw MixinError.invalidCall 141 | } 142 | methodAddress = address 143 | break 144 | } 145 | } 146 | 147 | return methodAddress 148 | } 149 | 150 | /// Replaces a struct's method with another method. Both methods must be on the same struct. 151 | public static func replaceStructMethod(_ method: T, with replacement: T) throws { 152 | // TODO: check both methods are on the same struct. 153 | 154 | let methodAddress = try getStructMethodAddress(method) 155 | let replacementAddress = try getStructMethodAddress(replacement) 156 | overwrite_function(methodAddress, replacementAddress) 157 | } 158 | 159 | /// Replaces an enum's method with another method. Both methods must be on the same enum. 160 | public static func replaceEnumMethod(_ method: T, with replacement: T) throws { 161 | try replaceStructMethod(method, with: replacement) 162 | } 163 | 164 | /// Replaces a struct's method with another method. Both methods must be on the same struct. 165 | public static func replaceClassMethod(_ method: T1, with replacement: T1, on type: T2.Type) throws { 166 | let methodAddress = try getClassMethodAddress(of: method, onType: type) 167 | let replacementAddress = try getClassMethodAddress(of: replacement, onType: type) 168 | overwrite_function(methodAddress, replacementAddress) 169 | } 170 | 171 | /// Returns the length of a function in bytes. This relies on a function's compiled form only 172 | /// having one `ret` instruction which seems to always be true. 173 | private static func getLength(ofFunctionAt address: UInt) throws -> UInt { 174 | var disassembler = try Disassembler(forFunctionAt: address) 175 | 176 | while true { 177 | let instruction = try disassembler.next() 178 | if instruction.bytes[0] == 0xc3 { // ret 179 | break 180 | } 181 | } 182 | 183 | return disassembler.offset 184 | } 185 | 186 | /// Returns a duplicate of the specified function so that it can be called even when 187 | /// the original is replaced. 188 | public static func duplicateFunction(_ function: T) throws -> T { 189 | let address = getFunctionAddress(of: function) 190 | let duplicateAddress = try duplicateFunction(at: address) 191 | withUnsafePointer(to: function, { pointer in 192 | pointer.withMemoryRebound(to: GenericFunction.self, capacity: 1, { pointer in 193 | let metadataPointer = UnsafeMutablePointer(mutating: pointer.pointee.metadata) 194 | metadataPointer.pointee.functionPointer = duplicateAddress 195 | }) 196 | }) 197 | 198 | return function 199 | } 200 | 201 | /// Creates a thunk that allows a function to still be called even after it is replaced. 202 | private static func duplicateFunction(at address: UInt) throws -> UInt { 203 | var disassembler = try Disassembler(forFunctionAt: address) 204 | while disassembler.offset < thunkSize { 205 | _ = try disassembler.next() 206 | } 207 | let numBytesToCopy = disassembler.offset 208 | let duplicateAddress = duplicate_function(address, numBytesToCopy) 209 | return duplicateAddress 210 | } 211 | 212 | /// Overwrites the destination method to be a backup that can be called to invoke the original function 213 | /// even after the original is replaced. 214 | public static func backupStructMethod(_ method: T, to destination: T) throws { 215 | // TODO: check that the methods are both on the same struct 216 | 217 | let methodAddress = try getStructMethodAddress(method) 218 | let destinationAddress = try getStructMethodAddress(destination) 219 | let backup = try duplicateFunction(at: methodAddress) 220 | 221 | overwrite_function(destinationAddress, backup) 222 | } 223 | 224 | /// Overwrites the destination method to be a backup that can be called to invoke the original function 225 | /// even after the original is replaced. 226 | public static func backupEnumMethod(_ method: T, to destination: T) throws { 227 | try backupStructMethod(method, to: destination) 228 | } 229 | 230 | /// Overwrites the destination method to be a backup that can be called to invoke the original function 231 | /// even after the original is replaced. Both methods must be on the specified type 232 | public static func backupClassMethod(_ method: T1, to destination: T1, on type: T2.Type) throws { 233 | // TODO: check that the methods are both on the same class 234 | 235 | let methodAddress = try getClassMethodAddress(of: method, onType: type) 236 | let destinationAddress = try getClassMethodAddress(of: destination, onType: type) 237 | let backupAddress = try duplicateFunction(at: methodAddress) 238 | 239 | overwrite_function(destinationAddress, backupAddress) 240 | } 241 | 242 | /// Returns the actual address of the specified method on the specified class. Both the 243 | /// method and the class have to be specified because of the inner workings of swift. 244 | public static func getClassMethodAddress(of method: T1, onType type: T2.Type) throws -> UInt { 245 | // Get the address of the class's metadata 246 | var classMetadataAddress: UInt = 0 247 | withUnsafePointer(to: type, { pointer in 248 | pointer.withMemoryRebound(to: UInt.self, capacity: 1, { pointer in 249 | classMetadataAddress = pointer.pointee 250 | }) 251 | }) 252 | 253 | // The method is located at an offset in the class's metadata when the method is not in an extension. 254 | // If the method is from an extension then getting its address is more similar to struct methods. 255 | 256 | // Get the address of the partial apply forwarder (a thunk) 257 | let implicitClosureAddress = implicitClosure(of: method) 258 | var disassembler = try Disassembler(forFunctionAt: implicitClosureAddress) 259 | var partialApplyForwarder: UInt = 0 260 | while true { 261 | let instruction: X86Instruction = try disassembler.next() 262 | if instruction.opcode[0] == 0x8d { // lea 263 | var offset: UInt32 = 0 264 | withUnsafePointer(to: instruction.bytes.advanced(by: 3), { pointer in 265 | pointer.withMemoryRebound(to: UInt32.self, capacity: 1, { pointer in 266 | offset = pointer.pointee 267 | }) 268 | }) 269 | partialApplyForwarder = UInt(disassembler.address + disassembler.offset) + UInt(instruction.size) + UInt(offset) 270 | break 271 | } 272 | } 273 | 274 | // Find the address that the partial apply forwarder jumps to (the address of the next implicit closure) 275 | disassembler = try Disassembler(forFunctionAt: partialApplyForwarder) 276 | var nestedImplicitClosure: UInt = 0 277 | while true { 278 | let instruction: X86Instruction = try disassembler.next() 279 | if instruction.isIn(group: .jump) { 280 | let hexString = instruction.operandsString.dropFirst(2) 281 | guard let address = UInt(hexString, radix: 16) else { 282 | print(instruction.description) 283 | throw MixinError.invalidJump 284 | } 285 | nestedImplicitClosure = address 286 | break 287 | } 288 | } 289 | 290 | // There are two different ways a class method is called. 291 | // Its address is either; 292 | // 1. stored at an offset in the class's metadata 293 | // 2. hardcoded into the implicit closure if the method is from an extension 294 | 295 | // In both cases the important instruction is at offset 22 296 | disassembler = try Disassembler(forFunctionAt: nestedImplicitClosure + 22) 297 | let instruction: X86Instruction = try disassembler.next() 298 | var methodAddress: UInt = 0 299 | if instruction.opcode[0] == 0x8b && instruction.size == 7 { // option 1 300 | // Read the offset in the class's metadata that the implicit closure reads the address of the method from. 301 | try withUnsafePointer(to: instruction.bytes.advanced(by: 3), { pointer in 302 | try pointer.withMemoryRebound(to: Int32.self, capacity: 1, { pointer in 303 | let methodOffset = pointer.pointee 304 | 305 | // Access the method's address from its offset in the class's metadata 306 | let methodAddressAddress: UInt 307 | if methodOffset > 0 { 308 | methodAddressAddress = classMetadataAddress + UInt(methodOffset) 309 | } else { 310 | methodAddressAddress = classMetadataAddress - UInt(UInt32(abs(methodOffset))) 311 | } 312 | 313 | guard let address = UnsafeRawPointer(bitPattern: methodAddressAddress)?.load(as: UInt.self) else { 314 | throw MixinError.createPointerFailed 315 | } 316 | 317 | methodAddress = address 318 | }) 319 | }) 320 | } else if instruction.isIn(group: .call) { // option 2 321 | // Read the destination of the call (dodgily) 322 | let hexString = instruction.operandsString.dropFirst(2) 323 | guard let address = UInt(hexString, radix: 16) else { 324 | print(instruction.description) 325 | throw MixinError.invalidCall 326 | } 327 | methodAddress = address 328 | } 329 | 330 | return methodAddress 331 | } 332 | 333 | /// Returns the address of the specified static method. 334 | public static func getStaticMethodAddress(of staticMethod: T) throws -> UInt { 335 | let nestedImplicitClosureAddress = implicitClosure(of: staticMethod) 336 | let methodAddress = try getStructMethodAddress(fromNestedImplicitClosureAt: nestedImplicitClosureAddress) 337 | return methodAddress 338 | } 339 | 340 | /// Replaces a static method with another static method. Static methods are the same on both structs and classes. 341 | public static func replaceStaticMethod(_ method: T, with replacement: T) throws { 342 | let methodAddress = try getStaticMethodAddress(of: method) 343 | let replacementAddress = try getStaticMethodAddress(of: replacement) 344 | overwrite_function(methodAddress, replacementAddress) 345 | } 346 | 347 | /// Backs up a static method to the specified destination method to allow the original method to still be called 348 | /// even once it is replaced. 349 | public static func backupStaticMethod(_ method: T, to destinationMethod: T) throws { 350 | let methodAddress = try getStaticMethodAddress(of: method) 351 | let destinationAddress = try getStaticMethodAddress(of: destinationMethod) 352 | let duplicateAddress = try duplicateFunction(at: methodAddress) 353 | overwrite_function(destinationAddress, duplicateAddress) 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /Sources/SwiftMixinC/include/swift-mixin.h: -------------------------------------------------------------------------------- 1 | // 2 | // swift-mixin.h 3 | // SwiftMixin 4 | // 5 | // Created by Rohan van Klinken on 21/6/21. 6 | // 7 | 8 | #ifndef swift_mixin_h 9 | #define swift_mixin_h 10 | 11 | int overwrite_function(unsigned long function, unsigned long replacement); 12 | unsigned long duplicate_function(unsigned long addr, unsigned long num_bytes_to_copy); 13 | int set_mem_prot(unsigned long addr, unsigned long length, int prot); 14 | 15 | unsigned long run_void_to_uint64_function(unsigned long addr); 16 | 17 | #endif /* swift_mixin_h */ 18 | -------------------------------------------------------------------------------- /Sources/SwiftMixinC/swift-mixin.c: -------------------------------------------------------------------------------- 1 | // 2 | // swift-mixin.c 3 | // SwiftMixin 4 | // 5 | // Created by Rohan van Klinken on 21/6/21. 6 | // 7 | 8 | #include "swift-mixin.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /* 18 | jmp ADDR ; addr is a 32-bit offset relative to the next instruction 19 | */ 20 | #define THUNK_TEMPLATE "\xe9""ADDR" 21 | 22 | char * jump_thunk_template = THUNK_TEMPLATE; 23 | size_t thunk_size = sizeof(THUNK_TEMPLATE) - 1; 24 | 25 | /// Overwrites the function pointed to by the first argument. 26 | int overwrite_function(unsigned long function_to_replace, unsigned long replacement) { 27 | // Add write permissions to the memory we need to write the thunk to 28 | set_mem_prot(function_to_replace, thunk_size, PROT_READ | PROT_WRITE | PROT_EXEC); 29 | 30 | // Overwrite the start of the function with the thunk template 31 | memcpy((void *)function_to_replace, (void *)jump_thunk_template, thunk_size); 32 | 33 | // Write the replacement function's offset to the correct spot in the template 34 | unsigned long offset = replacement - (function_to_replace + thunk_size); 35 | *(int *)((char*)function_to_replace + 1) = offset; 36 | 37 | // Remove write permissions again 38 | set_mem_prot(function_to_replace, thunk_size, PROT_READ | PROT_EXEC); 39 | 40 | return 0; 41 | } 42 | 43 | /// Creates a duplicate of a function only copying as much as needed to be correct after the thunk. 44 | unsigned long duplicate_function(unsigned long addr, unsigned long num_bytes_to_copy) { 45 | // Copy the first few instructions that need to be saved 46 | void * function = (void *)addr; 47 | void * copy = malloc(num_bytes_to_copy + thunk_size); 48 | memcpy(copy, function, (int)num_bytes_to_copy); 49 | 50 | // Install a jump back to the original after the thunk 51 | unsigned long jump_dst = addr + num_bytes_to_copy; 52 | unsigned long jump_src = (unsigned long)copy + num_bytes_to_copy + thunk_size; 53 | int offset = (int)(jump_dst - jump_src); 54 | memcpy((void *)((char *)copy + num_bytes_to_copy), jump_thunk_template, thunk_size); 55 | *(int *)((char *)copy + num_bytes_to_copy + 1) = offset; 56 | 57 | // Add execute permissions to the copy 58 | set_mem_prot((unsigned long)copy, num_bytes_to_copy + thunk_size, PROT_READ | PROT_EXEC | PROT_WRITE); 59 | return (unsigned long)copy; 60 | } 61 | 62 | /// Sets the protection of the memory in the specified range to the specified protection level. 63 | int set_mem_prot(unsigned long addr, unsigned long length, int prot) { 64 | int page_size = getpagesize(); 65 | unsigned long page_aligned = addr & ~(page_size-1); 66 | int num_pages = (addr - page_aligned) / page_size + 1; 67 | mprotect((void *)page_aligned, page_size * num_pages, prot); 68 | } 69 | 70 | /// Returns the result of a () -> UInt function located at `addr`. 71 | unsigned long run_void_to_uint64_function(unsigned long addr) { 72 | unsigned long (*thunk)() = addr; 73 | unsigned long result = thunk(); 74 | return result; 75 | } 76 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import SwiftMixinTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += SwiftMixinTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // SwiftMixinTests 4 | // 5 | // Created by Rohan van Klinken on 26/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A very simple function. 11 | func functionToReplace() { 12 | print("Replace me") 13 | } 14 | 15 | /// A pretty simple function with two parameters and a return. 16 | func addTwoNumbers(a: Int, b: Int) -> Int { 17 | return a + b 18 | } 19 | 20 | // MARK: Some simple replacement functions. 21 | 22 | /// A very simple replacement for `functionToReplace` 23 | func replacementFunction() { 24 | print("This is the replacement") 25 | } 26 | 27 | /// A simple (and terrible) replacement for `add`. 28 | func multiplyTwoNumbers(a: Int, b: Int) -> Int { 29 | return a * b 30 | } 31 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/SwiftMixinTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftMixin 3 | 4 | final class SwiftMixinTests: XCTestCase { 5 | func testSimpleFunctionReplacement() throws { 6 | let sum = addTwoNumbers(a: 2, b: 3) 7 | let product = multiplyTwoNumbers(a: 2, b: 3) 8 | 9 | XCTAssertEqual(sum, 5) 10 | XCTAssertEqual(product, 6) 11 | 12 | let addTwoNumbers_Original = try Mixin.duplicateFunction(addTwoNumbers) 13 | try Mixin.replaceFunction(addTwoNumbers, with: multiplyTwoNumbers) 14 | 15 | XCTAssertEqual(addTwoNumbers(a: 2, b: 3), product) 16 | XCTAssertEqual(addTwoNumbers_Original(2, 3), sum) 17 | } 18 | 19 | func testStructMethodReplacement() throws { 20 | let twoNumbers = TwoNumbers(a: 2, b: 3) 21 | let sum = twoNumbers.sum() 22 | let difference = twoNumbers.difference() 23 | 24 | XCTAssertEqual(sum, 5) 25 | XCTAssertEqual(difference, -1) 26 | 27 | try Mixin.backupStructMethod(TwoNumbers.sum, to: TwoNumbers.sum_Original) 28 | try Mixin.replaceStructMethod(TwoNumbers.sum, with: TwoNumbers.difference) 29 | 30 | XCTAssertEqual(twoNumbers.sum(), difference) 31 | XCTAssertEqual(twoNumbers.sum_Original(), sum) 32 | } 33 | 34 | func testClassMethodReplacement() throws { 35 | let threeNumbers = ThreeNumbers(a: 1, b: 2, c: 4) 36 | let sum = threeNumbers.sum() 37 | let product = threeNumbers.product() 38 | 39 | XCTAssertEqual(sum, 7) 40 | XCTAssertEqual(product, 8) 41 | 42 | try Mixin.backupClassMethod(ThreeNumbers.sum, to: ThreeNumbers.sum_Original, on: ThreeNumbers.self) 43 | try Mixin.replaceClassMethod(ThreeNumbers.sum, with: ThreeNumbers.product, on: ThreeNumbers.self) 44 | 45 | XCTAssertEqual(threeNumbers.sum(), product) 46 | XCTAssertEqual(threeNumbers.sum_Original(), sum) 47 | } 48 | 49 | func testStaticStructMethodReplacement() throws { 50 | let nice = TwoNumbers.getNice() 51 | let evil = TwoNumbers.getEvil() 52 | 53 | XCTAssertEqual(nice, 69) 54 | XCTAssertEqual(evil, 666) 55 | 56 | try Mixin.backupStaticMethod(TwoNumbers.getNice, to: TwoNumbers.getNice_Original) 57 | try Mixin.replaceStaticMethod(TwoNumbers.getNice, with: TwoNumbers.getEvil) 58 | 59 | XCTAssertEqual(TwoNumbers.getNice(), evil) 60 | XCTAssertEqual(TwoNumbers.getNice_Original(), nice) 61 | } 62 | 63 | func testStaticClassMethodReplacement() throws { 64 | let pythagorean = ThreeNumbers.pythagorean() 65 | let consecutive = ThreeNumbers.consecutive() 66 | 67 | XCTAssertEqual(pythagorean, ThreeNumbers(a: 3, b: 4, c: 5)) 68 | XCTAssertEqual(consecutive, ThreeNumbers(a: 1, b: 2, c: 3)) 69 | 70 | try Mixin.backupStaticMethod(ThreeNumbers.pythagorean, to: ThreeNumbers.pythagorean_Original) 71 | try Mixin.replaceStaticMethod(ThreeNumbers.pythagorean, with: ThreeNumbers.consecutive) 72 | 73 | XCTAssertEqual(ThreeNumbers.pythagorean(), consecutive) 74 | XCTAssertEqual(ThreeNumbers.pythagorean_Original(), pythagorean) 75 | } 76 | 77 | func testEnumMethodReplacement() throws { 78 | let number = Number.three 79 | let numberAsInt = number.toInt() 80 | let numberAsIntTimesTen = number.toIntTimesTen() 81 | 82 | XCTAssertEqual(numberAsInt, 3) 83 | XCTAssertEqual(numberAsIntTimesTen, 30) 84 | 85 | try Mixin.backupEnumMethod(Number.toInt, to: Number.toInt_Original) 86 | try Mixin.replaceEnumMethod(Number.toInt, with: Number.toIntTimesTen) 87 | 88 | XCTAssertEqual(number.toInt(), numberAsIntTimesTen) 89 | XCTAssertEqual(number.toInt_Original(), numberAsInt) 90 | } 91 | 92 | func testStaticEnumMethodReplacement() throws { 93 | let favourite = Number.favourite() 94 | let leastFavourite = Number.leastFavourite() 95 | 96 | XCTAssertEqual(favourite, .three) 97 | XCTAssertEqual(leastFavourite, .two) 98 | 99 | try Mixin.backupStaticMethod(Number.favourite, to: Number.favourite_Original) 100 | try Mixin.replaceStaticMethod(Number.favourite, with: Number.leastFavourite) 101 | 102 | XCTAssertEqual(Number.favourite(), leastFavourite) 103 | XCTAssertEqual(Number.favourite_Original(), favourite) 104 | } 105 | 106 | static var allTests = [ 107 | ("testSimpleFunctionReplacement", testSimpleFunctionReplacement), 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/Types/Number.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Number.swift 3 | // SwiftMixinTests 4 | // 5 | // Created by Rohan van Klinken on 26/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Number { 11 | case one 12 | case two 13 | case three 14 | 15 | /// A simple method. 16 | func toInt() -> Int { 17 | switch self { 18 | case .one: 19 | return 1 20 | case .two: 21 | return 2 22 | case .three: 23 | return 3 24 | } 25 | } 26 | 27 | static func favourite() -> Number { 28 | return .three 29 | } 30 | } 31 | 32 | // MARK: Adding some simple replacement methods and backup dummies. 33 | extension Number { 34 | /// A simple (and terrible) replacement for `toInt`. 35 | func toIntTimesTen() -> Int { 36 | return toInt_Original() * 10 37 | } 38 | 39 | /// A dummy to backup toInt to. `toIntTimesTen` uses this. 40 | func toInt_Original() -> Int { 41 | // TODO: consider what the best practice should be for the contents of a backup 42 | // most likely it should either be returning a call to the original but that's 43 | // annoying with more arguments. Maybe a fatalError would be better (also a good 44 | // reminder if you forget to create the backup or the backup fails. 45 | return toInt() 46 | } 47 | 48 | /// A simple replacement for `favourite` (ew). 49 | static func leastFavourite() -> Number { 50 | return .two 51 | } 52 | 53 | /// A dummy to backup `favourite` to. 54 | static func favourite_Original() -> Number { 55 | return favourite() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/Types/ThreeNumbers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeNumbers.swift 3 | // SwiftMixinTests 4 | // 5 | // Created by Rohan van Klinken on 26/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class ThreeNumbers: CustomStringConvertible { 11 | var a: Int 12 | var b: Int 13 | var c: Int 14 | 15 | var description: String { 16 | return "(\(a), \(b), \(c))" 17 | } 18 | 19 | /// A simple member-wise initializer. 20 | init(a: Int, b: Int, c: Int) { 21 | self.a = a 22 | self.b = b 23 | self.c = c 24 | } 25 | 26 | /// A simple instance method. 27 | func sum() -> Int { 28 | return a + b + c 29 | } 30 | 31 | /// A simple static method. 32 | static func pythagorean() -> ThreeNumbers { 33 | return ThreeNumbers(a: 3, b: 4, c: 5) 34 | } 35 | } 36 | 37 | // MARK: Adding some simple replacement methods and backup dummies. 38 | extension ThreeNumbers { 39 | /// A simple replacement for `sum`. 40 | func product() -> Int { 41 | return a * b * c 42 | } 43 | 44 | /// A dummy to backup `sum` to. 45 | func sum_Original() -> Int { 46 | return sum() // dummy 47 | } 48 | 49 | /// A simple replacement for `pythagorean`. 50 | static func consecutive() -> ThreeNumbers { 51 | return ThreeNumbers(a: 1, b: 2, c: 3) 52 | } 53 | 54 | /// A dummy to backup `pythagorean` to. 55 | static func pythagorean_Original() -> ThreeNumbers { 56 | return pythagorean() 57 | } 58 | } 59 | 60 | extension ThreeNumbers: Equatable { 61 | static func == (lhs: ThreeNumbers, rhs: ThreeNumbers) -> Bool { 62 | return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/Types/TwoNumbers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TwoNumbers.swift 3 | // SwiftMixinTests 4 | // 5 | // Created by Rohan van Klinken on 26/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A simple struct for testing replacements and backups on. 11 | struct TwoNumbers { 12 | var a: Int 13 | var b: Int 14 | 15 | /// A simple method. 16 | func sum() -> Int { 17 | return a + b 18 | } 19 | 20 | /// A simple static method. 21 | static func getNice() -> Int { 22 | return 69 23 | } 24 | } 25 | 26 | // MARK: Adding some simple replacement methods and backup dummies. 27 | extension TwoNumbers { 28 | /// A simple replacement for `sum`. 29 | func difference() -> Int { 30 | return a - b 31 | } 32 | 33 | /// A dummy to backup `sum` to. 34 | func sum_Original() -> Int { 35 | return sum() // dummy 36 | } 37 | 38 | /// A simple replacement for `getNice`. 39 | static func getEvil() -> Int { 40 | return 666 41 | } 42 | 43 | /// A dummy to backup `getNice` to. 44 | static func getNice_Original() -> Int { 45 | return getNice() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/SwiftMixinTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(SwiftMixinTests.allTests), 7 | ] 8 | } 9 | #endif 10 | --------------------------------------------------------------------------------