相關閱讀 |
>>> 創業先鋒 眾人拾柴火焰高 >>> | 簡體 傳統 |
很多朋友都知道,在Windows上exe是可直接執行的文件擴展名,而在Linux(以及很多版本的Unix)系統上ELF是可直接執行的文件格式,那么在蘋果的操作系統上又是怎樣的呢?在iOS(和Mac OS X)上,主要的可執行文件格式是Mach-O格式。本文就關于iOS上的可執行文件和Mach-O格式做一個簡要整理。 Mach-O格式是iOS系統上應用程序運行的基礎,了解Mach-O的格式,對于調試、自動化測試、安全都有意義。在了解二進制文件的數據結構以后,一切就都顯得沒有秘密。 這里先提醒大家一下,Mach不是Mac,Mac是蘋果電腦Macintosh的簡稱,而Mach則是一種操作系統內核。Mach內核被NeXT公司的NeXTSTEP操作系統使用。在Mach上,一種可執行的文件格是就是Mach-O(Mach Object file format)。1996年,喬布斯將NeXTSTEP帶回蘋果,成為了OS X的內核基礎。所以雖然Mac OS X是Unix的“后代”,但所主要支持的可執行文件格式是Mach-O。 iOS是從OS X演變而來,所以同樣是支持Mach-O格式的可執行文件。 作為iOS客戶端開發者,我們比較熟悉的一種文件是ipa包(iPhone Application)。但實際上這只是一個變相的zip壓縮包,我們可以把一個ipa文件直接通過unzip命令解壓。 解壓之后,會有一個Payload目錄,而Payload里則是一個.app文件,而這個實際上又是一個目錄,或者說是一個完整的App Bundle。 在這個目錄中,里面體積最大的文件通常就是和ipa包同名的一個二進制文件。找到它,我們用file命令來看一下這個文件的類型: 由此看來,這是一個支持armv7和armv7s兩種處理器架構的通用程序包,里面包含的兩部分都是Mach-O格式。 對于一個二進制文件來講,每個類型都可以在文件最初幾個字節來標識出來,即“魔數”。比如PNG圖片的最初幾個字節是\211 P N G \r \n \032 \n (89 50 4E 47 0D 0A 1A 0A)。我們再來看下這個Mach-O universal binary的: 沒錯,開始的4個字節是cafe babe,即“Cafe baby”。了解Java或者說class文件格式的同學可能會很熟悉,這也是.class文件開頭的“魔數”,但貌似是Mach-O在更早的時候就是用了它。在OS X上,可執行文件的標識有這樣幾個魔數(也就是文件格式): cafebabe feedface feadfacf 還有一個格式,就是以#!開頭的腳本 cafebabe就是跨處理器架構的通用格式,feedface和feedfacf則分別是某一處理器架構下的Mach-O格式,腳本的就很常見了,比如#!/bin/bash開頭的shell腳本。 這里注意一點是,feedface和cafebabe的字節順序不同,我們可以用lipo把上面cafebabe的文件拆出armv7架構的,看一下開頭的幾個字節: 接下來我們再來看看這個Mach-O格式到底是什么樣的格式。我們可以通過二進制查看工具查看這個文件的數據,結果發現,不是所有數據都是相連的,而是被分成了幾個段落。 在一位叫做JOE SAVAGE的老兄發布的圖片上來看,Mach-O的文件數據顯現出來是這個樣子的: 大家可以對數據的分布感受下。 雖然被五顏六色的標記出來,可能這還不是特別直接。再來引用蘋果官方文檔的示意圖: 從這張圖上來看,Mach-O文件的數據主體可分為三大部分,分別是頭部(Header)、加載命令(Load commands)、和最終的數據(Data)。 回過頭來,我們再看上面那張圖,也許就都明白了。黃色部分是頭部、紅色是加載命令、而其它部分則是被分割成Segments的數據。 這里,我們用otool來看下Mach-O的頭部信息,得到: 更詳細的,我們可以通過otool的V參數得到翻譯版: 前面幾個字段的意義,上下對比就能看懂,我這里主要說下這樣幾個字段: filetype,這個可以有很多類型,靜態庫(.a)、單個目標文件(.o)都可以通過這個類型標識來區分。 ncmds和sizeofcmds,這個cmd就是加載命令,ncmds就是加載命令的個數,而sizeofcmds就是所占的大小。 flags里包含的標記很多,比如TWOLEVEL是指符號都是兩級格式的,符號自身+加上自己所在的單元,PIE標識是位置無關的。 上面頭部中的數據已經說明了整個Mach-O文件的基本信息,但整個Mach-O中最重要的還要數加載命令。它說明了操作系統應當如何加載文件中的數據,對系統內核加載器和動態鏈接器起指導作用。一來它描述了文件中數據的具體組織結構,二來它也說明了進程啟動后,對應的內存空間結構是如何組織的。 我們可以用otool -l xxx來看一個Mach-O文件的加載命令: 上面這段是執行結果的一部分,是加載PAGE_ZERO和TEXT兩個segment的load command。PAGE_ZERO是一段“空白”數據區,這段數據沒有任何讀寫運行權限,方便捕捉總線錯誤(SIGBUS)。TEXT則是主體代碼段,我們注意到其中的r-x,不包含w寫權限,這是為了避免代碼邏輯被肆意篡改。 我再提一個加載命令,LC_MAIN。這個加載指令,會聲明整個程序的入口地址,保證進程啟動后能夠正常的開始整個應用程序的運行。 除此之外,Mach-O里還有LC_SYMTAB、LC_LOAD_DYLIB、LC_CODE_SIGNATURE等加載命令,大家可以去官方文檔查找其含義。 至于Data部分,在了解了頭部和加載命令后,就沒什么特別可說的了。Data是最原始的編譯數據,里面包含了Objective-C的類信息、常量等。 本文是對Mach-O文件格式的一個理解小結,希望能夠拋磚引玉,幫助各位朋友把握可執行文件的主題脈絡,進而解決各類問題。 (來源:三·石道)0Mach與Mach-O
1iOS可執行文件初探
2Mach-O格式
3Mach-O頭部
4加載命令
CocoaChina 2015-08-23 08:44:21
稱謂:
内容: