OC 編譯過(guò)程

Objective-C文件的編譯過(guò)程主要包括clang前端的預(yù)處理、編譯、后端優(yōu)化中間表示褒翰、生成匯編指令、鏈接匀泊、生成機(jī)器碼這幾個(gè)步驟优训。我們可以借助clang -ccc-print-phases xxx.m命令查看某個(gè)OC源文件的編譯的過(guò)程,如下: 輸入命令

<pre class="prism-token token language-javascript">clang -ccc-print-phases main.m</pre>

命令行輸出

<pre class="prism-token token language-javascript">0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image</pre>

可見(jiàn)編譯過(guò)程先后是讀取源文件各聘、預(yù)處理揣非、編譯生成中間表示、生成匯編碼躲因、鏈接生成image早敬、綁定架構(gòu)生成對(duì)應(yīng)機(jī)器碼忌傻。本篇文章我們著重分析預(yù)處理、編譯搞监、生成匯編代碼水孩、鏈接這4個(gè)步驟。

預(yù)處理

通常琐驴,一個(gè)源程序可能被分割為多個(gè)模塊俘种,并存放于獨(dú)立的文件中,把源程序“聚合”在一起的任務(wù)叫做預(yù)處理绝淡。預(yù)處理操作由預(yù)處理器獨(dú)立完成宙刘。正如我們所知,預(yù)處理器通常把那些稱(chēng)為宏的縮寫(xiě)形式轉(zhuǎn)換為源語(yǔ)言的語(yǔ)句牢酵。比如宏定義悬包、條件編譯、文件包含茁帽。 如下命令可以對(duì).c玉罐、.m源文件進(jìn)行預(yù)處理,其中參數(shù)-E就是對(duì)源文件進(jìn)行預(yù)處理操作:

<pre class="prism-token token language-javascript">clang -E xxx.m</pre>

如果我們的.m文件中import(文件包含)了其他的文件或者其他的庫(kù)潘拨,執(zhí)行以上命令對(duì)OC源文件進(jìn)行預(yù)處理可能會(huì)遇到如下錯(cuò)誤:

解決辦法: 添加-isysroot參數(shù)來(lái)指定iPhoneSimulator.sdk的路徑

-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
預(yù)處理 -E

<pre class="prism-token token language-javascript">clang -x objective-c -E -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m</pre>

當(dāng)然吊输,把-E換成 -rewrite-objc就可以把OC源文件轉(zhuǎn)成C++文件,如下: OC轉(zhuǎn)C++ -rewrite-objc

<pre class="prism-token token language-javascript">clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m</pre>

除了使用Clang查看OC源文件的預(yù)處理過(guò)程铁追,還可以使用xcrun命令季蚂,如下: 注意下面的命令中必須加上-E(-E代表對(duì)文件進(jìn)行預(yù)處理)

<pre class="prism-token token language-javascript">xcrun -sdk iphoneos clang -arch armv7 -F Foundation -E -fobjc-arc -c main.m</pre>

如果要生成.o目標(biāo)文件,只需要在最后指定目標(biāo)文件的名稱(chēng)琅束,如下:

<pre class="prism-token token language-javascript">xcrun -sdk iphoneos clang -arch armv7 -F Foundation -E -fobjc-arc -c main.m -o main.o</pre>

編譯

詞法分析

編譯器中負(fù)責(zé)將程序分解為一個(gè)一個(gè)符號(hào)的部分扭屁,一般稱(chēng)為“詞法分析器”(引用自《C Traps and Pitfalls》)。 詞法分析器讀入組成源程序的字符流涩禀,并且將他們組織成為有意義的詞素(lexeme)序列料滥。對(duì)于每個(gè)詞素,詞法分析器產(chǎn)生詞法單元token(符號(hào))作為輸出(引用自《編譯原理》)艾船。 token指的是程序的一個(gè)基本組成單元—詞法單元葵腹。token的作用相當(dāng)于一個(gè)句子中的單詞,從某種意義上來(lái)說(shuō)屿岂,一個(gè)單詞無(wú)論出現(xiàn)在哪個(gè)句子中难裆,它代表的意思都是一樣的诗充,是一個(gè)表義的基本單元弦疮。與此類(lèi)似匀伏,token就是程序中的一個(gè)基本信息單元。詞法分析器將源文件的字符流轉(zhuǎn)換為token的過(guò)程被稱(chēng)作詞法分析(lexical anaysis)运授。

