objdump&otool&nm查看Mach O - ShenYj/ShenYj.github.io GitHub Wiki

分析Mach-O文件

简介: Mach-O

工具

  • objdump

    objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具

  • otool

    用来查看可执行文件的mach-o信息

  • nm

    查看符号表的工具


一、查询mach-o文件中指定的的main函数入口

  • 使用objdump

    ❯ objdump -p ${MACH_PATH} | ag 'LC_MAIN' -A 3

    输出结果:

        cmd LC_MAIN
    cmdsize 24
    entryoff 8384
    stacksize 0

    使用的插件:


二、只看 Mach header

  • objdump既可以查看Mach-O文件, 也可以查看LinuxELF文件

    objdump --macho -private-header  ${MACH_PATH}

    输出结果:

    Mach header
        magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
    MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    27       3160   NOUNDEFS DYLDLINK TWOLEVEL PIE
  • 使用otool -h也是同样的效果

三、查看Mach-O文件的机器指令(__TEXT)

  • 使用工具objdump

    ❯ objdump --macho -d ${MACH_PATH}

四、otool查看Mach-O文件

  • 展示的数据比较原始

    otool -l ${MACH_PATH}
    (__TEXT,__text) section
    -[ViewController viewDidLoad]:
    100001e70:	55	pushq	%rbp
    100001e71:	48 89 e5	movq	%rsp, %rbp
    100001e74:	48 83 ec 20	subq	$32, %rsp
    100001e78:	48 89 7d f8	movq	%rdi, -8(%rbp)
    100001e7c:	48 89 75 f0	movq	%rsi, -16(%rbp)
    100001e80:	48 8b 45 f8	movq	-8(%rbp), %rax
    100001e84:	48 89 45 e0	movq	%rax, -32(%rbp)
    100001e88:	48 8b 05 11 75 00 00	movq	29969(%rip), %rax ## Objc class ref: ViewController
    100001e8f:	48 89 45 e8	movq	%rax, -24(%rbp)
    100001e93:	48 8b 35 de 74 00 00	movq	29918(%rip), %rsi ## Objc selector ref: viewDidLoad
    100001e9a:	48 8d 7d e0	leaq	-32(%rbp), %rdi
    100001e9e:	e8 55 05 00 00	callq	0x1000023f8 ## Objc message: -[[%rdi super] viewDidLoad]
    100001ea3:	48 83 c4 20	addq	$32, %rsp
    100001ea7:	5d	popq	%rbp
    100001ea8:	c3	retq
    100001ea9:	90	nop
    100001eaa:	90	nop
    100001eab:	90	nop
    100001eac:	90	nop
    100001ead:	90	nop
    100001eae:	90	nop
    100001eaf:	90	nop
    ...

五、 查看符号表

  • 使用工具: objdump查看符号表

    objdump --macho -syms ${MACH_PATH}
    SYMBOL TABLE:
    0000000100001e70 l     F __TEXT,__text -[ViewController viewDidLoad]
    0000000100001eb0 l     F __TEXT,__text -[AppDelegate application:didFinishLaunchingWithOptions:]
    0000000100001f30 l     F __TEXT,__text -[AppDelegate application:configurationForConnectingSceneSession:options:]
    0000000100002050 l     F __TEXT,__text -[AppDelegate application:didDiscardSceneSessions:]
    0000000100002150 l     F __TEXT,__text -[SceneDelegate scene:willConnectToSession:options:]
    0000000100002200 l     F __TEXT,__text -[SceneDelegate sceneDidDisconnect:]
    0000000100002240 l     F __TEXT,__text -[SceneDelegate sceneDidBecomeActive:]
    0000000100002280 l     F __TEXT,__text -[SceneDelegate sceneWillResignActive:]
    00000001000022c0 l     F __TEXT,__text -[SceneDelegate sceneWillEnterForeground:]
    0000000100002300 l     F __TEXT,__text -[SceneDelegate sceneDidEnterBackground:]
    0000000100002340 l     F __TEXT,__text -[SceneDelegate window]
    0000000100002360 l     F __TEXT,__text -[SceneDelegate setWindow:]
    00000001000023a0 l     F __TEXT,__text -[SceneDelegate .cxx_destruct]
    0000000100004050 lw    O __DATA_CONST,__objc_protolist __OBJC_LABEL_PROTOCOL_$_NSObject
    0000000100004058 lw    O __DATA_CONST,__objc_protolist __OBJC_LABEL_PROTOCOL_$_UIApplicationDelegate
    0000000100004060 lw    O __DATA_CONST,__objc_protolist __OBJC_LABEL_PROTOCOL_$_UISceneDelegate
    0000000100004068 lw    O __DATA_CONST,__objc_protolist __OBJC_LABEL_PROTOCOL_$_UIWindowSceneDelegate
    0000000100008050 l     O __DATA,__objc_const __OBJC_METACLASS_RO_$_ViewController
    0000000100008098 l     O __DATA,__objc_const __OBJC_$_INSTANCE_METHODS_ViewController
    00000001000080b8 l     O __DATA,__objc_const __OBJC_CLASS_RO_$_ViewController
    0000000100008100 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject
    00000001000082d0 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject
    00000001000082f0 l     O __DATA,__objc_const __OBJC_$_PROP_LIST_NSObject
    0000000100008338 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_METHOD_TYPES_NSObject
    00000001000083d8 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_REFS_UIApplicationDelegate
    00000001000083f0 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIApplicationDelegate
    0000000100008908 l     O __DATA,__objc_const __OBJC_$_PROP_LIST_UIApplicationDelegate
    0000000100008920 l     O __DATA,__objc_const __OBJC_$_PROTOCOL_METHOD_TYPES_UIApplicationDelegate
    0000000100008ad0 l     O __DATA,__objc_const __OBJC_CLASS_PROTOCOLS_$_AppDelegate
    0000000100008ae8 l     O __DATA,__objc_const __OBJC_METACLASS_RO_$_AppDelegate
    ...

    g: 全局符号 全局变量
    l: 本地符号 static修饰的变量

    __attribute__((visibility("hidden"))) 将全局符号隐藏掉或者使用static修饰变量编程本地符号

    弱定义 -> 解决重复符号, 提前声明为弱定义, 在查找的时候找到一个就不会再去查找了
    弱引用 -> 链接时查找符号, 如果不存在, 不会crash

    例如:

    int hidden_y __attribute__((visibility("hidden"))) = 99;

