http://blog.csdn.net/hursing/article/details/8697654
(前两节也许你会看得没劲,可直接先看第四节 (四)自动断点应用之NSNotificationCenter http://blog.csdn.net/hursing/article/details/8752235)
iOS模拟器程序的实质就是Mac OS X程序,只不过它需要以模拟器为载体来运行显示。故它的反汇编代码指令都是x86 CPU的,不是真机上的arm指令。
研究模拟器程序的反汇编有两个目的,或叫做好处:
一是为了研究深入到iOS系统的类库,你可以较容易地发现私有API,以及看到系统的实现。
二是,很直接地,模拟器调试比真机快。而且,相信大多数人更懂x86指令,各类高校教材的汇编教程都不是arm指令的吧。
首先问题是如何看到反汇编代码:
操作:Xcode顶部菜单->Product->Debug Workflow->Show Disassembly When Debugging打钩
如果是在调试的过程中打钩,则调试窗口会立刻更新显示反汇编代码,如图:

以上主要是为了看到自己写的代码的反汇编情况,当调试进入不是自己写的代码(没有debug symbol)时,无论是否对这个选项打钩,都会显示成反汇编。
例如,在gdb/lldb调试中,break状态下输入
再continue之后,如果有发生addSubview操作,不论是自己写的还是系统操作的,都会进入反汇编断点:
转载请注明出处:http://blog.csdn.net/hursing
这里当然不会从零开始讲汇编代码了,零基础的话可以看看 王爽 写的书《汇编语言》,请自己找度娘或谷哥要了。
这一节主要讲讲书上没有的东西。
在xcode中看到的汇编语法不是熟悉的intel格式,而是叫AT&T汇编。基本上只要懂intel,at&t会很快上手。两者的区别请参考这篇文章:
AT&T汇编http://blog.csdn.net/bigloomy/article/details/6581754
例如,在前一节的截图中,会看到movl这样的指令,比熟悉的mov多了个l。这个l表示本次操作是长字型(4字节)数据,操作数是32bit。
这里要注意,lldb和gdb看到的结果会有些差别,下面是对同一个C++函数的截图:
这个是lldb:

这个是gdb:

可以看到lldb会比gdb在AT&T的格式表示上更规范化,gdb只会在一些特定的地方才加l。
lldb以十进制表示常量;左侧内存地址省略了0;
gdb在尖括号内多显示了函数内的偏移。
其实最新版的gdb又会显示得不同,但是apple在强推lldb,不再升级gdb了……
如果实际的代码是在处理浮点数,则在反汇编中会看到xmm0,xmm1等的寄存器,如:

这些寄存器叫xmm寄存器,是为SSE指令服务的,所以也叫sse寄存器。操作他们的指令会有ss后缀。度娘给出了更多信息:http://baike.baidu.com/view/2679650.htm
sse寄存器是一组128bit的寄存器,其值有很多种可能性,可通过gdb查看,详情请查看下一节。
总结一下,主要明白这几个概念就行:
1. mov xxx, yyy 表示把xxx的值赋给yyy
2. call xxx 表示调用某函数
3. jmp xxx 表示直接跳转到某内存地址,一般是函数的入口地址
4. ret 表示要返回下一层堆栈了,如果函数有返回值,会存在eax寄存器里。
在Objective-C函数的入口处(第一行)加断点,可用esp指针来探查参数。
以esp为基址,往后的偏移分别是:
0:函数执行完毕后的返回地址(不是返回值的地址哦)
4:对象实例的指针,即self指针
8:selector,实际是一个char数组型的字符串,即char*
12:(如果有)第一个参数
…(前一个参数的基址+前一个参数所占的字节数):(如果有)第n个参数
由此,要调试这样一个函数
-
- (void)para1:(id)arg1 para2:(CGRect)p2 para3:(CGPoint)p3 para4:(id)p4
-
{
-
NSLog(@"para1:(id)arg1 para2:(CGRect)p2 para3:(CGPoint)p3 para4:(id)p4");
-
}
-
-
- (void)viewDidLoad
-
{
-
[self para1:[UIApplication sharedApplication] para2:CGRectMake(10, 20, 30, 40) para3:CGPointMake(50, 60) para4:self];
时,断点后可在gdb这样调试:
po *(id*)($esp+4) // 得到对象实例的描述
p (char*)*(SEL*)($esp+8) // 得到selector的名字
po *(id*)($esp+12) // 得到p1的description
p *(CGRect*)($esp+16) // 得到p2的各个成员值,输出结果为:“(CGRect) $1 = origin=(x=10, y=20) size=(width=30, height=40)”
p *(CGPoint*)($esp+32) // 因为一个CGRect结构体占16字节(4个float),所以是“+32”,即“+16+16”, 得到p3的各个成员值,输出结果为:“(CGPoint) $4 = (x=50, y=60)”
po *(id*)($esp+40) // 因为一个CGPoint结构体占8字节(2个float),所以是“+40”,即“+32+8”、得到p4的description
-
(gdb) po *(id*)($esp+4)
-
<ViewController: 0x757f8c0>
-
(gdb) p (char*)*(SEL*)($esp+8)
-
$1 = 0xf37b "para1:para2:para3:para4:"
-
(gdb) po *(id*)($esp+12)
-
<UIApplication: 0x9250000>
-
(gdb) p *(CGRect*)($esp+16)
-
$2 = {
-
origin = {
-
x = 10,
-
y = 20
-
},
-
size = {
-
width = 30,
-
height = 40
-
}
-
}
-
(gdb) p *(CGPoint*)($esp+32)
-
$3 = {
-
x = 50,
-
y = 60
-
}
-
(gdb) po *(id*)($esp+40)
-
<ViewController: 0x757f8c0>
由于在第一行,push了ebp,会导致esp被修改,而第二行又立刻把esp的值赋给ebp,所以在经过函数的这些头部时,可以用ebp类似地访问,不过参数的偏移都需要+4。
下面是如何查看浮点型参数。
浮点型参数会通过SSE寄存器来传递,可以在gdb中这样查看:
(lldb不能用上面的命令,暂没去研究用什么替代)
例如调试上例中的
-
CGRectMake(10, 20, 30, 40)
编译器会把参数反序送入xmm寄存器组,即40传入xmm0,30传入xmm1……
在CGRectMake加断点,执行到下图中的位置时输入命令

命令结果如下:
-
(gdb) p $xmm0
-
$1 = {
-
v4_float = {0, 0, 0, 40},
-
v2_double = {0, 5.4811317061554153e-315},
-
v16_int8 = {0 <repeats 12 times>, 66, 32, 0, 0},
-
v8_int16 = {0, 0, 0, 0, 0, 0, 16928, 0},
-
v4_int32 = {0, 0, 0, 1109393408},
-
v2_int64 = {0, 1109393408},
-
uint128 = 35467839930368
-
}
因为SSE寄存器是128位的,gdb并不知道哪些bit是有意义的,所以列出了一些猜测的结果。
我们传入的是CGFloat,即float,故v4_float = {0, 0, 0, 40} 是有意义的。
当传入的是double型,则v2_double有意义。
知道怎么查看后,先看看有什么实际应用,拿NSNotificationCenter来做实践吧。
首先在某个容易进入的地方加断点,并停在那,例如main函数。在gdb或lldb输入命令,手工添加断点。如下图:

输入的命令如下:
-
b -[NSNotificationCenter addObserver:selector:name:object:]
-
b -[NSNotificationCenter addObserverForName:object:queue:usingBlock:]
然后continue运行。很快,NSNotificationCenter的断点就会触发,是由系统的类调用的。因为是命令行添加的断点,所以不会被xcode记录,下次启动也不会再进入,此时我们需要在xcode里再加断点,如下图,在函数第一行的行号栏单击,标上蓝色断点标记。
然后,在断点上右键单击,选择“Edit Breakpoint...“

按照下图设置好:

点击”Done“后完成(另一个函数的照样做就行)。 最后,把Main函数上的断点去掉,然后重启程序。 这时就会看见命令行狂打印log了。

这log有什么用?呵呵,自己挖掘吧,至少你已经发现好多个non-public API了。
转载请注明出处:http://blog.csdn.net/hursing
反汇编调试objective-c,遇到最多的就是objc_msgSend这函数了,本节主要讲讲它的实现以及调试过程的一些技巧。
以UIWebView为例子,看看它在loadRequest时做了什么。
首先必须明白,原始代码中调用
-
[uiWebViewInstance loadRequest:request]
的实质是调用了
-
objc_msgSend(uiWebViewInstance, "loadRequest:"的selector, request)
编写一个用到UIWebView的demo,主要功能是打开一个网页。
在main函数加断点,触发后,在命令行输入:
-
b -[UIWebView loadRequest:]
然后continue,直到触发断点,如下图:

用step over跑到第17行,或直接在那加个断点continue到那。旁边的注释已经显示,call的是存储了objc_msgSend这个函数的偏移地址的代码位置。
17行的前三行,可以看出其动作是往栈上传参数,这三个参数原本是放在edx,ecx,eax的,所以如果参数不多,也可以不用esp为基址来查参数。
在当前的命令行输入
来查看各寄存器的值。lldb会把当前值的意义也输出来。但gdb不会,gdb对应的命令是
结果如下图:

从ecx的内容可以看出,要call的selector的名字同样是loadRequest:,后面会验证。
接着,step into,可以看到:

那里只有一个跳转命令,继续step into,跳到objc_msgSend真正的地址,看到下图所示的代码:

简单点说,这些代码做了这些事:
1. 先判断传来的对象是否nil,是的话,把返回值(esp+4)置0就返回了,不是就进行下一步。即使是void型的函数,也是被当做有返回值的,只是没用。
2. 查找这个method有没有被cache,有就jmpl到cache了的IMP地址;没有就以selector代表的字符串从这个类的method list里找出method来(第13到20行的循环),cache后再jmpl。如果找不到method就抛出异常然后程序崩溃了。
一个小技巧是,如果想省去点好多次step over来跳过查找的步骤,可以在两个jmpl *%eax的地方加断点,然后run到那里(图中的第31行和38行)。如果传来的对象不是nil,最后都会到这来。此时eax里保存的是函数的地址,即objective-c运行时库定义的IMP(指向函数的指针),jump过去就能到目标函数里了。
另外,在调试反汇编时,step over和step into不要用快捷键,要用调试工具栏上的按钮。不知道为什么按快捷键都等于run了……
接着,在jmpl处step into,就到真实的函数了:

证实了刚才ecx是传了selector名字。
在lldb中搜索
-
image lookup -r -s objc_msgSend
可以看到:
-
13 symbols match the regular expression 'objc_msgSend' in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk/usr/lib/libobjc.A.dylib:
-
Address: libobjc.A.dylib[0x0001708c] (libobjc.A.dylib.__TEXT.__text + 90252)
-
Summary: libobjc.A.dylib`objc_msgSend Address: libobjc.A.dylib[0x00017104] (libobjc.A.dylib.__TEXT.__text + 90372)
-
Summary: libobjc.A.dylib`objc_msgSendSuper Address: libobjc.A.dylib[0x0001716c] (libobjc.A.dylib.__TEXT.__text + 90476)
-
Summary: libobjc.A.dylib`objc_msgSendSuper2 Address: libobjc.A.dylib[0x00017468] (libobjc.A.dylib.__TEXT.__text + 91240)
-
Summary: libobjc.A.dylib`objc_msgSendSuper2_debug Address: libobjc.A.dylib[0x00017338] (libobjc.A.dylib.__TEXT.__text + 90936)
-
Summary: libobjc.A.dylib`objc_msgSendSuper2_stret Address: libobjc.A.dylib[0x00017478] (libobjc.A.dylib.__TEXT.__text + 91256)
-
Summary: libobjc.A.dylib`objc_msgSendSuper2_stret_debug Address: libobjc.A.dylib[0x000172c8] (libobjc.A.dylib.__TEXT.__text + 90824)
-
Summary: libobjc.A.dylib`objc_msgSendSuper_stret Address: libobjc.A.dylib[0x00017460] (libobjc.A.dylib.__TEXT.__text + 91232)
-
Summary: libobjc.A.dylib`objc_msgSend_debug Address: libobjc.A.dylib[0x000171dc] (libobjc.A.dylib.__TEXT.__text + 90588)
-
Summary: libobjc.A.dylib`objc_msgSend_fpret Address: libobjc.A.dylib[0x00017480] (libobjc.A.dylib.__TEXT.__text + 91264)
-
Summary: libobjc.A.dylib`objc_msgSend_fpret_debug Address: libobjc.A.dylib[0x00017488] (libobjc.A.dylib.__TEXT.__text + 91272)
-
Summary: libobjc.A.dylib`objc_msgSend_noarg Address: libobjc.A.dylib[0x00017254] (libobjc.A.dylib.__TEXT.__text + 90708)
-
Summary: libobjc.A.dylib`objc_msgSend_stret Address: libobjc.A.dylib[0x00017470] (libobjc.A.dylib.__TEXT.__text + 91248)
-
Summary: libobjc.A.dylib`objc_msgSend_stret_debug
objc_msgSend会有好多个版本:
objc_msgSend是普通调用,返回值为void、id、指针、int等32bit能表示的数据的objective-c函数会用到它。
objc_msgSend_stret在返回值为结构体时用到,stret表示structure return
objc_msgSend_fpret在返回值为浮点数时用到,fpret表示floating point return
带Super的表示调用父类的函数。
如上节所说,在obj_msgSend的第一行加自动断点 p (char*)*(SEL*)($esp+8) 就会打印多到你不想看的selector名字。注意这个地方不能去po对象,因为po的实质是调用对象的description,这个会再调用obj_msgSend,无限递归就挂了。
转载请注明出处:http://blog.csdn.net/hursing
引用第二节的例子:

函数的入口处,通常都是把esp的值传给ebp保存,然后下面的操作以ebp为基准做偏移量引用。因为esp作为栈指针,push和pop都会自动修改其值,所以用ebp可以不受影响。
还有的常见情形是开头和结尾对应着
-
subl $8, %esp
-
addl $8, %esp
这里体现着所谓的“局部变量在栈上分配”原则,说明本函数需要用8字节作为局部变量的保存空间。同时因为ebp已指向未subl前的esp地址,所以往下的操作中,-x($ebp)这样的地方是在操作局部变量,其中x为非负整数。不过需要明确的是,未必x字节是被用完作为局部变量的,编译器出于字节对齐的考虑,如果只用了7字节,那么也会分配到8字节。
首先自己写一段代码,加好断点,分别在Build Configuration为Debug和Release下运行,查看反汇编

-
- (void)test
-
{
-
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10.0, 20.0f, 30.1, 40)];
-
[button setTitle:@"test" forState:UIControlStateNormal];
-
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
-
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
-
[self.view addSubview:button];
-
[button release];
-
}
debug下的反汇编结果:
-
0x6a10: pushl %ebp
-
0x6a11: movl %esp, %ebp
-
0x6a13: pushl %ebx
-
0x6a14: pushl %edi
-
0x6a15: pushl %esi
-
0x6a16: subl $108, %esp
-
0x6a19: calll 0x6a1e ; -[ViewController test] + 14 at ViewController.mm:375
-
0x6a1e: popl %eax
-
0x6a1f: movl 12(%ebp), %ecx
-
0x6a22: movl 8(%ebp), %edx
-
0x6a25: leal -40(%ebp), %esi
-
0x6a28: movl $10, %edi
-
0x6a2d: cvtsi2ss%edi, %xmm0
-
0x6a31: movl $20, %edi
-
0x6a36: cvtsi2ss%edi, %xmm1
-
0x6a3a: movss 44642(%eax), %xmm2
-
0x6a42: movl $40, %edi
-
0x6a47: cvtsi2ss%edi, %xmm3
-
0x6a4b: movl %edx, -16(%ebp)
-
0x6a4e: movl %ecx, -20(%ebp)
-
0x6a51: movl 57106(%eax), %ecx
-
0x6a57: movl 56146(%eax), %edx
-
0x6a5d: movl %ecx, (%esp)
-
0x6a60: movl %edx, 4(%esp)
-
0x6a64: movl %eax, -44(%ebp)
-
0x6a67: movss %xmm2, -48(%ebp)
-
0x6a6c: movss %xmm3, -52(%ebp)
-
0x6a71: movl %esi, -56(%ebp)
-
0x6a74: movss %xmm0, -60(%ebp)
-
0x6a79: movss %xmm1, -64(%ebp)
-
0x6a7e: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6a83: movl -56(%ebp), %ecx
-
0x6a86: movl %ecx, (%esp)
-
0x6a89: movss -60(%ebp), %xmm0
-
0x6a8e: movss %xmm0, 4(%esp)
-
0x6a94: movss -64(%ebp), %xmm1
-
0x6a99: movss %xmm1, 8(%esp)
-
0x6a9f: movss -48(%ebp), %xmm2
-
0x6aa4: movss %xmm2, 12(%esp)
-
0x6aaa: movss -52(%ebp), %xmm3
-
0x6aaf: movss %xmm3, 16(%esp)
-
0x6ab5: movl %eax, -68(%ebp)
-
0x6ab8: calll 0x6900 ; CGRectMake(float, float, float, float) at CGGeometry.h:269
-
0x6abd: subl $4, %esp
-
0x6ac0: movl $64, %eax
-
0x6ac5: movl $0, %ecx
-
0x6aca: movl -44(%ebp), %edx
-
0x6acd: leal 58762(%edx), %esi
-
0x6ad3: leal -40(%ebp), %edi
-
0x6ad6: movl 56158(%edx), %ebx
-
0x6adc: movl -68(%ebp), %edx
-
0x6adf: movl %edx, (%esp)
-
0x6ae2: movl %ebx, 4(%esp)
-
0x6ae6: movl (%edi), %edx
-
0x6ae8: movl %edx, 8(%esp)
-
0x6aec: movl 4(%edi), %edx
-
0x6aef: movl %edx, 12(%esp)
-
0x6af3: movl 8(%edi), %edx
-
0x6af6: movl %edx, 16(%esp)
-
0x6afa: movl 12(%edi), %edx
-
0x6afd: movl %edx, 20(%esp)
-
0x6b01: movl %eax, -72(%ebp)
-
0x6b04: movl %ecx, -76(%ebp)
-
0x6b07: movl %esi, -80(%ebp)
-
0x6b0a: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6b0f: movl %eax, -24(%ebp)
-
0x6b12: movl -24(%ebp), %eax
-
0x6b15: movl -44(%ebp), %ecx
-
0x6b18: movl 56374(%ecx), %edx
-
0x6b1e: movl %eax, (%esp)
-
0x6b21: movl %edx, 4(%esp)
-
0x6b25: movl -80(%ebp), %eax
-
0x6b28: movl %eax, 8(%esp)
-
0x6b2c: movl $0, 12(%esp)
-
0x6b34: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6b39: movl -24(%ebp), %eax
-
0x6b3c: movl -44(%ebp), %ecx
-
0x6b3f: movl 57074(%ecx), %edx
-
0x6b45: movl 56274(%ecx), %esi
-
0x6b4b: movl %edx, (%esp)
-
0x6b4e: movl %esi, 4(%esp)
-
0x6b52: movl %eax, -84(%ebp)
-
0x6b55: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6b5a: movl -44(%ebp), %ecx
-
0x6b5d: movl 56386(%ecx), %edx
-
0x6b63: movl -84(%ebp), %esi
-
0x6b66: movl %esi, (%esp)
-
0x6b69: movl %edx, 4(%esp)
-
0x6b6d: movl %eax, 8(%esp)
-
0x6b71: movl $0, 12(%esp)
-
0x6b79: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6b7e: movl -24(%ebp), %eax
-
0x6b81: movl -16(%ebp), %ecx
-
0x6b84: movl -44(%ebp), %edx
-
0x6b87: movl 56454(%edx), %esi
-
0x6b8d: movl 56394(%edx), %edi
-
0x6b93: movl %eax, (%esp)
-
0x6b96: movl %edi, 4(%esp)
-
0x6b9a: movl %ecx, 8(%esp)
-
0x6b9e: movl %esi, 12(%esp)
-
0x6ba2: movl $64, 16(%esp)
-
0x6baa: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6baf: movl -16(%ebp), %eax
-
0x6bb2: movl -44(%ebp), %ecx
-
0x6bb5: movl 56350(%ecx), %edx
-
0x6bbb: movl %eax, (%esp)
-
0x6bbe: movl %edx, 4(%esp)
-
0x6bc2: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6bc7: movl -24(%ebp), %ecx
-
0x6bca: movl -44(%ebp), %edx
-
0x6bcd: movl 56354(%edx), %esi
-
0x6bd3: movl %eax, (%esp)
-
0x6bd6: movl %esi, 4(%esp)
-
0x6bda: movl %ecx, 8(%esp)
-
0x6bde: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6be3: movl -24(%ebp), %eax
-
0x6be6: movl -44(%ebp), %ecx
-
0x6be9: movl 56138(%ecx), %edx
-
0x6bef: movl %eax, (%esp)
-
0x6bf2: movl %edx, 4(%esp)
-
0x6bf6: calll 0xe56a ; symbol stub for: objc_msgSend
-
0x6bfb: addl $108, %esp
-
0x6bfe: popl %esi
-
0x6bff: popl %edi
-
0x6c00: popl %ebx
-
0x6c01: popl %ebp
-
0x6c02: ret
release下的反汇编结果:
-
0x5310: pushl %ebp
-
0x5311: movl %esp, %ebp
-
0x5313: pushl %ebx
-
0x5314: pushl %edi
-
0x5315: pushl %esi
-
0x5316: subl $44, %esp
-
0x5319: calll 0x531e ; -[ViewController test] + 14 at ViewController.mm:377
-
0x531e: popl %edi
-
0x531f: movl 46610(%edi), %eax
-
0x5325: movl 45650(%edi), %ecx
-
0x532b: movl %ecx, 4(%esp)
-
0x532f: movl %eax, (%esp)
-
0x5332: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x5337: movl $1092616192, -32(%ebp)
-
0x533e: movl $1101004800, -28(%ebp)
-
0x5345: movl $1106300109, -24(%ebp)
-
0x534c: movl $1109393408, -20(%ebp)
-
0x5353: movl 45662(%edi), %ecx
-
0x5359: movsd -32(%ebp), %xmm0
-
0x535e: movsd -24(%ebp), %xmm1
-
0x5363: movsd %xmm1, 16(%esp)
-
0x5369: movsd %xmm0, 8(%esp)
-
0x536f: movl %ecx, 4(%esp)
-
0x5373: movl %eax, (%esp)
-
0x5376: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x537b: movl %eax, %esi
-
0x537d: movl 45878(%edi), %eax
-
0x5383: leal 48262(%edi), %ecx
-
0x5389: movl %ecx, 8(%esp)
-
0x538d: movl %eax, 4(%esp)
-
0x5391: movl %esi, (%esp)
-
0x5394: movl $0, 12(%esp)
-
0x539c: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x53a1: movl 46578(%edi), %eax
-
0x53a7: movl 45778(%edi), %ecx
-
0x53ad: movl %ecx, 4(%esp)
-
0x53b1: movl %eax, (%esp)
-
0x53b4: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x53b9: movl 45890(%edi), %ecx
-
0x53bf: movl %eax, 8(%esp)
-
0x53c3: movl %ecx, 4(%esp)
-
0x53c7: movl %esi, (%esp)
-
0x53ca: movl $0, 12(%esp)
-
0x53d2: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x53d7: movl 45898(%edi), %eax
-
0x53dd: movl 45958(%edi), %ecx
-
0x53e3: movl %ecx, 12(%esp)
-
0x53e7: movl 8(%ebp), %ebx
-
0x53ea: movl %ebx, 8(%esp)
-
0x53ee: movl %eax, 4(%esp)
-
0x53f2: movl %esi, (%esp)
-
0x53f5: movl $64, 16(%esp)
-
0x53fd: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x5402: movl 45854(%edi), %eax
-
0x5408: movl %eax, 4(%esp)
-
0x540c: movl %ebx, (%esp)
-
0x540f: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x5414: movl 45858(%edi), %ecx
-
0x541a: movl %esi, 8(%esp)
-
0x541e: movl %ecx, 4(%esp)
-
0x5422: movl %eax, (%esp)
-
0x5425: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x542a: movl 45642(%edi), %eax
-
0x5430: movl %eax, 4(%esp)
-
0x5434: movl %esi, (%esp)
-
0x5437: calll 0xa78e ; symbol stub for: objc_msgSend
-
0x543c: addl $44, %esp
-
0x543f: popl %esi
-
0x5440: popl %edi
-
0x5441: popl %ebx
-
0x5442: popl %ebp
-
0x5443: ret
单从行数上看,debug是127行,release是72行,差距很大。这当然是编译器优化的结果,特别对于一些分支多的、带循环的源码时,反汇编和源码的实际动作执行顺序都可能不同。
可以看到:
debug版在0x6ab8调用CGRectMake之前做了好多多余动作,往返访问xmm寄存器。debug版的浮点数,像30.1这样的数值才被真正当做浮点,10.0和20.0f都被当整数了。而release版都是立即数。反汇编中会把float所占的4字节空间的值显示为十进制数,需要自行转换实际的值,转换方法参见http://blog.csdn.net/hursing/article/details/8688862。
CGRectMake的实质是个inline函数,在debug版还存在单独的函数代码入口位置,但release版就没有了。
函数的开头表示debug版要用108字节作为局部变量空间,而release版只需要44字节。
这样的区别还有很多,可通过反汇编自己的代码来观察到。当调试进入系统的代码时,可以看出都是release版的,即debug版链接的仍是release版的静态库。
掌握了基本技巧后,基本上已不难理解所有的反汇编结果。授之以鱼不如授之以渔:
通过观察自己写的代码的反汇编来掌握各种代码的反汇编结果,从而逆向推测系统代码的源码。
调试自己写的代码时,可以不断切换查看源码和反汇编来定位代码执行到何处
这里用分别用两个很简单的C++和Objective-C类来做示例:
-
class TestC {
-
int m_var;
-
-
public:
-
int getVar();
-
void setVar(int var);
-
};
-
-
@interface TestOC : NSObject
-
{
-
int m_var;
-
}
-
-
- (void)setVar:(int)var;
-
-
@end
-
-
int g_var = 0;
-
int *g_pVar = &g_var;
-
-
void TestC::setVar(int var)
-
{
-
int l_var = 0;
-
l_var = var;
-
m_var = var;
-
g_var = var;
-
*g_pVar = var;
-
}
-
-
@implementation TestOC
-
-
- (void)setVar:(int)var
-
{
-
int l_var = 0;
-
l_var = var;
-
m_var = var;
-
g_var = var;
-
*g_pVar = var;
-
}
-
-
@end
在外部,这样调用:
-
- (void)viewDidLoad
-
{
-
TestC test;
-
test.setVar(100);
-
TestOC *t = [[TestOC new] autorelease];
-
[t setVar:100];
-
.......
分别在两个类的setVar加断点,看反汇编结果。
-
WebViewResearch`TestC::setVar(int) at TestClass.mm:15:
-
0x8bda: pushl %ebp
-
0x8bdb: movl %esp, %ebp
-
0x8bdd: calll 0x8be2 ; TestC::setVar(int) + 8 at TestClass.mm:15
-
0x8be2: popl %eax
-
0x8be3: movl 12(%ebp), %ecx
-
0x8be6: movl 8(%ebp), %edx
-
0x8be9: movl %ecx, (%edx)
-
0x8beb: movl %ecx, 30406(%eax)
-
0x8bf1: movl 29170(%eax), %eax
-
0x8bf7: movl %ecx, (%eax)
-
0x8bf9: popl %ebp
-
0x8bfa: ret
-
WebViewResearch`-[TestOC setVar:] at TestClass.mm:25:
-
0x8bfb: pushl %ebp
-
0x8bfc: movl %esp, %ebp
-
0x8bfe: pushl %esi
-
0x8bff: calll 0x8c04 ; -[TestOC setVar:] + 9 at TestClass.mm:29
-
0x8c04: popl %eax
-
0x8c05: movl 29372(%eax), %edx
-
0x8c0b: movl 16(%ebp), %ecx
-
0x8c0e: movl 8(%ebp), %esi
-
0x8c11: movl %ecx, (%esi,%edx)
-
0x8c14: movl %ecx, 30372(%eax)
-
0x8c1a: movl 29136(%eax), %eax
-
0x8c20: movl %ecx, (%eax)
-
0x8c22: popl %esi
-
0x8c23: popl %ebp
-
0x8c24: ret
以上是release版的结果。可以看到,编译器做了优化,没有实际用处的局部变量l_var直接被省略到了,既不为它分配空间,连对它的赋值语句都没要。
对于操作成员变量、全局变量,没法直观地看出来,需要自己计算好各个偏移,才会明白那些带括号的间接寻址的操作。这些麻烦的事情可以借助IPA Pro来看(后面会有一系列来讲)。
下面是调用两个类的setVar函数的反汇编语句。
-
0x441e: leal -16(%ebp), %eax
-
0x4421: movl %eax, (%esp)
-
0x4424: movl $100, 4(%esp)
-
0x442c: calll 0x8bda ; TestC::setVar(int) at TestClass.mm:15
-
0x445b: movl 45819(%esi), %ecx
-
0x4461: movl %ecx, 4(%esp)
-
0x4465: movl %eax, (%esp)
-
0x4468: movl $100, 8(%esp)
-
0x4470: calll 0x97c0 ; symbol stub for: objc_msgSend
可以看到C++能更直接地看出下一步的去向,OC则需要知道是哪个类的对象以及Selector(可用register read来查看)。
对比类的函数以及被调用处的行数,也可以间接地表明,Objective-C的效率会比C++慢一点,但也差不了多少。
Apple也有一些帮助反汇编调试的文档:
http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
|