二進制的生命周期(翻譯)

幾乎每個人會去編寫一個程序,接著編譯,然后運行該程序并查看您辛勤編碼的成果 。 嘴周看到程序正常運行起來會感覺很棒! 但是,要使這些所有工作順利進行,我們還要感謝其他人。那就是您的編譯器(當然,假設(shè)您使用的是編譯語言,而不是解釋性語言),它在幕后會做很多工作。

在本文中,我將嘗試向您展示如何將您編寫的源代碼轉(zhuǎn)換為計算機可以實際運行的代碼, 我在這里選擇Linux作為我的主機,并選擇C作為編程語言,不用糾結(jié)語言,這里的概念一通百通,可以應(yīng)用于許多編譯語言。

注意: 如果要按照本文中的說明進行操作,則必須確保在本地計算機上安裝了gcc,elfutils

讓我們從一個簡單的C程序開始,看看編譯器如何轉(zhuǎn)換它

image

該程序創(chuàng)建兩個變量,將它們加起來并在屏幕上打印結(jié)果。很簡單吧?

但是,讓我們看看這個看似簡單的程序必須經(jīng)過什么才能最終在您的系統(tǒng)上執(zhí)行。

編譯器通常具有以下五個步驟(最后一步是操作系統(tǒng)的一部分)-

image

讓我們詳細介紹每個步驟。

image

第一步是預(yù)處理步驟,由預(yù)處理器完成。預(yù)處理程序的工作是處理代碼中存在的所有預(yù)處理程序指令。 這些指令以開頭。 但是在處理它們之前,它首先從代碼中刪除了所有注釋,因為這些注釋僅提高人類易讀性。 然后,它找到所有的命令,并執(zhí)行命令所"說"的內(nèi)容。

在上面的代碼中,我們僅使用了#include指令,該指令只是對處理器說,可以復(fù)制stdio.h文件并將其粘貼到當前位置的該文件中。

您可以通過將-E標志傳遞給gcc編譯器來查看預(yù)處理器的輸出

gcc -E sample.c

您將獲得類似以下內(nèi)容的信息


image

image

令人困惑的是,第二步也稱為編譯。編譯器從預(yù)處理器獲取輸出,并負責執(zhí)行以下重要任務(wù)。

  • 將輸出傳遞給詞法分析器,以識別文件中存在的各種標記。 令牌只是程序中存在的文字,例如int,return,void,0等。 詞法分析器還將令牌的類型與每個令牌相關(guān)聯(lián),無論令牌是字符串文字,整數(shù),浮點數(shù),if令牌等。
  • 將詞法分析器的輸出傳遞給語法分析器,以檢查程序是否以滿足程序所用語言的語法規(guī)則的方式編寫。例如,在分析此行代碼時,它將引發(fā)語法錯誤
b = a + ;
  • 將語法分析器的輸出傳遞給語義分析器,該語義分析器將檢查程序是否滿足語言的語義,例如類型檢查和變量在首次使用之前就已聲明,等等
  • 如果程序在語法上是正確的,則將源代碼轉(zhuǎn)換為指定目標體系結(jié)構(gòu)的匯編指令。 默認情況下,它會為其運行的計算機生成程序集。 但是假設(shè)您正在為嵌入式系統(tǒng)構(gòu)建程序,那么您可以傳遞目標計算機的體系結(jié)構(gòu),gcc將為該計算機生成程序集

要查看此階段的輸出,請將-S標志傳遞給gcc編譯器。

gcc -S sample.c

根據(jù)您的環(huán)境,您將獲得類似以下的內(nèi)容


image

如果您不懂匯編語言,乍一看,一切都會讓人感到恐懼,但還不錯。與通常的高級語言代碼相比,理解匯編代碼要花更多的時間,但是如果有足夠的時間,您肯定可以閱讀。

讓我們看看這個文件包含什么。

所有以.開頭的行都是匯編程序指令。.file表示源文件的名稱,可用于調(diào)試目的。我們的源代碼%d\n中的字符串文字現(xiàn)在位于.rodata節(jié)中(ro表示只讀),因為它是只讀字符串。 編譯器將此字符串命名為LC0,以便以后在代碼中引用它。 每當您看到以.L開頭的標簽時,即表示這些標簽在當前文件本地,而其他文件不可見。

