【學(xué)習(xí)總結(jié)】05 | 鏈接器:符號(hào)是怎么綁定到地址上的?

1坎背、前言

首先替劈,我認(rèn)為學(xué)習(xí)總結(jié),要有所總得滤,所結(jié)陨献,就是有歸納后,能用自己的話告訴別人懂更!有所結(jié)眨业,就是有所收獲輸出,一般我認(rèn)為是思維導(dǎo)圖沮协,所以龄捡,每篇文章前,我都會(huì)先給出文章的腦圖:

iOS開(kāi)發(fā)高手課-05-鏈接器:符號(hào)是怎么綁定到地址上的慷暂?.png

2聘殖、正文

注意,本系列總結(jié)不會(huì)引用或提供原課程文章所有的內(nèi)容或代碼行瑞,只會(huì)作出思維導(dǎo)圖奸腺,需要學(xué)習(xí)可購(gòu)買課程 《iOS開(kāi)發(fā)高手課 - 極客時(shí)間》

鏈接器 對(duì)于剛剛接觸程序開(kāi)發(fā)的同學(xué)可能基本不懂,雖然大學(xué)學(xué)習(xí)編譯原理血久、計(jì)算機(jī)原理等突照。這些知識(shí)比較抽象,為了讓大家明白氧吐,我們需要站在巨人的肩膀上讹蘑!所以凶伙,本文需要一個(gè)巨人 ----- 編譯器 來(lái)幫助大家理解 鏈接器 是什么矿微?為什么?大家有沒(méi)有想過(guò)械荷,你寫(xiě)的代碼豁翎,最終是怎么樣在計(jì)算機(jī)或手機(jī)上運(yùn)行的角骤??心剥?那現(xiàn)在就讓我們來(lái)了解一下吧邦尊!

《程序是怎樣跑起來(lái)的》

“計(jì)算機(jī)組成原理”圖解趣味版,蹲馬桶就能看懂的編程基礎(chǔ)知識(shí)优烧!普及計(jì)算機(jī)知識(shí)蝉揍。如何向小學(xué)生講解CPU和二進(jìn)制?如何向中學(xué)生講解內(nèi)存和磁盤(pán)畦娄?如何向女高中生講解操作系統(tǒng)的原理又沾?如何向老奶奶說(shuō)明顯示器和電視的不同?

《程序是怎樣跑起來(lái)的》作者是日本矢澤久雄熙卡,我們需要解析 編譯器杖刷,所以需要簡(jiǎn)單入門(mén)級(jí)的計(jì)算機(jī)組成原理,這本書(shū)就是這樣一本優(yōu)秀的書(shū)驳癌!大家可能通過(guò)微信讀書(shū)讀到電子版: 程序是怎樣跑起來(lái)的 - 微信讀書(shū)滑燃,目前2020年微信還有很多無(wú)限卡會(huì)員的免費(fèi)閱讀,當(dāng)然如果必要花 19.99 元我認(rèn)為也值得颓鲜。好了我們就不多說(shuō)了表窘,下面我主要摘取書(shū)中幾個(gè)示意圖來(lái)解析 編譯器

程序運(yùn)行流程示例

圖1-1-程序運(yùn)行流程示例.jpg

上圖展示程序運(yùn)行流程示例,從我們編寫(xiě)的高級(jí)語(yǔ)言的代碼甜滨,到 CPU(Central Processing Unit乐严,中央處理器)所負(fù)責(zé)的就是解釋和運(yùn)行最終轉(zhuǎn)換成機(jī)器語(yǔ)言的程序內(nèi)容,其實(shí)是 編譯器 工作的流程衣摩。

CPU的四個(gè)構(gòu)成部分

圖1-2-CPU的四個(gè)構(gòu)成部分.jpg

CPU 的內(nèi)部由寄存器昂验、控制器運(yùn)算器時(shí)鐘四個(gè)部分構(gòu)成艾扮,各部分之間由電流信號(hào)相互連通既琴。本文我們不關(guān)注 CPU 的工作原理,本圖主要是后面要解析代碼運(yùn)行在不同 CPU 上栏渺,需要 編譯器 對(duì)不同的 CPU 做兼容和優(yōu)化呛梆。為什么會(huì)有不同的 CPU,上圖就說(shuō)明了磕诊,簡(jiǎn)單來(lái)說(shuō)寄存器填物、控制器、運(yùn)算器和時(shí)鐘的設(shè)計(jì)和數(shù)量不同霎终,那就是不同的 CPU滞磺,所以代碼在不同的 CPU 運(yùn)行就可能有不一樣的要求,這是硬件方面莱褒。這里就不深入解析了击困,大家可以看看本書(shū),或自行搜索了解更多。

CPU負(fù)責(zé)解析并運(yùn)行從源代碼編譯而來(lái)的本地代碼

圖7-2-CPU負(fù)責(zé)解析并運(yùn)行從源代碼編譯而來(lái)的本地代碼.jpg

CPU 只能解釋其自身固有的機(jī)器語(yǔ)言阅茶,機(jī)器語(yǔ)言的程序稱為本地代碼(native code)蛛枚。不同的CPU能解釋的機(jī)器語(yǔ)言的種類也是不同的。例如脸哀,CPU有 x86蹦浦、MIPSSPARC撞蜂、PowerPC盲镶、ARM等幾種類型,它們各自的機(jī)器語(yǔ)言是完全不同的蝌诡。

程序員用C語(yǔ)言等編寫(xiě)的程序溉贿,在編寫(xiě)階段僅僅是文本文件,文本文件(排除文字編碼的問(wèn)題)在任何環(huán)境下都能顯示和編輯浦旱,我們稱之為源代碼宇色。通過(guò)對(duì)源代碼進(jìn)行編譯,就可以得到本地代碼闽寡。到目前代兵,我們了解了代碼編譯的流程啦,那具體的流程又是怎么樣的呢爷狈?在講解之前植影,我們先看看 CPU 的歷史,這樣對(duì)我們了解編譯器也起到重要作用啊~