對(duì)某一個(gè)源文件進(jìn)行詞法分析烤惊,可以使用下面這個(gè)命令

<pre class="prism-token token language-javascript">clang -fmodules -E -Xclang -dump-tokens main.m</pre>

當(dāng)然乔煞,和預(yù)處理一樣,如果源文件中有import其他文件撕氧,那么還需要使用-isysroot 參數(shù)來(lái)指定iPhoneSimulator.sdk的路徑瘤缩,如下:

<pre class="prism-token token language-javascript">clang -fmodules -E -Xclang -dump-tokens -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m</pre>

本文本著示例簡(jiǎn)單、避免干擾的原則伦泥,將會(huì)對(duì)如下源碼進(jìn)行詞法分析

<pre class="prism-token token language-javascript">//
// main.m
// ArrayWithArray:
//
// Created by sw on 2018/1/5.
// Copyright ? 2018年 sw. All rights reserved.
//

include <stdio.h>

define name "ws"

int main(int argc, char * argv[]) {
@autoreleasepool {
printf("hello %s", name);
return 1;
}
}</pre>

如下是main.m文件詞法分析后的結(jié)果

<pre class="prism-token token language-javascript">annot_module_include '#include <stdio.h>

define name "ws"

int main(int argc, char * argv[]) {
@autoreleasepool {
printf("hello %s", name);
' Loc=<main.m:9:1>
int 'int' [StartOfLine] Loc=<main.m:12:1>
identifier 'main' [LeadingSpace] Loc=<main.m:12:5>
l_paren '(' Loc=<main.m:12:9>
int 'int' Loc=<main.m:12:10>
identifier 'argc' [LeadingSpace] Loc=<main.m:12:14>
comma ',' Loc=<main.m:12:18>
char 'char' [LeadingSpace] Loc=<main.m:12:20>
star '*' [LeadingSpace] Loc=<main.m:12:25>
identifier 'argv' [LeadingSpace] Loc=<main.m:12:27>
l_square '[' Loc=<main.m:12:31>
r_square ']' Loc=<main.m:12:32>
r_paren ')' Loc=<main.m:12:33>
l_brace '{' [LeadingSpace] Loc=<main.m:12:35>
at '@' [StartOfLine] [LeadingSpace] Loc=<main.m:13:5>
identifier 'autoreleasepool' Loc=<main.m:13:6>
l_brace '{' [LeadingSpace] Loc=<main.m:13:22>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<main.m:14:9>
l_paren '(' Loc=<main.m:14:15>
string_literal '"hello %s"' Loc=<main.m:14:16>
comma ',' Loc=<main.m:14:26>
string_literal '"ws"' [LeadingSpace] Loc=<main.m:14:28 <Spelling=main.m:10:14>>
r_paren ')' Loc=<main.m:14:32>
semi ';' Loc=<main.m:14:33>
return 'return' [StartOfLine] [LeadingSpace] Loc=<main.m:15:9>
numeric_constant '1' [LeadingSpace] Loc=<main.m:15:16>
semi ';' Loc=<main.m:15:17>
r_brace '}' [StartOfLine] [LeadingSpace] Loc=<main.m:16:5>
r_brace '}' [StartOfLine] Loc=<main.m:17:1>
eof '' Loc=<main.m:17:2></pre>

如上,int 'int' [StartOfLine] Loc=<main.m:12:1>就是一個(gè)token锦溪,identifier 'main' [LeadingSpace] Loc=<main.m:12:5>也是一個(gè)token不脯。每個(gè)token后面的Loc代表這個(gè)token在源文件中的位置。例如Loc=<main.m:12:1>代表這個(gè)token位于main.m文件中的第12行第1個(gè)位置刻诊。注意:這里的位置是從1開(kāi)始防楷,而非0。 而上述token中的'int'则涯、'main' 就是詞素复局。 ps:由上面詞法分析后的結(jié)果和源文件對(duì)照可知,注釋雖然沒(méi)有真實(shí)的意義粟判,但是注釋占用的行依舊是有效的亿昏,在詞法分析階段并沒(méi)有被忽略掉。這樣的好處是档礁,詞法分析后的token的Loc就是真實(shí)的Location角钩。

語(yǔ)法分析

將詞法分析的token解析處理成抽象語(yǔ)法樹(shù)AST(abstract syntax tree)的過(guò)程稱(chēng)作語(yǔ)法分析(semantic analysis)。即語(yǔ)法分析的輸入是token呻澜,輸出是AST递礼。AST則更加直觀(guān)的反映了代碼的內(nèi)部結(jié)構(gòu)和邏輯。使用clang -fmodules -fsyntax-only -Xclang -ast-dump main.m可以對(duì)源文件進(jìn)行語(yǔ)法分析羹幸,如下:

<pre class="prism-token token language-javascript">TranslationUnitDecl 0x7f8c10009ee8 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x7f8c1000a780 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| -BuiltinType 0x7f8c1000a480 '__int128' |-TypedefDecl 0x7f8c1000a7e8 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128' |-BuiltinType 0x7f8c1000a4a0 'unsigned __int128'
|-TypedefDecl 0x7f8c1000a880 <<invalid sloc>> <invalid sloc> implicit SEL 'SEL *'
| -PointerType 0x7f8c1000a840 'SEL *' |-BuiltinType 0x7f8c1000a6e0 'SEL'
|-TypedefDecl 0x7f8c1000a958 <<invalid sloc>> <invalid sloc> implicit id 'id'
| -ObjCObjectPointerType 0x7f8c1000a900 'id' |-ObjCObjectType 0x7f8c1000a8d0 'id'
|-TypedefDecl 0x7f8c1000aa38 <<invalid sloc>> <invalid sloc> implicit Class 'Class'
| -ObjCObjectPointerType 0x7f8c1000a9e0 'Class' |-ObjCObjectType 0x7f8c1000a9b0 'Class'
|-ObjCInterfaceDecl 0x7f8c1000aa88 <<invalid sloc>> <invalid sloc> implicit Protocol
|-TypedefDecl 0x7f8c100275e8 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| -RecordType 0x7f8c10027400 'struct __NSConstantString_tag' |-Record 0x7f8c1000ab50 '__NSConstantString_tag'
|-TypedefDecl 0x7f8c10027680 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char '
| -PointerType 0x7f8c10027640 'char *' |-BuiltinType 0x7f8c10009f80 'char'
|-TypedefDecl 0x7f8c10027948 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'struct __va_list_tag [1]'
| -ConstantArrayType 0x7f8c100278f0 'struct __va_list_tag [1]' 1 |-RecordType 0x7f8c10027770 'struct __va_list_tag'
| -Record 0x7f8c100276d0 '__va_list_tag' |-ImportDecl 0x7f8c100281c0 <main.m:9:1> col:1 implicit Darwin.C.stdio |-FunctionDecl 0x7f8c100f6a78 <line:12:1, line:17:1> line:12:5 main 'int (int, char **)' | |-ParmVarDecl 0x7f8c10028210 <col:10, col:14> col:14 argc 'int' | |-ParmVarDecl 0x7f8c10028320 <col:20, col:32> col:27 argv 'char **':'char **' |-CompoundStmt 0x7f8c100f7188 <col:35, line:17:1>
| -ObjCAutoreleasePoolStmt 0x7f8c100f7178 <line:13:5, line:16:5> |-CompoundStmt 0x7f8c100f7158 <line:13:22, line:16:5>
| |-CallExpr 0x7f8c100f70a0 <line:14:9, col:32> 'int'
| | |-ImplicitCastExpr 0x7f8c100f7088 <col:9> 'int (
)(const char *, ...)' <FunctionToPointerDecay>
| | | -DeclRefExpr 0x7f8c100f6f58 <col:9> 'int (const char *, ...)' Function 0x7f8c100f6b88 'printf' 'int (const char *, ...)' | | |-ImplicitCastExpr 0x7f8c100f70f0 <col:16> 'const char *' <BitCast> | | |-ImplicitCastExpr 0x7f8c100f70d8 <col:16> 'char *' <ArrayToPointerDecay>
| | | -StringLiteral 0x7f8c100f6fb8 <col:16> 'char [9]' lvalue "hello %s" | |-ImplicitCastExpr 0x7f8c100f7108 <line:10:14> 'char *' <ArrayToPointerDecay>
| | -StringLiteral 0x7f8c100f7028 <col:14> 'char [3]' lvalue "ws" |-ReturnStmt 0x7f8c100f7140 <line:15:9, col:16>
| -IntegerLiteral 0x7f8c100f7120 <col:16> 'int' 1-<undeserialized declarations></pre>

有了抽象語(yǔ)法樹(shù)脊髓,clang就可以對(duì)這個(gè)樹(shù)進(jìn)行分析,找出代碼中的錯(cuò)誤栅受,很多編譯期的檢查都是針對(duì)于抽象語(yǔ)法樹(shù)的檢查将硝。比如類(lèi)型不匹配,未實(shí)現(xiàn)對(duì)應(yīng)的方法窘疮。

AST是開(kāi)發(fā)者編寫(xiě)clang插件主要交互的數(shù)據(jù)結(jié)構(gòu)袋哼,clang也提供很多API去讀取AST。詳情參考:Introduction to the Clang AST闸衫。

語(yǔ)義分析

使用語(yǔ)法分析產(chǎn)生的語(yǔ)法樹(shù)和符號(hào)表檢查源程序是否和語(yǔ)言定義的語(yǔ)義一致的過(guò)程被稱(chēng)為語(yǔ)義分析涛贯。這個(gè)定義聽(tīng)起來(lái)比較繞,后面會(huì)解釋蔚出。語(yǔ)義分析的過(guò)程同時(shí)也收集類(lèi)型信息弟翘,并把類(lèi)型信息存儲(chǔ)在語(yǔ)法樹(shù)或符號(hào)表中虫腋,以便隨后的中間代碼生成過(guò)程中使用。 語(yǔ)義分析一個(gè)重要的部分就是“類(lèi)型檢查”和“自動(dòng)類(lèi)型轉(zhuǎn)換”稀余。編譯器檢查每個(gè)運(yùn)算符是否有匹配的運(yùn)算分量悦冀。所謂運(yùn)算分量就是指被運(yùn)算符操作的量。拿C語(yǔ)言的語(yǔ)義分析舉例睛琳,比如a + b, 其中“+”就是運(yùn)算符盒蟆,a和b就是這個(gè)運(yùn)算符的分量。如果a和b都是整型或浮點(diǎn)型师骗,這說(shuō)明“+”運(yùn)算符具有匹配的運(yùn)算分量历等。如果a或b其中一個(gè)是字符串類(lèi)型,則說(shuō)明“+”運(yùn)算符不具備匹配的運(yùn)算分量辟癌。又比如寒屯,很多語(yǔ)言中要求數(shù)組的下標(biāo)是一個(gè)非負(fù)整數(shù),如果浮點(diǎn)數(shù)作為下標(biāo)黍少,編譯器就必須報(bào)告錯(cuò)誤寡夹。

生成中間代碼

在把源程序翻譯成目標(biāo)代碼的過(guò)程中,一個(gè)編譯器可能構(gòu)造出一個(gè)或多個(gè)中間表示(Intermediate Representation或IR)厂置。這些中間表示可以有多種形式菩掏。語(yǔ)法樹(shù)(AST)就是一種中間表示形式。--摘抄自《編譯原理》 我們已經(jīng)知道农渊,語(yǔ)法分析生成AST患蹂,語(yǔ)義分析會(huì)對(duì)根據(jù)AST和符號(hào)表對(duì)源程序進(jìn)行檢查。那么語(yǔ)法分析和語(yǔ)義分析都完成后砸紊,clang會(huì)遍歷AST生成一種明確的传于、低級(jí)的或類(lèi)機(jī)器語(yǔ)言的中間表示。LLVM IR是LLVM套件里面的中間表示(LLVM Intermediate Representation)醉顽,LLVM IR也是前端(clang)的輸出沼溜,后端的輸入。 LLVM IR有3種表示形式游添,分別是:

  • text格式:便于閱讀的文本格式系草,類(lèi)似于匯編語(yǔ)言,拓展名.ll唆涝,

xcrun clang -S -emit-llvm main.c -o main.ll

  • memory格式:內(nèi)存格式
  • bitcode格式:二進(jìn)制格式找都,拓展名.bc, $ clang -c -emit-llvm main.m ps:以上三種形式的本質(zhì)是等價(jià)的廊酣,就好比水可以有氣體能耻、液體、固體3種形態(tài)。 我們使用clang -S -emit-llvm main.m命令來(lái)獲取text格式的文件晓猛,文件后綴名是.ll饿幅,使用文本編輯器即可打開(kāi),如下:

<pre class="prism-token token language-javascript">; ModuleID = 'main.m'
source_filename = "main.m"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

@.str = private unnamed_addr constant [9 x i8] c"hello %s\00", align 1
@.str.1 = private unnamed_addr constant [3 x i8] c"ws\00", align 1

; Function Attrs: noinline optnone ssp uwtable
define i32 @main(i32, i8) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8
, align 8
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
%6 = call i8* @objc_autoreleasePoolPush() #2
%7 = call i32 (i8, ...) @printf(i8 getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0))
store i32 1, i32* %3, align 4
call void @objc_autoreleasePoolPop(i8* %6)
%8 = load i32, i32* %3, align 4
ret i32 %8
}