.globl聲明main是一個全局符號,這意味著可以從其他文件中調(diào)用main。 .type聲明main是一個函數(shù)。 然后進行主要功能的組裝。 您可以忽略以cfi開頭的指令。 它們用于在異常情況下展開調(diào)用堆棧。 我們將在本文中忽略它們,但是您可以在此處了解更多信息。

現(xiàn)在,讓我們嘗試了解主功能的反匯編。

image
  • 11行:您必須知道,在調(diào)用函數(shù)時,會為該函數(shù)創(chuàng)建一個新的堆棧框架。 為了使之成為可能,我們需要某種方法來知道新函數(shù)返回時調(diào)用方函數(shù)框架指針的開始。 這就是為什么我們將存儲在rbp寄存器中的當前幀指針推入堆棧的原因。
  • 14行:將當前的堆棧指針移至基本指針。 這成為我們當前的功能框架指針。 圖1示出了推入rbp寄存器之前的狀態(tài),圖2示出了推入前一幀指針并將堆棧指針移至當前幀指針之后的狀態(tài)。
  • 16行:我們的程序中有3個局部變量,所有類型均為int。 在我的機器上,每個int占用4個字節(jié),因此我們在堆棧上需要12個字節(jié)的空間來保存我們的局部變量。 我們?yōu)槎褩I系木植孔兞縿?chuàng)建空間的方式是將堆棧指針遞減我們局部變量所需的字節(jié)數(shù)。 遞減,因為堆棧從較高的地址增長到較低的地址。 但是在這里您看到我們遞減的是16,而不是12。原因是,空間是在16個字節(jié)的塊中分配的。 因此,即使您有1個局部變量,也會在堆棧上分配16個字節(jié)的空間。 出于某些架構(gòu)上的性能原因而執(zhí)行此操作。 請參閱圖3,以查看堆棧現(xiàn)在的布局。
  • 17-22行: 這段代碼非常簡單。 編譯器已將插槽rbp-12用作變量a的存儲空間,將rbp-8用作b的存儲空間,并將rbp-4用作c的存儲空間。 它將值1和2分別移動到變量a和b的地址。 為了準備加法,它將b值移至edx寄存器,將a寄存器的值移至eax寄存器。 相加的結(jié)果存儲在eax寄存器中,該寄存器隨后被傳送到c變量的地址。
  • 23-27行:然后,我們準備進行printf調(diào)用。 首先,將c變量的值移至esi寄存器。 然后,將字符串常量%d\n的地址移至edi寄存器。 現(xiàn)在,esiedi寄存器保存我們的printf調(diào)用的參數(shù)。 edi持有第一個參數(shù),而esi持有第二個參數(shù)。 然后,我們調(diào)用printf函數(shù)來打印格式為整數(shù)值的變量c的值。 這里要注意的是,此時未定義printf符號。 我們將在本文稍后看到如何解決這個printf符號。
  • .size告知主要功能的大?。ㄒ宰止?jié)為單位)。 .-main是一個表達式,其中。 符號表示當前行的地址。 因此,該表達式的值等于主函數(shù)的行的地址-current_address_,從而為我們提供了主函數(shù)的大小(以字節(jié)為單位)。
  • .ident只是告訴匯編器在.comment部分添加以下行。.note.GNU-stack用于告知該程序的堆棧是否可執(zhí)行。 通常,此偽指令的值為空字符串,這表明堆棧不可執(zhí)行。
image

現(xiàn)在,我們的程序是以匯編語言編寫的,但仍然是處理器無法理解的語言。 我們必須將匯編語言轉(zhuǎn)換為機器語言,并且該工作由匯編器完成。 匯編器獲取您的匯編文件并生成一個目標文件,該文件是一個二進制文件,其中包含您程序的機器指令。

讓我們將程序集文件轉(zhuǎn)換為目標文件,以查看實際過程。 要獲取程序的目標文件,請將c標志傳遞給gcc編譯器。

gcc -c sample.c

您將得到一個擴展名為.o的目標文件。 由于這是一個二進制文件,因此您將無法在常規(guī)文本編輯器中將其打開以查看其內(nèi)容。 但是我們有可用的工具來找出那些目標文件中的內(nèi)容。