這里解析一下各種 CPU :

  • x86:美國(guó) Intel (英特爾)的微處理器涎永,是按照8086思币、80286、80386羡微、80486谷饿、Pentium(奔騰)……這樣的順序不斷升級(jí)的。因?yàn)檫@些型號(hào)的后面都帶有86妈倔,所以總稱為x86博投。32位處理器 Intel官方文檔里面稱為“IA-32”,64位處理器稱“x86_64”盯蝴、“x86-64”毅哗,又稱x64,即英文詞 64-bit extended捧挺,64位拓展的簡(jiǎn)寫(xiě)虑绵。這個(gè)64位有點(diǎn)故事,下面講∶隼樱現(xiàn)在的PC機(jī)都是64位 CPU 啦翅睛。
  • MIPS:美國(guó) MIPS 科技公司開(kāi)發(fā)的CPU,曾出現(xiàn)過(guò)面向 MIPS工作站的 Windows,不過(guò)現(xiàn)在市面上已經(jīng)不再出售了捕发。
  • SPARC:美國(guó) SUN系統(tǒng)開(kāi)發(fā)的CPU疏旨,很多工作站都采用了該CPU。
  • PowerPC:是美國(guó)蘋(píng)果爬骤、IBM充石、摩托羅拉共同開(kāi)發(fā)的CPU莫换,蘋(píng)果的Power Mac及IBM的工作站都采用了該CPU霞玄,不過(guò)現(xiàn)在的Mac采用的是Intel的x86系列CPU。
  • ARM ARM拉岁,安謀控股公司(英語(yǔ):ARM Holdings plc.)坷剧,又稱ARM公司。ARM的前身為艾康電腦喊暖,1978年于英國(guó)劍橋創(chuàng)立惫企。2016年7月18日,日本軟銀集團(tuán)斥資3.3萬(wàn)億日元(約合311億美元)收購(gòu)了ARM公司陵叽。ARM架構(gòu)版本從 ARMv3 到 ARMv7 支持32位狞尔,2011年發(fā)布的ARMv8-A 架構(gòu)添加了對(duì)64位支持。現(xiàn)在移動(dòng)設(shè)備主流就是使用 ARM 巩掺。
x86-64 的故事

x84_64x86 32位 CPU 開(kāi)始邁向64位的時(shí)候偏序,有2種選擇:

  1. 向下兼容x86。
  2. 完全重新設(shè)計(jì)指令集胖替,不兼容x86研儒。

結(jié)果在 1999 年 AMD 搶跑了!比 Intel 率先制造出了商用的兼容 x86 的CPU独令,AMD稱之為 AMD64端朵,搶了64位PC的第一桶金,得到了用戶的認(rèn)同燃箭。所以 Intel 為了面子就選擇了設(shè)計(jì)一種不兼容x86的全新64為指令集冲呢,稱之為IA-64(Itanium,安騰)招狸,但是比 AMD 晚了一步敬拓,因?yàn)?AMD64 能有效地把x86架構(gòu)移至64位的環(huán)境,并且能兼容原有的x86應(yīng)用程序瓢颅。并且 IA-64 也挺慘淡的恩尾,因?yàn)槭侨略O(shè)計(jì)的CPU,沒(méi)有編譯器挽懦,也不支持 Windows(微軟把intel給忽悠了翰意,承諾了會(huì)出對(duì)應(yīng)的 Windows server 版,但是遲遲拿不出東西),處理器本身和軟件移植的成本難以控制冀偶,導(dǎo)致 IA-64 最終流產(chǎn)醒第。后來(lái)不得不在時(shí)機(jī)落后的情況下也開(kāi)始支持AMD64的指令集,但是換了個(gè)名字进鸠,叫x86_64稠曼,表示是 x86 指令集的64擴(kuò)展,大概是不愿意承認(rèn)這玩意是AMD設(shè)計(jì)出來(lái)的客年?現(xiàn)時(shí)英特爾稱之為“Intel 64”霞幅,在之前曾使用過(guò)“Clackamas Technology” (CT)、“IA-32e”及“EM64T”等名字量瓜。換湯不換藥司恳,核心與 AMD64 幾乎相同,猶如一對(duì)孿生兄弟绍傲,其實(shí)都是 x86-64 架構(gòu)扔傅。當(dāng)年有媒體為 EM64T 起了“iAMD64”別名,諷刺 Intel 在迎擊 AMD 的民用64位技術(shù)上烫饼,使用了 AMD 的技術(shù)猎塞,直接把 AMD64 吸納過(guò)來(lái),并以新名重新包裝使用杠纵,所以最后荠耽,Intel 索性將此技術(shù)正式命名為 Intel 64

所以淡诗,CPU 的 32 位還是 64位骇塘,一般是用 x86-64 表示64位,而32位現(xiàn)在已經(jīng)沒(méi)有了韩容。另外款违,不同公司對(duì) x86-64 架構(gòu),名字上還是有一些區(qū)別群凶,蘋(píng)果公司和RPM包管理員以“x86-64”或“x86_64”稱呼此64位架構(gòu)插爹。甲骨文公司及Microsoft稱之為“x64”,BSD家族及其他Linux發(fā)行版則使用“x64-64”请梢,32位版本則稱為“i386”(或 i486/586/686)赠尾,Arch Linux用x86_64稱呼此64位架構(gòu)。

最后一個(gè)問(wèn)題毅弧,x86气嫁、x86_64主要的區(qū)別是什么?就是32位和64位的問(wèn)題够坐,x86 中只有8個(gè)32位通用寄存器寸宵,分別是 eax,ebx,ecx崖面,edx, ebp, esp, esi, edi。x86_64 把這8個(gè)通用寄存器擴(kuò)展成了64位的梯影,并且比x86增加了若干個(gè)寄存器(好像增加了8個(gè)巫员,變成了總共16個(gè)通用寄存器)。譬如四個(gè)通用寄存器(RAX, RBX, RCX, RDX)是由32位的(EAX, EBX, ECX, EDX)64位擴(kuò)展而來(lái)甲棍,相應(yīng)的還有指針寄存器(RIP, RBP, RSP, RSI, RDI)简识,以及增加八個(gè)通用寄存器(R8~R15)等等。 這些資源只可在x64處理器的64位模式下使用感猛,在用來(lái)支持x86軟件的遺留模式和兼容模式中是不可見(jiàn)的七扰。