declare i8* @objc_autoreleasePoolPush()

declare i32 @printf(i8*, ...) #1

declare void @objc_autoreleasePoolPop(i8*)

attributes #0 = { noinline optnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
!llvm.ident = !{!8}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 10, i32 14]}
!1 = !{i32 1, !"Objective-C Version", i32 2}
!2 = !{i32 1, !"Objective-C Image Info Version", i32 0}
!3 = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
!4 = !{i32 4, !"Objective-C Garbage Collection", i32 0}
!5 = !{i32 1, !"Objective-C Class Properties", i32 64}
!6 = !{i32 1, !"wchar_size", i32 4}
!7 = !{i32 7, !"PIC Level", i32 2}
!8 = !{!"Apple LLVM version 10.0.1 (clang-1001.0.46.4)"}</pre>

Clang還會(huì)收集源程序的信息戒职,并把信息存放在符號(hào)表(symbol table)中栗恩。符號(hào)表和LLVM IR會(huì)被傳遞給后端。

代碼生成

代碼生成(CodeGen)由代碼生成器完成洪燥。以源程序的中間表示(IR)作為輸入磕秤,并把它映射到目標(biāo)語(yǔ)言。如果目標(biāo)語(yǔ)言是機(jī)器代碼蚓曼,那么就必須為程序使用的每個(gè)變量選擇寄存器或內(nèi)存位置亲澡。然后中間指令被翻譯成為能夠完成相同任務(wù)的機(jī)器指令序列。代碼生成的一個(gè)至關(guān)重要的方面是合力分配寄存器以存放變量的值纫版。

