目錄
ABI Mach-O
Mach-0(Mach object)
是macOS、i0S蹂安、iPadOS
存儲程序和庫的文件格式逢渔。對應系統(tǒng)通過應用二進制接口(application binary interface
)诸衔, 縮寫為ABI
來運行該格式的文件。
Mach-O
格式用來替代BSD
系統(tǒng)的a.out
格式税手。Mach-O
文件格式保存了在編譯過程和鏈接過程中產生的機器代碼和數(shù)據(jù)挤土,從而為靜態(tài)鏈接和動態(tài)鏈接的代碼提供了單一文件格式檐束。
之前段始終是4096
字節(jié)或4 KB
的倍數(shù)伤溉,其中4096
字節(jié)是最小大小般码。
現(xiàn)在段是16 KB
的倍數(shù),在macOS_x86_64
上是16k
乱顾,在iOS
上是32k
板祝。
,-----------------------------,
Header | Mach header |
| Segment 1 |
| Section 1 (_ text) | --,
|-----------------------------| |
Data | blob | <--'
'-----------------------------'
Symbol Table
通過兩個load commands
:
- LC_SYMTAB:當前
Mach-O
中的符號表信息。 - LC_DYSYMTAB:描述動態(tài)鏈接器使用其他的
Symbol Table
信息走净。
用來描述Symbol Table
的大小和位置券时,以及其他元數(shù)據(jù)。
LC_SYMTAB
用來描述該文件的符號表伏伯。不論是靜態(tài)鏈接器還是動態(tài)鏈接器在鏈接此文件時橘洞,都要使用該load command
。調試器也可以使用該load command
找到調試信息说搅。
symtab_command
定義LC_SYMTAB
加載命令具體屬性炸枣。在/usr/include/mach-o/loader.h
中定義:
struct symtab_command {
// 共有屬性。指明當前描述的加載命令弄唧,當前被設置為LC_SYMTAB
uint32_t cmd ;
// 共有屬性适肠。指明加載命令的大小,當前被設置為sizeof(symtab_command)
uint32_t cmdsize;
// 表示從文件開始到symbol table所在位置的偏移量候引。symbol table用[nlist]來表示
uint32_t symoff;
// 符號表內符號的數(shù)量
uint32_t nsyms;
// 表示從文件開始到string table所在位置的偏移量侯养。
uint32_t stroff;
// 表示string table大小(以byteカ單位)
uint32_t strsize;
};
nlist
定義符號的具體表示含義:
struct nlist {
// 表示垓符號在string table的索引
union {
//在Mach-0中不使用此字段
char *n_name;
// 索引
long n_strx;
} n_un;
unsigned char n_type; /* type flag, see below */
unsigned char n_sect; /* section number or NO_ SECT */
short n_desc; /* see <mach-o/stab.h> */
unsigned long n_value; /* value of this symbol (or stab offset) */
};
n_type
1字節(jié),通過四位掩碼保存數(shù)據(jù):
-
N_STAB(0xe0)
:如果當前的n_type
包含這3位中的任何一位背伴,則該符號為調試符號表(stab)
沸毁。在這種情況下,整個n_type
字段將被解釋為stab value
傻寂。請參閱/usr/include/mach-o/stab.h
以獲取有效的stab value
。 -
N_PEXT(0x10)
:如果當前的n_type
包含此位携兵。則將此符號標記為私有外部符號__private_extern__(visibility=hidden)
疾掰, 只在程序內可引用和訪問。當文件通過靜態(tài)鏈接器鏈接的時候徐紧,不要將其轉換成靜態(tài)符號(可以通過ld
的(-keep_private_externs
關閉靜態(tài)鏈接器的這種行為)静檬。 -
N_TYPE(0x0e)
:如果當前的n_type
包含此位。則使用預先定義的符號類型并级。 -
N_EXT(0x01)
:如果當前的n_type
包含此位拂檩。則此符號為外部符號。該符號在該文件外部定義或在該文件中定義嘲碧,但可在其他文件中使用稻励。
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_sect
字段設置為NO_SECT
。 -
N_INDR(0xa)
:該符號定義為與另一個符號相同鸠窗。n_value
字段是string table
中的索引妓羊,用于指定另一個符號的名稱。鏈接該符號時塌鸯,此符號和另一個符號都具有相同的定義類型和值侍瑟。
stab value
包括:
#define N_GSYM 0x20 /* 全局符號: name, ,N0_ SECT,type,0 */
#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,N0_ SECT,0,0 */
#define N_FUN 0x24 /* 方法/函數(shù): name,,n_ sect,linenumber , address */
#define N_STSYM 0x26 /* 靜態(tài)符號: name,,n sect, type , address */
#define N_LCSYM 0x28 /* .lcomm 符號: name,,n sect , type , address */
#define N_BNSYM 0x2e /* nsect符號開始: 0,,n sect,0, address */
#define N_OPT 0x3c /* emitted with gccZ_ compiled and in gcc source */
#define N_RSYM 0x40 /* 寄存器符號: name,NO_ _SECT, type,register */
#define N_SLINE 0x44 /* 代碼行數(shù): 0,,n ,sect,linenumber , address */
#define N_ENSYM 0x4e /* nsect符號結束: ?,,n sect,?, address */
#define N_SSYM 0x60 /* 結構體符號: name,, NO SECT, type,struct_ offset */
#define N_SO 0x64 /* 源碼名稱: name,,n sect, 0, address */
#define N_OSO 0x66 /* 目標代碼名稱: name, ,0,0,st_ mtime */
#define N_LSYM 0x80 /* 本地符號: name, ,N0_ SECT,type ,offset */
#define N_BINCL 0x82 /* include file 開始: name,,NO_ SECT,0,sum */
#define N_SOL 0x84 /* #included file 名稱: name,,n sect ,0, address */
#define N_PARAMS 0x86 /* 編譯器參數(shù): name,,NO_ SECT,0,0 */
#define N_VERSION 0x88 /* 編譯器版本: name,,N0_ SECT,0,0 */
#define N_OLEVEL 0x8A /* 編譯器-O級別: name,NO_ _SECT,0,0 */
#define N_PSYM 0xa0 /* 參數(shù): name,,No_ _SECT, type,offset */
#define N_EINCL 0xa2 /* include file 結束: name,,NO_ SECT,0,0 */
#define N_ENTRY 0xa4 /* alternate entry: name, ,n. sect,linenumber , address */
#define N_LBRAC 0xc0 /* 左括號: 0,,N0_ SECT,nesting level,address */
#define N_EXCL 0xc2 /* deleted include file: name, ,NO_ SECT,0,sum */
#define N_RBRAC 0xe0 /* 右括號: 0,,N0. _SECT ,nesting level , address */
#define N_BCOMM 0xe2 /* 通用符號開始: name,,NO. SECT,0,0 */
#define N_ECOMM 0xe4 /* 通用符號結束: name,n. sect,0,0 */
#define N_ECOML 0xe8 /* end common (local name): 0,,n_ sect , 0, address */
#define N_LENG 0xfe /* second stab entry with length information */
/*
* for the berkeley pascal compiler, pC(1):
*/
#define N_ _PC 0x30 /* global pascal symbol: name, ,NO_ SECT, subtype,line */
n_ sect
整數(shù),用來在指定編號的section
中找到此符號丙猬;如果在該image
的任何部分都找不到該符號涨颜,則為NO_SECT
。根據(jù)section
在LC_SEGMENT
加載命令中出現(xiàn)的順序茧球,這些section
從1開始連續(xù)編號庭瑰。
n_desc
16-bit
值,用來描述非調試符號抢埋。低三位使用REFERENCE_TYPE
:
-
REFERENCE_FLAG_UNDEFINED_NON_LAZY(0x0)
:該符號是外部非延遲(數(shù)據(jù))符號的引用弹灭。 -
REFERENCE_FLAG_UNDEFINED_LAZY(0x1)
:該符號是外部延遲性符號(即對函數(shù)調用)的引用。 -
REFERENCE_FLAG_DEFINED(0x2)
: 該符號在該模塊中定義揪垄。 - `REFERENCE_ FLAG_ PRIVATE_ DEFINED(0x3) :該符號在該模塊中定義穷吮,但是僅對該共享庫中的模塊可見。
-
REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY(0x4)
:該符號在該文件的另一個模塊中定義饥努,是非延遲加載(數(shù)據(jù))符號捡鱼,并且僅對該共享庫中的模塊可見。 -
REFERENCE_FLAG_PRIVATE_LUNDEFINED_LAZY(0x5)
:該符號在該文件的另一個模塊中定義酷愧,是延遲加載(函數(shù))符號驾诈,僅對該共享庫中的模塊可見。
另外還可以設置如下標識位:
-
REFERENCED_DYNAMICALLY(0x10)
:定義的符號必須是使用在動態(tài)加載器中(例如dlsym
和NSLookupSymbolInImage
) 溶浴。而不是普通的未定義符號引用乍迄。strip
使用該位來避免刪除那些必須存在的符號(如果符號設置了該位,則strip
不會剝離它)士败。 -
N_DESC_DISCARDED(0x20)
:在完全鏈接的image
在運行時動態(tài)鏈接器有可能會使用此符號闯两。不要在完全鏈接的image
中設置此位。 -
N_NO_DEAD_STRIP(0x20)
: 定義在可重定位目標文件(類型為MH_0BJECT
)中的符號設置時,指示靜態(tài)鏈接器不對該符號進行(dead-strip
生蚁。 (請注意噩翠,與N_DESC_DISCARDED(0x20)
用于兩個不同的目的。) -
N_WEAK_REF(0x40)
:表示此未定義符號是弱引用邦投。如果動態(tài)鏈接器找不到該符號的定義伤锚,則將其符號地址設置為0。靜態(tài)鏈接器會將此符號設置弱鏈接標志志衣。 -
N_WEAK_DEF(0x80)
:表示此符號為弱定義符號屯援。如果靜態(tài)鏈接器或動態(tài)鏈接器為此符號找到另一個(非弱)定義,則弱定義將被忽略念脯。只能將合并部分中的符號標記為弱定義狞洋。
如果該文件是兩級命名two-level namespace image
(即如果mach_header
中設置了MH_TWOLEVEL
標志),則( n_desc
的高8位表示定義此未定義符號的庫的編號绿店。使用宏GET_LIBRARY_ORDINAL
來獲取此值吉懊,或者使用宏SET_LIBRARY_0RDINAL
來設置此值。0指定當前image假勿。1到253根據(jù)文件中LC_LOAD_DYLIB
命令的順序表明庫號借嗽。254用于需要動態(tài)查找的未定義符號(僅在OS X v10.3和更高版本中受支持) 。對于從可執(zhí)行程序加載符號的插件转培。255用來指定可執(zhí)行image
)恶导。 對于flat namespace images
,高8位必須為0浸须。
n_ value
符號值惨寿。對于symbol table
中的每一項, 該值的表達的意思都不同(具體由n_type
字段說明)删窒。對于N_SECT
符號類型裂垦,(n_value
是符號的地址。有關其他可能值的信息肌索,請參見n_type
字段的描述缸废。
Common symbols
必須為N_UNDF
類型,并且必須設置N_EXT
)位驶社。Common symbols
的n_value
是符號表示的數(shù)據(jù)的大小(以字節(jié)為單位)。在C
語言中测萎,Common symbol
是在該文件中聲明但未初始化的變量亡电。Common symbols
只能出現(xiàn)在MH_OBJECT
) 類型的Mach-0
文件中。
section名稱與作用
名稱 | 作用 |
---|---|
TEXT.text | 可執(zhí)行的機器碼 |
TEXT.cstring | 去重后的C字符串 |
TEXT.const | 初始化過的常量 |
TEXT.stubs | 符號樁硅瞧。lazybinding的表對 應項指針指向的地址的代碼份乒。 |
TEXT.stub_ helper | 輔助函數(shù)。當在lazybinding的表中沒有找到對應項的指針表示的真正的符號地址的時候,指向這或辖。 |
TEXT.unwind_info | 存儲處理異常情況信息 |
TEXT.eh_frame | 調試輔助信息 |
DATA.data | 初始化過的可變的數(shù)據(jù) |
DATA.nI_symbol_ptr | 非lazy-binding的指針表瘾英,每個表中的指針指向一個在裝載過程中,被動態(tài)鏈接器搜索完成的符號 |
DATA.Ia_symbol_ptr | lazy-binding的指針表颂暇,每個表中的指針一開始指向stub_helper |
DATA.const | 沒有初始化過的常量 |
DATA.mod_init_func | 初始化函數(shù)缺谴,在main之前調用 |
DATA.mod_term_func | 終止函數(shù),在main返回之后調用 |
DATA.bss | 沒有初始化的靜態(tài)變量 |
DATA.common | 沒有初始化過的符號聲明(for example, int I;) |
nm命令
打印nlist
結構的符號表symbol table
常用nm命令參數(shù)
nm -pa a.o
-a: 顯示符號表的所有內容
-g: 顯示全局符號
-p: 不排序耳鸯。顯示符號表本來的順序
-r: 逆轉順序
-u: 顯示未定義符號:
-m: 顯示N_SECT類型的符號(Mach-0符號)顯示湿蛔。