以上內(nèi)容和關(guān)于 CPU 更多歷史和資料,可查看來(lái)源: x86 - 維基百科 唱遭、 x86-64 - 維基百科戳寸,自由的百科全書(shū)X86、X64和X86_64區(qū)別 拷泽。

轉(zhuǎn)換成本地代碼后就變成了同樣的語(yǔ)言

圖8-2-轉(zhuǎn)換成本地代碼后就變成了同樣的語(yǔ)言.jpg

前面提到,CPU 只能解釋其自身固有的機(jī)器語(yǔ)言袖瞻,而轉(zhuǎn)換成機(jī)器語(yǔ)言的程序就是本地代碼司致。不管用那種編程語(yǔ)言編寫(xiě)的源代碼,最后只要能翻譯(編譯)成本地代碼聋迎,那么 CPU 就能理解脂矫。這個(gè)過(guò)程,其實(shí)就是編譯器的工作內(nèi)容霉晕!

同樣的源代碼可以轉(zhuǎn)換成適用于不同處理器的本地代碼

圖8-5-同樣的源代碼可以轉(zhuǎn)換成適用于不同處理器的本地代碼.jpg

根據(jù) CPU 類型的不同庭再,本地代碼的類型也不同。因而牺堰,編譯器不僅和編程語(yǔ)言的種類有關(guān)拄轻,和 CPU 的類型也是相關(guān)的。這就是前面為什么要重點(diǎn)說(shuō) CPU 相關(guān)知識(shí)伟葫,現(xiàn)在是不是有點(diǎn)理解編譯器了恨搓!

把C語(yǔ)言等高級(jí)編程語(yǔ)言編寫(xiě)的源代碼轉(zhuǎn)換本地代碼的程序稱為編譯器,這個(gè)轉(zhuǎn)換過(guò)程經(jīng)過(guò)語(yǔ)法解析筏养、句法解析斧抱、語(yǔ)義解析等。每種編程語(yǔ)言編寫(xiě)的源代碼都需要其專用的編譯器渐溶,或者是同用類編譯器辉浦,比如 C 家族(C/C++)的編輯器。

Windows中的編譯和鏈接機(jī)制

圖8-8-Windows中的編譯和鏈接機(jī)制.jpg

剛才說(shuō)的高級(jí)編程語(yǔ)言的源代碼轉(zhuǎn)換本地代碼茎辐,本地文件是無(wú)法直接運(yùn)行的宪郊,還需要把多個(gè)目標(biāo)文件結(jié)合眉睹,生成1個(gè)可執(zhí)行文件(圖中是EXE文件,針對(duì)Windows系統(tǒng)的废膘。)竹海,這個(gè)處理就是鏈接,運(yùn)行連接的程序就稱為鏈接器(linkage editor丐黄,或連結(jié)器)斋配。至此,我們終于說(shuō)到鏈接器啦9喙搿<枵!現(xiàn)在明白鏈接器是在那里使用了吧桂对?

存儲(chǔ)著目標(biāo)文件的實(shí)體荔睹,并直接和可執(zhí)行文件結(jié)合的庫(kù)文件形式稱為靜態(tài)鏈接庫(kù),經(jīng)常簡(jiǎn)稱為靜態(tài)庫(kù)怕膛。與之相反的,就叫動(dòng)態(tài)鏈接庫(kù)(動(dòng)態(tài)庫(kù))宅此,就是參與鏈接過(guò)程机错,但是并不會(huì)與可執(zhí)行文件結(jié)合,就是說(shuō)鏈接時(shí)會(huì)做標(biāo)記父腕,綁定的地址在加載后再?zèng)Q定弱匪。這個(gè)不理解,沒(méi)關(guān)系璧亮,下面還會(huì)在詳細(xì)說(shuō)說(shuō)萧诫,下一章的內(nèi)容就是動(dòng)態(tài)鏈接庫(kù)動(dòng)態(tài)鏈接器的內(nèi)容,所以枝嘶,這個(gè)需要了解一下帘饶。

至此,我們就過(guò)編譯器相關(guān)的內(nèi)容躬络,簡(jiǎn)單的解說(shuō)了一下尖奔,不知道大家有點(diǎn)理解沒(méi)有,下面穷当,我們?cè)谏钊胍稽c(diǎn)提茁,了解編譯器的具體工作流程。

編譯器

編譯器一般采用 Three-Phase(三階段/三相)架構(gòu)設(shè)計(jì):

  1. 編譯器前端(Front end)
  2. 中間端(Middle end馁菜,或叫優(yōu)化器茴扁,Optimizer)
  3. 后端(Backend)

編譯器前端(Front end)

  1. Preprocess - 預(yù)處理
  2. Parser - 解析器
  3. Lexical Analysis - 詞法分析
  4. Semantic Analysis - 語(yǔ)義分析
  5. AST(Abstract Syntax Tree,抽象語(yǔ)法樹(shù))
  6. Static Analysis - 靜態(tài)分析
  7. CodeGen - IR 代碼生成

編譯器前端的任務(wù):預(yù)處理汪疮、語(yǔ)法分析峭火,語(yǔ)義分析毁习,在這個(gè)過(guò)程中,也會(huì)進(jìn)行類型檢查卖丸,如果發(fā)現(xiàn)錯(cuò)誤或者警告會(huì)標(biāo)注出來(lái)在哪一行等纺且,最終生成中間代碼 IR(Intermediate Representation,中間端表達(dá)式)稍浆。對(duì)于 LLVM 來(lái)說(shuō)载碌,前端就是 CLang

中間端(Middle end衅枫,或叫優(yōu)化器嫁艇,Optimizer)

  1. LLVM Bitcode - 生成字節(jié)碼
  2. Assemble - 生成Target相關(guān)匯編
  3. Assemble - 生成Target相關(guān) Object(Mach-o)
  4. Link 生成 Executable 可執(zhí)行文件

