• Mach-O文件概述:
  • Mach-O(Mach Object)macOSiOSiPadOS存储程序和库的文件格式。对应系统通过应用二进制接口(application binary interface),缩写为ABI来运行该格式的文件。
  • Mach-O格式用来替代BSD(Berkeley Software Distribution:伯克利软件套件)Unix衍生系统的a.out格式。Mach-O文件格式保存在编译过程和链接过程中产生的机器代码和数据,从而为静态链接和动态链接的代码提供了单一文件格式。
  • 段之前始终是4096字节或4KB的倍数,其中4096字节是最小大小。现在段是16KB的倍数,在macOS_x86_64上是16k,在iOS上是32k。
  • Mach-O 文件格式:
  • 一个Mach-O文件有两部分组成:headerdata
  • header: 代表了文件的映射,描述了文件的内容以及文件所有内容所在的目录。
  • Mach header 属于header的一部分,它包含了整个文件的信息和segment信息。
  • data:紧跟header之后,由多个二进制组成,一个接着一个。
  • Load Commands:
  • 进制文件加载进内存要执行的一些指令。
  • 这里的指令主要在负责我们App对应进程的创建和基本设置(分配虚拟内存,创建主线程,处理代码签名/加密的工作),然后对动态链接库(.dylib系统库和我们自己创建的动态库)进行库加载和符号解析工作。
  • Segemnts(segment commands):指定操作系统应该将Segment加载到内存中的什么位置,以及为该Segments分配的字节数。还指定文件中的哪些字节属于该Segments,以及文件包含多少sectionsMac始终是4096kb4kb的倍数,其中4096是最小大小。iOS最小是8kb。Segments名称的约定是使用全大写字母,后跟双下划线(例如 __TEXT).
  • Section: 所有sections都在每个segment之后一个接一个的描述。sections里面定义其名称,在内存中的地址,大小,文件中section数据的偏移量和segment名称。Section的名称约定是使用全小写字母,再加上双下划线(例如 __text)。
  • __TEXT: 只读区域:包含可执行代码和常量数据。
  • __DATA: 读/写:包含初始化、未初始化数据和一些动态链接专属数据。
  • 可执行文件调用过程(官方描述)
  • 调用‘fork’函数,创建一个‘process’
  • 调用’execve’或其衍生函数,在该进程上加载,执行我们的‘Mach-O’文件。
  • 当我们调用时‘execve’(程序加载器),内核实际上在执行以下操作。
  • 将文件加载到内存。
  • 开始分析‘Mach-O’中的‘mach_header’,以确认它是有效的‘Mach-O’文件。
    要了解Mach-O文件、
  • 我们先创建一个项目–>
  • 然后写上一句输出日志代码–>
  • 运行之后–>
  • 在Products文件夹下–>
  • Show In Finder–> 显示包内容–>
  • 拿到Unix可执行文件(这里我生成的为MachOFile)。
  • Mach-O文件 我们这样理解 == 文件配置 + 二进制代码
  • 拿到的Mach-O文件、我们在终端这样操作: 拿到Load Command
//查看mach-header
$ objdump --macho --private-headers MachOFile
MachOFile:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    27       3160   NOUNDEFS DYLDLINK TWOLEVEL PIE
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
   vmaddr 0x0000000000000000
   vmsize 0x0000000100000000
