Mach-O Inside: 命令行工具集 otool objdump od 与 dwarfdump
1 otool
otool 命令行工具用来查看 Mach-O 文件的结构。
1.1 查看文件头
otool -h -v 文件路径
-h选项表明查看 Mach-O 文件头。
-v 选项表明将展示的内容进行"符号化"处理。
上面命令行输出的一个例子如下:
magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64    ARM64        ALL  0x00     EXECUTE    23       3752   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
从输出结果可以看出,完全符合 XNU 内核头文件loader.h中定义的struct mach_header_64的结构:
struct mach_header_64 {
	uint32_t	magic;		/* mach magic number identifier */
	cpu_type_t	cputype;	/* cpu specifier */
	cpu_subtype_t	cpusubtype;	/* machine specifier */
	uint32_t	filetype;	/* type of file */
	uint32_t	ncmds;		/* number of load commands */
	uint32_t	sizeofcmds;	/* the size of all the load commands */
	uint32_t	flags;		/* flags */
	uint32_t	reserved;	/* reserved */
};
如果命令行没有加 -v选项,输出的结果会是这样:
 magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    23       3752 0x00218085
可以看到,使用-v 选项输出结果会更让人易读。
如果一个 Mach-O 文件是 Universal 类型的,也就是包含多重架构(一般 .o 目标文件或者静态库会是 Universal 的,包含 ARM 和 X86_64 两种架构),使用-h选项只会输出其中一种架构的头信息。
想要将所有架构头信息都输出,需要使用-f选项:
otool -f -v 文件路径
上面命令行输出的一个例子是:
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
architecture x86_64
    cputype CPU_TYPE_X86_64
    cpusubtype CPU_SUBTYPE_X86_64_ALL
    capabilities 0x0
    offset 48
    size 36752
    align 2^3 (8)
architecture arm64
    cputype CPU_TYPE_ARM64
    cpusubtype CPU_SUBTYPE_ARM64_ALL
    capabilities 0x0
    offset 36800
    size 37384
    align 2^3 (8)