優(yōu)化是非常重要的,很多直接轉(zhuǎn)換來(lái)的代碼是不合適且消耗內(nèi)存的弦撩,因?yàn)槭侵苯愚D(zhuǎn)換步咪,所以必然會(huì)有這樣的問(wèn)題,而優(yōu)化放在這一步的好處在于前端不需要考慮任何優(yōu)化過(guò)程益楼,減少了前端的開(kāi)發(fā)工作猾漫。中間代碼要經(jīng)過(guò)一系列的優(yōu)化,優(yōu)化用的是 Pass偏形,中間代碼的優(yōu)化也可以開(kāi)發(fā)者自己編寫(xiě)静袖,可以插入一個(gè) Pass

后端(Backend)

  • ARM(ARM俊扭,安謀控股公司)
  • x86|x86-64(Intel,英特爾)
  • PowerPC(Apple & IBM & Motorola坠陈,AIM聯(lián)盟)

編譯器后端會(huì)進(jìn)行機(jī)器無(wú)關(guān)的代碼優(yōu)化萨惑,生成機(jī)器語(yǔ)言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化仇矾。對(duì)于 iOS 的編譯過(guò)程庸蔼,后端的處理:LLVM優(yōu)化器會(huì)進(jìn)行BitCode的生成,鏈接期優(yōu)化等贮匕,LLVM機(jī)器碼生成器會(huì)針對(duì)不同的架構(gòu)姐仅,比如arm64等生成不同的機(jī)器碼。

這里的后端刻盐,最終就是生成對(duì)應(yīng)的 CPU 能執(zhí)行的機(jī)器語(yǔ)言掏膏,前面已經(jīng)介紹過(guò) CPU 相關(guān)的。

上面說(shuō)了編譯器的組成架構(gòu)和簡(jiǎn)要的工作流程敦锌,詳細(xì)的工作流程這里就不多說(shuō)了馒疹,因?yàn)橐呀?jīng)有很多優(yōu)秀的內(nèi)容了,并且本文的主要目的是要說(shuō)明編譯器是什么乙墙!你知道它是什么后颖变,再了解它是怎么用生均,有什么優(yōu)缺點(diǎn),自然不是難事腥刹!以上的部分內(nèi)容是針對(duì) iOS 和 LLVM 的马胧,所以,我們接下來(lái)就來(lái)介紹一下 LLVM 編譯器吧衔峰!

要講解 LLVM 前佩脊,有必要的說(shuō)說(shuō) GCC(GNU Compiler Collection,GNU編譯器套件) 朽色,因?yàn)樘O(píng)果最初也是使用 GCC邻吞,后來(lái)慢慢的替換為 LLVM 的。

GCC(GNU Compiler Collection葫男,GNU編譯器套件)

原名:GNU C語(yǔ)言編譯器(GNU C Compiler)抱冷,通常是跨平臺(tái)軟件的編譯器首選。(在所有平臺(tái)上都使用同一個(gè)前端處理程序梢褐,產(chǎn)生一樣的中間碼)旺遮。

但是由于以下問(wèn)題,導(dǎo)致蘋(píng)果轉(zhuǎn)為 LLVM:

  • GCC 的 Objective-C Frontend 不給力
  • GCC 插件盈咳、工具耿眉、IDE的支持薄弱

GCC的前端不是蘋(píng)果提供維護(hù)的,想要添加一些語(yǔ)法提示等功能還得去求GCC的前端去做鱼响。 很多編譯器特性沒(méi)有鸣剪,自動(dòng)補(bǔ)全、代碼提示丈积、warning筐骇、靜態(tài)分析等這些流程不是很給力,都是需要IDE調(diào)用底層命令完成的江滨,結(jié)果需要以插件的形式暴露出來(lái)铛纬,這一塊GCC做的不是很好。

LLVM

早年Apple一直使用 GCC 作為官方的編譯器唬滑,但Apple對(duì) GCC 的性能不滿意告唆,再者 Objective-C 在 GCC 中優(yōu)先級(jí)低,GCC 對(duì) Objective-C 語(yǔ)言新特性的支持程度也不高晶密。因此Apple一直在尋找compiler(編譯器)的開(kāi)源替代品擒悬,于是他們將目光轉(zhuǎn)移到LLVM身上。

來(lái)自維基百科:關(guān)于LLVM這個(gè)名字的來(lái)源惹挟,LLVM的命名最早源自于底層虛擬機(jī)(Low Level Virtual Machine)的首字母縮寫(xiě)茄螃,由于這個(gè)項(xiàng)目的范圍并不局限于創(chuàng)建一個(gè)虛擬機(jī),這個(gè)縮寫(xiě)導(dǎo)致了廣泛的疑惑连锯。

LLVM 就是 LLVM, 并不是 Low Level Virtual Machine(底層虛擬機(jī))的簡(jiǎn)寫(xiě)归苍!現(xiàn)今LLVM 已單純成為一個(gè)品牌用狱!根據(jù)官網(wǎng)說(shuō)明:

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name "LLVM" itself is not an acronym; it is the full name of the project.
LLVM項(xiàng)目是模塊化和可重用的編譯器及工具鏈技術(shù)的集合。盡管名稱如此拼弃,LLVM與傳統(tǒng)虛擬機(jī)關(guān)系不大夏伊。名稱“ LLVM”本身不是縮寫(xiě)。它是項(xiàng)目的全名吻氧。

2000年溺忧,伊利諾伊大學(xué)厄巴納-香檳分校(University of Illinois at Urbana-Champaign 簡(jiǎn)稱UIUC)這所享有世界聲望的一流公立研究型大學(xué)的 Chris Lattner(克里斯·拉特納,twitter @clattner_llvm ) 和 Vikram Adve(維克拉姆·艾夫)想要為所有靜態(tài)及動(dòng)態(tài)語(yǔ)言創(chuàng)造出動(dòng)態(tài)的編譯技術(shù)盯孙,而開(kāi)發(fā)的編譯器開(kāi)發(fā)工具套件鲁森。2005年,蘋(píng)果電腦雇用了 Chris Lattner 克里斯·拉特納及他的團(tuán)隊(duì)為蘋(píng)果電腦開(kāi)發(fā)應(yīng)用程序系統(tǒng),LLVM 涉及范圍越來(lái)越大振惰,可以用于常規(guī)編譯器歌溉,JIT編譯器,匯編器骑晶,調(diào)試器痛垛,靜態(tài)分析工具等一系列跟編程語(yǔ)言相關(guān)的工作。Chris Lattner 后來(lái)又開(kāi)發(fā)了 Clang桶蛔,使得 LLVM 直接挑戰(zhàn) GCC 的地位匙头。