.......
//查看 __TEXT
$ objdump --macho -d MachOFile
MachOFile:
(__TEXT,__text) section
-[ViewController viewDidLoad]:
100001e50:	55	pushq	%rbp
100001e51:	48 89 e5	movq	%rsp, %rbp
100001e54:	48 83 ec 30	subq	$48, %rsp
100001e58:	48 89 7d f8	movq	%rdi, -8(%rbp)
100001e5c:	48 89 75 f0	movq	%rsi, -16(%rbp)
100001e60:	48 8b 45 f8	movq	-8(%rbp), %rax
100001e64:	48 89 45 e0	movq	%rax, -32(%rbp)
100001e68:	48 8b 05 49 75 00 00	movq	30025(%rip), %rax ## Objc class ref: ViewController
100001e6f:	48 89 45 e8	movq	%rax, -24(%rbp)
100001e73:	48 8b 35 0e 75 00 00	movq	29966(%rip), %rsi ## Objc selector ref: viewDidLoad
100001e7a:	48 8d 7d e0	leaq	-32(%rbp), %rdi
100001e7e:	e8 9b 05 00 00	callq	0x10000241e ## Objc message: -[[%rdi super] viewDidLoad]
100001e83:	48 8b 05 16 75 00 00	movq	29974(%rip), %rax ## Objc class ref: _OBJC_CLASS_$_AFURLSessionManager
100001e8a:	48 89 c7	movq	%rax, %rdi
100001e8d:	e8 98 05 00 00	callq	0x10000242a ## symbol stub for: _objc_opt_new
100001e92:	48 8d 0d 7f 21 00 00	leaq	8575(%rip), %rcx ## Objc cfstring ref: @"Watch a Mach-O file %@"
.......
//还可以通过 otool -h MachOFile查看 mach-header 少量信息
$ otool -h MachOFile
MachOFile:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x00           2    27       3160 0x00200085
  • 由于内容过多、那么我们可以通过附加参数来获取我们想要的信息、
  • 先执行下 ag --version 命令、查看下自己是否安装了ag。如果没有的话brew install ag 即可。
$ ag --version
ag version 2.2.0
Features:
  +jit +lzma +zlib
  • 我们查看下主程序的Load Command信息:
$ objdump --macho --private-headers MachOFile | ag 'LC_MAIN'
 cmd LC_MAIN
 //附加搜索条件、-A 在当前之后输出3行即可获得其全部信息、可以搜索更多行、会返回其他Load Command信息 (在当前搜索结果之前搜索为 -B )
$ objdump --macho --private-headers MachOFile | ag 'LC_MAIN' -A 3
   cmd LC_MAIN
   cmdsize 24
  entryoff 8416
 stacksize 0
  • 为了简化我们的输出及更深刻理解Mach-O文件、下面我们借助于一个machoinfo 来查看信息
  • 编译运行、拿到可执行文件、此时我们就可以直接使用其中的命令了。
  • 进入含有MachOFile可执行文件的文件夹中、执行命令
$ ./machoinfo MachOFile
x86_64						//架构类型
LC: 0 segname: __PAGEZERO vmaddr: 0 nsects: 0	//捕捉空指针引用。通过在存储器的开头保留一个大的内存部分,通过一个空指针的任何访问都会被捕获并申请中止。
LC: 1 segname: __TEXT vmaddr: 100000000 nsects: 9	//代码段
LC: 2 segname: __DATA_CONST vmaddr: 100004000 nsects: 5	//数据常量段
LC: 3 segname: __DATA vmaddr: 100008000 nsects: 8		//数据段
LC: 4 segname: __LINKEDIT vmaddr: 10000c000 nsects: 0	//SEG_LINKEDIT : 包含所有结构的段;由链接编辑器创建和维护。
LC: 8 LC_LOAD_DYLINKER dyld: /usr/lib/dyld			//动态链接器
LC: 9 LC_UUID uuid 3D45E7A9-EE30-33C7-A6FE-7BFA717A58C9	//UUID
LC: 10 LC_BUILD_VERSION platform: iossimulator, sdk: 14.3, minos: 14.3, ntools: 1 //平台信息
LC: 11 LC_SOURCE_VERSION version: 0.0			//用于构建二进制文件的源代码版本
LC: 12 LC_MAIN entryoff: 8416 stacksize: 0			//主函数入口
LC: 13 LC_LOAD_DYLIB dylid: @rpath/AFNetworking.framework/AFNetworking //加载动态链接的共享库
LC: 14 LC_LOAD_DYLIB dylid: /System/Library/Frameworks/ImageIO.framework/ImageIO
LC: 15 LC_LOAD_DYLIB dylid: @rpath/SDWebImage.framework/SDWebImage
LC: 16 LC_LOAD_DYLIB dylid: /System/Library/Frameworks/Foundation.framework/Foundation
LC: 17 LC_LOAD_DYLIB dylid: /usr/lib/libobjc.A.dylib
LC: 18 LC_LOAD_DYLIB dylid: /usr/lib/libSystem.B.dylib
LC: 19 LC_LOAD_DYLIB dylid: /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
LC: 20 LC_LOAD_DYLIB dylid: /System/Library/Frameworks/UIKit.framework/UIKit
LC: 21 LC_RPATH path: @executable_path/Frameworks		//添加运行路径
LC: 22 LC_RPATH path: @loader_path/Frameworks
LC: 23 LC_RPATH path: @executable_path/Frameworks
LC: 24 LC_FUNCTION_STARTS cmdsize: 16 dataoff: 50432 datasize: 24	//函数起始地址的压缩表
LC: 25 LC_DATA_IN_CODE cmdsize: 16 dataoff: 50456 datasize: 0	//__text中的非指令表
LC: 26 LC_CODE_SIGNATURE cmdsize: 16 dataoff: 60160 datasize: 19200 //本地代码签名
  • 如此可见、这样输出的Mach-O文件信息比较简洁、比刚才用objdump输出可视化的多,而且输出结果基本一致、Load Command一共有26个、也输出了动态链接库路径。
  • 还可以直接在
  • machinfo项目中–>
  • Edit Scheme -->
  • Arguments -->
  • Arguments Passed On Launch -->
  • 将可执行文件路径放置其中,运行后控制台得到输出信息。
  • 配置方式
  • ios的文件格式是什么 苹果的文件格式_LoadCommands

  • 运行后输出结果
  • ios的文件格式是什么 苹果的文件格式_LoadCommands_02

  • 下面我们来探索编译与链接过程
  • 编译的过程就是在将我们的代码放到对应的page中:全局符号、本地符号、外部符号、本地符号,在编译过程中进行分 类。
  • 链接的本质就是将多个目标文件组合成一个文件、多个目标文件的符号表合并到一起。
  • 接下来我们来看一下符号表
  • Symbol Table就是用来保存符号的表
  • String Table 就是用来保存符号的名称
  • Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使用的外部动态库的符号,比如NSLog符号。是Symbol Table 的子集。
  • 下面我们通过案例来理解符号
  • 定义一个 VisibilitySymbol.h文件来测试我们符号的可见性。