目標文件可能具有許多不同的文件格式。 我們將特別關(guān)注一種在Linux上使用的ELF文件格式。

ELF文件包含以下信息-

  • ELF標頭
  • 程序頭表
  • 節(jié)標題表
  • 上表引用的其他一些數(shù)據(jù)

ELF標頭包含有關(guān)目標文件的一些元信息,例如文件的類型,生成二進制文件的機器,版本,標頭的大小等。要查看標頭,只需將-h標志傳遞給eu-readelf實用程序。

image

從上面的清單中可以看出,該文件沒有任何程序標題,這很好。 程序頭僅存在于可執(zhí)行文件和共享庫中。 在下一步中鏈接文件時,我們將看到程序頭。

但是我們確實有13個部分。 讓我們看看這些部分是什么。 使用-S標志。

image

您無需了解以上所有內(nèi)容。 但是從本質(zhì)上講,它為每個section列出了各種信息,例如section的名稱,section的大小以及section距文件開頭的偏移量。 我們使用的重要部分如下:

  • 文字部分包含我們的機器代碼
  • rodata部分包含我們程序中的只讀數(shù)據(jù)。它可能是您在程序中使用的常量或字符串文字。這里只包含%d\n
    數(shù)據(jù)部分包含我們程序的初始化數(shù)據(jù)。這是空的,因為我們沒有任何初始化數(shù)據(jù)
  • bss部分類似于data部分,但包含我們程序的未初始化數(shù)據(jù)。未初始化的數(shù)據(jù)可以是聲明為int arr [100]的數(shù)組,該數(shù)組將成為本節(jié)的一部分。關(guān)于bss部分需要注意的一點是,與其他部分根據(jù)其內(nèi)容占用空間不同,bss部分僅包含該部分的大小,而沒有其他內(nèi)容。原因是在加載時,所需要做的只是在本節(jié)中需要分配的字節(jié)數(shù)。這樣,我們減小了最終可執(zhí)行文件的大小
  • strtab部分列出了程序中包含的所有字符串
  • symtab節(jié)是符號表。它包含了我們程序的所有符號(變量名和函數(shù)名)。
  • rela.text部分是重定位部分。稍后再詳細介紹。

您也可以查看這些部分的內(nèi)容,只需將相應(yīng)的部分編號傳遞給eu-readelf程序即可。 您也可以使用objdump工具。 它還可以為您提供某些部分的分解。

讓我們更詳細地討論rela.text部分。 記住我們在程序中使用的printf函數(shù)。 現(xiàn)在,printf是我們自己尚未定義的東西,它是C庫的一部分。 通常,當您編譯C程序時,編譯器將以某種方式編譯它們,以使您調(diào)用的C函數(shù)不會與可執(zhí)行文件捆綁在一起,從而減小了最終可執(zhí)行文件的大小。 取而代之的是,表由所有這些符號組成,稱為重定位表,該表隨后由裝入程序中的某些內(nèi)容填充。 稍后我們將討論有關(guān)加載器部分的更多信息,但是現(xiàn)在,重要的是,如果您查看rela.text部分,您會在此處找到列出的printf符號。 讓我們在這里確認一次。

image

您可以忽略第二個重定位部分.rela.eh_frame。 它與異常處理有關(guān),在這里我們對它沒有太大興趣。 讓我們在這里看到第一部分。 在那里,我們可以看到兩個條目,其中之一是我們的printf符號。 該條目的意思是,此文件中使用了一個符號,其名稱為printf,但尚未定義,該符號位于此文件中距.text節(jié)開始的偏移量0x31處。 現(xiàn)在,在.text部分中檢查偏移量0x31處的內(nèi)容。

image

在這里您可以看到偏移量為0x30的調(diào)用指令。 e8代表調(diào)用指令的操作碼,后跟從偏移量0x310x34的4個字節(jié),應(yīng)該與我們現(xiàn)在沒有的printf函數(shù)實際地址相對應(yīng),所以它們僅為00。 (稍后,我們將看到該位置實際上并不保存printf地址,而是使用稱為plt的表間接調(diào)用該地址。稍后我們將介紹這一部分)

image