1.2 查看汇编代码
otool -t -v 文件路径
-t选项会展示 (__TEXT, __text) Section 的内容。
-v选项会反汇编展示的内容,展示汇编代码,前提是待展示的内容是代码指令。
上面命令行输出的一个例子是:
(__TEXT,__text) section
0000000100004000        sub     sp, sp, #0x30
0000000100004004        stp     x20, x19, [sp, #0x10]
0000000100004008        stp     x29, x30, [sp, #0x20]
000000010000400c        add     x29, sp, #0x20
0000000100004010        adrp    x8, 12 ; 0x100010000
0000000100004014        ldr     x9, [x8]
0000000100004018        add     x9, x9, #0x1
000000010000401c        str     x9, [x8]
0000000100004020        add     x19, x3, x2
0000000100004024        str     x19, [sp]
0000000100004028        adrp    x0, 8 ; 0x10000c000
000000010000402c        add     x0, x0, #0x1f0 ; Objc cfstring ref: @"bad cfstring ref"
0000000100004030        bl      0x100007c68 ; symbol stub for: _NSLog
0000000100004034        mov     x0, x19
0000000100004038        ldp     x29, x30, [sp, #0x20]
000000010000403c        ldp     x20, x19, [sp, #0x10]
0000000100004040        add     sp, sp, #0x30
0000000100004044        ret
上面汇编代码最左边的列是指令的地址。
上面命令行会将 (__TEXT, __text) Section 全部反汇编,如果内容较长不容易查看,因此最好结合more命令:
otool -t -v 文件路径 | more
如果想展示汇编代码的同时,展示汇编指令的编码以及指令的偏移量,可以结合-j与-function_offsets选项:
otool -t -v -j -function_offsets 文件路径
-j选项展示汇编指令的编码。
-function_offsets选项展示指令的偏移量。
上面命令行输出的一个例子是:
(__TEXT,__text) section
    +0 0000000100004000 d100c3ff        sub     sp, sp, #0x30
    +4 0000000100004004 a9014ff4        stp     x20, x19, [sp, #0x10]
    +8 0000000100004008 a9027bfd        stp     x29, x30, [sp, #0x20]
   +12 000000010000400c 910083fd        add     x29, sp, #0x20
   +16 0000000100004010 90000068        adrp    x8, 12 ; 0x100010000
   +20 0000000100004014 f9400109        ldr     x9, [x8]
   +24 0000000100004018 91000529        add     x9, x9, #0x1
   +28 000000010000401c f9000109        str     x9, [x8]
   +32 0000000100004020 8b020073        add     x19, x3, x2
   +36 0000000100004024 f90003f3        str     x19, [sp]
   +40 0000000100004028 90000040        adrp    x0, 8 ; 0x10000c000
   +44 000000010000402c 9107c000        add     x0, x0, #0x1f0 ; Objc cfstring ref: @"bad cfstring ref"
   +48 0000000100004030 94000f0e        bl      0x100007c68 ; symbol stub for: _NSLog
   +52 0000000100004034 aa1303e0        mov     x0, x19
   +56 0000000100004038 a9427bfd        ldp     x29, x30, [sp, #0x20]
   +60 000000010000403c a9414ff4        ldp     x20, x19, [sp, #0x10]
   +64 0000000100004040 9100c3ff        add     sp, sp, #0x30
   +68 0000000100004044 d65f03c0        ret
上面输出从左起第 1 列是汇编指令偏移量,第 2 列是指令地址,第 3 列是指令的编码。
1.3 查看不同架构的汇编代码
对于 Universal 类型的 Mach-O 文件,可以使用-arch选项查看指定架构的汇编代码,命令如下:
otool -t -v -arch arm64 文件路径
otool -t -v -arch x86_x64 文件路径
-arch选项指定架构,arm64 表示 64bit 的 ARM CPU 架构,x86_64 表示 64bit 的 Intel x86_64 CPU 架构。
-arch选项的值除了可以是 arm64 和 x86_64,其他的值可以通过下面命令行的输出查看:
man 3 arch
上面命令行输出的一部分结果如下所示:
The currently known architectures are:
     Name          CPU Type              CPU Subtype                 Description
     x86_64        CPU_TYPE_X86_64       CPU_SUBTYPE_X86_64_ALL      Intel x86-64
     i386          CPU_TYPE_I386         CPU_SUBTYPE_I386_ALL        Intel 80x86
     arm           CPU_TYPE_ARM          CPU_SUBTYPE_ARM_ALL         ARM
     arm64         CPU_TYPE_ARM64        CPU_SUBTYPE_ARM64_ALL       ARM64
     arm64e        CPU_TYPE_ARM64        CPU_SUBTYPE_ARM64E          ARM64E
     arm64_32      CPU_TYPE_ARM64_32     CPU_SUBTYPE_ARM64_32_V8     ARM64_32
     ppc           CPU_TYPE_POWERPC      CPU_SUBTYPE_POWERPC_ALL     PowerPC
1.4 按符号查看汇编代码
如果 Mach-O 文件是 DEBUG 类型,或者虽然是 Relese 类型但是没有设置 Xcode 的如下 Build Setting:

那么就可以使用-p选项使用符号查看汇编代码。
otool -t -v -p "-[X addi:d:]" 文件路径
上面命令行会从符号-[X addi:d:]开始进行反汇编,一直展示到 (__TEXT, __text) Section 末尾,输出内容如下:
(__TEXT,__text) section
-[X addi:d:]:
0000000100004134        sub     sp, sp, #0x30
0000000100004138        str     x0, [sp, #0x28]
000000010000413c        str     x1, [sp, #0x20]
0000000100004140        str     x2, [sp, #0x18]
0000000100004144        str     d0, [sp, #0x10]
0000000100004148        adrp    x9, 12 ; 0x100010000
000000010000414c        ldr     x8, [x9, #0x18]
0000000100004150        add     x8, x8, #0x1
0000000100004154        str     x8, [x9, #0x18]
0000000100004158        ldr     d0, [sp, #0x18]
000000010000415c        scvtf   d0, d0
0000000100004160        ldr     d1, [sp, #0x10]
0000000100004164        fadd    d0, d0, d1
0000000100004168        str     d0, [sp, #0x8]
000000010000416c        ldr     d0, [sp, #0x8]
0000000100004170        add     sp, sp, #0x30
0000000100004174        ret
1.5 查看指定 Section
otool 可以通过-s选项查看指定的 Section,-s选项的值形式为-s segmentName sectionName:
otool -v -s __TEXT __text 文件路径
上面命令行查看 (__TEXT, __text) Section 的内容
otool -s __DATA __data 文件路径
上面命令行查看 (__DATA, __data) Section 的内容。
1.6 查看间接符号表
间接符号表是程序加载是动态连接器 dyld 会使用到符号表,动态链接器使用这个符号表来完成符号的绑定。otool 可以使用-I选项来查看间接符号表:
otool -I -v 文件路径
-I选项表明查看间接符号表。
-v选项会让展示的结果更详细,更易读。
上面命令行输出的一个例子是:
Indirect symbols for (__TEXT,__stubs) 50 entries
address            index name
0x0000000100007c68     4 _NSLog
0x0000000100007c74     5 _NSStringFromClass
0x0000000100007c80    13 _UIApplicationMain
0x0000000100007c8c    15 ___error
0x0000000100007c98    16 ___stack_chk_fail
0x0000000100007ca4    20 _atexit
0x0000000100007cb0    21 _atoi
上面输出从左起address代表符号的地址,index代表该符号在符号表里的索引(符号表里有所有符号),name是符号名。
1.7 查看 Load Commands
otool 可以使用-l选项查看 Mach-O 中的 Load Commands:
otool -l 文件路径
1.8 查看依赖的动态库
otool 可以使用-L选项查看当前 Mach-O 依赖的动态库:
otool -L 文件路径
一个输出例子如下:
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 2048.1.101)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0)
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 2048.1.101)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 7082.1.111)
更多 otool 功能可以参考man otool的输出内容。
2 objdump
由于历史原因,苹果使用曾经使用 objdump 展示 Mach-O 信息,因此 objdump 与 otool 许多功能类似。
2.1 查看文件头
objdump -m --private-header 文件路径
-m选项表明是查看 Mach-O 文件。
--private-header表明查看 Mach-O 文件头。
这个命令类似 otool 的otool -h -v。
如果想要查看 Univeral 类型 Mach-O 的文件头,可以使用:
objdump -m --universal-headers 文件路径
-m选项表明是查看 Mach-O 文件。
--universal-headers表明显示 Mach-O 文件中所有架构的文件头。
这个命令类似 otool 的otool -f -v。
2.2 查看汇编代码
objdump 支持按照指定的地址范围显示汇编代码,这个功能在实际中很有用处:
objdump -d --start-address=起始地址 --stop-address=结束地址 文件路径
-d选项表明要执行反汇编。
--start-address选项是反汇编的起始地址。
--stop-address选项是反汇编的结束地址,需要注意的是,反汇编内容不包含 --stop-address 指定的地址,也就是说反汇编的范围是[--start-address, --stop-address)。
上面命令行输出的一个例子如下:
Disassembly of section __TEXT,__text:
0000000100004000 <__text>:
100004000: d100c3ff    	sub	sp, sp, #48
100004004: a9014ff4    	stp	x20, x19, [sp, #16]
100004008: a9027bfd    	stp	x29, x30, [sp, #32]
10000400c: 910083fd    	add	x29, sp, #32
100004010: 90000068    	adrp	x8, 0x100010000 <__text+0x40>
100004014: f9400109    	ldr	x9, [x8]
100004018: 91000529    	add	x9, x9, #1
10000401c: f9000109    	str	x9, [x8]
上面输出从左起第 1 列是汇编指令的地址,第 2 类是指令的编码。和 otool 不同,objdump 默认就显示汇编指令的编码。如果不想显示汇编指令编码,加入--no-show-raw-insn选项即可。
objdump 不支持展示汇编代码时,显示偏移量。
如果反汇编时只有-d选项,那么 objdump 会反汇编所有可执行的代码。
2.3 查看不同结构的汇编代码
objdump 可以使用--arch选项,选择不同的架构进行反汇编:
objdump -d --arch=arm64 文件路径
objdump -d --arch=x86_64 文件路径
--arch选项指明 CPU 架构,架构名和 otool 一样,可以使用man 3 arch查看。
2.4 按符号查看汇编代码
和 otool 类似,objdump 支持按照符号进行反汇编:
objdump --disassemble-symbols="符号1","符号2"... 文件路径
--disassemble-symbols选项可以有多个符号,符号之间使用逗号间隔。
2.5 反汇编时加入源码行号信息
如果有 DSYM 文件,那么使用 objdump 进行反汇编时,可以显示源码行号信息:
objdump -l --dsym=DSYM文件路径 文件路径
-l选项表明反汇编时展示源码行号信息。
--dsym选项指定 DSYM 文件路径。需要注意的是,需要在 DSYM 文件上右键'Show Package Contents',然后依次进入 Contents->Resources->DWARF 目录,然后给目录里面的文件添加.app后缀,否则会报如下错误:

上面命令行输出的一个例子如下图所示:

2.6 查看指定的 Section
objdump 通过-j选项查看指定的 Section:
objdump -m -j segmentName,sectionName 文件路径
-m选项表明查看 Mach-O 文件。
-j选项指定要查看的 Section,比如 __TEXT,__text __DATA,__data。
这个命令类似 otool 的otool -s。
2.7 查看间接符号表
objdump 可以查看间接符号表:
objdump -m --indirect-symbols 文件路径
-m选项表明查看 Mach-O 文件
--indirect-symbols选项表明查看间接符号表。
这个命令类似 otool 的otool -Iv,输出的格式也类似:
Indirect symbols for (__TEXT,__stubs) 50 entries
address            index name
0x0000000100007c68     4 _NSLog
0x0000000100007c74     5 _NSStringFromClass
0x0000000100007c80    13 _UIApplicationMain
0x0000000100007c8c    15 ___error
0x0000000100007c98    16 ___stack_chk_fail
0x0000000100007ca4    20 _atexit
2.8 查看符号表
objdump 可以直接查看整个符号表:
objdump -t 文件路径
其输出格式如下:
SYMBOL TABLE:
0000000005614542      d  *UND* .hidden radr://5614542
000000010000d878  w    O __DATA,__data ___llvm_profile_filename
000000010000d880  w    O __DATA,__data ___llvm_profile_raw_version
0000000100000000 g     F __TEXT,__text __mh_execute_header
0000000000000000         *UND* _NSLog
0000000000000000         *UND* _NSStringFromClass
0000000000000000         *UND* _OBJC_CLASS_$_NSObject
0000000000000000         *UND* _OBJC_CLASS_$_UIResponder
0000000000000000  w      *UND* _OBJC_CLASS_$_UISceneConfiguration
0000000000000000         *UND* _OBJC_CLASS_$_UIViewController
0000000000000000         *UND* _OBJC_METACLASS_$_NSObject
0000000000000000         *UND* _OBJC_METACLASS_$_UIResponder
0000000000000000         *UND* _OBJC_METACLASS_$_UIViewController
0000000000000000         *UND* _UIApplicationMain
0000000000000000         *UND* ___CFConstantStringClassReference
0000000000000000         *UND* ___error
0000000000000000         *UND* ___stack_chk_fail
0000000000000000         *UND* ___stack_chk_guard
0000000000000000         *UND* ___stderrp
0000000000000000         *UND* __objc_empty_cache
0000000000000000         *UND* _atexit
0000000000000000         *UND* _atoi
从输出上可以看出来,按照间接符号表的index列可以对应到符号表里。比如 __NSLog在间接符号表的index 为 5,符号表的第 5 项(符号表索引以 0 开始),正好是__NSLog。
2.9 查看 Load Commands
objdump 可以查看 Load Commands:
objdump -m -x 文件路径
-m表明要查看 Mach-O 文件。
-x表明展示 Load Commands。
这个命令类似 otool 的otool -l。
2.10 查看依赖的动态库
objdump 可以查看依赖的动态:
objdump -m --dylibs-used 文件路径
-m选项表明要查看 Mach-O 文件。
--dylibs-used选项表明查看依赖的动态库。
这个命令类似 otool 的otool -L。
2.11 Demangle Symbol
源代码中的符号在编译之后,会被编译器混淆:

objdump 支持将混淆的符号还原:
objdump -C 文件路径
-C选项表明要还原混淆的符号。
上图中混淆的符号还原之后:

3 od
od 命令可以用来查看二进制的内容,可以用它来查看 Mach-O 文件的原始字节。
3.1 指定展示格式
od -A x -t x 文件路径
-A选项表明地址是用 10(d) 进制、8(o) 进制、16(x) 进制展示,默认使用 8 进制展示。
-t选项表明用哪种进制展示内容,可以是 10(d) 进制、8(o) 进制、16(x) 进制,默认是 8 进制。
上面命令行输出的一个例子是:

上图从左边起第 1 列就是地址或者说字节的偏移量更准确,其他列都是 Mach-O 文件的内容,可以看到第 2 列第 1 行 feedfacf 正式 Mach-O 里面的魔数。
选项-t 值后面还可以接一个 10 进制数,表示按多少个字节一组展示内容,比如-t x1就表示一个字节一个字节的展示文件内容:
od -A x -t x1 文件路径
上面命令行输出的例子如下:

从上图红框可以看到,Mach-O 文件是小端在前(Little-Endian)字节序。
3.2 跳过部分字节
od 支持跳过指定的字节数然后展示:
od -A x -t x -j 0x4000 文件路径
-j选项表明跳过多少字节,上面例子中要跳过 0x4000 个字节。
4 dwarfdump
dwarfdump 用来查看 DWARF 文件内容。
4.1 查看 UUID
dwarfdump 可以查看任何一个 Mach-O 文件的 UUID,这在分析崩溃堆栈时十分有用:
dwarfdump --uuid 文件路径
4.2 查找地址对应的符号信息
dwarfdump 可以查找地址对应的符号信息:
dwarfdump --lookup=地址 文件路径
--lookup选项后面是要查找的符号地址。
上面命令行一个输出的例子为:

当在分析崩溃信息时,可以使用这个方法找到地址对应的函数名。
4.3 查找符号名对应的信息
dwarfdump 可以查找符号名对应的详细信息:
dwarfdump -f 符号名 文件路径
-f选项后面是要查询的符号名,比如:
dwarfdump -f "-[X add:j:]" 文件路径
输出的结果如下:

当在分析崩溃信息时,可以用这个方法反查崩溃函数的地址,然后使用 objdump 工具查看崩溃地址的具体汇编代码。
Mach-O Inside: 命令行工具集 otool objdump od 与 dwarfdump的更多相关文章
- 从零开始打造个人专属命令行工具集——yargs完全指南
		
前言 使用命令行程序对程序员来说很常见,就算是前端工程师或者开发gui的,也需要使用命令行来编译程序或者打包程序 熟练使用命令行工具能极大的提高开发效率,linux自带的命令行工具都非常的有用,但是这 ...
 - 基于node和npm的命令行工具——tive-cli
		
前端开发过程中经常会用到各种各样的脚手架工具.npm全局工具包等命令行工具,如:Vue脚手架@vue/cli.React脚手架create-react-app.node进程守卫工具pm2.本地静态服务 ...
 - 命令行工具解析Crash文件,dSYM文件进行符号化
		
备份 文/爱掏蜂窝的熊(简书作者)原文链接:http://www.jianshu.com/p/0b6f5148dab8著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 序 在日常开发 ...
 - 转:windows下命令行工具
		
转自: http://www.cnblogs.com/haochuang/p/5593411.html Windows下CMD不好用,远没有Linux,或者一些SSH工具用起来方便.其实Windows ...
 - JVM性能监控与故障处理命令行工具
		
JDK命令行工具 Sun公司作为”礼物“赠送给JDK使用者的工具: 这些命令行工具大多是jdk/lib/tools.jar类库的一层薄包装,主要功能代码是在tools类库(不属于java的标准API) ...
 - 10款Windows命令行工具
		
Windows下CMD不好用,远没有Linux,或者一些SSH工具用起来方便.其实Windows下,也有一些不错的工具替代CMD: 0.powercmd经过比较,我最终选择了这款,这里补充一下截图:
 - 微软开放技术发布针对 Mac 和 Linux 的更新版 Azure Node.JS SDK 和命令行工具
		
发布于 2013-12-04 作者 Eduard Koller 这次为我们使用Linux 的朋友带来了更多关于部署云上虚拟机的消息.今天,微软开放技术有限公司 (MS Open Tech),想与大家分 ...
 - 显示器 Linux 性能 18 (一个命令行工具传递)
		
对于系统和网络管理员来说每天监控和调试Linux系统的性能问题是一项繁重的工作.在IT领域作为一名Linux系统的管理员工作5年后,我逐渐认识到监控和保持系统启动并执行是多么的不easy.基于此原因. ...
 - 探索Windows命令行系列(2):命令行工具入门
		
1.理论基础 1.1.命令行的前世今生 1.2.命令执行规则 1.3.使用命令历史 2.使用入门 2.1.启动和关闭命令行 2.2.执行简单的命令 2.3.命令行执行程序使用技巧 3.总结 1.理论基 ...
 - Windows 编程,程序编译使用的命令行工具。
		
Windows 编程,程序编译使用的命令行工具. 1.cl.exe文件是Visual C\C++的编译器,它将程序源代码文件编译为obj文件. 2.rc.exe文件是资源编译器.工程项目中的.rc文件 ...
 
随机推荐
- Git无法push提示报错443
			
更新 设置代理不能完美解决问题,需要检查本地的SSH配置是否正确,以及正确配置SSH密钥关联github账户 配置SSH参考链接: <GitHub如何配置SSH Key> https:// ...
 - The server time zone value '?泄???????' is unrecognized or represents more t
			
hibernate配置文件如下 运行在服务器上,报错如下 解决方案: 在jdbc连接的url后面加上serverTimezone=GMT即可解决问题,因为是数据库和系统时区差异所造成的, 即 < ...
 - 无需学习Python,一个公式搞定领导想看的大屏
			
摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 不要让"做不了"成为数字化转型的障碍 随着 ...
 - vue基本操作[2] 续更----让世界感知你的存在
			
Vue文件解析 什么是<template/>标签 template是html5新元素,主要用于保存客户端中的内容,表现为浏览器解析该内容但不渲染出来,可以将一个模板视为正在被存储以供随后在 ...
 - ChatGPT变笨了,好在还有自知之明
			
大家好,我是老章 好久没有写文章了,顺便向大家汇报一下最近在忙的事情(多数无疾而终): 1 开发了一个IMG2Latex工具(截图一个公式,自动把latex代码塞进剪贴板) 2 开发了一个播客转文字稿 ...
 - 【持续更新】C++ 并不完全是 C 的超集!
			
一些容易被忽略的 C 与 C++ 的不兼容特性 头文件和命名空间 C 标准库头文件名在 C++ 中通常去除扩展名,并加上 c 前缀,如: stdio.h -> cstdio stdlib.h - ...
 - echarts-for-react:实时更新数据
			
解决方案 echarts 注解 详细链接 https://echarts.apache.org/zh/api.html#echartsInstance.setOption 参考链接 https://b ...
 - Pycharm:显示每一行代码的修改记录
			
解决方案 安装插件GitToolBox
 - Cesium Billboard加载Gif图片
			
https://blog.csdn.net/xietao20/article/details/109404491
 - selenium报错:This version of ChromeDriver only supports Chrome version 109 Current browser version is 112.0.5615.49...解决办法
			
前言:跟GPT交互,让其写一段代码,执行失败.经过排查验证,GPT写的代码没有问题,是本地环境问题. 执行报错: selenium.common.exceptions.SessionNotCreate ...