extern int hidden_y;
extern double default_y;
extern double protected_y;
  • VisibilitySymbol.m文件中实现部分
#import "VisibilitySymbol.h"
// visibility属性,控制文件导出符号,限制符号可见性
/**
    -fvisibility:clang参数
    default:用它定义的符号将被导出。
    hidden:用它定义的符号将不被导出。
 */
int hidden_y __attribute__((visibility("hidden"))) = 99;
double default_y __attribute__((visibility("default"))) = 100;
double protected_y __attribute__((visibility("protected"))) = 120;
  • 定义一个WeakImportSymbol.h文件来声明一个弱导入的符号
void weak_import_function(void) __attribute__((weak_import));
  • WeakImportSymbol.m中这样实现
#import "WeakImportSymbol.h"
#import <Foundation/Foundation.h>
void weak_import_function(void) {
    NSLog(@"weak_import_function");
}
  • WeakSymbol.h定义弱引用及弱定义符号
void weak_function(void)  __attribute__((weak));
void weak_hidden_function(void) __attribute__((weak, visibility("hidden")));
  • WeakSybol.m中这样实现
#import "WeakSymbol.h"
#import <Foundation/Foundation.h>
void weak_function(void) {
    NSLog(@"weak_function");
}
void weak_hidden_function(void) {
    NSLog(@"weak_hidden_function");
}
  • 我们在main.m中设置如下变量
//未初始化全局变量
int global_uninit_value;
//初始化全局变量
int global_init_value = 10;
double default_x __attribute__((visibility("hidden")));
//局部静态初始化变量
static int static_init_value = 99;
//局部静态未初始化变量
static int static_uninit_value;
int main(int argc, const char * argv[]) {
    static_uninit_value = 10;
    NSLog(@"%d",static_init_value);
    return 0;
}
  • 为了结果可视化、我们将运行结果直接输出到终端上。
  • 创建xcconfig文件,文件中我们配置一个变量HOST_URL = 192.168.0.1 ,并在PROJECTDebug模式下配置该xcconfig文件–>
  • 在终端上输入 tty 获取当前终端编号/dev/ttys000 -->
  • 在当前Target -- Build Phases --> Run Script输出当前定义的变量值 -->