到目前為止,我們所做的所有工作都在一個源文件上進行。 但實際上,這種情況很少見。 在實際的生產(chǎn)代碼中,您有數(shù)十萬個源代碼文件,您需要編譯和創(chuàng)建可執(zhí)行文件。 現(xiàn)在,在這種情況下,我們將如何比較到目前為止的步驟?

好吧,所有步驟都將保持不變。 所有源代碼文件將分別進行預(yù)處理,編譯,組裝,最后我們將獲得單獨的目標代碼文件。

現(xiàn)在,每個源代碼文件都不會孤立地編寫。 它們必須具有某些函數(shù),這些全局變量必須在某個文件中定義,并在其他文件的不同位置使用。

鏈接器的工作是收集所有目標文件,遍歷每個目標文件并跟蹤每個文件定義的符號以及使用的符號。 它可以在每個目標文件的符號表中找到所有這些信息。 收集了所有這些信息之后,鏈接器將創(chuàng)建一個目標文件,將每個目標文件中的所有部分組合到相應(yīng)的部分中,并重新放置所有可以解析的符號。

在我們的例子中,我們沒有源文件的集合,只有一個文件,但是由于我們使用C庫中的printf函數(shù),因此我們的源文件將與C庫動態(tài)鏈接。 現(xiàn)在,我們鏈接程序并進一步調(diào)查輸出。

gcc sample.c

在這里我將不做詳細介紹,因為它也是我們上面看到的ELF文件,只有一些新的部分。這里要注意的一件事是,當我們看到從匯編程序獲得的目標文件時,所看到的地址是相對的。但是,在鏈接了所有文件之后,我們幾乎知道了所有內(nèi)容的去向,因此,如果您檢查這些階段的輸出,則它也包含絕對地址。

在此階段,鏈接器已識別出程序中正在使用的所有符號,使用這些符號的人以及定義這些符號的人。鏈接程序僅將符號定義的地址映射到符號的用法。但是在完成所有這些操作之后,此時仍然存在一些尚未解析的符號,其中之一就是我們的printf符號。通常,這些符號既可以是外部定義的變量,也可以是外部定義的函數(shù)。鏈接器還會創(chuàng)建一個重定位表,該重定位表與匯編程序創(chuàng)建的重定位表相同,其中的條目仍未解析。

此時,您應(yīng)該知道一件事。您從其他庫中使用的功能和數(shù)據(jù)可以進行靜態(tài)鏈接或動態(tài)鏈接。靜態(tài)鏈接意味著將這些庫中的函數(shù)和數(shù)據(jù)復(fù)制并粘貼到可執(zhí)行文件中。而如果您進行動態(tài)鏈接,則不會將這些功能和數(shù)據(jù)復(fù)制到可執(zhí)行文件中,從而減小了最終的可執(zhí)行文件大小。

為了使libray具有動態(tài)鏈接的功能,該庫必須是共享庫(so文件)。通常,許多程序使用的公共庫是共享庫,其中之一就是我們的libc庫。 libc被許多程序使用,如果每個程序開始靜態(tài)鏈接到它,那么在任何時候,同一代碼的副本將占據(jù)內(nèi)存中的大量空間。具有動態(tài)鏈接可以解決此問題,并且在任何時候,只有l(wèi)ibc的一個副本會占用內(nèi)存中的空間,并且所有程序都將從該共享庫中引用。

為了使動態(tài)鏈接成為可能,鏈接器還會創(chuàng)建兩個在匯編器生成的目標代碼中不存在的節(jié)。 這些是.plt(過程鏈接表)和.got(全局偏移表)部分。 我們將在加載可執(zhí)行文件時介紹這些部分,因為這些部分在實際加載可執(zhí)行文件時會很有用。

image

現(xiàn)在是時候?qū)嶋H運行我們的可執(zhí)行文件了。

當您在GUI中單擊文件或從命令行運行該文件時,將間接調(diào)用execev系統(tǒng)調(diào)用。 正是這個系統(tǒng)調(diào)用,內(nèi)核在其中開始將可執(zhí)行文件加載到內(nèi)存中的工作。

記住上面的程序頭表。 這是非常有用的地方。

image

