in Cocoa
by sunnyxx
在 Hello World 背后
#include <stdio.h>
int main() {
printf("hello world");
return 0;
}
程序执行的时候都在干嘛?
本来挺简单的,直到有了 function
caller
callee
卧槽,我刚才的局部变量呢?
存在寄存器里的值也丢了!
刚才手头紧,临时用了下
规定在函数调用中:
prologue
epilogue
your code
保存现场
恢复现场
一个编译后的函数
int addUp(int first, int second) {
return first + second + 789;
}
int main() {
int ret = addUp(123, 456);
return 0
}
$clang -S main.m -arch x86_64
_addUp:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp) // arg0 = edi
movl %esi, -8(%rbp) // arg1 = esi
movl -4(%rbp), %esi // esi = arg0(123)
addl -8(%rbp), %esi // esi += arg1(456)
addl $789, %esi // esi += 789
movl %esi, %eax // eax = esi (eax储存返回值)
popq %rbp
retq
_main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp // 申请 16 btyes 栈内存
movl $123, %edi // edi = 123
movl $456, %esi // esi = 456
movl $0, -4(%rbp)
callq _addUp // call addUp
xorl %esi, %esi
movl %eax, -8(%rbp) // 得到返回值,存到栈上
movl %esi, %eax
addq $16, %rsp
popq %rbp
retq
栈顶 高地址 0x7fffffff
bp
sp
本次函数调用使用的栈内存
申请局部变量时
rsp指向更低的地址
don't overflow stack
caller
管理栈内存
@interface Sark : NSObject
@end
@implementation Sark
- (int)fooWithBar:(int)bar baz:(int)baz {
return bar + baz;
}
@end
int fakeFoo(id self, SEL _cmd, int bar, int baz) {
return bar * baz;
}
extern void fake_msgSend(void);
__asm(
".global _fake_msgSend \n"
"_fake_msgSend:\n"
"jmp _fakeFoo\n"
);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Sark *sark = [Sark new];
int ret = ((int (*)(id, SEL, int, int))fake_msgSend)
(sark, sel_registerName("fooWithBar:baz"), 123, 456);
NSLog(@"ret: %d", ret);
}
return 0;
}
imp_implementationWithBlock
__a1a2_tramphead:
popq %r10
andq $0xFFFFFFFFFFFFFFF8, %r10
subq $ PAGE_SIZE, %r10
movq %rdi, %rsi // arg1 -> arg2
movq (%r10), %rdi // block -> arg1
jmp *16(%rdi)
如何将 IMP 转发到 block->invoke ?
i24@0:8i16i20
i@:ii
_TFC12TestFFISwift4Sark3foofT3barSi_Si
NSInvocation 实现了 Objective-C Calling Conventions
Message Forwarding!
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
( JSPatch's Swizzling)
1. ffi_call 已知返回值和各参数type,动态调用一个 C 函数(objc,甚至 swift 方法)
2. ffi_closure 已知返回值和各参数type,动态生成一个函数指针,且这个函数被调用时,将调用的参数和返回值汇集到一个处理函数
可代替 NSInvocation
可代替 Forward 方式进行 Swizzle Patch
可动态生成 Block
https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html
https://en.wikipedia.org/wiki/Calling_convention
https://msdn.microsoft.com/en-us/library/ms235286.aspx
https://www.mikeash.com/pyblog/friday-qa-2011-12-16-disassembling-the-assembly-part-1.html
https://www.raywenderlich.com/37181/ios-assembly-tutorial
https://www.mikeash.com/pyblog/friday-qa-2013-03-08-lets-build-nsinvocation-part-i.html
http://arigrant.com/blog/2014/2/12/why-objcmsgsend-must-be-written-in-assembly
http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
Refs