echo "${HOST_URL}" > /dev/ttys000
  • 运行后,当前终端可见输出日志: 192.168.0.1
  • 因此我们可以衍生为通过Run Script输出要执行的命令,并且显示输出结果。
  • xcconfig中我们这样设置、最终实现通过命令获取运行后的可执行文件的Mach-O信息
//CMD 运行的命令
//CMD_FLAG 运行的命令参数
//TTY 终端
//$SRCROOT 项目根目录
//nm -pa /machinfo 
//-p  不排序
//-a  显示所有符号,包含调试符号
MACH_PATH = ${BUILD_DIR}/${CONFIGURATION}/${EFFECTIVE_PLATFORM_NAME}/*
CMD = nm
CMD_FLAG = -pa ${MACH_PATH}
TTY = /dev/ttys000
  • 将脚本放置进项目根目录、并添加上可执行权限。Build Phases中,添加Run Script输入下方命令。
/bin/sh "$SRCROOT/xcode_run_cmd.sh"
  • 运行后可获取当前可执行文件输出信息:部分如下
  • 由于信息太多、我们可以进行输出优化、输出结果中不显示调试信息、在xcconfig文件中我们使用这样的标志符。
// -Xlinker 表示要传递给链接器ld的参数
OTHER_LDFLAGS = -Xlinker -S
  • 此时此刻、输出信息为
  • Symbol Table
    通过两个Load Commands
  • LC_SYMTAB:当前 Mach-O中的符号信息。
  • LC_DYSYMTAB:描述动态链接器使用其他的Symbol Table信息
  • 用来描述Symbol Table的大小和位置,以及其他元数据。
    LC_SYMTAB
  • 用来描述该文件的符号表。不论是静态链接器还是动态链接器在链接此文件时,都要使用load command。调试器也可以使用该 load command找到调试信息。
    symtab_command
  • 定义LC_SYMTAB加载命令具体属性。在/usr/include/mach-o/loader.h中定义:
#import <mach-o/loader.h>
struct symtab_command {
   //共有属性。指明当前描述的加载命令,当前被设置为LC_SYMTAB
	uint32_t	cmd;		/* LC_SYMTAB */
 	//共有属性。指明加载命令的大小,当前被设置为sizeof(symtab_command)
	uint32_t	cmdsize;	/* sizeof(struct symtab_command) */
 	//表示从文件开始到symbol table所在位置的便宜量。symbol table用 [nlist]来表示
	uint32_t	symoff;		/* symbol table offset */
 	//符号表内符号的数量
	uint32_t	nsyms;		/* number of symbol table entries */
   //表示从文件开始到string table所在位置的偏移量。
	uint32_t	stroff;		/* string table offset */
   //表示string table大小(以byte为单位)
	uint32_t	strsize;	/* string table size in bytes */
};
  • nlist 定义符号的具体表示含义
#import <mach-o/nlist.h>
struct nlist {
    //表示该符号在string table的索引
	union {
#ifndef __LP64__
    	//在Mach-O中不使用此字段
		char *n_name;	/* for use when in-core */
#endif
    	//索引
		uint32_t n_strx;	/* index into the string table */
	} n_un;
	uint8_t n_type;		/* type flag, see below */
	uint8_t n_sect;		/* section number or NO_SECT */
	int16_t n_desc;		/* see <mach-o/stab.h> */
	uint32_t n_value;	/* value of this symbol (or stab offset) */
};
  • 用于64位体系结构的符号表条目结构如下:
struct nlist_64 {
    union { //索引
        uint32_t  n_strx; /* index into the string table */
    } n_un;
    uint8_t n_type;        /* type flag, see below */
    uint8_t n_sect;        /* section number or NO_SECT */
    uint16_t n_desc;       /* see <mach-o/stab.h> */
    uint64_t n_value;      /* value of this symbol (or stab offset) */
};
  • n_type: 1字节,通过四位掩码保存数据:
  • N_STAB(0xe0):如果当前的n_type包含这3位中的任何一位,则该符号为 调试符号表(stab)。在这中情况下,整个n_type字段将被解释为stab value
  • N_PEXT(0x10):如果当前的n_type包含此位。则将此符号标记位私有外部符号 __private_extern__(visibility=hidden),只在程序内可引用和访问。当文件通过静态链接器链接时,不要将其转换成静态符号(可以通过ld-keep_private_externs关闭静态链接器的这种行为)。
  • N_TYPE(0x0e): 如果当前的n_type包含此位。则使用预先定义的符号类型。
  • N_EXT(0x01):如果当前n_type包含此位。则此符号为外部符号,该符号在该文件外部定义或在该文件中定义,但可以在其他文件中使用。
  • N_TYPE 字段值包括:
  • N_UNDF(0x0): 该符号未定义。未定义符号是在当前模块中引用,但是被定义在其他模块中的符号。n_sect字段设置为NO_SECT
  • N_ABS(0x2): 该符号是绝对符号。链接器不会更改绝对符号的值。n_sect字段设置为 NO_SECT.
  • N_SECT(0xe):该符号在n_sect中指定的段号中定义。
  • N_PBUD(0xc):该符号定义为与另一个符号相同。n_value字段是 string table 中的索引,用于指定另一个符号的名称。链接该符号时,此符号和另一个符号都具有相同的定义类型和值。
  • stab value包括:
