OS X 應用程序 格式講解
OS X 如何執行應用程序
譯者:51test2003 譯自http://0xfe.blogspot.com/2006/03 ... s-applications.html
作為長期的 UNIX 用戶, 我通常有一些排除系統故障的工具. 最近, 我正在開發軟件並新增了Apple's OS X 系統支持; 但是和其他傳統UNIX 變種不同, OS X 不支持許多與加載,鏈接和執行程序相關的工具.
例如, 當共享庫重定位出錯時, 我所做的首要事情就是對可執行文件運行ldd. ldd工具列出了可執行文件所依賴的共享庫(包括所在路徑)。但是在OS X , 試圖運行ldd將報錯.
evil:~ mohit$ ldd /bin/ls
-bash: ldd: command not found
沒找到? 但在所有的UNIX上基本上都有的啊. 我想知道objdump是否可用.
$ objdump -x /bin/ls
-bash: objdump: command not found
命令未找到. 怎麼回事?
問題在於與Linux, Solaris, HP-UX, 和其他許多UNIX 變種不同, OS X 不使用 ELF二進制文件. 另外, OS X 不屬於GNU 項目的一部分。該項目包含想ldd和objdump這樣的工具.
為了在OS X獲得可執行文件所依賴的共享庫列表,需要使用 otool 工具.
evil:~ mohit$ otool /bin/ls
otool: one of -fahlLtdoOrTMRIHScis must be specified
Usage: otool [-fahlLDtdorSTMRIHvVcXm] object_file ...
-f print the fat headers
-a print the archive header
-h print the mach header
-l print the load commands
-L print shared libraries used
-D print shared library id name
-t print the text section (disassemble with -v)
-p start dissassemble from routine name
-s print contents of section
-d print the data section
-o print the Objective-C segment
-r print the relocation entries
-S print the table of contents of a library
-T print the table of contents of a dynamic shared library
-M print the module table of a dynamic shared library
-R print the reference table of a dynamic shared library
-I print the indirect symbol table
-H print the two-level hints table
-v print verbosely (symbolicly) when possible
-V print disassembled operands symbolicly
-c print argument strings of a core file
-X print no leading addresses or headers
-m don't use archive(member) syntax
evil:~ mohit$ otool -L /bin/ls
/bin/ls:
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
好多了. 我們可以看見/bin/ls引用了兩個動態庫. 盡管, 文件擴展名我們根本不熟悉.
我相信許多UNIX / Linux 用戶使用OS X系統時有類似的經歷,所以我決定寫一點目前我所知道的關於 OS X 可執行文件的知識.
OS X 運行時架構運行時環境是OS X上代碼擴展的一個框架。它一組定義代碼如何被加載,被管理,被執行的集合組成。一旦應用程序運行, 合適的運行時環境就加載程序到內存, 解決外部庫的引用, 並為執行准備代碼.
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