內(nèi)核如何知道在文件中的哪里找到該表?好了,可以在ELF標頭中找到該信息,該標頭始終從文件的偏移量0開始。完成此操作后,內(nèi)核將查找所有類型為LOAD的條目,并將它們加載到進程的內(nèi)存空間中。

從上面的清單中可以看到,有兩個類型為LOAD的條目。您還可以查看每個細分中包含哪些部分。

現(xiàn)代操作系統(tǒng)和處理器根據(jù)頁面來管理內(nèi)存。您的計算機內(nèi)存分為固定大小的塊,當任何進程要求一些內(nèi)存時,操作系統(tǒng)都會為該進程分配一定數(shù)量的頁面。除了有效管理內(nèi)存的好處外,這還具有提供安全性的好處。操作系統(tǒng)和內(nèi)核可以為每個頁面設(shè)置保護位。保護位指定特定頁面是只讀頁面,可以寫入頁面還是可以執(zhí)行頁面。保護位設(shè)為“只讀”的頁面無法修改,因此可以防止有意或無意地修改數(shù)據(jù)。

只讀頁面還有一個好處,即同一程序的多個運行進程可以共享同一頁面。由于頁面是只讀的,因此任何正在運行的進程都不能修改這些頁面,因此,每個進程都可以正常工作。

要設(shè)置這些保護位,我們必須以某種方式告訴內(nèi)核,哪些頁面必須標記為只讀,哪些頁面可以寫入和執(zhí)行。這些信息存儲在上面每個條目的標志中。

注意第一個LOAD條目。它標記為R和E,這意味著可以讀取和執(zhí)行這些段,但是不能對其進行修改,如果您向下看并看到這些段中的哪些部分,則可以在其中看到兩個熟悉的部分,.text和。 rodata。因此,我們的代碼和只讀數(shù)據(jù)只能被讀取和執(zhí)行,而不能被修改,這應(yīng)該發(fā)生。

同樣,第二個LOAD條目包含已初始化和未初始化的數(shù)據(jù)GOT表(稍后會詳細介紹),它們被標記為RW,因此可以讀寫,但無法執(zhí)行。

加載這些段并設(shè)置它們的權(quán)限后,內(nèi)核會檢查是否存在.interp段。在靜態(tài)鏈接的可執(zhí)行文件中,不需要此段,因為該可執(zhí)行文件包含它所需的所有代碼,但是對于動態(tài)鏈接的可執(zhí)行文件,此段很重要。該段包含.interp節(jié),其中包含動態(tài)鏈接器的路徑。 (您可以通過將-static標志傳遞給gcc編譯器并檢查生成的可執(zhí)行文件中的頭表來檢查靜態(tài)鏈接的可執(zhí)行文件中是否沒有.interp段。)

在我們的例子中,它將找到一個,并指向/lib64/ld-linux-x86-64.so.2路徑中的動態(tài)鏈接器。與我們的可執(zhí)行文件類似,內(nèi)核將通過讀取標頭,查找其段并將其加載到當前程序的內(nèi)存空間中來開始加載這些共享庫。在不需要所有這些的靜態(tài)鏈接的可執(zhí)行文件中,內(nèi)核將控制權(quán)交給我們的程序,這里內(nèi)核將控制權(quán)交給了動態(tài)鏈接器,并將主函數(shù)的地址壓入堆棧,以便在動態(tài)鏈接器之后完成工作,它知道將控制權(quán)移交給哪里。

現(xiàn)在,我們應(yīng)該了解已經(jīng)跳過太長時間的兩個表,過程鏈接表和全局偏移表,因為它們與動態(tài)鏈接器的功能密切相關(guān)。

程序中可能需要兩種類型的重定位。變量重定位和函數(shù)重定位。對于外部定義的變量,我們將該條目包括在GOT表中,而外部定義的函數(shù)將這些條目包括在兩個表中。因此,從本質(zhì)上講,GOT表具有所有外部定義變量和函數(shù)的條目,而PLT表僅具有函數(shù)的條目。下面的示例將清楚我們有兩個函數(shù)條目的原因。

讓我們以printf函數(shù)為例,看看這些表是如何工作的。 在我們的主要功能中,我們來看一下printf函數(shù)的調(diào)用說明。

400556:    e8 a5 fe ff ff           callq   0x400400

該調(diào)用指令正在調(diào)用.plt部分的地址。 讓我們看看那里是什么。

