生成目標(biāo)文件
目標(biāo)文件包含了機(jī)器指令代碼逐沙、數(shù)據(jù)、鏈接時(shí)需要的信息搏讶、符號(hào)表佳鳖、調(diào)試信息、字符串表媒惕。
1.不指定target
腋颠,默認(rèn)是Mach-O 64-bit object x86_64
clang-x c -g -c a.c -o a.o
-x: 指定編譯文件語言類型
-g: 生成調(diào)試信息
-c: 生成目標(biāo)文件,只運(yùn)行`preprocess`吓笙、`compile`淑玫、 `assemble`不鏈接
-o: 輸出文件
-I<directory>:在指定目錄尋找頭文件
-L <dir> :指定庫文件路徑(.a'.dylib庫文件)
-l<library_name> :指定鏈接的庫文件名稱(.a'.dylib庫文件)
-F<directory> :在指定鏈接的framework名稱
-framework <framework_name> framework
生成相應(yīng)的LLVM文件格式,來進(jìn)行鏈接時(shí)間優(yōu)化
當(dāng)我們配合著-S使用時(shí)面睛,生成匯編語言文件絮蒿。否則生成bitcode格式的目標(biāo)文件
-flto=<value> :設(shè)置LTO的模式:full or thin
-flto :設(shè)置LTO的模式:full
-flto=full:默認(rèn)值,單片(monolithic) LTO通過將所有輸入合并到單個(gè)模塊中來實(shí)現(xiàn)此目的
-flto=thin:使用ThinLTO代替
-emit-llvm
-install_name :指定動(dòng)態(tài)庫初次安裝時(shí)的默認(rèn)路徑叁鉴,向'LC_ID_DYLIB'添加安裝路徑土涝,該路徑作為dyld定位該庫。
clang -o
是將.c
源文件編譯成為一個(gè)可執(zhí)行的二進(jìn)制代碼(-o
選項(xiàng)其實(shí)是指定輸出文件文件名幌墓,如果不加-c
選項(xiàng), 會(huì)編譯鏈接生成可執(zhí)行文件但壮,文件的名稱由-o
選項(xiàng)指定)冀泻。
clang -c
是使用LLVM
匯編器將源文件轉(zhuǎn)化為目標(biāo)代碼。
2.指定生成Mach-O 64-bit x86-64
目標(biāo)文件格式
clang -x c -target x86_64-apple-macos11.1 -g -c a.c -o a.o
3.如果指定target
不帶apple
系統(tǒng)版本(包括macOS
蜡饵、ipadOS
弹渔、iOS
真機(jī)和模擬器)。例如x86_64
溯祸,那么生成的目標(biāo)文件是Linux
的 ELF 64-bit
clang -x c -target x86_64 -g -c a.c -o a.o
4. 編譯.m
macOS
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
iOS
clang -x c -g \
-target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPho neOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
-c a.c -o a.o
5. 編譯.mm
macOS
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
iOS模擬器
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 \
-c test.m -o test.o
在模擬器上鏈接其他三方庫
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/framework Headers 目錄/Headers \
-F/代碼庫目錄\
-c test.m -o test.o \
clang -target x86_64-apple-ios13.5-simulator \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/i PhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-F/代碼庫目錄 \
-fobjc-arc \
-framework AFNetworking \
-v test.o -o test \
clang -target x86_64-apple-ios13.5-simulator \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/i PhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.6.sdk \
-L/代碼庫目錄 \
-fobjc-arc \
-lAFNetworking \
-dead-strip test.o -o test
編譯成arm64真機(jī)
clang -target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk \
-L/代碼倉庫 \
-fobjc-arc \
-lAFNetworking \
test .o -o test \
clang -target arm64-apple-ios13.5 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.pla tform/Developer/SDKs/iPhoneOS13.6.sdk \
-F/代碼倉庫 \
-fobjc-arc \
-framework AFNetworking \
test.o -o test
6. 生成dSYM文件
clang -x c -g1 a.c -o a.o
-g1
:將調(diào)試信息寫入DWARF
格式文件
查看調(diào)試信息
dwarfdump
取出并驗(yàn)證DWARF
格式調(diào)試信息:
dwarfdump a.o
dwarfdump a.dSYM
dwarfdump --lookup 0x100000f20 --arch=x86_64 a.dSYM
--lookup
查看地址的調(diào)試信息肢专。將顯示出所在的目錄,文件焦辅,函數(shù)等信息博杖。
查看文件內(nèi)容
otool
用來查看Mach-o
文件內(nèi)部結(jié)構(gòu)
otool -l liba.dylib
otool -h libTest.a
-I
:顯示解析后的 mach header
和 load command
-h
:顯示未解析的mach header
-L
:打印所有鏈接的動(dòng)態(tài)庫路徑
-D
:打印當(dāng)前動(dòng)態(tài)庫的install_name
objdump
用來查看文件內(nèi)部結(jié)構(gòu),包括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
:打印各個(gè)段的基本信息
-x
:打印各個(gè)段更詳細(xì)的信息
-d
:將所有包含指定的段反匯編
-s
:將所有段的內(nèi)容以16
進(jìn)制的方式打印出來
--lazy-bind
:打印 lazy binding info
--syms
:打印符號(hào)表
靜態(tài)庫的壓縮和解壓縮
ar
壓縮目標(biāo)文件筷登,并對(duì)其進(jìn)行編號(hào)和索引剃根,形成靜態(tài)庫。同時(shí)也可以解壓縮靜態(tài)庫前方,查看有哪些目標(biāo)文件 狈醉。
ar -rc a.a a.o
-r
:添加or
替換文件
-c
:不輸出任何信息
-t
:列出包含的目標(biāo)文件
創(chuàng)建靜態(tài)庫
創(chuàng)建庫命令:libtool
,可以創(chuàng)建靜態(tài)庫和動(dòng)態(tài)庫镣丑。
//macOS
libtool -static -arch_only x86_64 a.o -o a.a
//iOS
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)建動(dò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
查看符號(hào)表
nm
命令:
nm -pa a.o
-a
:顯示符號(hào)表的所有內(nèi)容
-g
:顯加全局符號(hào)
-p
:不排序舔糖。顯示符號(hào)表本來的順序
-r
:逆轉(zhuǎn)順序
-u
:顯示未定義符號(hào)
生成dSYM文件
dsymutil
可以被理解為是調(diào)試信息鏈接器。
它按照以下的步驟執(zhí)行:
- 讀取
debug map
- 從
.o
文件中加載DWARF
- 重新定位所有地址
- 最后將全部的
DWARF
打包成dSYM Bundle
有了 dSYM
后莺匠,我們就擁有了最標(biāo)準(zhǔn)的DWARF
的文件金吗,任何dwarf
讀取工具(可以處理Mach-O
二進(jìn)制文件)都可以處理該標(biāo)準(zhǔn)DWARF
。
dsymutil
操作DWARF
格式的debug symbol
趣竣∫∶恚可以將可執(zhí)行文件debug symbol
生成DWARF
格式的文件:
dsymutil -f a -o a.dSYM
-f
: .dwarf
格式文件
-o <filename>
:輸出.dSYM
格式文件
移除符號(hào)
strip
用來移除和修改符號(hào)表
strip -S a.o
-S
:刪除調(diào)試符號(hào)
-X
:移除本地符號(hào),L
開頭的
-x
:移除全部的本地符號(hào)遥缕,只保留全局符號(hào)
鏈接器
ld
-all_load
:加載靜態(tài)庫的包含的所有文件卫袒。
-ObjC
:加載靜態(tài)庫的包含的所有義的Objective-C
類和Category
。
-force_load <path_to_archive>
:加載靜態(tài)庫中指定的文件单匣。
鏈接動(dòng)態(tài)庫與靜態(tài)庫
ld -dylib -arch x86_64 -macosx_version_min 10.13 a.dylib -o a
Id -static -arch x86_64 -e _main a.a -o a
Xcode打印加載的庫
Pre-main Time
指main
函數(shù)執(zhí)行之前的加載時(shí)間夕凝,包括dylib
動(dòng)態(tài)庫加載, Objective-C Runtime
加載等。
Xcode
自身提供了一個(gè)在控制臺(tái)打印這些時(shí)間的方法户秤,在Xcode
中Edit Scheme -> Run -> Auguments
添加環(huán)境變量 DYLD_PRINT_STATISTICS
并把其值設(shè)為 1
码秉。
DYLD_PRINT_LIBRARIES
:打印出所有被加載的庫。
DYLD_PRINT_LIBRARIES_POST_LAUNCH
:打印的是通過dlopen
調(diào)用返回的庫鸡号,包括動(dòng)態(tài)庫的依賴庫转砖,主要發(fā)生在main
函數(shù)運(yùn)行之后鲸伴。
二進(jìn)制重排
鏈接 order.file
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
:生成二進(jìn)制用到的link單元的路徑和文件編號(hào)
Sections
: 記錄Mach-O
每個(gè) Segment/section
的地址范圍
Symbols
:按順序記錄每個(gè)符號(hào)的地址范圍
install_name_tool
更改動(dòng)態(tài)共享庫的安裝名稱并操縱運(yùn)行路徑。
install_name_tool -add_rpath <directory> libs_File
install_name_tool -delete_rpath <directory> libs_File
install_name_tool -rpath <old> <new> libs_File
概念說明
生成目標(biāo)文件的過程中發(fā)生了什么?
- 編譯器(
clang-cl
)->匯編器(llvm-as
) - 鏈接器(
llvm-ld
)并沒有被執(zhí)行
所以輸出的目標(biāo)文件不會(huì)包含Unix
程序在被裝載和執(zhí)行時(shí)所必須的包含信息姓赤,但它以后可以被鏈接到一個(gè)程序赡译。
Mach-o File Format
—個(gè)Mach-o
文件有兩部分組成:header
和data
。
-
header
:代表了文件的映射模捂,描述了文件的內(nèi)容以及文件所有內(nèi)容所在的位置捶朵。header
包含三種類型:Mach header
,segment
,sections
蜘矢。header
內(nèi)的section
描述了對(duì)應(yīng)的二進(jìn)制信息狂男。 -
data
:緊跟header
之后,由多個(gè)二進(jìn)制組成品腹,one by one岖食。
-
Mach header
:屬于header
的一部分,它包含了整個(gè)文件的信息和segment
信息舞吭。 -
Segments(segment commands)
:指定操作系統(tǒng)應(yīng)該將Segments
加載到內(nèi)存中的什么位置泡垃,以及為該Segments
分配的字節(jié)數(shù)。還指定文件中的哪些字節(jié)屬于該Segments
,以及文件包含多少sections
羡鸥。始終是4096
字節(jié)或4KB
的倍數(shù)蔑穴,其中4096
字節(jié)是最小大小。 -
Section
:所有sections
都在每個(gè)segment``之后一個(gè)接一個(gè)地描述惧浴。sections
里面定義其名稱存和,在內(nèi)存中的地址,大小衷旅,文件中section
數(shù)據(jù)的偏移量和segment
名稱捐腿。
Load Commands
二進(jìn)制文件加載進(jìn)內(nèi)存要執(zhí)行的一些指令。
這里的指令主要在負(fù)責(zé)我們APP
對(duì)應(yīng)進(jìn)程的創(chuàng)建和基本設(shè)置(分配虛擬內(nèi)存柿顶,創(chuàng)建主線程茄袖,處理代碼簽名/加密的工作),然后對(duì)動(dòng)態(tài)鏈接庫(.dylib
系統(tǒng)庫和我們自己創(chuàng)建的動(dòng)態(tài)庫)進(jìn)行庫加載和符號(hào)解析的工作嘁锯。