LLVM IR

有些編譯器的結(jié)構(gòu)單純的分為前端和后端,比如GCC客情。而LLVM的結(jié)構(gòu)并不是單純的分為前端和后端其弊。LLVM編譯器集合是圍繞著一組精心設(shè)計(jì)的中間表示形式而創(chuàng)建的,這些中間表示形式使得我們可以把特定語(yǔ)言的前端和特定目標(biāo)機(jī)的后端相結(jié)合膀斋。使用這些集合梭伐,我們可以把不同的前端和某個(gè)目標(biāo)機(jī)的后端結(jié)合起來(lái),為不同的源語(yǔ)言建立該目標(biāo)機(jī)上的編譯器仰担。類(lèi)似的糊识,我們可以把一個(gè)前端和不同的目標(biāo)機(jī)后端結(jié)合,簡(jiǎn)歷針對(duì)不同目標(biāo)機(jī)的編譯器摔蓝。 這樣說(shuō)可能比較繞赂苗,本質(zhì)上是LLVM IR優(yōu)化器會(huì)做一些與代碼無(wú)關(guān)的優(yōu)化,所以如果LLVM將來(lái)需要支持一門(mén)新的編程語(yǔ)言贮尉,只需針對(duì)這個(gè)編程語(yǔ)言提供一個(gè)新的前端拌滋。如果將來(lái)LLVM需要支持一款新的機(jī)器架構(gòu),只需要針對(duì)這款機(jī)器架構(gòu)提供一個(gè)新的后端猜谚。而LLVM IR優(yōu)化器是通用的败砂。這樣一來(lái)LLVM就變得易擴(kuò)展。