Chris Lattner 大神簡(jiǎn)介

Chris Lattner 生于 1978 年,2005年加入蘋(píng)果仔雷,將蘋(píng)果使用的 GCC 全面轉(zhuǎn)為 LLVM蹂析。2010年開(kāi)始主導(dǎo)開(kāi)發(fā) Swift 語(yǔ)言。2017年1月,克里斯·拉特納 辭去在蘋(píng)果的工作棘捣,入職特斯拉汽車,負(fù)責(zé)自動(dòng)駕駛軟件的開(kāi)發(fā)。2017年8月14日贸人,克里斯·拉特納 發(fā)表Twitter表示將于一周后加入聚焦于深度學(xué)習(xí)與人工智能研發(fā)的Google Brain團(tuán)隊(duì)。

Xcode 歷史

Clang 編譯效率是 GCC 的3倍褒颈,編譯器性能好廓推,編譯出的文件小。LLVM 3.0 發(fā)布已完整支持所有 ISO C++ 標(biāo)準(zhǔn)锻煌,代表著 LLVM 正式走向成熟妓布。所以,也標(biāo)志著 Xcode 的變化宋梧。

  • Xcode3:GCC前端-LLVM后端匣沼,
  • Xcode4.2:Clang-LLVM 3.0 成為默認(rèn)編譯器,
  • Xcode5: GCC 被廢棄捂龄,新的編譯器是 LLVM 5.0

從 GCC前端 到 LLVM后端的編譯器释涛,到 Clang-LLVM 的編譯器加叁,一步步收回對(duì)編譯工具鏈的控制,也為 Swift 的出現(xiàn)奠定基礎(chǔ)唇撬。所以它匕,Chris Lattner 真的是大神!

LLVM 工具鏈

LLVM工具鏈集合.png

最后用這張圖來(lái)表示完整的LLVM工具鏈集合的六大執(zhí)行單元窖认。

  1. 詞法分析: 將源代碼中的所有字符切分成記號(hào)(Token)的序列豫柬。包括了詞法分析器、記號(hào)序列化生成器和掃描器扑浸,不過(guò)掃描器常常作為詞法分析器的第一階段烧给。
  2. 語(yǔ)法分析: 分析符合一定語(yǔ)法規(guī)則的一串符號(hào),它通常會(huì)生成一個(gè)抽象語(yǔ)法樹(shù)(AST - Abstract Syntax tree)喝噪,用于表示記號(hào)之間的語(yǔ)法關(guān)系础嫡。
  3. 語(yǔ)義分析: 通過(guò)語(yǔ)法分析的解析后,這個(gè)過(guò)程將從源代碼中收集必要的語(yǔ)義信息仙逻,通常包括類型檢查驰吓、在使用之前確保聲明了變量等等。
  4. 中間代碼(IR)生成:代碼在這個(gè)階段會(huì)轉(zhuǎn)換為中間表示式(IR)系奉,這是一種中立的語(yǔ)言檬贰,與源語(yǔ)言(前端)和機(jī)器(后端)無(wú)關(guān)。
  5. 優(yōu)化中間表達(dá)式:中間代碼常常會(huì)有冗余和死代碼的情況出現(xiàn)缺亮,而優(yōu)化器可以處理這些問(wèn)題以獲得更優(yōu)異的性能翁涤。
  6. 生成目標(biāo)代碼: 最后后端會(huì)生成在目標(biāo)機(jī)器上運(yùn)行的機(jī)器碼萌踱,我們也將其稱之為目標(biāo)代碼葵礼。

鏈接器

  • 為什么要讓鏈接器做符號(hào)和地址綁定?不綁定的話并鸵,又會(huì)有什么問(wèn)題鸳粉?
  • 鏈接器為什么還要把項(xiàng)目中的多個(gè) Mach-O 文件合并成一個(gè)。

回到本文開(kāi)始的問(wèn)題园担,大家對(duì)這2個(gè)問(wèn)題心中有答案了嗎届谈?

鏈接器的作用是將符號(hào)綁定到地址上,符號(hào)表是內(nèi)存地址與函數(shù)名弯汰、文件名艰山、行號(hào)的映射表。所以咏闪,變量曙搬、函數(shù)與地址綁定,CPU 才能理解解析代碼。而為什么要多個(gè) Mach-O 文件合并一個(gè)最終的 Mach-O纵装?過(guò)去面向過(guò)程編程時(shí)征讲,可以寫(xiě)一個(gè)文件中,而現(xiàn)在主流是面向?qū)ο舐Р粒陀蓄惻c對(duì)象關(guān)系稳诚,所以一般是分類、分模塊瀑踢、分空間來(lái)編寫(xiě)源代碼扳还,所以,需要把這些類與對(duì)象和他們之前調(diào)用關(guān)系綁定一起橱夭,合并成一個(gè)Mach-O氨距。為了更好理解這個(gè) Mach-O,需要了解 CPU 是怎么執(zhí)行他們的棘劣,這里就不展開(kāi)了俏让,感興趣的同學(xué)可以先自行搜索了解更多。

觸類旁通

Clang/Swift-LLVM編譯器架構(gòu).png

讓我們?cè)俅位仡櫼幌戮幾g器的工作流程茬暇,以 LLVM 為例首昔,從 iOS 開(kāi)發(fā)視角來(lái)看這個(gè)問(wèn)題。這張圖片來(lái)源:從Swift橋接文件到Clang-LLVM - 掘金糙俗。詳細(xì)的編譯器過(guò)程大家可以參考文章勒奇,這里就不過(guò)多重復(fù)。