六、查看导出符号(比如全局变量)

  • 全局符号 --> 导出符号 (strip 动态库时 不是全局符号的符号)

    OC默认都是全局符号/导出符号, 想要动态库的体积尽可能小, 尽量把不想暴露的符号strip

    OC方法不暴露方法也是导出符号

    • 符号的可剥离性

      1. App (导出符号一般不需要提供给外部使用, 导入符号不能脱(间接符号表))-> 本地 + 全局 都可以脱掉 = 只留下间接符号表中的符号
      2. 静态库 (.o 合集 + 重定位符号表) -> 重定位符号表中的符号在链接时需要使用, 只有调试符号可以脱掉 = .o中的所有符号 + 可定位的符号 (可能是全局、可能是本地也可能是导出)
      3. 动态库 -> 只要不是全局符号都可以脱掉 = app使用动态库时都放在间接符号表中
    • 从符号的角度考虑, 使用静态库体积会小一些

      • 通过Strip剥离符号时, 只要不是间接符号表中的符号都可以剥离, 而静态库在链接到App中时可能会是全局、本地、导出符号, 而动态库都存在间接符号表中
    • SDK提供商角度来讲, 动态库更具有优势, 符号信息会全一些

    将导出符号变成不导出

    -Xlinker -unexported_symbol -Xlinker __OBJC_METACLASS_RO_$_ViewController

    Xcode 通过设置可以生成 Link Map File 帮助我们查看二进制符号及顺序

    objdump --macho --exports-trie ${MACH_PATH}
    ❯ objdump --macho --exports-trie ${MACH_PATH}                                ─╯
    /Users/shenyj/Desktop/macho/MultiEnvByXCConfigWOConflict:
    Exports trie:
    0x100000000  __mh_execute_header
    0x1000020C0  _main
    0x1000093B0  _OBJC_CLASS_$_ViewController
    0x100009428  _OBJC_CLASS_$_AppDelegate
    0x100009478  _OBJC_CLASS_$_SceneDelegate
    0x1000093D8  _OBJC_METACLASS_$_ViewController
    0x100009400  _OBJC_METACLASS_$_AppDelegate
    0x100009450  _OBJC_METACLASS_$_SceneDelegate

七、查看间接符号表

  • 间接符号表 --> 动态库符号 (不能动)

    objdump --macho --indirect-symbols ${MACH_PATH}
    ❯ objdump --macho --indirect-symbols ${MACH_PATH}                            ─╯
    /Users/shenyj/Desktop/macho/MultiEnvByXCConfigWOConflict:
    Indirect symbols for (__TEXT,__stubs) 10 entries
    address            index name
    0x00000001000023d4   184 _NSStringFromClass
    0x00000001000023da   191 _UIApplicationMain
    0x00000001000023e0   194 _objc_alloc
    0x00000001000023e6   195 _objc_autoreleasePoolPop
    0x00000001000023ec   196 _objc_autoreleasePoolPush
    0x00000001000023f2   197 _objc_autoreleaseReturnValue
    0x00000001000023f8   199 _objc_msgSendSuper2
    0x00000001000023fe   200 _objc_opt_class
    0x0000000100002404   202 _objc_retainAutoreleasedReturnValue
    0x000000010000240a   203 _objc_storeStrong
    Indirect symbols for (__DATA_CONST,__got) 3 entries
    address            index name
    0x0000000100004000   198 _objc_msgSend
    0x0000000100004008   201 _objc_release
    0x0000000100004010   204 dyld_stub_binder
    Indirect symbols for (__DATA,__la_symbol_ptr) 10 entries
    address            index name
    0x0000000100008000   184 _NSStringFromClass
    0x0000000100008008   191 _UIApplicationMain
    0x0000000100008010   194 _objc_alloc
    0x0000000100008018   195 _objc_autoreleasePoolPop
    0x0000000100008020   196 _objc_autoreleasePoolPush
    0x0000000100008028   197 _objc_autoreleaseReturnValue
    0x0000000100008030   199 _objc_msgSendSuper2
    0x0000000100008038   200 _objc_opt_class
    0x0000000100008040   202 _objc_retainAutoreleasedReturnValue
    0x0000000100008048   203 _objc_storeStrong

八、nm 查看符号信息

  • 利用-m参数输出格式会更友好一些

    nm -m ${MACH_PATH}
  • -pa

    nm -pa ${MACH_PATH}

    参数说明:

    • p: 不排序,符号表中的顺序是什么就原样输出,不进行排序
    • a: 全部符号,输出所有符号,包括调试符号
⚠️ **GitHub.com Fallback** ⚠️