生成匯編代碼

LLVM對(duì)IR進(jìn)行優(yōu)化后魏铅,會(huì)針對(duì)不同架構(gòu)生成不同的目標(biāo)代碼昌犹,最后以匯編代碼的格式輸出:

生成arm 64匯編:

<pre class="prism-token token language-javascript">xcrun clang -S main.c -o main.s</pre>

匯編器

匯編器以匯編代碼作為輸入,將匯編代碼轉(zhuǎn)換為機(jī)器代碼览芳,最后輸出目標(biāo)文件(object file)斜姥。

<pre class="prism-token token language-javascript">xcrun clang -fmodules -c main.c -o main.o</pre>

鏈接

鏈接器把編譯產(chǎn)生的.o文件和(dylib,a,tbd)文件,生成一個(gè)mach-o文件。

<pre class="prism-token token language-javascript">xcrun clang main.o -o main</pre>

我們就得到了一個(gè)mach o格式的可執(zhí)行文件

<pre class="prism-token token language-javascript">file main main: Mach-O 64-bit executable x86_64 ./main
hello debug</pre>

在用nm命令疾渴,查看可執(zhí)行文件的符號(hào)表:

<pre class="prism-token token language-javascript">$ nm -nm main</pre>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末千贯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搞坝,更是在濱河造成了極大的恐慌搔谴,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桩撮,死亡現(xiàn)場(chǎng)離奇詭異敦第,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)店量,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)芜果,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人融师,你說(shuō)我怎么就攤上這事右钾。” “怎么了旱爆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵舀射,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我怀伦,道長(zhǎng)脆烟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任房待,我火速辦了婚禮邢羔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑孩。我一直安慰自己拜鹤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布洼怔。 她就那樣靜靜地躺著署惯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镣隶。 梳的紋絲不亂的頭發(fā)上极谊,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音安岂,去河邊找鬼轻猖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛域那,可吹牛的內(nèi)容都是我干的咙边。 我是一名探鬼主播猜煮,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼败许!你這毒婦竟也來(lái)了王带?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤市殷,失蹤者是張志新(化名)和其女友劉穎愕撰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體醋寝,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搞挣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了音羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囱桨。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗅绰,靈堂內(nèi)的尸體忽然破棺而出舍肠,到底是詐尸還是另有隱情,我是刑警寧澤窘面,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布貌夕,位于F島的核電站,受9級(jí)特大地震影響民镜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜险毁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一制圈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畔况,春花似錦鲸鹦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吵瞻,卻和暖如春葛菇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橡羞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工眯停, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卿泽。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓莺债,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子齐邦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 前言 語(yǔ)言類(lèi)型 我們有很多維度可以將計(jì)算機(jī)語(yǔ)言進(jìn)行分類(lèi)椎侠,其中以編譯/執(zhí)行方式為維度,可以將計(jì)算機(jī)語(yǔ)言分為: 編譯型...
    AiLearn閱讀 2,390評(píng)論 1 6
  • 目錄 傳統(tǒng)編譯器設(shè)計(jì) 輸入源代碼(Obj-C, Swift, ...) → 編譯器處理 → 輸出機(jī)器碼(01010...
    小瞎_MarkDash閱讀 1,226評(píng)論 0 2
  • 前面一個(gè)章節(jié)已經(jīng)簡(jiǎn)單介紹了LLVM措拇。該章節(jié)主要介紹LLVM的編譯過(guò)程我纪。 1. OC源文件 2. 編譯過(guò)程 命令...
    Aliv丶Zz閱讀 466評(píng)論 0 0
  • 什么是LLVM LLVM項(xiàng)目是模塊化、可重用的編譯器以及工具鏈技術(shù)的集合儡羔。 美國(guó)計(jì)算機(jī)協(xié)會(huì) (ACM) 將其201...
    Coder_LRT閱讀 2,366評(píng)論 0 1
  • 引言 維基百科:編譯語(yǔ)言(英語(yǔ):Compiled language)是一種以編譯器來(lái)實(shí)現(xiàn)的編程語(yǔ)言宣羊。它不像解釋型語(yǔ)...
    Flame_Dream閱讀 8,541評(píng)論 5 52