#import <mach-o/stab.h>
#define	N_GSYM	0x20	/*全局符号 global symbol: name,,NO_SECT,type,0 */
#define	N_FNAME	0x22	/*程序名称 procedure name (f77 kludge): name,,NO_SECT,0,0 */
#define	N_FUN	0x24	/*方法/函数名称 procedure: name,,n_sect,linenumber,address */
#define	N_STSYM	0x26	/*静态符号 static symbol: name,,n_sect,type,address */
#define	N_LCSYM	0x28	/*.lcomm符号 .lcomm symbol: name,,n_sect,type,address */
#define 	N_BNSYM 0x2e	/*nsect符号开始 begin nsect sym: 0,,n_sect,0,address */
#define	N_AST	0x32	/*语法树文件路径 AST file path: name,,NO_SECT,0,0 */
#define 	N_OPT	0x3c	/*用gcc2_compiled和gcc源发出 emitted with gcc2_compiled and in gcc source */
#define	N_RSYM	0x40	/*寄存器符号:register sym: name,,NO_SECT,type,register */
#define	N_SLINE	0x44	/*代码行数: src line: 0,,n_sect,linenumber,address */
#define    N_ENSYM 0x4e	/*nsect符号结束: end nsect sym: 0,,n_sect,0,address */
#define	N_SSYM	0x60	/*结构体符号 structure elt: name,,NO_SECT,type,struct_offset */
#define	N_SO	0x64	/*源码名称: source file name: name,,n_sect,0,address */
#define	N_OSO	0x66	/*目标代码名称: object file name: name,,(see below),0,st_mtime historically N_OSO set n_sect to 0.*/ 
    			/* The N_OSO  n_sect may instead hold the low byte of the cpusubtype value from the Mach-O header. */
#define	N_LSYM	0x80	/*本地符号: local sym: name,,NO_SECT,type,offset */
#define 	N_BINCL	0x82	/*include文件开始: include file beginning: name,,NO_SECT,0,sum */
#define	N_SOL	0x84	/*include文件名称: #included file name: name,,n_sect,0,address */
#define	N_PARAMS  0x86	/*编译器参数: compiler parameters: name,,NO_SECT,0,0 */
#define	N_VERSION 0x88	/*编译器版本: compiler version: name,,NO_SECT,0,0 */
#define	N_OLEVEL  0x8A	/*编译器-O级别 compiler -O level: name,,NO_SECT,0,0 */
#define	N_PSYM	0xa0	/*参数: parameter: name,,NO_SECT,type,offset */
#define 	N_EINCL	0xa2	/*include文件结束: include file end: name,,NO_SECT,0,0 */
#define	N_ENTRY	0xa4	/*可选程序入口 alternate entry: name,,n_sect,linenumber,address */
#define	N_LBRAC	0xc0	/*左括号 left bracket: 0,,NO_SECT,nesting level,address */
#define 	N_EXCL	0xc2	/*删除include文件 deleted include file: name,,NO_SECT,0,sum */
#define	N_RBRAC	0xe0	/*右括号: right bracket: 0,,NO_SECT,nesting level,address */
#define	N_BCOMM	0xe2	/*通用符号开始: begin common: name,,NO_SECT,0,0 */
#define	N_ECOMM	0xe4	/*通用符号结束: end common: name,,n_sect,0,0 */
#define	N_ECOML	0xe8	/*本地通用符号结束: end common (local name): 0,,n_sect,0,address */
#define	N_LENG	0xfe	/*带有长度信息的第二个stab 入口 second stab entry with length information */
/*
 * for the berkeley pascal compiler, pc(1):
 */
