Last active
October 27, 2018 13:17
-
-
Save hayeah/025075123f47bc816f3d to your computer and use it in GitHub Desktop.
objective C objc_msgSend assembly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. | |
* | |
* @APPLE_LICENSE_HEADER_START@ | |
* | |
* This file contains Original Code and/or Modifications of Original Code | |
* as defined in and that are subject to the Apple Public Source License | |
* Version 2.0 (the 'License'). You may not use this file except in | |
* compliance with the License. Please obtain a copy of the License at | |
* http://www.opensource.apple.com/apsl/ and read it before using this | |
* file. | |
* | |
* The Original Code and all software distributed under the License are | |
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
* Please see the License for the specific language governing rights and | |
* limitations under the License. | |
* | |
* @APPLE_LICENSE_HEADER_END@ | |
*/ | |
#ifdef __x86_64__ | |
/******************************************************************** | |
******************************************************************** | |
** | |
** objc-msg-x86_64.s - x86-64 code to support objc messaging. | |
** | |
******************************************************************** | |
********************************************************************/ | |
#define __OBJC2__ 1 | |
#undef OBJC_ASM | |
#define OBJC_ASM | |
#include "objc-rtp.h" | |
/******************************************************************** | |
* Data used by the ObjC runtime. | |
* | |
********************************************************************/ | |
.data | |
// Substitute receiver for messages sent to nil (usually also nil) | |
// id _objc_nilReceiver | |
.align 4 | |
.globl __objc_nilReceiver | |
__objc_nilReceiver: | |
.quad 0 | |
// _objc_entryPoints and _objc_exitPoints are used by objc | |
// to get the critical regions for which method caches | |
// cannot be garbage collected. | |
.globl _objc_entryPoints | |
_objc_entryPoints: | |
.quad __cache_getImp | |
.quad __cache_getMethod | |
.quad _objc_msgSend | |
.quad _objc_msgSend_fpret | |
.quad _objc_msgSend_fp2ret | |
.quad _objc_msgSend_stret | |
.quad _objc_msgSendSuper | |
.quad _objc_msgSendSuper_stret | |
.quad 0 | |
.globl _objc_exitPoints | |
_objc_exitPoints: | |
.quad LGetImpExit | |
.quad LGetMethodExit | |
.quad LMsgSendExit | |
.quad LMsgSendFpretExit | |
.quad LMsgSendFp2retExit | |
.quad LMsgSendStretExit | |
.quad LMsgSendSuperExit | |
.quad LMsgSendSuperStretExit | |
.quad 0 | |
/******************************************************************** | |
* | |
* Names for parameter registers. | |
* | |
********************************************************************/ | |
#define a1 rdi | |
#define a2 rsi | |
#define a3 rdx | |
#define a4 rcx | |
#define a5 r8 | |
#define a6 r9 | |
#define a6d r9d | |
/******************************************************************** | |
* | |
* Structure definitions. | |
* | |
********************************************************************/ | |
// objc_super parameter to sendSuper | |
receiver = 0 | |
class = 8 | |
// Selected field offsets in class structure | |
isa = 0 | |
#if __OBJC2__ | |
cache = 16 | |
#else | |
cache = 64 | |
#endif | |
// Method descriptor | |
method_name = 0 | |
method_imp = 16 | |
// Cache header | |
mask = 0 | |
occupied = 8 | |
buckets = 16 // variable length array | |
// typedef struct { | |
// uint128_t floatingPointArgs[8]; // xmm0..xmm7 | |
// long linkageArea[4]; // r10, rax, ebp, ret | |
// long registerArgs[6]; // a1..a6 | |
// long stackArgs[0]; // variable-size | |
// } *marg_list; | |
#define FP_AREA 0 | |
#define LINK_AREA (FP_AREA+8*16) | |
#define REG_AREA (LINK_AREA+4*8) | |
#define STACK_AREA (REG_AREA+6*8) | |
////////////////////////////////////////////////////////////////////// | |
// | |
// ENTRY functionName | |
// | |
// Assembly directives to begin an exported function. | |
// | |
// Takes: functionName - name of the exported function | |
////////////////////////////////////////////////////////////////////// | |
.macro ENTRY | |
.text | |
.globl $0 | |
.align 2, 0x90 | |
$0: | |
.endmacro | |
////////////////////////////////////////////////////////////////////// | |
// | |
// END_ENTRY functionName | |
// | |
// Assembly directives to end an exported function. Just a placeholder, | |
// a close-parenthesis for ENTRY, until it is needed for something. | |
// | |
// Takes: functionName - name of the exported function | |
////////////////////////////////////////////////////////////////////// | |
.macro END_ENTRY | |
.endmacro | |
/* DWARF support | |
These macros work for objc_msgSend variants and others that call | |
CacheLookup/MethodTableLookup or SaveRegisters/RestoreRegisters | |
without otherwise building a frame or clobbering callee-save registers | |
The macros build appropriate FDEs and tie them to the CIE. | |
*/ | |
#define DW_CFA_offset 0x80 | |
#define DW_CFA_restore 0xc0 | |
#define DW_CFA_advance_loc4 0x4 | |
#define DW_CFA_same_value 0x8 | |
#define DW_CFA_def_cfa 0xc | |
#define DW_CFA_def_cfa_register 0xd | |
#define DW_CFA_def_cfa_offset 0xe | |
#define DW_CFA_offset_extended_sf 0x11 | |
#define DW_CFA_def_cfa_offset_sf 0x13 | |
#define DW_rax 0 | |
#define DW_rdx 1 | |
#define DW_rcx 2 | |
#define DW_rsi 4 | |
#define DW_rdi 5 | |
#define DW_rbp 6 | |
#define DW_rsp 7 | |
#define DW_r8 8 | |
#define DW_r9 9 | |
#define DW_r10 10 | |
#define DW_ra 16 | |
#define DW_xmm0 17 | |
#define DW_xmm1 18 | |
#define DW_xmm2 19 | |
#define DW_xmm3 20 | |
#define DW_xmm4 21 | |
#define DW_xmm5 22 | |
#define DW_xmm6 23 | |
#define DW_xmm7 24 | |
#define DW_a1 DW_rdi | |
#define DW_a2 DW_rsi | |
#define DW_a3 DW_rdx | |
#define DW_a4 DW_rcx | |
#define DW_a5 DW_r8 | |
#define DW_a6 DW_r9 | |
// CIE | |
// 8-byte data multiplier | |
// 1-byte insn multiplier | |
// PC-relative everything | |
// No prologue | |
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support | |
CIE: | |
.set L$set$0,LECIE1-LSCIE1 | |
.long L$set$0 # Length of Common Information Entry | |
LSCIE1: | |
.long 0 # CIE Identifier Tag | |
.byte 0x3 # CIE Version | |
.ascii "zPR\0" # CIE Augmentation: size + personality + FDE encoding | |
.byte 0x1 # uleb128 0x1; CIE Code Alignment Factor | |
.byte 0x78 # sleb128 -0x8; CIE Data Alignment Factor | |
.byte 0x10 # CIE RA Column | |
.byte 0x6 # uleb128 0x1; Augmentation size | |
// Personality augmentation | |
.byte 0x9b | |
.long ___objc_personality_v0+4@GOTPCREL | |
// FDE-encoding augmentation | |
.byte 0x10 | |
// Prefix instructions | |
// CFA is %rsp+8 | |
.byte DW_CFA_def_cfa | |
.byte DW_rsp | |
.byte 8 | |
// RA is at 0(%rsp) aka -8(CFA) | |
.byte DW_CFA_offset | DW_ra | |
.byte 1 | |
.align 3 | |
LECIE1: | |
.macro EMIT_FDE | |
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support | |
// FDE header | |
.globl $0.eh | |
$0.eh: | |
LSFDE$0: | |
.set LLENFDE$0, LEFDE$0-LASFDE$0 | |
.long LLENFDE$0 # FDE Length | |
LASFDE$0: | |
.long LASFDE$0-CIE # FDE CIE offset | |
.quad LF0$0-. # FDE address start | |
.quad LLEN$0 # FDE address range | |
.byte 0x0 # uleb128 0x0; Augmentation size | |
// DW_START: set by CIE | |
.if $2 == 1 | |
// pushq %rbp | |
.byte DW_CFA_advance_loc4 | |
.long LFLEN0$0+1 | |
.byte DW_CFA_def_cfa_offset | |
.byte 16 | |
.byte DW_CFA_offset | DW_rbp | |
.byte -16/-8 | |
// movq %rsp, %rbp | |
.byte DW_CFA_advance_loc4 | |
.long 3 | |
.byte DW_CFA_def_cfa_register | |
.byte DW_rbp | |
.endif | |
.align 3 | |
LEFDE$0: | |
.text | |
.endmacro | |
.macro DW_START | |
LF0$0: | |
.endmacro | |
.macro DW_FRAME | |
LF1$0: | |
.set LFLEN0$0, LF1$0-LF0$0 | |
.endmacro | |
.macro DW_END | |
.set LLEN$0, .-LF0$0 | |
EMIT_FDE $0, LLEN$0, 1 | |
.endmacro | |
.macro DW_END2 | |
.set LLEN$0, .-LF0$0 | |
EMIT_FDE $0, LLEN$0, 2 | |
.endmacro | |
///////////////////////////////////////////////////////////////////// | |
// | |
// SaveRegisters | |
// | |
// Pushes a stack frame and saves all registers that might contain | |
// parameter values. | |
// | |
// On entry: | |
// $0 = 0 if normal, 1 if CacheLookup already saved a4, a5, a6 | |
// stack = ret | |
// | |
// On exit: | |
// %rsp is 16-byte aligned | |
// | |
///////////////////////////////////////////////////////////////////// | |
.macro SaveRegisters | |
.if $0 == 0 | |
movq %a4, -32(%rsp) | |
movq %a5, -24(%rsp) | |
movq %a6, -16(%rsp) | |
.else | |
// a4-a6 already saved by CacheLookup | |
.endif | |
DW_FRAME $1 | |
pushq %rbp | |
movq %rsp, %rbp | |
subq $$ 128+64, %rsp | |
movdqa %xmm0, -192(%rbp) | |
movdqa %xmm1, -176(%rbp) | |
movdqa %xmm2, -160(%rbp) | |
movdqa %xmm3, -144(%rbp) | |
movdqa %xmm4, -128(%rbp) | |
movdqa %xmm5, -112(%rbp) | |
movdqa %xmm6, -96(%rbp) | |
movdqa %xmm7, -80(%rbp) | |
movq %r10, -64(%rbp) // fixme needed? | |
movq %rax, -56(%rbp) // might be xmm parameter count | |
movq %a1, -48(%rbp) | |
movq %a2, -40(%rbp) | |
movq %a3, -32(%rbp) | |
// movq %a4, -24(%rbp) | |
// movq %a5, -16(%rbp) | |
// movq %a6, -8(%rbp) | |
.endmacro | |
///////////////////////////////////////////////////////////////////// | |
// | |
// RestoreRegisters | |
// | |
// Pops a stack frame pushed by SaveRegisters | |
// | |
// On entry: | |
// %rbp unchanged since SaveRegisters | |
// | |
// On exit: | |
// stack = ret | |
// | |
///////////////////////////////////////////////////////////////////// | |
.macro RestoreRegisters | |
movdqa -192(%rbp), %xmm0 | |
movdqa -176(%rbp), %xmm1 | |
movdqa -160(%rbp), %xmm2 | |
movdqa -144(%rbp), %xmm3 | |
movdqa -128(%rbp), %xmm4 | |
movdqa -112(%rbp), %xmm5 | |
movdqa -96(%rbp), %xmm6 | |
movdqa -80(%rbp), %xmm7 | |
movq -64(%rbp), %r10 | |
movq -56(%rbp), %rax | |
movq -48(%rbp), %a1 | |
movq -40(%rbp), %a2 | |
movq -32(%rbp), %a3 | |
movq -24(%rbp), %a4 | |
movq -16(%rbp), %a5 | |
movq -8(%rbp), %a6 | |
movq %rbp, %rsp | |
popq %rbp | |
.endmacro | |
///////////////////////////////////////////////////////////////////// | |
// | |
// | |
// CacheLookup selectorRegister, cacheMissLabel | |
// | |
// Locate the implementation for a selector in a class method cache. | |
// | |
// Takes: | |
// $0 = register containing selector (%a1 or %a2 ONLY) | |
// $1 = if method is not cached then jmp LCacheMiss$1 | |
// %r11 = class whose cache is to be searched | |
// stack = ret | |
// | |
// On exit: (found) method triplet in %r11 | |
// (not found) jumps to cacheMissLabel | |
// stack = ret | |
// | |
///////////////////////////////////////////////////////////////////// | |
.macro CacheLookup | |
// load variables and save caller registers. | |
movq %a4, -32(%rsp) // save scratch registers in red zone | |
movq %a5, -24(%rsp) | |
movq %a6, -16(%rsp) | |
movq cache(%r11), %a5 // cache = class->cache | |
movl mask(%a5), %a6d | |
shlq $$3, %a6 // %a6 = cache->mask << 3 | |
mov $0, %a4 // bytes = sel | |
andq %a6, %a4 // bytes &= (mask << 3) | |
// search the receiver's cache | |
// r11 = method (soon) | |
// a4 = bytes | |
// a5 = cache | |
// a6 = mask << 3 | |
// $0 = sel | |
LMsgSendProbeCache_$1: | |
movq buckets(%a5, %a4), %r11 // method = cache->buckets[bytes/8] | |
testq %r11, %r11 // if (method == NULL) | |
je LCacheMiss$1 // goto cacheMissLabel | |
addq $$8, %a4 // bytes += 8 | |
andq %a6, %a4 // bytes &= (mask << 3) | |
cmpq method_name(%r11), $0 // if (method_name != sel) | |
jne LMsgSendProbeCache_$1 // goto loop | |
// cache hit, r11 = method triplet | |
// restore saved registers | |
movq -32(%rsp), %a4 | |
movq -24(%rsp), %a5 | |
movq -16(%rsp), %a6 | |
.endmacro | |
///////////////////////////////////////////////////////////////////// | |
// | |
// MethodTableLookup classRegister, selectorRegister, fn | |
// | |
// Takes: $0 = class to search (%a1 or %a2 or %r11 ONLY) | |
// $1 = selector to search for (%a2 or %a3 ONLY) | |
// | |
// Stack: ret (%rsp+0), pad, %a4, %a5, %a6 (saved by CacheLookup) | |
// | |
// On exit: restores registers saved by CacheLookup | |
// imp in %r11 | |
// | |
///////////////////////////////////////////////////////////////////// | |
.macro MethodTableLookup | |
SaveRegisters 1, $2 | |
// _class_lookupMethodAndLoadCache(class, selector) | |
movq $0, %a1 | |
movq $1, %a2 | |
call __class_lookupMethodAndLoadCache | |
// IMP is now in %rax | |
movq %rax, %r11 | |
RestoreRegisters $2 | |
.endmacro | |
/******************************************************************** | |
* Method _cache_getMethod(Class cls, SEL sel, IMP msgForward_internal_imp) | |
* | |
* On entry: a1 = class whose cache is to be searched | |
* a2 = selector to search for | |
* a3 = _objc_msgForward_internal IMP | |
* | |
* If found, returns method triplet pointer. | |
* If not found, returns NULL. | |
* | |
* NOTE: _cache_getMethod never returns any cache entry whose implementation | |
* is _objc_msgForward_internal. It returns 1 instead. This prevents thread- | |
* thread-safety and memory management bugs in _class_lookupMethodAndLoadCache. | |
* See _class_lookupMethodAndLoadCache for details. | |
* | |
* _objc_msgForward_internal is passed as a parameter because it's more | |
* efficient to do the (PIC) lookup once in the caller than repeatedly here. | |
********************************************************************/ | |
ENTRY __cache_getMethod | |
DW_START __cache_getMethod | |
// do lookup | |
movq %a1, %r11 // move class to r11 for CacheLookup | |
CacheLookup %a2, __cache_getMethod | |
// cache hit, method triplet in %r11 | |
cmpq method_imp(%r11), %a3 // if (imp==_objc_msgForward_internal) | |
je 1f // return (Method)1 | |
movq %r11, %rax // return method triplet address | |
ret | |
1: movq $1, %rax | |
ret | |
LCacheMiss__cache_getMethod: | |
// cache miss, return nil | |
xorq %rax, %rax // erase %rax | |
ret | |
LGetMethodExit: | |
DW_END2 __cache_getMethod | |
END_ENTRY __cache_getMethod | |
/******************************************************************** | |
* IMP _cache_getImp(Class cls, SEL sel) | |
* | |
* On entry: a1 = class whose cache is to be searched | |
* a2 = selector to search for | |
* | |
* If found, returns method implementation. | |
* If not found, returns NULL. | |
********************************************************************/ | |
ENTRY __cache_getImp | |
DW_START __cache_getImp | |
// do lookup | |
movq %a1, %r11 // move class to r11 for CacheLookup | |
CacheLookup %a2, __cache_getImp | |
// cache hit, method triplet in %r11 | |
movq method_imp(%r11), %rax // return method imp address | |
ret | |
LCacheMiss__cache_getImp: | |
// cache miss, return nil | |
xorq %rax, %rax // erase %rax | |
ret | |
LGetImpExit: | |
DW_END2 __cache_getImp | |
END_ENTRY __cache_getImp | |
/******************************************************************** | |
* | |
* id objc_msgSend(id self, SEL _cmd,...); | |
* | |
********************************************************************/ | |
ENTRY _objc_msgSend | |
DW_START _objc_msgSend | |
// check whether selector is ignored | |
cmpq $ kIgnore, %a2 | |
je LMsgSendReturnSelf // ignore and return self | |
// check whether receiver is nil | |
testq %a1, %a1 | |
je LMsgSendNilSelf | |
// receiver (in %a1) is non-nil: search the cache | |
LMsgSendReceiverOk: | |
movq isa(%a1), %r11 // class = self->isa | |
CacheLookup %a2, _objc_msgSend | |
// CacheLookup placed method in r11 | |
movq method_imp(%r11), %r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSend: | |
MethodTableLookup isa(%a1), %a2, _objc_msgSend | |
// MethodTableLookup placed IMP in r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// message sent to nil: redirect to nil receiver, if any | |
LMsgSendNilSelf: | |
movq __objc_nilReceiver(%rip), %a1 | |
testq %a1, %a1 // if (receiver != nil) | |
jne LMsgSendReceiverOk // send to new receiver | |
// message sent to nil - return 0 | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
LMsgSendReturnSelf: | |
movq %a1, %rax | |
ret | |
LMsgSendExit: | |
DW_END _objc_msgSend | |
END_ENTRY _objc_msgSend | |
#if __OBJC2__ | |
ENTRY _objc_msgSend_fixup | |
DW_START _objc_msgSend_fixup | |
testq %a1, %a1 | |
je LMsgSendFixupNilSelf | |
SaveRegisters 0, _objc_msgSend_fixup | |
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef | |
movq 8(%a2), %r11 // selector | |
movq isa(%a1), %a6 // isa = *receiver | |
movq cache(%a6), %a5 // cache = *isa | |
movq mask(%a5), %a4 // *cache | |
// a1 = receiver | |
// a2 = address of message ref | |
movq %a2, %a3 | |
movq $0, %a2 | |
// __objc_fixupMessageRef(receiver, 0, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSend_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 | |
LMsgSendFixupNilSelf: | |
// message sent to nil - return 0 | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
DW_END _objc_msgSend_fixup | |
END_ENTRY _objc_msgSend_fixup | |
ENTRY _objc_msgSend_fixedup | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
jmp _objc_msgSend | |
END_ENTRY _objc_msgSend_fixedup | |
#endif | |
/******************************************************************** | |
* | |
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...); | |
* | |
* struct objc_super { | |
* id receiver; | |
* Class class; | |
* }; | |
********************************************************************/ | |
ENTRY _objc_msgSendSuper | |
DW_START _objc_msgSendSuper | |
// check whether selector is ignored | |
cmpq $ kIgnore, %a2 | |
je LMsgSendSuperReturnSelf | |
// search the cache (objc_super in %a1) | |
movq class(%a1), %r11 // class = objc_super->class | |
CacheLookup %a2, _objc_msgSendSuper | |
// CacheLookup placed method in r11 | |
movq method_imp(%r11), %r11 | |
movq receiver(%a1), %a1 // load real receiver | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSendSuper: | |
MethodTableLookup class(%a1), %a2, _objc_msgSendSuper | |
// MethodTableLookup placed IMP in r11 | |
movq receiver(%a1), %a1 // load real receiver | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
LMsgSendSuperReturnSelf: | |
movq receiver(%a1), %rax | |
ret | |
LMsgSendSuperExit: | |
DW_END _objc_msgSendSuper | |
END_ENTRY _objc_msgSendSuper | |
#if __OBJC2__ | |
ENTRY _objc_msgSendSuper2_fixup | |
DW_START _objc_msgSendSuper2_fixup | |
SaveRegisters 0, _objc_msgSendSuper2_fixup | |
// a1 = address of objc_super2 | |
// a2 = address of message ref | |
movq %a2, %a3 | |
movq %a1, %a2 | |
movq receiver(%a1), %a1 | |
// __objc_fixupMessageRef(receiver, objc_super, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSendSuper2_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
// Load receiver from objc_super2 | |
movq receiver(%a1), %a1 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 | |
DW_END _objc_msgSendSuper2_fixup | |
END_ENTRY _objc_msgSendSuper2_fixup | |
ENTRY _objc_msgSendSuper2_fixedup | |
// objc_super->class is superclass of class to search | |
movq class(%a1), %r11 // cls = objc_super->class | |
movq 8(%a2), %a2 // load _cmd from message_ref | |
movq 8(%r11), %r11 // cls = cls->superclass | |
movq %r11, class(%a1) | |
// objc_super->class is now the class to search | |
jmp _objc_msgSendSuper | |
END_ENTRY _objc_msgSendSuper2_fixedup | |
ENTRY _objc_msgSendSuper2 | |
// objc_super->class is superclass of class to search | |
movq class(%a1), %r11 // cls = objc_super->class | |
movq 8(%r11), %r11 // cls = cls->superclass | |
movq %r11, class(%a1) | |
// objc_super->class is now the class to search | |
jmp _objc_msgSendSuper | |
END_ENTRY _objc_msgSendSuper2 | |
#endif | |
/******************************************************************** | |
* | |
* double objc_msgSend_fpret(id self, SEL _cmd,...); | |
* Used for `long double` return only. `float` and `double` use objc_msgSend. | |
* | |
********************************************************************/ | |
ENTRY _objc_msgSend_fpret | |
DW_START _objc_msgSend_fpret | |
// check whether selector is ignored | |
cmpq $ kIgnore, %a2 | |
je LMsgSendFpretReturnZero | |
// check whether receiver is nil | |
testq %a1, %a1 | |
je LMsgSendFpretNilSelf | |
// receiver (in %a1) is non-nil: search the cache | |
LMsgSendFpretReceiverOk: | |
movq isa(%a1), %r11 // class = self->isa | |
CacheLookup %a2, _objc_msgSend_fpret | |
// CacheLookup placed method in r11 | |
movq method_imp(%r11), %r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSend_fpret: | |
MethodTableLookup isa(%a1), %a2, _objc_msgSend_fpret | |
// MethodTableLookup placed IMP in r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// message sent to nil: redirect to nil receiver, if any | |
LMsgSendFpretNilSelf: | |
1: movq __objc_nilReceiver(%rip),%a1 | |
testq %a1, %a1 // if (receiver != nil) | |
jne LMsgSendFpretReceiverOk // send to new receiver | |
LMsgSendFpretReturnZero: | |
// Long double return. | |
fldz | |
// Clear int and float/double return too. | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
LMsgSendFpretExit: | |
DW_END _objc_msgSend_fpret | |
END_ENTRY _objc_msgSend_fpret | |
#if __OBJC2__ | |
ENTRY _objc_msgSend_fpret_fixup | |
DW_START _objc_msgSend_fpret_fixup | |
testq %a1, %a1 | |
je LMsgSendFpretFixupNilSelf | |
SaveRegisters 0, _objc_msgSend_fpret_fixup | |
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef | |
movq 8(%a2), %r11 // selector | |
movq isa(%a1), %a6 // isa = *receiver | |
movq cache(%a6), %a5 // cache = *isa | |
movq mask(%a5), %a4 // *cache | |
// a1 = receiver | |
// a2 = address of message ref | |
movq %a2, %a3 | |
movq $0, %a2 | |
// __objc_fixupMessageRef(receiver, 0, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSend_fpret_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 | |
LMsgSendFpretFixupNilSelf: | |
// Long double return. | |
fldz | |
// Clear int and float/double return too. | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
DW_END _objc_msgSend_fpret_fixup | |
END_ENTRY _objc_msgSend_fpret_fixup | |
ENTRY _objc_msgSend_fpret_fixedup | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
jmp _objc_msgSend_fpret | |
END_ENTRY _objc_msgSend_fpret_fixedup | |
#endif | |
/******************************************************************** | |
* | |
* double objc_msgSend_fp2ret(id self, SEL _cmd,...); | |
* Used for `complex long double` return only. | |
* | |
********************************************************************/ | |
ENTRY _objc_msgSend_fp2ret | |
DW_START _objc_msgSend_fp2ret | |
// check whether selector is ignored | |
cmpq $ kIgnore, %a2 | |
je LMsgSendFp2retReturnZero | |
// check whether receiver is nil | |
testq %a1, %a1 | |
je LMsgSendFp2retNilSelf | |
// receiver (in %a1) is non-nil: search the cache | |
LMsgSendFp2retReceiverOk: | |
movq isa(%a1), %r11 // class = self->isa | |
CacheLookup %a2, _objc_msgSend_fp2ret | |
// CacheLookup placed method in r11 | |
movq method_imp(%r11), %r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSend_fp2ret: | |
MethodTableLookup isa(%a1), %a2, _objc_msgSend_fp2ret | |
// MethodTableLookup placed IMP in r11 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 // goto *imp | |
// message sent to nil: redirect to nil receiver, if any | |
LMsgSendFp2retNilSelf: | |
1: movq __objc_nilReceiver(%rip),%a1 | |
testq %a1, %a1 // if (receiver != nil) | |
jne LMsgSendFp2retReceiverOk // send to new receiver | |
LMsgSendFp2retReturnZero: | |
// complex long double return. | |
fldz | |
fldz | |
// Clear int and float/double return too. | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
LMsgSendFp2retExit: | |
DW_END _objc_msgSend_fp2ret | |
END_ENTRY _objc_msgSend_fp2ret | |
#if __OBJC2__ | |
ENTRY _objc_msgSend_fp2ret_fixup | |
DW_START _objc_msgSend_fp2ret_fixup | |
testq %a1, %a1 | |
je LMsgSendFp2retFixupNilSelf | |
SaveRegisters 0, _objc_msgSend_fp2ret_fixup | |
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef | |
movq 8(%a2), %r11 // selector | |
movq isa(%a1), %a6 // isa = *receiver | |
movq cache(%a6), %a5 // cache = *isa | |
movq mask(%a5), %a4 // *cache | |
// a1 = receiver | |
// a2 = address of message ref | |
movq %a2, %a3 | |
movq $0, %a2 | |
// __objc_fixupMessageRef(receiver, 0, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSend_fp2ret_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
cmp %r11, %r11 // set nonstret (eq) for forwarding | |
jmp *%r11 | |
LMsgSendFp2retFixupNilSelf: | |
// complex long double return. | |
fldz | |
fldz | |
// Clear int and float/double return too. | |
movq $0, %rax | |
movq $0, %rdx | |
xorps %xmm0, %xmm0 | |
xorps %xmm1, %xmm1 | |
ret | |
DW_END _objc_msgSend_fp2ret_fixup | |
END_ENTRY _objc_msgSend_fp2ret_fixup | |
ENTRY _objc_msgSend_fp2ret_fixedup | |
// Load _cmd from the message_ref | |
movq 8(%a2), %a2 | |
jmp _objc_msgSend_fp2ret | |
END_ENTRY _objc_msgSend_fp2ret_fixedup | |
#endif | |
/******************************************************************** | |
* | |
* void objc_msgSend_stret(void *st_addr, id self, SEL _cmd, ...); | |
* | |
* objc_msgSend_stret is the struct-return form of msgSend. | |
* The ABI calls for %a1 to be used as the address of the structure | |
* being returned, with the parameters in the succeeding locations. | |
* | |
* On entry: %a1 is the address where the structure is returned, | |
* %a2 is the message receiver, | |
* %a3 is the selector | |
********************************************************************/ | |
ENTRY _objc_msgSend_stret | |
DW_START _objc_msgSend_stret | |
// check whether receiver is nil | |
testq %a2, %a2 | |
je LMsgSendStretNilSelf | |
// receiver (in %a2) is non-nil: search the cache | |
LMsgSendStretReceiverOk: | |
movq isa(%a2), %r11 // class = self->isa | |
CacheLookup %a3, _objc_msgSend_stret | |
// CacheLookup placed method in %r11 | |
movq method_imp(%r11), %r11 | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSend_stret: | |
MethodTableLookup isa(%a2), %a3, _objc_msgSend_stret | |
// MethodTableLookup placed IMP in r11 | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
// message sent to nil: redirect to nil receiver, if any | |
LMsgSendStretNilSelf: | |
movq __objc_nilReceiver(%rip), %a2 | |
testq %a2, %a2 // if (receiver != nil) | |
jne LMsgSendStretReceiverOk // send to new receiver | |
ret // else just return | |
LMsgSendStretExit: | |
DW_END _objc_msgSend_stret | |
END_ENTRY _objc_msgSend_stret | |
#if __OBJC2__ | |
ENTRY _objc_msgSend_stret_fixup | |
DW_START _objc_msgSend_stret_fixup | |
testq %a2, %a2 | |
je LMsgSendStretFixupNilSelf | |
SaveRegisters 0, _objc_msgSend_stret_fixup | |
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef | |
movq 8(%a3), %r11 // selector | |
movq isa(%a2), %a6 // isa = *receiver | |
movq cache(%a6), %a5 // cache = *isa | |
movq mask(%a5), %a4 // *cache | |
// a2 = receiver | |
// a3 = address of message ref | |
movq %a2, %a1 | |
movq $0, %a2 | |
// __objc_fixupMessageRef(receiver, 0, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSend_stret_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a3), %a3 | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
LMsgSendStretFixupNilSelf: | |
ret | |
DW_END _objc_msgSend_stret_fixup | |
END_ENTRY _objc_msgSend_stret_fixup | |
ENTRY _objc_msgSend_stret_fixedup | |
// Load _cmd from the message_ref | |
movq 8(%a3), %a3 | |
jmp _objc_msgSend_stret | |
END_ENTRY _objc_msgSend_stret_fixedup | |
#endif | |
/******************************************************************** | |
* | |
* void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...); | |
* | |
* struct objc_super { | |
* id receiver; | |
* Class class; | |
* }; | |
* | |
* objc_msgSendSuper_stret is the struct-return form of msgSendSuper. | |
* The ABI calls for (sp+4) to be used as the address of the structure | |
* being returned, with the parameters in the succeeding registers. | |
* | |
* On entry: %a1 is the address where the structure is returned, | |
* %a2 is the address of the objc_super structure, | |
* %a3 is the selector | |
* | |
********************************************************************/ | |
ENTRY _objc_msgSendSuper_stret | |
DW_START _objc_msgSendSuper_stret | |
// search the cache (objc_super in %a2) | |
movq class(%a2), %r11 // class = objc_super->class | |
CacheLookup %a3, _objc_msgSendSuper_stret | |
// CacheLookup placed method in %r11 | |
movq method_imp(%r11), %r11 | |
movq receiver(%a2), %a2 // load real receiver | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
// cache miss: go search the method lists | |
LCacheMiss_objc_msgSendSuper_stret: | |
MethodTableLookup class(%a2), %a3, _objc_msgSendSuper_stret | |
// MethodTableLookup placed IMP in r11 | |
movq receiver(%a2), %a2 // load real receiver | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
LMsgSendSuperStretExit: | |
DW_END _objc_msgSendSuper_stret | |
END_ENTRY _objc_msgSendSuper_stret | |
#if __OBJC2__ | |
ENTRY _objc_msgSendSuper2_stret_fixup | |
DW_START _objc_msgSendSuper2_stret_fixup | |
SaveRegisters 0, _objc_msgSendSuper2_stret_fixup | |
// a2 = address of objc_super2 | |
// a3 = address of message ref | |
movq receiver(%a2), %a1 | |
// __objc_fixupMessageRef(receiver, objc_super, ref) | |
call __objc_fixupMessageRef | |
movq %rax, %r11 | |
RestoreRegisters _objc_msgSendSuper2_stret_fixup | |
// imp is in r11 | |
// Load _cmd from the message_ref | |
movq 8(%a3), %a3 | |
// Load receiver from objc_super2 | |
movq receiver(%a2), %a2 | |
test %r11, %r11 // set stret (ne) for forward; r11!=0 | |
jmp *%r11 // goto *imp | |
DW_END _objc_msgSendSuper2_stret_fixup | |
END_ENTRY _objc_msgSendSuper2_stret_fixup | |
ENTRY _objc_msgSendSuper2_stret_fixedup | |
// objc_super->class is superclass of class to search | |
movq class(%a2), %r11 // cls = objc_super->class | |
movq 8(%a3), %a3 // load _cmd from message_ref | |
movq 8(%r11), %r11 // cls = cls->superclass | |
movq %r11, class(%a2) | |
// objc_super->class is now the class to search | |
jmp _objc_msgSendSuper_stret | |
END_ENTRY _objc_msgSendSuper2_stret_fixedup | |
ENTRY _objc_msgSendSuper2_stret | |
// objc_super->class is superclass of class to search | |
movq class(%a2), %r11 // cls = objc_super->class | |
movq 8(%r11), %r11 // cls = cls->superclass | |
movq %r11, class(%a2) | |
// objc_super->class is now the class to search | |
jmp _objc_msgSendSuper_stret | |
END_ENTRY _objc_msgSendSuper2_stret | |
#endif | |
/******************************************************************** | |
* | |
* id _objc_msgForward(id self, SEL _cmd,...); | |
* | |
********************************************************************/ | |
// _FwdSel is @selector(forward::), set up in map_images(). | |
// ALWAYS dereference _FwdSel to get to "forward::" !! | |
.data | |
.align 3 | |
.private_extern _FwdSel | |
_FwdSel: .quad 0 | |
.cstring | |
.align 3 | |
LUnkSelStr: .ascii "Does not recognize selector %s\0" | |
.data | |
.align 3 | |
.private_extern __objc_forward_handler | |
__objc_forward_handler: .quad 0 | |
.data | |
.align 3 | |
.private_extern __objc_forward_stret_handler | |
__objc_forward_stret_handler: .quad 0 | |
ENTRY __objc_msgForward_internal | |
.private_extern __objc_msgForward_internal | |
// Method cache version | |
// THIS IS NOT A CALLABLE C FUNCTION | |
// Out-of-band condition register is NE for stret, EQ otherwise. | |
jne __objc_msgForward_stret | |
jmp __objc_msgForward | |
END_ENTRY __objc_msgForward_internal | |
ENTRY __objc_msgForward | |
// Non-stret version | |
// Call user handler, if any | |
movq __objc_forward_handler(%rip), %r11 | |
testq %r11, %r11 // if (handler == NULL) | |
je 1f // skip handler | |
jmp *%r11 // else goto handler | |
1: | |
// No user handler | |
// Die if forwarding "forward::" | |
cmpq %a2, _FwdSel(%rip) | |
je LMsgForwardError | |
// Record current return address. It will be copied elsewhere in | |
// the marg_list because this location is needed for register args | |
movq (%rsp), %r11 | |
// Push stack frame | |
// Space for: fpArgs + regArgs + linkage - ret (already on stack) | |
subq $ 8*16 + 6*8 + (4-1)*8, %rsp | |
// Save return address in linkage area. | |
movq %r11, 16+LINK_AREA(%rsp) | |
// Save parameter registers | |
movq %a1, 0+REG_AREA(%rsp) | |
movq %a2, 8+REG_AREA(%rsp) | |
movq %a3, 16+REG_AREA(%rsp) | |
movq %a4, 24+REG_AREA(%rsp) | |
movq %a5, 32+REG_AREA(%rsp) | |
movq %a6, 40+REG_AREA(%rsp) | |
// Save side parameter registers | |
movq %r10, 0+LINK_AREA(%rsp) // static chain (fixme needed?) | |
movq %rax, 8+LINK_AREA(%rsp) // xmm count | |
// 16+LINK_AREA is return address | |
// Save xmm registers | |
movdqa %xmm0, 0+FP_AREA(%rsp) | |
movdqa %xmm1, 16+FP_AREA(%rsp) | |
movdqa %xmm2, 32+FP_AREA(%rsp) | |
movdqa %xmm3, 48+FP_AREA(%rsp) | |
movdqa %xmm4, 64+FP_AREA(%rsp) | |
movdqa %xmm5, 80+FP_AREA(%rsp) | |
movdqa %xmm6, 96+FP_AREA(%rsp) | |
movdqa %xmm7, 112+FP_AREA(%rsp) | |
// Call [receiver forward:sel :margs] | |
movq %rsp, %a4 // marg_list | |
movq %a2, %a3 // sel | |
movq _FwdSel(%rip), %a2 // forward:: | |
// %a1 is already the receiver | |
call _objc_msgSend | |
// Retrieve return address from linkage area | |
movq 16+LINK_AREA(%rsp), %r11 | |
// Pop stack frame | |
subq $ 8*16 + 6*8 + (4-1)*8, %rsp | |
// Put return address back | |
movq %r11, (%rsp) | |
ret | |
LMsgForwardError: | |
// Tail-call __objc_error(receiver, "unknown selector %s", "forward::") | |
// %a1 is already the receiver | |
leaq LUnkSelStr(%rip), %a2 // "unknown selector %s" | |
movq _FwdSel(%rip), %a3 // forward:: | |
jmp ___objc_error // never returns | |
END_ENTRY __objc_msgForward | |
ENTRY __objc_msgForward_stret | |
// Struct-return version | |
// Call user handler, if any | |
movq __objc_forward_stret_handler(%rip), %r11 | |
testq %r11, %r11 // if (handler == NULL) | |
je 1f // skip handler | |
jmp *%r11 // else goto handler | |
1: | |
// No user handler | |
// Die if forwarding "forward::" | |
cmpq %a3, _FwdSel(%rip) | |
je LMsgForwardStretError | |
// Record current return address. It will be copied elsewhere in | |
// the marg_list because this location is needed for register args | |
movq (%rsp), %r11 | |
// Push stack frame | |
// Space for: fpArgs + regArgs + linkage - ret (already on stack) | |
subq $ 8*16 + 6*8 + (4-1)*8, %rsp | |
// Save return address in linkage area. | |
movq %r11, 16+LINK_AREA(%rsp) | |
// Save parameter registers | |
movq %a1, 0+REG_AREA(%rsp) | |
movq %a2, 8+REG_AREA(%rsp) | |
movq %a3, 16+REG_AREA(%rsp) | |
movq %a4, 24+REG_AREA(%rsp) | |
movq %a5, 32+REG_AREA(%rsp) | |
movq %a6, 40+REG_AREA(%rsp) | |
// Save side parameter registers | |
movq %r10, 0+LINK_AREA(%rsp) // static chain (fixme needed?) | |
movq %rax, 8+LINK_AREA(%rsp) // xmm count | |
// 16+LINK_AREA is return address | |
// Save xmm registers | |
movdqa %xmm0, 0+FP_AREA(%rsp) | |
movdqa %xmm1, 16+FP_AREA(%rsp) | |
movdqa %xmm2, 32+FP_AREA(%rsp) | |
movdqa %xmm3, 48+FP_AREA(%rsp) | |
movdqa %xmm4, 64+FP_AREA(%rsp) | |
movdqa %xmm5, 80+FP_AREA(%rsp) | |
movdqa %xmm6, 96+FP_AREA(%rsp) | |
movdqa %xmm7, 112+FP_AREA(%rsp) | |
// Call [receiver forward:sel :margs] | |
movq %a2, %a1 // receiver | |
movq _FwdSel(%rip), %a2 // forward:: | |
// %a3 is already the selector | |
movq %rsp, %a4 // marg_list | |
call _objc_msgSend // forward:: is NOT struct-return | |
// Retrieve return address from linkage area | |
movq 16+LINK_AREA(%rsp), %r11 | |
// Pop stack frame | |
subq $ 8*16 + 6*8 + (4-1)*8, %rsp | |
// Put return address back | |
movq %r11, (%rsp) | |
ret | |
LMsgForwardStretError: | |
// Tail-call __objc_error(receiver, "unknown selector %s", "forward::") | |
movq %a2, %a1 // receiver | |
leaq LUnkSelStr(%rip), %a2 // "unknown selector %s" | |
movq _FwdSel(%rip), %a3 // forward:: | |
jmp ___objc_error // never returns | |
END_ENTRY __objc_msgForward_stret | |
ENTRY _method_invoke | |
movq method_imp(%a2), %r11 | |
movq method_name(%a2), %a2 | |
jmp *%r11 | |
END_ENTRY _method_invoke | |
ENTRY _method_invoke_stret | |
movq method_imp(%a3), %r11 | |
movq method_name(%a3), %a3 | |
jmp *%r11 | |
END_ENTRY _method_invoke_stret | |
/******************************************************************** | |
* | |
* id vtable_prototype(id self, message_ref *msg, ...) | |
* | |
* This code is copied to create vtable trampolines. | |
* The instruction following LvtableIndex is modified to | |
* insert each vtable index. | |
* | |
* This code is placed in its own section to prevent dtrace from | |
* instrumenting it. Otherwise, dtrace would insert an INT3, the | |
* code would be copied, and the copied INT3 would cause a crash. | |
* | |
********************************************************************/ | |
.macro VTABLE /* byte-offset, name */ | |
.align 2 | |
.private_extern _$1 | |
_$1: | |
test %a1, %a1 | |
je LvtableReturnZero_$1 // nil check | |
movq 8(%a2), %a2 // load _cmd (fixme schedule?) | |
movq 0(%a1), %r10 // load isa | |
movq 24(%r10), %r11 // load vtable | |
LvtableIndex_$1: | |
movq $0 (%r11), %r10 // load imp (DO NOT CHANGE) | |
jmp *%r10 | |
LvtableReturnZero_$1: | |
// integer registers only; not used for fpret / stret / etc | |
movq $$0, %rax | |
movq $$0, %rdx | |
ret | |
LvtableEnd_$1: | |
nop | |
.endmacro | |
.section __TEXT,__objc_codegen,regular | |
VTABLE 0x7fff, vtable_prototype | |
.data | |
.align 2 | |
.private_extern _vtable_prototype_size | |
_vtable_prototype_size: | |
.long LvtableEnd_vtable_prototype - _vtable_prototype | |
.private_extern _vtable_prototype_index_offset | |
_vtable_prototype_index_offset: | |
.long LvtableIndex_vtable_prototype - _vtable_prototype | |
/******************************************************************** | |
* | |
* id vtable_ignored(id self, message_ref *msg, ...) | |
* | |
* Vtable trampoline for GC-ignored selectors. Immediately returns self. | |
* | |
********************************************************************/ | |
.text | |
.align 2 | |
.private_extern _vtable_ignored | |
_vtable_ignored: | |
movq %a1, %rax | |
ret | |
/******************************************************************** | |
* | |
* id objc_msgSend_vtable<n>(id self, message_ref *msg, ...) | |
* | |
* Built-in expansions of vtable_prototype for the default vtable. | |
* | |
********************************************************************/ | |
.text | |
.align 4 | |
.private_extern _defaultVtableTrampolineDescriptors | |
_defaultVtableTrampolineDescriptors: | |
// objc_trampoline_header | |
.short 16 // headerSize | |
.short 8 // descSize | |
.long 16 // descCount | |
.quad 0 // next | |
// objc_trampoline_descriptor[16] | |
.macro TDESC /* n */ | |
L_tdesc$0: | |
.long _objc_msgSend_vtable$0 - L_tdesc$0 | |
.long (1<<0) + (1<<2) // MESSAGE and VTABLE | |
.endmacro | |
TDESC 0 | |
TDESC 1 | |
TDESC 2 | |
TDESC 3 | |
TDESC 4 | |
TDESC 5 | |
TDESC 6 | |
TDESC 7 | |
TDESC 8 | |
TDESC 9 | |
TDESC 10 | |
TDESC 11 | |
TDESC 12 | |
TDESC 13 | |
TDESC 14 | |
TDESC 15 | |
// trampoline code | |
.align 4 | |
VTABLE 0*8, objc_msgSend_vtable0 | |
VTABLE 1*8, objc_msgSend_vtable1 | |
VTABLE 2*8, objc_msgSend_vtable2 | |
VTABLE 3*8, objc_msgSend_vtable3 | |
VTABLE 4*8, objc_msgSend_vtable4 | |
VTABLE 5*8, objc_msgSend_vtable5 | |
VTABLE 6*8, objc_msgSend_vtable6 | |
VTABLE 7*8, objc_msgSend_vtable7 | |
VTABLE 8*8, objc_msgSend_vtable8 | |
VTABLE 9*8, objc_msgSend_vtable9 | |
VTABLE 10*8, objc_msgSend_vtable10 | |
VTABLE 11*8, objc_msgSend_vtable11 | |
VTABLE 12*8, objc_msgSend_vtable12 | |
VTABLE 13*8, objc_msgSend_vtable13 | |
VTABLE 14*8, objc_msgSend_vtable14 | |
VTABLE 15*8, objc_msgSend_vtable15 | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Cool