目錄
- 一、生成目標文件: .o
- 二、查看調試信息
- 三、查看文件內容
- 四、靜態(tài)庫的壓縮和解壓縮
- 五、創(chuàng)建靜態(tài)庫
- 六、創(chuàng)建動態(tài)庫
- 七、查看符號表
- 八、生成dSYM文件
- 九、移除符號
- 十、鏈接器
- 十一、鏈接動態(tài)庫與靜態(tài)庫
- 十二、Xcode打印加載的庫
- 十三、二進制重排
- 十四、Mach-O File Format
一、生成目標文件: .o
目標文件包含了機器指令代碼、數(shù)據(jù),鏈接時需要的信息,符號表、調試信息,字符串表。
- 不指定
target, 默認是Mach-O 64-bit object x86_64:
clang -x c -g -c a.c -o a.o
-x:指定編譯文件語言類型
-g:生成調試信息
-c:生成目標文件,只運行preprocess, compile, assemble, 不鏈接
-o:輸出文件
-I<directory>在指定目錄尋找頭文件
-L<dir>指定庫文件路徑(. a\.dylib庫文件)
-l<library_name>指定鏈接的庫文件名稱(. a\.dylib庫文件)
-F<directory>在指定目錄尋找framework頭文件
-framework <framework_name>在指定鏈接的framework名稱生成相應的LLVM文件格式,來進行鏈接時間優(yōu)化
當我們配合著-S使用時,生成匯編語言文件。否則生成bitcode格式的目標文件
-flto=<value>設置LTO的模式: full or thin
-flto 設置LTO的模式: full
-flto=full, 默認值,單片(monolithic) LTO通過將所有輸入合并到單個模塊中來實現(xiàn)此目的
-flto=thin,使用ThinLTO代替
-emit-llvm
-install_name指定動態(tài)庫初次安裝時的默認路徑,向'LC_ID_DYLIB '添加安裝路徑,該路徑作為dyld定位該庫。
clang -o是將.c源文件編譯成為一個可執(zhí)行的二進制代碼( -o 選項其實是指定輸出文件文件名,如果不加-c選項,clang默認會編譯鏈接生成可執(zhí)行文件,文件的名稱由-o選項指定)。
clang-c是使用LLVM匯編器將源文件轉化為目標代碼。
- 指定生成
Mach-O 64-bit x86-64目標文件格式:
clang -x c -target x86_64-apple-macos10.15 -g -c a.c -o a.o
- 如果指定
target不帶apple系統(tǒng)版本(包括macOS,ipadOS,iOS,真機和模擬器)。例如x86_64, 那么生成的目標文件是Linux的ELF 64-bit:
clang -x c -target x86_64 -g -c a.c -o a.o
- 編譯
.m:
clang -x objective-c -target x86_64-apple-macos10.15 \
-fobjc-arc -fmodules -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-c test.m -o test.o
clang -x c -g -target arm64- apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
-c a.c -o a.o
- 編譯
.mm:
在mac上編譯:
clang -x objective-c++ \
-target x86_64-apple -macos10.15 \
-std=c++11 \
-stdlib=libc++ \
-fobjc-arc \
-fmodules \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-c test.mm -o test.o
在模擬器上編譯:
clang -x objective-c \
-target x86_64-apple-ios13.5-simulator \
-fobjc-arc \
-fmodules \
-isysroot /Applications/Xcodе.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-c test.m o test.0
在模擬器上鏈接其他三方庫:
clang -x objective-c \
-target x86_64-apple-ios13.5-simulator \
-fobjc-arc \
- fmodules \
-isysroot /Applications/Xcode. app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-I/Users/ws/Desktop/Library/AFNetworking.framework/Headers -F/Users/ws/Desktop/Library \
-c test.m -o test.o
clang -target x86_64-apple-ios13.5-simulator \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-F/Users/ws/Desktop/Library \
-fobjc-arc \
-framework AFNetworking \
-v test.o -o test
clang -target x86_64-apple-ios13.5-simulator \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-L/Users/ws/Desktop/Library \
-fobjc-arc \
-lAFNetworking \
-dead-strip \
test.o -o test
編譯成arm64真機:
clang -target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
-L/Users/ ws/Desktop/Library \
-fobjc-arc \
-lAFNetworking \
test.o -o test
clang -target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
-F/Users/ws/Desktop//Library \
-fobjc-arc \
-framework AFNetworking \
test.o -o test
- 生成
dSYM文件:
clang -x c -g1 a.c -o a.o
-g1: 將調試信息寫入DWARF格式文件
二、查看調試信息
dwarfdump取出并驗證DWARF格式調試信息:
dwarfdump a.o
dwarfdump a.dSYM
dwarfdump --lookup 0x100000f20 --arch=x86_64 a.dSYM
--lookup查看地址的調試信息。將顯示出所在的目錄,文件,函數(shù)等信息
三、查看文件內容
otool用來查看Mach-O文件內部結構:
otool -l liba.dylib
otool -h libTest.a
-l:顯示解析后的mach header和load command
-h:顯示未解析的mach header
-L:打印所有鏈接的動態(tài)庫路徑
-D:打印當前動態(tài)庫的install_ name
objdump用來查看文件內部結構,包括ELF和Mach-O:
objdump --macho -h a.o
objdump --macho -x a.o
objdump --macho -s -d a.o
objdump --macho --syms a.o
--macho:指定Mach-O類型
-h: 打印各個段的基本信息
-x: 打印各個段更詳細的信息
-d: 將所有包含指定的段反匯編
-s:將所有段的內容以16進制的方式打印出來
--lazy-bind:打印lazy binding info
--syms打印符號表
四、靜態(tài)庫的壓縮和解壓縮
ar壓縮目標文件,并對其進行編號和索引,形成靜態(tài)庫。同時也可以解壓縮靜態(tài)庫,查看有哪些目標文件:
ar -rc a.a a.o
-r:添加or替換文件
-c: 不輸出任何信息
-t:列出包含的.o目標文件
五、創(chuàng)建靜態(tài)庫
創(chuàng)建庫命令:libtool。 可以創(chuàng)建靜態(tài)庫和動態(tài)庫:
libtool -static -arch_only x86_64 a.o -o a.a
libtool -static -arch_only arm64 \
-D \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
test.o -o libTest.a
六、創(chuàng)建動態(tài)庫
clang -dynamiclib \
-target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
a.o -o a.dylib
七、查看符號表
nm命令:
nm -pa a.o
-a:顯示符號表的所有內容
-g:顯示全局符號
-p:不排序。顯示符號表本來的順序
-r:逆轉順序
-u: 顯示未定義符號
八、生成dSYM文件
dsymutil可以被理解為是調試信息鏈接器。它按照上面的步驟執(zhí)行:
- 讀取
debug map - 從
.o文件中加載DWARF - 重新定位所有地址
- 最后將全部的
DWARF打包成dSYM Bundle
有了dsYM后,我們就擁有了最標準的DWARF的文件,任何可以dwarf讀取工具(可以處理Mach-O二進制文件)都可以處理該標準DWARF)。
dsymutil操作DWARF格式的debug symbol。 可以將可執(zhí)行文件debug symbol的生成DWARF格式的文件:
dsymutil -f a -o a.dSYM
-f: .dwarf格式文件
-o <filename>: 輸出.dSYM格式文件
九、移除符號
strip用來移除和修改符號表:
strip -S a.o
-S刪除調試符號
-X移除本地符號,‘L '開頭的
-x移除全部的本地符號,只保留全局符號
十、鏈接器
ld
-all_load加載靜態(tài)庫的包含的所有文件。
-ObjC 加載靜態(tài)庫的包含的所有義的Objective-C類和Category.
-force_load <path_to_archive> 加載靜態(tài)庫中指定的文件
十一、鏈接動態(tài)庫與靜態(tài)庫
ld -dylib -arch x86_64 -macosx_version_min 10.13 a.dylib -o a
ld -static -arch x86_64 -e _main a.a -o a
十二、Xcode打印加載的庫
Pre-main Time指main函數(shù)執(zhí)行之前的加載時間,包括dylib動態(tài)庫加載, Mach-O 文件加載, Rebase/Binding,Objective-C Runtime加載等。
Xcode自身提供了一個在控制臺打印這些時間的方法:在Xcode中Edit Scheme -> Run -> Auguments添加環(huán)境變量DYLD_PRINT_STATISTICS并把其值設為1。
DYLD_PRINT_LIBRARIES:打印出所有被加載的庫。
DYLD_PRINT_LIBRARIES_POST_LAUNCH:打印的是通過dlopen調用返回的庫,包括動態(tài)庫的依賴庫,主要發(fā)生在main函數(shù)運行之后。
十三、二進制重排
鏈接order.fle
ld -o test test.o -lsystem -order_file test.order
ld -o test test.o -lsystem -lc++ -framework Foundation -order_file test.order
ld -map output.map -lsystem -o output a.o
生成Link Map文件
ld -map output .map -lsystem -lc++ -framework Foundation test.o -o output
-map map_file_path生成map文件
主要包括三大部分:
Object Files: 生成二進制用到的`link`單元的路徑和文件編號
Sections:記錄 Mach-O 每個Segment/section 的地址范圍
Symbols:按順序記錄每 個符號的地址范圍
install_name_tool
更改動態(tài)共享庫的安裝名稱并操縱運行路徑
install_name_tool -add_rpath <directory> libs_File
install name_tool -delete_rpath <directory> libs_File
install_name_tool -rpath <old> <new> libs_File
生成目標文件的過程中發(fā)生了什么?
編譯器(clang-cl) --> 匯編器(lvm-as)
鏈接器(lvm-ld)并沒有被執(zhí)行
所以輸出的目標文件不會包含Unix程序在被裝載和執(zhí)行時所必須的包含信息,但它以后可以被鏈接到一個程序。
十四、Mach-O File Format
一個Mach-O文件由兩部分組成header 和data。
header:代表了文件的映射,描述了文件的內容以及文件所有內容所在的位置。
data:緊跟header之后,由多個二進制組成,one by one。
header:
header:包含三種類型。Mach header, segment, sections
header內的section描述了對應的二進制信息。
注意:Mach header屬于header的一部分,它包含了整個文件的信息和segment信息segment:
Segments(segment commands);指定操作系統(tǒng)應該將Segments加載到內存中的什么位置,以及為該Segments分配的字節(jié)數(shù)。還指定文件中的哪些字節(jié)屬于該Segments,以及文件包含多少sections。始終是4096字節(jié)或4KB的倍數(shù),其中4096字節(jié)是最小大小。sections:
Section:所有sections都在每個segment之后一個接個地描述。sections里面定義其名稱,在內存中的地址,大小,文件中section數(shù)據(jù)的偏移量和segment名稱。
Load Commands
進制文件加載進內存要執(zhí)行的一些指令。
這里的指令主要在負責我們APP對應進程的創(chuàng)建和基本設置(分配虛擬內存,創(chuàng)建主線程,處理代碼簽名/加密的工作),然后對動態(tài)鏈接庫(.dylib系統(tǒng)庫和我們自己創(chuàng)建的動態(tài)庫)進行庫加載和符號解析的工作。