#define	N_PC	0x30	/*全局pascal符号 global pascal symbol: name,,NO_SECT,subtype,line */
  • n_sect: 整数,用来指定编号的section中找到此符号;如果在该image的任何部分都找不到该符号,则为NO_SECT.根据secitonLC_SEGMENT加载命令中出现的顺序,这些section从1开始连续编号。
    n_desc: 16-bit值,用来描述非调试符号。低三位使用REFERENCE_TYPE:
  • REFERENCE_FLAG_UNDEFINED_NON_LAZY(0x0): 该符号是外部非延迟(数据)符号的引用。
  • REFERENCE_FLAG_UNDEFINED_LAZY(0x1):该符号是外部延迟性符号(即对函数调用)的引用。
  • REFERENCE_FLAG_DEFINED(0x2): 该符号在该模块中定义。
  • REFERENCE_FLAG_PRIVATE_DEFINED(0x3):该符号在该模块中定义,但是仅对该共享库中的模块可见。
  • REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY(0x4):该符号在该文件的另一个模块中定义,是非延迟加载(数据)符号,并且仅对该共享库中的模块可见。
  • REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY(0x5):该符号在该文件的另一个模块中定义,是延迟加载(函数)符号,仅对该共享库中的模块可见。
  • 另外还可以设置如下标识位:
  • REFERENCE_DYNAMICALLY(0x10):定义的符号必须是使用在动态库加载器中(例如 dlsymNSLookupSymbolInImage)。而不是普通的未定义符号引用。strip使用该位来避免删除那些必须存在的符号(如果符号设置了该位,则strip不会剥离它)。
  • N_DESC_DISCARAED(0x20):在完全链接的image在运行时动态链接器有可能会使用此符号。不要在完全链接的image中设置此为。
  • N_NO_DEAD_STRIP(0x20):定义在可重定位目标文件(类型为MH_OBJECT)中的符号设置时,指示静态链接器不对该符号进行dead-strip。(与N_DESC_DISCARDED(0x20)用于两个不同的目的)。
  • N_WEAK_REF(0x40): 表示此未定义符号是弱引用。如果动态链接器找不到该符号的定义,则将其符号地址设置为0。静态链接器会将此符号设置弱链接标志。
  • N_WEAK_DEF(0x80): 表示此符号为弱定义符号。如果静态链接器或动态链接器为此符号找到另一个(非弱)定义,则弱定义将被忽略。
  • 如果该文件是两级命名 two-level namespace image(即如果 mach_header中设置了 MH_TWOLEVEL标志),则n_desc的高8位表示定义此为定义符号的库的编号。使用宏GET_LIBRARY_ORDINAL来获取此值。0指定当前image。 1到253根据文件中LC_LOAD_DYLIB命令的顺序表明库号。254用于需要动态查找的未定义符号(仅在OS X V10.3和更高版本中支持)。对于从可执行程序加载符号的插件。255用来指定可执行image。对于 flat_namespace_images,高8位必须是0.
  • n_value:符号值。对于symbol table中的每一项,该值的表达的意思都不同(具体由n_type字段说明)。对于N_SECT符号类型,n_value是符号的地址。有关其他可能值的信息,请参见n_type字段的描述。
  • Common symobls必须为N_UNDF类型,并且必须设置N_EXT位。Common Symbolsn_value是符号表示的数据的大小(以字节为单位)。在C语言中,Common symbol是在该文件中声明但未初始化的变量。Common symbols只能出现在MH_OBJECT类型的 Mach-O文件中。
  • section名称与作用
  • ios的文件格式是什么 苹果的文件格式_LC_SYMTAB_03

  • nm命令 : 打印nlist结构的符号表( symbol table).
  • 常用nm 命令参数
$ nm -pa a.o
-a: 显示符号表的所有内容
-g: 显示全局符号
-p: 不排序。显示符号表本来的顺序
-r: 逆序顺序
-u: 显示未定义符号
-m: 显示N_SECT类型的符号(Mach-O符号)显示