image

對于每個外部定義的函數(shù),我們在plt部分中都有一個條目,并且所有外觀都相同,并且除第一個條目外,都有三條指令。 這是一個特殊的條目,我們將在以后使用。

在那里,我們找到了跳轉(zhuǎn)到地址0x601018包含的值的信息。 這些地址是GOT表中的一個條目。 讓我們看看這些地址的內(nèi)容。

image

這就是魔術(shù)發(fā)生的地方。除了第一次調(diào)用printf函數(shù)外,此地址處的值將是C庫中printf函數(shù)的實際地址,我們只需跳轉(zhuǎn)到該位置即可。但是第一次,其他事情發(fā)生了。

首次調(diào)用printf函數(shù)時,此位置的值是printf函數(shù)的plt條目中下一條指令的地址。從上面的清單中可以看到,它是以小字節(jié)序格式存儲的400406。在plt條目中的此位置,我們有一個push指令,該指令將0壓入堆棧。每個plt條目都有相同的推送指令,但它們推送的編號不同。 0表示重定位表中printf符號的偏移量。然后,在推入指令之后跟隨跳轉(zhuǎn)指令,該跳轉(zhuǎn)指令跳轉(zhuǎn)到第一個plt條目中的第一個指令。

從上面記住,當我告訴您第一個條目很特殊時。這是因為在這里調(diào)用動態(tài)鏈接器來解析外部符號并重新定位它們。為此,我們跳轉(zhuǎn)到got表中地址601010中包含的地址。這些地址應(yīng)包含用于處理重定位的動態(tài)鏈接程序例程的地址?,F(xiàn)在,這些條目用0填充,但是當程序?qū)嶋H運行且內(nèi)核調(diào)用動態(tài)鏈接器時,鏈接器將填充此地址。

調(diào)用例程時,鏈接器將從外部共享對象中解析更早推送的符號(在本例中為0),并將符號的正確地址放入get表中。因此,從現(xiàn)在開始,當調(diào)用printf函數(shù)時,我們不必查閱鏈接器,我們可以直接從plt跳轉(zhuǎn)到C庫中的printf函數(shù)。

此過程稱為延遲加載。一個程序可能包含許多外部符號,但它可能不會在該程序的一次運行中調(diào)用它們。因此,符號解析被推遲到實際使用,這為我們節(jié)省了一些程序啟動時間。

從上面的討論中可以看到,我們不必修改plt部分,而只需修改gott部分。這就是為什么plt節(jié)位于第一個LOAD段中并標記為只讀,而gett節(jié)位于第二個LOAD段中并標記為Write。

這就是動態(tài)鏈接器的工作方式。我已經(jīng)跳過了很多細節(jié),但是如果您有興趣了解更多詳細信息,那么可以查看這篇文章。

讓我們回到程序加載中。 我們已經(jīng)完成了大部分工作。 內(nèi)核已經(jīng)加載了所有可加載的段,已經(jīng)調(diào)用了動態(tài)鏈接器。 剩下的就是調(diào)用我們的主要功能。 鏈接器完成后就完成了該工作。 當它調(diào)用我們的main函數(shù)時,我們在終端中獲得以下輸出-

3

感謝您閱讀我的文章。 如果您喜歡我的文章或?qū)ξ矣腥魏纹渌ㄗh,請在下面的評論部分中告訴我。 而且,請隨時分享:)

原文鏈接:https://kishuagarwal.github.io/life-of-a-binary.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

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

  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,857評論 0 27
  • 轉(zhuǎn)自http://blog.csdn.net/navyhu/article/details/47023317理解鏈...
    扎Zn了老Fe閱讀 1,464評論 0 0
  • 動態(tài)鏈接,在可執(zhí)行文件裝載時或運行時,由操作系統(tǒng)的裝載程序加載庫。大多數(shù)操作系統(tǒng)將解析外部引用(比如庫)作為加載過...
    小5筒閱讀 5,539評論 0 3
  • 總結(jié)自書籍《程序員的自我修養(yǎng)—鏈接、裝載與庫》 1. Hello World運行中被隱藏的過程 HoelloWor...
    Mr希靈閱讀 1,635評論 3 20
  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,424評論 0 5