Clang-LLVM編譯過(guò)程.png

另外巧骚,原文還提到很多的概念赊颠,這里也不一一提出了,具體可以看看本文最前面的思維導(dǎo)圖劈彪。比如:

  • AST(Abstract Syntax Tree竣蹦,抽象語(yǔ)法樹(shù))
  • Mach-O:是 Mach Object 文件格式的縮寫(xiě),它是一種用于可執(zhí)行文件沧奴,目標(biāo)代碼痘括,動(dòng)態(tài)庫(kù),內(nèi)核轉(zhuǎn)儲(chǔ)的文件格式滔吠。作為a.out格式的替代远寸,Mach-O提供了更強(qiáng)的擴(kuò)展性,并提升了符號(hào)表中信息的訪問(wèn)速度屠凶。
  • 符號(hào)表:是內(nèi)存地址與函數(shù)名、文件名肆资、行號(hào)的映射表矗愧。格式如:<起始地址> <結(jié)束地址> <函數(shù)> [<文件名:行號(hào)>]
  • dyld(the dynamic link editor):當(dāng)加載 Mach-O 文件時(shí),動(dòng)態(tài)鏈接器會(huì)先檢查是否有共享緩存。每個(gè)進(jìn)程都會(huì)在自己的地址空間映射這些共享緩存唉韭,這樣做可以起到優(yōu)化App啟動(dòng)速度的作用夜涕。
  • LLDB(LLVM項(xiàng)目的調(diào)試器組件)
  • ASLR (Address space layout randomization,地址空間配置隨機(jī)加載)
  • 解釋器:不需要經(jīng)過(guò)編譯的過(guò)程属愤,而是在執(zhí)行的時(shí)候通過(guò)一個(gè)中間的解釋器將代碼解釋為CPU可以執(zhí)行的代碼女器。JavaScript、Python住诸、PHP等都是直譯式語(yǔ)言驾胆。
  • Bitcode:字節(jié)碼
  • SIL(Swift Intermediate Language):AST 和LLVM IR之間的另一種中間代碼表示形式。主要是用來(lái)彌補(bǔ)一些 Clang編譯器的缺陷贱呐,如無(wú)法執(zhí)行一些高級(jí)分析丧诺,可靠的診斷和優(yōu)化等。

中間代碼 LLVM IR

中間端表達(dá)式 IR(Intermediate Representation)奄薇,有3種表示形式驳阎,但本質(zhì)是等價(jià)的,就好比水可以有氣體馁蒂,液體呵晚,固體3種形式。

  1. text:便于閱讀的文本格式沫屡,類似于匯編語(yǔ)言饵隙,拓展名.II。生成命令:$ clang -S -emit-llvm main.m
  2. memory:內(nèi)存格式谁鳍,開(kāi)發(fā)時(shí)操作 LLVM IR癞季。
  3. bitcode:二進(jìn)制格式,拓展名.bc倘潜。生成命令:$ clang -c -emit-llvm main.m

廣義與狹義 LLVM 與 Clang 的關(guān)系

廣義 LLVM:整個(gè) LLVM 架構(gòu)绷柒。

狹義 LLVM:LLVM 后端(代碼優(yōu)化,目標(biāo)文件生成等涮因。)

Clang(C Lange Family Frontend for LLVM)废睦,發(fā)音為/?kl??/,是C养泡、C++嗜湃、Objective-C、Objective-C++ 編程語(yǔ)言的編譯器前端澜掩。

所以购披,非特指,LLVM 就是廣義 LLVM肩榕,指整個(gè)LLVM項(xiàng)目刚陡,包括Clang前端。另外,本文主要講解了 LLVM 在 Apple 平臺(tái)上的使用筐乳,LLVM 現(xiàn)在被作為實(shí)現(xiàn)各種靜態(tài)和運(yùn)行時(shí)編譯語(yǔ)言的通用基礎(chǔ)結(jié)構(gòu)歌殃,可以使用 LLVM 來(lái)編譯Ruby,Python蝙云,Haskell氓皱,Java,D勃刨,PHP波材,Pure,Lua和許多其他語(yǔ)言朵你。LLVM 的主要優(yōu)勢(shì)在于其多功能性各聘,靈活性和可重用性,這就是它被用于各種不同任務(wù)的原因:從輕量級(jí)JIT編譯嵌入式語(yǔ)言(如Lua)到編譯Fortran代碼(用于大型超級(jí))電腦抡医。

相比較GCC吼拥,Clang具有哪些優(yōu)點(diǎn)

  • Clang是:
  • LLVM項(xiàng)目的一個(gè)子項(xiàng)目
  • 基于LLVM架構(gòu)的 C趴俘、C++摩骨、Objective-C疆虚、Objective-C++ 編譯器前端
  • Clang優(yōu)點(diǎn):
  • 編譯速度快:在某些平臺(tái)上,Clang的編譯速度顯著的快過(guò)GCC
  • 占用內(nèi)存兴ⅰ:Clang生成的AST(語(yǔ)法樹(shù))所占用的內(nèi)存是GCC的五分之一左右
  • 模塊化設(shè)計(jì):Clang采用基于庫(kù)的模塊化設(shè)計(jì)镰矿,易于IDE集成及其他用途的重用
  • 診斷信息可讀性強(qiáng):在編譯過(guò)程中,Clang創(chuàng)建并保存了大量詳細(xì)的元數(shù)據(jù)俘种,有利于調(diào)試
  • 設(shè)計(jì)清晰簡(jiǎn)單秤标,容易理解,易于擴(kuò)展增強(qiáng)

Swift 如何橋接 Objtive-C 文件到 Clang-LLVM 宙刘?

在 Swift 的項(xiàng)目中苍姜,Objtive-C 代碼可以生成 IR(Intermediate Representation,中間端表達(dá)式)悬包,從而與 Swift 生成的 IR 聯(lián)通衙猪。這個(gè)也是分段(層)的好處,編譯器中間端(Middle end布近,或叫優(yōu)化器垫释,Optimizer),作為中間者通過(guò)前端獲取 IR 撑瞧,從而不用關(guān)心前端的編程語(yǔ)言是什么棵譬,這些設(shè)計(jì)特點(diǎn),帶來(lái)了非常大的好處预伺。

