文章刚开始介绍一下来自知乎的链接,介绍了很多:
http://www.zhihu.com/question/20317296
1. 首先你的可执行文件可能包含多个库armv6,armv7等等,我们可以先把它们分开,方便分开研究
lipo -thin armv7 /Users/**/Desktop/yourexcute -output ty_heihei.armv7 这样就把armv7分离出来,方便下面的分析
2. 找到可执行文件中用到的API
nm -g /System/Library/Frameworks/AppKit.framework/AppKit
nm ty_heihei.armv7| grep demo 可以来查找ty_heihei.armv7 中关于demo的函数
对此对nm进行一下了解,到底是个什么东西?能干什么?
nm用于列出目标文件的符号清单
对于每一个符号,nm列出其值(the symbol value),类型(the symbol type)和其名字(the symbol name)。
符号 类型 | 说明 |
A | 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。 |
B | 该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中 |
C | 该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。 |
D | 该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。 |
G | 该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。 |
I | 该符号是对另一个符号的间接引用。 |
N | 该符号是一个debugging符号。 |
R | 该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。 |
S | 符号位于非初始化数据区,用于small object。 |
T | 该符号位于代码区text section。 |
U | 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。 |
V | 该符号是一个weak object。 |
W | The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. |
- | 该符号是a.out格式文件中的stabs symbol。 |
? | 该符号类型没有定义 |
3. 利用class-dump 可以将库中的头文件解出来。好像还有class-dump-x,class-dump-z.
class-dump-z 比较好用推荐 :可以从http://code.google.com/p/networkpx/wiki/class_dump_z下载。
class-dump-z ty_heihei.armv7
使用class-dump只能获取简单的数据,如果想要获取复杂的数据则需要nm 得到 Symbol Table的帮助。
4. 另外还有otool,otool可用于解析与OS X Mach-O二进制文件有关的信息,因此,可简单将其描述为:OS X系统下的类似于objdump的实用工具。下面的代码说明了如何使用otool显示一个Mach-O二进制文件的动态库依赖关系,从而执行类似于ldd的功能。otool可用于显示与文件的头部和符号表有关的信息,并对文件的代码部分进行反汇编。
otool -L ty_heihei.armv7 获取到链接的动态库
以下是otool的一些理解,转自:http://hi.baidu.com/onejw/item/c47740420ad3e32011ee1eee
OS X 不使用 ELF二进制文件,在OS X获得可执行文件所依赖的共享库列表,需要使用 otool 工具. OS X 支持三种运行时环境:
dyld 运行时环境:基于 dyld库管理器的推荐环境.
CFM 运行时环境: OS 9遗留环境. 实际用来设计需要使用 OS X新特色, 但还没完全移植到dyld的应用程序.
The Classic环境: OS 9 (9.1 or 9.2) 程序无需修改直接在OS X运行.
本文主要关注于Dyld 运行时环境.
Mach-O 可执行文件格式在 OS X, 几乎所有的包含可执行代码的文件,如:应用程序、框架、库、内核扩展……, 都是以Mach-O文件实现. Mach-O 是一种文件格式,也是一种描述可执行文件如何被内核加载并运行的ABI (应用程序二进制接口). 专业一点讲, 它告诉系统:
使用哪个动态库加载器
加载哪个共享库.
如何组织进程地址空间.
函数入口点地址,等.
Mach-O 不是新事物. 最初由开放软件基金会 (OSF) 用于设计基于 Mach 微内核OSF/1 操作系统. 后来移植到 x86 系统OpenStep.
为了支持Dyld 运行时环境, 所有文件应该编译成Mach-O 可执行文件格式.
Mach-O 文件的组织
Mach-O 文件分为三个区域: 头部、载入命令区Section和原始段数据. 头部和载入命令区描述文件功能、布局和其他特性;原始段数据包含由载入命令引用的字节序列。为了研究和检查 Mach-O 文件的各部分, OS X 自带了一个很有用的程序otool,其位于/usr/bin目录下.
接下来, 将使用 otool来了解 Mach-O 文件如何组织的.
头部查看文件的 Mach-O头部, 使用otool 命令的 -h参数
evil:~ mohit$ otool -h /bin/ls
/bin/ls:
Mach header
magic cputype cpusubtype filetype ncmds sizeofcmds flags
0xfeedface 18 0 2 11 1608 0x00000085
头部首先指定的是魔数(magic number). 魔数标明文件是32位还是64位的Mach-O 文件. 也标明 CPU字节顺序. 魔数的解释,参看/usr/include/mach-o/loader.h.
头部也指定文件的目标架构. 这样就允许内核确保该代码不会在不是为此处理器编写的CPU上运行。例如, 在上面的输出, cputype 设成18, 它代表CPU_TYPE_POWERPC, 在 /usr/include/mach/machine.h中定义.
从上两项信息,我们推断出此二进制文件用于32-位基于PowerPC 的系统.
有时二进制文件可能包含不止一个体系的代码。通常称为Universal Binaries, 通常以 fat_header这额外的头部开始。检查 fat_header内容, 使用otool命令的 -f开关参数.
cpusubtype 属性制定了CPU确切模型, 通常设成CPU_SUBTYPE_POWERPC_ALL 或 CPU_SUBTYPE_I386_ALL.
filetype 指出文件如何对齐如何使用。实际上它告诉你文件是库、静态可执行文件、core file等。上面的 filetype等于MH_EXECUTE, 指出demand paged executable file. 下面是从/usr/include/mach-o/loader.h截取的片段,列出了不同的文件类型。
#define MH_OBJECT 0x1 /* relocatable object file */
#define MH_EXECUTE 0x2 /* demand paged executable file */
#define MH_FVMLIB 0x3 /* fixed VM shared library file */
#define MH_CORE 0x4 /* core file */
#define MH_PRELOAD 0x5 /* preloaded executable file */
#define MH_DYLIB 0x6 /* dynamically bound shared library */
#define MH_DYLINKER 0x7 /* dynamic link editor */
#define MH_BUNDLE 0x8 /* dynamically bound bundle file */
#define MH_DYLIB_STUB 0x9 /* shared library stub for static */
/* linking only, no section contents */
接下来的两个属性涉及到载入命令区段, 指定了命令的数目和大小.
最后, 获得了状态信息, 这些可能在装载和执行时被内核使用。
载入命令载入命令区段包含一个告知内核如何载入文件中的各个原始段的命令列表。典型的描述如何对齐,保护每个段及各段在内存中的布局.
查看文件中的载入命令列表, 使用otool 命令的 -l开关参数.
evil:~/Temp mohit$ otool -l /bin/ls
/bin/ls:
Load command 0
cmd LC_SEGMENT
cmdsize 56
segname __PAGEZERO
vmaddr 0x00000000
vmsize 0x00001000
fileoff 0
filesize 0
maxprot 0x00000000
initprot 0x00000000
nsects 0
flags 0x4
Load command 1
cmd LC_SEGMENT
cmdsize 600
segname __TEXT
vmaddr 0x00001000
vmsize 0x00006000
fileoff 0
filesize 24576
maxprot 0x00000007
initprot 0x00000005
nsects 8
flags 0x0
Section
sectname __text
segname __TEXT
addr 0x00001ac4
size 0x000046e8
offset 2756
align 2^2 (4)
reloff 0
nreloc 0
flags 0x80000400
reserved1 0
reserved2 0
[ ___SNIPPED FOR BREVITY___ ]
Load command 4
cmd LC_LOAD_DYLINKER
cmdsize 28
name /usr/lib/dyld (offset 12)
Load command 5
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libncurses.5.4.dylib (offset 24)
time stamp 1111407638 Mon Mar 21 07:20:38 2005
current version 5.4.0
compatibility version 5.4.0
Load command 6
cmd LC_LOAD_DYLIB
cmdsize 52
name /usr/lib/libSystem.B.dylib (offset 24)
time stamp 1111407267 Mon Mar 21 07:14:27 2005
current version 88.0.0
compatibility version 1.0.0
Load command 7
cmd LC_SYMTAB
cmdsize 24
symoff 28672
nsyms 101
stroff 31020
strsize 1440
Load command 8
cmd LC_DYSYMTAB
cmdsize 80
ilocalsym 0
nlocalsym 0
iextdefsym 0
nextdefsym 18
iundefsym 18
nundefsym 83
tocoff 0
ntoc 0
modtaboff 0
nmodtab 0
extrefsymoff 0
nextrefsyms 0
indirectsymoff 30216
nindirectsyms 201
extreloff 0
nextrel 0
locreloff 0
nlocrel 0
Load command 9
cmd LC_TWOLEVEL_HINTS
cmdsize 16
offset 29884
nhints 83
Load command 10
cmd LC_UNIXTHREAD
cmdsize 176 flavor PPC_THREAD_STATE
count PPC_THREAD_STATE_COUNT
r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000
r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000
r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x00000000 r14 0x00000000
r15 0x00000000 r16 0x00000000 r17 0x00000000 r18 0x00000000 r19 0x00000000
r20 0x00000000 r21 0x00000000 r22 0x00000000 r23 0x00000000 r24 0x00000000
r25 0x00000000 r26 0x00000000 r27 0x00000000 r28 0x00000000 r29 0x00000000
r30 0x00000000 r31 0x00000000 cr 0x00000000 xer 0x00000000 lr 0x00000000
ctr 0x00000000 mq 0x00000000 vrsave 0x00000000 srr0 0x00001ac4 srr1 0x00000000
上面的文件在头部下有11 加载命令直接定位, 从 0 到 10.
前四个命令(LC_SEGMENT), 从 0 到 3, 定义了文件中的段如何映射到内存中去。段定义了Mach-O binary 二进制文件中的字节序列, 可以包含零个或更多的 sections. 稍候我们谈谈段。
Load command 4 (LC_LOAD_DYLINKER) 指定使用哪个动态链接器. 几乎总是设成OS X默认动态链接器 /usr/lib/dyld。
Commands 5 and 6 (LC_LOAD_DYLIB) 指定文件需要链接的共享库。它们由command 4规定的动态链接器载入。
Commands 7 and 8 (LC_SYMTAB, LC_DYNSYMTAB) 指定由文件和动态链接器分别使用的符号表. Command 9 (LC_TWOLEVEL_HINTS) 包含两级名称空间的hint table。最后, command 10 (LC_UNIXTHREAD), 定义进程主线程的初始状态. 该命令仅仅包含在可执行文件里。
Segments and Sections
上面涉及到的大多数加载命令都引用了文件中的段. 段是Mach-O文件直接被内核和动态链接器映射到虚拟内存中的一系列字符序列. 头部和加载命令区域认为是文件的首段。一个典型的 OS X 可执行文件通常由下列五段::
__PAGEZERO : 定位于虚拟地址0,无任何保护权利。此段在文件中不占用空间,访问NULL导致立即崩溃.
__TEXT : 包含只读数据和可执行代码.
__DATA : 包含可写数据. 这些 section通常由内核标志为copy-on-write .
__OBJC : 包含Objective C 语言运行时环境使用的数据。
__LINKEDIT :包含动态链接器用的原始数据.
__TEXT和 __DATA段可能包含0或更多的section. 每个section由指定类型的数据, 如, 可执行代码, 常量, C 字符串等组成.
查看某section内容, 使用otool命令 -s选项.
evil:~/Temp mohit$ otool -sv __TEXT __cstring /bin/ls
/bin/ls:
Contents of (__TEXT,__cstring) section
00006320 00000000 5f5f6479 6c645f6d 6f645f74
00006330 65726d5f 66756e63 73000000 5f5f6479
00006340 6c645f6d 616b655f 64656c61 7965645f
00006350 6d6f6475 6c655f69 6e697469 616c697a
__SNIP__
反汇编__text section, 使用 the -tv 开关参数.
evil:~/Temp mohit$ otool -tv /bin/ls
/bin/ls:
(__TEXT,__text) section
00001ac4 or r26,r1,r1
00001ac8 addi r1,r1,0xfffc
00001acc rlwinm r1,r1,0,0,26
00001ad0 li r0,0x0
00001ad4 stw r0,0x0(r1)
00001ad8 stwu r1,0xffc0(r1)
00001adc lwz r3,0x0(r26)
00001ae0 addi r4,r26,0x4
__SNIP__
在 __TEXT段里, 存在四个主要的 section:
__text : 编译后的机器码。
__const : 通用常量数据.
__cstring : 字面量字符串常量.
__picsymbol_stub : 动态链接器使用的位置无关码stub 路由.
这样保持了可执行的和不可执行的代码在段里的明显隔离.
运行应用程序既然知道了Mach-O 文件的格式, 接下来看看OS X 如何载入并运行应用程序的。运行应用程序时, shell首先调用fork()系统调用. fork 创建调用进程(shell) 逻辑拷贝并准备好执行. 子进程然后调用execve()系统调用,当然需要提供要执行的程序路径.
内核载入指定的文件, 检查其头部验证是否是合法的Mach-O 文件. 然后开始解释载入命令,将子进程地址空间替换成文件中的各段。同时,内核也执行有二进制文件指定的动态链接器, 着手加载、链接所有依赖库。在绑定了运行所必备的各个符号后,调用entry-point 函数.
在build应用程序时entry-point 函数通常从/usr/lib/crt1.o静态链接(标准函数). 此函数初始化内核环境,调用可执行文件的main()函数.
应用程序现在运行了.
动态链接器
OS X 动态链接器/usr/lib/dyld, 负责加载依赖的共享库, 导入变量符号和函数,与当前进程的绑定。进程首次运行时, 链接器所做的就是把共享库导入到进程地址空间。取决于程序的build方式, 实际绑定也足执行不同的方式。
载入后立即绑定—— load-time绑定.
当符号引用时—— just-in-time绑定.
预绑定
如未指定绑定类型, 使用 just-in-time绑定.
应用程序仅仅当所有需要的符号和段从不同的目标文件解决是才能继续运行。为了寻找库和框架, 标准动态链接器/usr/bin/dyld, 将搜索预定义的目录集合. 要修改目录, 或提供回滚路径, 可以设置DYLD_LIBRARY_PATH或DYLD_FALLBACK_LIBRARY_PATH环境变量
5. Objective-C会存储一部分的类型名和类型、类名参考等等信息到文件中。这些信息可以通过strings获取到:
strings ty_heihei.armv7
6.