/* * 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(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