假如有N種語(yǔ)言(C茫船、OC琅束、C++、Swift...)的前端算谈,同時(shí)也有M個(gè)架構(gòu)(模擬器、arm64料滥、x86...)的Target然眼,是否就需要 N × M 個(gè)編譯器?
三相架構(gòu)的價(jià)值就體現(xiàn)出來(lái)了葵腹,通過(guò)共享優(yōu)化器的中轉(zhuǎn)高每,很好的解決了這個(gè)問(wèn)題。
假如你需要增加一種語(yǔ)言践宴,只需要增加一種前端鲸匿;假如你需要增加一種處理器架構(gòu),也只需要增加一種后端阻肩,而其他的地方都不需要改動(dòng)带欢。這復(fù)用思想很牛逼吧。

來(lái)自:淺談iOS編譯過(guò)程 - 簡(jiǎn)書(shū)

需要注意的是 Clang 是基于LLVM架構(gòu)的 C烤惊、C++乔煞、Objective-C、Objective-C++ 編譯器前端柒室,不包括 Swift渡贾!蘋(píng)果針對(duì) Swift 做了單獨(dú)的前端,與 Clang 是非常相似的雄右,特點(diǎn)就是增加了 SILSwift Intermediate Language)空骚。詳細(xì)具體可以查看 WWDC 視頻:What's New in LLVM - WWDC 2016 - Videos - Apple DeveloperBehind the Scenes of the Xcode Build Process - WWDC 2018 - Videos - Apple Developer擂仍。

方舟編譯器

方舟編譯器架構(gòu)示意圖.png

當(dāng)前方舟編譯器支持Java/Kotlin程序字節(jié)碼的前端輸入囤屹,其它編程語(yǔ)言的支持(如 C/C++/JS 等)還在規(guī)劃中,方舟編譯器的中間表示(IR)轉(zhuǎn)換器將前端輸入轉(zhuǎn)換成方舟IR防楷,并輸送給后端的優(yōu)化器牺丙,最終生成二進(jìn)制文件,二進(jìn)制文件與編譯器運(yùn)行時(shí)庫(kù)文件鏈接生成可執(zhí)行文件复局,在方舟的運(yùn)行環(huán)境中就可執(zhí)行該文件冲簿。

方舟編譯器IR是支持程序編譯和運(yùn)行的中間程序表示。程序源代碼中的任何信息對(duì)于程序分析和優(yōu)化都是有幫助的亿昏,所以方舟IR的目標(biāo)是盡可能完整詳細(xì)地提供源程序的信息峦剔。

方舟編譯器開(kāi)源范圍示意圖.png

首次開(kāi)源范圍是編譯器 IR( Intermediate Representation)、RC(Reference Counting)和多語(yǔ)言設(shè)計(jì)思想等角钩,用于與業(yè)界吝沫、學(xué)術(shù)界溝通交流呻澜。后續(xù)將陸續(xù)開(kāi)源編譯器前端、后端惨险,支持其它語(yǔ)言(如 JavaScript)的編譯等羹幸,當(dāng)前部分Java語(yǔ)言特性和JVM虛擬機(jī)特性的支持未包括在本次開(kāi)源代碼中,包括:annotation辫愉、lambda表達(dá)式栅受、泛型等。目前仍有很多地方不完善恭朗,會(huì)在社區(qū)陸續(xù)迭代屏镊,遇到問(wèn)題請(qǐng)?jiān)谏鐓^(qū)提交 issue,歡迎在社區(qū)繼續(xù)討論設(shè)計(jì)和代碼共建痰腮。

怎么樣而芥!現(xiàn)在你至少看的懂這個(gè)方舟編譯器架構(gòu)的大體流程了吧,是不是發(fā)現(xiàn)其實(shí)并沒(méi)有那么難理解膀值!那就對(duì)了棍丐,萬(wàn)事開(kāi)頭難,慢慢的一切都會(huì)簡(jiǎn)單起來(lái)~ 至少去知x上看看這些評(píng)論都看懂了啊虫腋,如何看待方舟編譯器于 2019 年 8 月 31 日開(kāi)源骄酗? - 知乎,是不是很開(kāi)心~

詳細(xì)可見(jiàn)官方網(wǎng)頁(yè):方舟編譯器 OpenArkCompiler - FAQ

龍書(shū)悦冀、虎書(shū)趋翻、鯨書(shū) --- 編譯原理三大圣書(shū)

  1. 龍書(shū):《編譯原理》(編譯原理 技術(shù)與工具)(Compilers: Principles,Techniques, and Tools)
  2. 虎書(shū):《現(xiàn)代編譯原理 — C語(yǔ)言描 》(Modern Compiler Implement in C)
  3. 鯨書(shū):《高級(jí)編譯器設(shè)計(jì)與實(shí)現(xiàn)》(Advanced Compiler Design and Implementation)

龍書(shū):《編譯原理》是編譯領(lǐng)域無(wú)可替代的經(jīng)典著作,被廣大計(jì)算機(jī)專業(yè)人士譽(yù)為“龍書(shū)”盒蟆。
虎書(shū):增加了數(shù)據(jù)流分析踏烙、循環(huán)優(yōu)化、內(nèi)存管理等內(nèi)容历等。
鯨書(shū):包含了一些更比較高級(jí)的編譯器的設(shè)計(jì)和實(shí)現(xiàn)讨惩。

大家是不是瞬間感覺(jué)自己就要買這幾本書(shū)呢!:汀荐捻!我到底能不能看的懂呢?可以先自測(cè)一下自己是不是適配現(xiàn)在就學(xué)習(xí)編譯原理:

學(xué)習(xí)編譯原理前自測(cè)
  • a. 編譯型語(yǔ)言和解釋型語(yǔ)言的區(qū)別是什么?
  • b. 編譯經(jīng)歷了哪幾個(gè)基本過(guò)程? 每個(gè)過(guò)程主要干了什么事情?
  • c. 詳細(xì)描述一下你最熟悉的語(yǔ)言中寡夹,賦值號(hào)的左邊可以由哪些部分組成? 右邊可以由哪些部分組成?
  • d. 什么是遞歸? 如何終止一個(gè)遞歸行為?
  • e. 什么是貪心過(guò)程?
  • f. 聽(tīng)說(shuō)過(guò) KMP 嗎? 介紹一下它的基本思想.
  • g. 生成樹(shù)是什么?
  • h. 你覺(jué)得編程語(yǔ)言和自然語(yǔ)言最重要的區(qū)別有哪些?
  • i. 什么是有限狀態(tài)自動(dòng)機(jī)? 你知道幾種不同的自動(dòng)機(jī)類型? 它們之間的區(qū)別和聯(lián)系?
  • j. 內(nèi)存被人為的劃分成了哪幾種不同的類型, 它們之間的區(qū)別和聯(lián)系?
  • k. 什么是中斷? CPU 在響應(yīng)中斷時(shí)會(huì)做什么事情?
  • l. 你最熟悉的語(yǔ)言有垃圾回收機(jī)制嗎? 若有描述它進(jìn)行垃圾回收的原理, 若沒(méi)有則描述你在編寫(xiě)程序時(shí)是如何管理內(nèi)存的?

本節(jié)內(nèi)容來(lái)自:如何學(xué)習(xí)編譯原理处面? - 歐長(zhǎng)坤的回答 - 知乎

3、總結(jié)

終于到總結(jié)部分啦菩掏!這個(gè)文章魂角,之前給我們組內(nèi)同學(xué)分享時(shí),在回顧都忘記了一大部分智绸,另外在寫(xiě)成文字時(shí)野揪,發(fā)現(xiàn)有很多東西需要考證访忿,每篇文章花了很多心思,正好2020年新型肺炎冠狀病毒都在家里斯稳,正好有這時(shí)間海铆。而 維基百科 就是一個(gè)很棒的網(wǎng)站,關(guān)于大多數(shù)知識(shí)和歷史都能查到挣惰,真的很有趣游添!

關(guān)于編譯器的知識(shí)有太多了,深入的還可以自己寫(xiě)一個(gè)編譯器通熄!這里就達(dá)不到這樣的層次,所以就提一下找都,有需要的同學(xué)可以自行搜索唇辨。關(guān)于LLVM的知識(shí)也還有很多沒(méi)有講到,比如抽象語(yǔ)法樹(shù)(AST)能耻,前端 Clang 是重中之器赏枚,Clang 插件開(kāi)發(fā)等,還有中間端的 Pass晓猛,后端的Mach-O 和 CPU 執(zhí)行 Mach-O 的過(guò)程等饿幅。

最后,本文還是有很多細(xì)節(jié)無(wú)法用文字記錄下來(lái)戒职,大家可以閱讀下面的參考鏈接栗恩,這樣收獲會(huì)更大。關(guān)于編譯器鏈接器就到這里先洪燥,對(duì)于大家理解這個(gè)概念應(yīng)該是沒(méi)有問(wèn)題啦磕秤!具體的實(shí)現(xiàn)細(xì)節(jié),需要大家多學(xué)習(xí)捧韵,如果有能力市咆,可以看看龍書(shū)!虎書(shū)再来!鯨書(shū)蒙兰! 想想也是一件快樂(lè)的事件!

2020年芒篷,手機(jī)只是小小一部分搜变,智慧家居大屏、穿戴梭伐、車機(jī)痹雅、音響、手表糊识、PC等等各種各樣設(shè)備绩社,作為開(kāi)發(fā)者摔蓝,我們不要只關(guān)注自己開(kāi)發(fā)的App蹬敲,應(yīng)該去理解這個(gè)世界發(fā)生什么變化辆憔,為什么變化尉剩,怎么變化捶障,抓住變化操刀!當(dāng)然我承認(rèn)世界是掌握于小數(shù)人夫晌,但能先抓住自己麻献!這就是好的開(kāi)始~


注:更多關(guān)于 iOS 開(kāi)發(fā)和程序開(kāi)發(fā)相關(guān)的內(nèi)容辨嗽,可以查看系列赌渣,目前還在連載中 【學(xué)習(xí)總結(jié)】iOS開(kāi)發(fā)高手課 -- (連載中) | iHTCboy's blog魏铅,以上,希望對(duì)你有用坚芜!

參考

WWDC

Article


  • 如有侵權(quán)舀射,聯(lián)系必刪窘茁!
  • 如有不正確的地方,歡迎指導(dǎo)脆烟!
  • 如有疑問(wèn)山林,歡迎在評(píng)論區(qū)一起討論!


注:本文首發(fā)于 iHTCboy's blog邢羔,如若轉(zhuǎn)載驼抹,請(qǐng)注來(lái)源

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桑孩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子框冀,更是在濱河造成了極大的恐慌流椒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件明也,死亡現(xiàn)場(chǎng)離奇詭異宣虾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)温数,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)绣硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人撑刺,你說(shuō)我怎么就攤上這事域那。” “怎么了猜煮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)败许。 經(jīng)常有香客問(wèn)我王带,道長(zhǎng),這世上最難降的妖魔是什么市殷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任愕撰,我火速辦了婚禮,結(jié)果婚禮上醋寝,老公的妹妹穿的比我還像新娘搞挣。我一直安慰自己,他們只是感情好音羞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布囱桨。 她就那樣靜靜地躺著,像睡著了一般嗅绰。 火紅的嫁衣襯著肌膚如雪舍肠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天窘面,我揣著相機(jī)與錄音翠语,去河邊找鬼。 笑死财边,一個(gè)胖子當(dāng)著我的面吹牛肌括,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酣难,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谍夭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黑滴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起慧库,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跷跪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后齐板,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吵瞻,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年甘磨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了橡羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡济舆,死狀恐怖卿泽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滋觉,我是刑警寧澤签夭,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站椎侠,受9級(jí)特大地震影響第租,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜我纪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一慎宾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浅悉,春花似錦趟据、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至荞估,卻和暖如春比被,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泼舱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工等缀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娇昙。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓尺迂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子噪裕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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