csapp讀書筆記3-程序的機器級表示

程序編碼

gcc -Og -o p p1.c p2.c

採用gcc編譯c語言java

  • Og:優化級別較低,產生和原始c語言代碼相符的機器代碼,適合學習閱讀
  • O1或O2:優化級別較高,產生代碼和原始代碼差距較大,適合實際使用

gcc命令會調用一些列程序:算法

  • C預處理器:給源文件插入#include文件,擴展#define宏
  • 編譯器:源文件->彙編代碼p1.s和p2.s
  • 彙編器:彙編代碼->二進制目標文件p1.o和p2.o
  • 連接器:合併目標文件與函數庫二進制文件,生成可執行文件p(根據-o p指定)

其中目標代碼和可執行文件都是機器代碼編程

機器級代碼

計算機系統對複雜的機器級編程進行抽象:數組

  • 使用指令集架構(ISA)定義機器級程序的指令的做用
  • 機器級程序使用的內存地址是虛擬地址,即一個大字節數組抽象了底層多個存儲設備組合

彙編代碼接近機器代碼,但提供了更好的可讀性安全

x86-64機器代碼與c差距巨大,處理器中不少狀態在c語言隱藏可是在x86-64中均可見:數據結構

  • 程序計數器:也稱PC,用%rip表示。表示下一個要執行的指令的內存地址
  • 整數寄存器文件:包含16個命名位置,分別存儲64位值,可存儲地址(c語言指針)、整數。部分存儲程序狀態,部分存儲臨時數據(參數、局部變量、返回值)
  • 條件碼寄存器:存儲最近執行的算術或邏輯指令的狀態,如if、while的狀態
  • 一組向量寄存器:存儲一個或多個整數或浮點數

程序內存包含了下面幾個部分:架構

  • 程序可執行的機器代碼
  • 操做系統須要的一些信息
  • 管理函數調用、返回的運行時棧
  • 用戶分配的內存塊(malloc等分配)

機器代碼須要知道程序的虛擬內存地址便可,由操做系統將虛擬內存地址轉爲物理內存地址函數

在機器代碼中,內存中不存在數據結構和對象,只有一個大字節數組和其中的虛擬地址性能

操做示例

源代碼mstore.c學習

gcc -Og -S mstore.c:生成彙編文件mstore.s

gcc -Og -c mstore.c:生成二進制目標代碼文件mstore.o

objdump -d mstore.o:經過反彙編器將機器代碼翻譯成彙編代碼

數據格式

因爲最先從16位體系結構發展起來,Intel稱(word)表示16位數據類型,32位和64位就成爲雙字和四字

c語言的數據類型與Intel數據類型的映射關係:

彙編代碼中操做指令後跟上這些數據類型的後綴,能夠代表是操做哪一種類型的數據。如movb表示傳送字節

訪問信息

16個整數寄存器分別能夠存儲64位值,具體用來存整數和指針

  • 8位操做能夠訪問字節
  • 16位操做能夠訪問字
  • 32位操做能夠訪問雙字
  • 64位操做能夠訪問四字

操做數

大多指令有N個操做數,表明一個操做指令所使用的源數據值和放置的目的位置。操做數有三種類型:

  • 當即數:常量值
  • 寄存器:某個寄存器標識符
  • 存儲器:內存地址

  • 格式表明在彙編中的顯示方式
  • 操做數值表明計算公式,也就是根據指定格式算出來的具體地址或者數值

數據傳送指令

將數據從源位置複製到目的位置的指令,有三種mov,movz,movs:前者作普通拷貝,高位保持不變;後二者用於將較小的源值拷貝到較大的目的地址,並採用0或者符號位擴展高位

mov

源和目的地能夠是當即數、寄存器或內存地址,但不能同時是內存地址,拷貝以後不作任何變更(例外:movl的目的地是寄存器時,高四位字節置零)

movz

movs

壓棧和出棧

程序棧存放在內存的某個區域,將棧視爲一個大樹組的話,棧頂在數組低位,並向「下」擴展

  • pushq:將四字數據壓入棧頂,有一個操做數表明源數據
  • popq:將棧頂的四字數據彈出,並放置到目的位置,有一個操做數表明目的位置

算術和邏輯操做

分爲三種類型:

  • 加載有效地址
  • 一元和二元操做
  • 移位操做

加載有效地址

leaq:將內存數據讀取到寄存器,相似movq

  • movq的源爲內存地址時,會首先根據公式算出表達式,而後解析內存地址中的數據並填充到表達式中,生成結果並存到目的地
  • leaq不會解析內存地址的數據,只是單純的根據源操做數和公式計算出一個表達式,而後將表達式做爲結果存入目的地

示例:

通常使用leaq運算的指令會比movq的指令更少,所以每每leaq更高效

一元和二元操做

  • 一元操做:只有一個操做數,既是源又是目的
  • 二元操做:有兩個操做數,第二個既是源又是目的

移位操做

有兩個操做數:

  • 第一個是移位量:能夠是當即數或者單字節寄存器%cl(數值存在該寄存器中)
  • 第二個是要移位的數:能夠是寄存器或者內存地址,既是源又是目的

特殊算術操做

描述64位相乘(得128位數字)和128位整除的指令,思想是將128位數採用兩個64位寄存器存儲

控制

上述全部指令都是順序執行,爲了支持c語言條件語句、循環語句、分支語句,引入jump指令

### 條件碼

除了整數寄存器,cpu還維護一組單個位條件碼寄存器,記錄最近算術和邏輯運算的狀態。檢測這些屬性來執行條件分支指令,經常使用的四個:

  • CF:進位標誌。最近的操做使最高位產生進位,檢測無符號運算溢出
  • ZF:零標誌。最近的操做結果爲0
  • SF:符號標誌。最近的操做結果爲負數
  • OF:溢出標誌。最近的操做致使一個補碼溢出-正溢出或負溢出,檢測有符號運算溢出

上述指令除了leaq,都會設置條件碼

上述指令,CMP相似SUB,TEST相似AND,區別在CMP和TEST只設置條件碼而不修改寄存器的值

訪問條件碼

一般不會直接讀取條件碼,經常使用的使用方法爲:

  • 能夠根據條件碼的某種組合,設置目的地的低位單字節設置爲0或1(SET指令)

  • 能夠根據條件跳轉到程序的某個位置(JMP指令)

間接跳轉中,區分跳轉須要區分跳轉目標是從寄存器仍是內存地址讀出來的

jmp *%rax:用寄存器%rax的值做爲跳轉目的地

jmp *(%rax):用寄存器%rax中的值做爲地址,讀出該地址內存的值,做爲跳轉目的地

  • 能夠有條件的傳送數據

跳轉指令的編碼

彙編中jmp到目標位置,在機器碼中,實現方式爲:

  • 採用PC相對的編碼方式。指定地址偏移量(能夠是一、2或4個字節),加上PC(程序計數器)中下一條指令的地址,算出目的地址,進行跳轉
  • 採用絕對地址編碼。指定一個絕對地址(4個字節),直接跳轉到該地址

示例採用PC相對的編碼方式:

  • 第2行跳轉:4004d5(PC存儲的地址)+ 03(單字節相對編碼,十進制3)= 4004d8,跳轉到目標地址4004d8
  • 地5行跳轉:4004dd(PC存儲的地址)+ f8(單字節相對編碼,十進制-8) = 4004d5,跳轉到目標地址4004d5

PC相對編碼更經常使用,相比絕對地址編碼的優點在於:

  • 因爲是計算差值,PC相對編碼須要比起絕對地址編碼,須要的指令更少(上面兩行跳轉只用了2個字節,若是用絕對地址編碼共要8個字節)
  • 目標代碼能夠不作改變就移到內存的不一樣位置

用條件控制來實現條件分支

設置條件碼,並經過JUMP類型的指令跳轉到指定分支。這種方式簡單、通用

用條件傳送來實現條件分支

計算一個條件操做的兩種結果,而後根據條件是否知足從中選取一個。這種使用受限,可是性能更優,GCC只要在兩個表達式都只是很簡單的指令時,纔會選擇條件傳送,不然,絕大多數狀況都是採用條件控制

上圖列舉了x86-64的一些條件傳送指令

對比兩種條件分支實現

  • 條件控制更簡單、通用。GCC絕大多數狀況都採用此策略
  • 條件傳送更高效。因爲須要計算兩個分支的結果,因此當其中一個分支指令出現錯誤的時候,或者兩個分支操做比較複雜的時候,都沒法使用這個策略,只有在兩個分支都是很是簡單的指令運算的時候,纔可以使用

條件傳送高效的祕密:處理器的流水線機制。流水線機制就是將一條指令拆成多個步驟(從內存取指令、肯定指令類型、從內存讀數據、執行算術運算、向內存寫數據、更新PC),經過重疊連續指令的步驟(執行指令運算的同時取下一條指令),保證流水線充滿了待執行的指令,提升了高性能。這樣就必須預測後續指令,若是出現條件分支,可能致使預測錯誤,致使必須丟棄取到的指令並從新獲取,致使加大指令執行的時鐘週期,致使性能降低

循環

do-while循環:

while循環:

gcc -Og模式下翻譯方式:

gcc -O1模式下翻譯方式:

for循環:

翻譯成while循環的兩種模式(取決於優化等級)

switch語句

switch會利用跳轉表來實現高效跳轉到正確的分支。跳轉表就是一個數組,存儲了全部分支代碼段的地址

上圖彙編代碼,就是一個跳轉表的聲明:

  1. 在名爲.rodata的目標代碼中,聲明8個元素的地址段(數組元素)
  2. 將第一個地址值設爲代碼標號爲.L4的指令地址,後面元素值陸續設爲爲.L3 ~ .L7的指令地址

上圖表是switch語句從原始C代碼到彙編代碼的翻譯過程:

  1. 獲取數值,並計算偏移量
  2. 根據偏移量生成跳轉表索引,依次定位跳轉表的地址
  3. 根據定位的地址,跳轉到指定代碼段,執行代碼邏輯
  4. 若是代碼段結尾存在jmp,jmp到指定位置(C語言break語句);若是代碼段結尾不存在jmp,繼續執行下一個代碼塊

在分支量較大,並且跳轉表索引密集的時候,GCC會自動選擇跳轉表來優化switch語句。經過跳轉表直接定位代碼段,致使分支較多的狀況下,switch語句相比if-else更高效

過程

過程調用的實現機制涉及三個方面(假設過程P調用過程Q,Q執行後返回P):

  • 傳遞控制:進入過程Q時,PC被設爲Q代碼起始地址,Q執行後返回時,PC被設爲P中調用Q的後面那條指令的地址
  • 傳遞數據:P可以給Q提供一個或多個參數,Q可以給P提供一個返回值
  • 內存管理:開始時,Q可能須要爲局部變量分配空間;返回前,必須釋放這些空間

運行時棧

  • 系統爲過程分配的棧空間就是棧幀,過程返回會釋放棧幀
  • 分配棧幀的操做就是將棧指針減少一個適當值,釋放棧幀就是將棧指針增長一個適當值
  • 當函數參數不超過6個時而且沒有在其中調用其餘函數時,全部參數能夠放到寄存器,而且該函數不須要棧幀
  • 大多數過程的棧幀都是定長的,過程剛開始就分配好
  • P調用Q時,會先將返回地址壓入P的棧幀,以後才執行Q代碼段的指令,返回時會彈出返回地址,做爲下一步執行指令

控制轉移

涉及過程調用、控制轉移的指令:

帶星號表示間接調用,就是獲取給操做數的值做爲地址,進行調用

假設存在程序,main函數調用multstore函數:

main

multstore

運行時棧

流程說明(%rip爲程序計數器PC的值,%rsp爲棧指針的值):

  • main函數中,程序指令移動到0x400563,將%rip修改成該地址,此時%rsp指向0x7fffffffe840
  • 執行callq,調用multstore。幕後的操做是:將後續返回地址0x400568壓入棧(致使%rsp減少8個字節,0x7fffffffe840到0x7fffffffe838),而後跳轉到multstore函數的第一個指令,地址爲0x400540(致使%rip指向該地址)
  • 執行mulstore函數的指令直到retq
  • 執行retq指令,返回到main函數。幕後的操做是:從棧中彈出8個字節的地址0x400568(%rsp增大8個字節,0x7fffffffe838到0x7fffffffe840),而後跳轉到該地址(%rip修改成該地址)

數據傳送

P調用Q,P須要將前6個參數複製到寄存器,從Q返回時,Q須要將惟一的返回值寫入%rax

圖表中列出參數在指定的寄存器中的存放順序

當參數N>6時,P須要爲7 ~ N個參數分配棧空間,並按N到7的前後順序壓入棧,保證第7個參數在棧頂,只有分配了棧空間,才能將控制轉移給Q,Q來訪問棧空間上的參數

如圖展現,前六個參數在寄存器,後兩個在棧

這裏最後一個參數不在棧頂而是在%rsp+8的位置,是由於在參數入棧以後,返回地址也入棧了

棧上的局部存儲

雖然不少時候局部變量能夠存在寄存器,可是如下這些狀況須要必須爲局部變量分配棧空間:

  • 寄存器不足以存下全部局部變量
  • 對局部變量使用了地址運算符‘&’,代表該變量數據應該被存到內存,經過棧指針引用這片內存
  • 某些變量是數組或結構(暫不討論)

實例解析1

  1. 在caller中對arg1,arg2使用了運算符&,須要分配16字節棧幀
  2. 將arg1,arg2的值存入棧的棧幀
  3. 因爲須要調用swap_add並傳參(參數爲arg1,arg2的地址),將參數拷貝到寄存器
  4. caller ret以前,須要釋放給局部變量arg1,arg2分配的棧幀

實例解析2

  1. 在call_proc中對x1-x4使用了運算符&,分配32字節棧幀
  2. 將x1-x4值存入棧幀
  3. 調用proc須要傳參,將前六個拷貝到寄存器,後兩個存入棧中剩餘位置
  4. call_proc ret以前,會釋放給x1-x4分配的棧幀

寄存器中的局部存儲空間

寄存器是全部過程的共享空間,須要保證過程P調用過程Q的時候,Q不會篡改P等會要用的寄存器

  • P:調用者
  • Q:被調用者

爲防止上述狀況,x86-64定義了一組規範:

其中着重關注:

  • 調用者保存的寄存器:P調用Q,P要保存這些寄存器的值不變,保證調用結束以後還能用上。Q能夠隨意修改
  • 被調用者保存的寄存器:P調用Q,Q要保存這些寄存器的值不變,保證過程返回後P以後還能用這些值

保存這些寄存器值的方案:

  • 不作修改
  • 將寄存器的原值入棧,這時能夠隨意修改該寄存器,最後彈出原值並賦值給該寄存器便可

實例講解:

函數P中有兩個值在彙編代碼中存在被修改的風險:

  • x:進入P時,x是做爲第一個參數,放在%rdi。執行Q(y)時,y爲第一個參數,須要存入%rdi,這時x的值會被篡改。須要提早保存x的值,策略是使用%rbp保存
  • Q(y)返回值:Q(y)的值會存入%rax,Q(x)的值也會存入%rax,致使前者被篡改。須要提早保留Q(y),策略是使用%rbx保存

上述兩個保存操做使用了%rbp和%rbx這兩個被調用者保存的寄存器,爲保證P返回後還能正常使用,P須要提早保存它的值

遞歸過程

保證遞歸正常工做的機制:

  • 每一個過程調用在棧中都有本身的私有空間,這些私有空間在用完之後會被釋放
  • x86-64有防止寄存器值遭篡改的策略

實例講解:

每次調用都會將n減1,因此須要提早保留n的值保證後續正常使用

數組分配和訪問的基本原則

最右邊一、八、四、8這些因子,表明char、char指針、int、double指針的字節大小

設E爲int類型數組,想要獲取第i的元素E[i]。E的地址存放在%rdx,i的值存放在%rcx,則獲取E[i]的方式爲:

movl (%rdx,%rcx,4),%eax

指針運算

  • &配合對象表達式:&Expr表示獲取對象Expr的指針
  • *配合地址表達式:*Expr表示獲取地址Expr的值

嵌套的數組

int A[5][3]

等價於

typedef int row3_t[3];
row3_t A[5];

即:A是個包含5個元素的數組,每一個元素都是一個存儲了3個元素的數組

這個嵌套數組A也可視爲5行3列的二維數組

A的佔用字節爲5*3*4=60

公式:對任意二維數組T D[R][C],元素D[i][j]的內存地址爲&D[i][j]=Xd + L*(C*i + j)

  • Xd爲數組D初始位置
  • i,j爲要訪問的元素
  • L爲數組元素的數據類型的字節大小

編譯器訪問二維數組中元素的方式爲根據上述公式計算出元素內存地址,而後使用MOV命令讀取該地址的值

給出彙編代碼訪問A[i][j]的方式:

該算法將12i這個乘法表達式拆成多個加法表達式,這是利用了第二章乘以常數這一節的特性

定長數組

根據上圖定義的定長二維數組(矩陣),給出下面矩陣乘法的算法:

每次計算矩陣元素時,都得使用上一小節給出的公式,GCC會針對這種狀況進行優化,減小對公式的直接使用,轉而依賴指針運算

變長數組

int A[n1][n2],容許n一、n2是表達式,編譯期沒法得知該數組的大小。該功能在C99才引入

聲明變長數組時,須要將參數n放到參數A[n][n]以前

上圖爲彙編代碼訪問A[n][n]數組中,A[i][j]元素的方式。相比定長數組中,因爲編譯期間沒法知曉n的值,沒法對4(n*j)進行優化(拆分紅多個加法)

上圖爲GCC對變長數組的矩陣相乘算法的優化,並無給出彙編代碼。跟定長數組中的優化同樣,避免使用公式,轉而採用指針運算(變長數組直接使用公式的效果更差,由於不知道n的大小,沒法對4(n*j)進行優化)

結構

假設struct rec*類型的變量r存放在寄存器%rdi,編譯器會將r->j=r->i翻譯成:

更復雜的實例:

綜上,結構的各字段在編譯時會轉換成內存地址

聯合

  • 結構:將多種數據類型分紅多塊,存在連續的內存區域
  • 聯合:將多種數據類型存放在一塊內存區域中,每次提取的時候只能按照一種類型獲取該區域的數據

結構和聯合:

在內存佔用上的區別:

  • 結構:內存佔用量爲其中全部數據大小的總和
  • 聯合:內存佔用量爲其中全部數據最大的那一個

上圖中展現的佔用量包含了數據對齊,這部分下一節介紹

相比結構,聯合在存儲多個字段的時候佔用內存更小。但改善空間不大的時候,使用聯合繞過了C語言類型系統,容易致使bug產生

實例:訪問不一樣數據類型的位模式

強轉生成的u,值與d同樣,可是二者位表示大相徑庭(0.0除外)

使用聯合生成的u,位模式跟d如出一轍,可是值不一樣(0.0除外)。參考java庫中的Double.doubleToRawLongBits

實例:分段表示位模式,須要注意機器字節順序

  • 小端法機器:word0取低4位,word1取高4位
  • 大端法機器:word0取高4位,word1取低4位

數據對齊

爲了簡化訪問基本數據類型的方式,一般須要讓基本數據類型的地址是其字節大小的整數倍。

針對結構:

不對齊:

對齊:

  • 不對齊:節省內存,可是須要執行兩次訪問才能讀出j(第一次訪問區域4-8,第二次訪問區域8-12)
  • 對齊:浪費一部份內存,換取更快的訪問速度(須要執行一次8-12的訪問便可讀出j)

理解指針

  • 每一個指針對應一個類型。該類型代表該指針指向的對象的類型
  • 每一個指針都有一個值。該值表名指向的對象的地址,NULL(0)代表該指針沒有指向任何地址
  • 指針用&運算符建立。&運算符的機器碼經常使用leaq指令來實現
  • *操做符用於間接引用指針。其結果是一個值,該值是該指針地址所存儲的值
  • 數組與指針關係緊密。數組名能夠像指針變量同樣引用,數組引用和指針間接引用效果同樣
  • 將指針從一種類型強轉成另外一種類型,只改變類型不改變值
  • 指針能夠指向函數。該函數指針的值是該函數第一條指令的地址

緩衝區溢出

存在溢出風險的程序:

該程序的棧空間爲:

若是用戶輸入的字節超過24字節,就會破壞調用者的棧幀。根據用戶輸入的字節數,程序會受到以下破壞:

這個破壞容易致使程序受到攻擊。具體方式爲:

  • 輸入一些字符串,其中包含可執行代碼的字節編碼,成爲攻擊代碼
  • 輸入的特定字符超過24字節,並保證恰好將返回地址修改成攻擊代碼的地址
  • echo ret時會跳轉到攻擊代碼

這樣程序的意圖就遭到了篡改

對抗緩衝區溢出攻擊

對抗溢出攻擊的方式有兩種:

  • 棧隨機化。想要攻擊程序,就須要知道攻擊代碼的地址,棧隨機化使得程序開始時會在棧上隨機分配0-N字節的佔位空間,保證攻擊代碼的地址每次運行都不一樣,增大攻擊難度
  • 棧破壞檢測。在緩衝區與返回地址的空隙中,插入一個特殊值(金絲雀值),每次運行隨機產生。程序返回前檢測這個值有沒有被修改

  • 限制可執行代碼的區域。保證編譯期代碼使用的內存空間纔是可執行的,運行時分配的內存空間不可執行。對java不適用,java支持動態生成可執行代碼

支持變長棧幀

以前的機器代碼,在編譯期就能肯定須要爲棧幀分配多少空間。有些函數(如alloca)在運行期纔會在棧上(malloc是在堆上)分配空間

上圖代碼是一個實例:存在變長數組,編譯期沒法肯定數組分配空間(n未知),因此這部分空間須要運行時分配,可是分配後該如何釋放這部分空間,並正確的返回到返回地址所在的位置?

x86-64採用寄存器%rbp做爲幀指針(幀指針只會在實現變長棧幀時纔會用),用來保存動態分配以前棧指針的位置,在函數返回前經過leave指令釋放棧指針和恢復幀指針

leave等價於:

浮點體系結構

介紹了程序計數器、整數寄存器、條件碼寄存器,如今只有向量寄存器沒有介紹,這部分關聯了浮點數的機器級操做方式

處理器定義了浮點體系結構,關係着浮點數據操做如何映射到機器上,這個結構包含:

  • 如何存儲和訪問浮點數。一般用某種寄存器來完成
  • 對浮點數操做的指令
  • 向函數傳遞浮點數參數和從函數返回浮點數結果的規則
  • 函數調用過程當中寄存器的保存規則。一部分寄存器是調用者保存,一部分爲被調用者保存

本文檔基於AVX2,在Core i7 Haswell處理器引入。該體系結構容許數據存在16個YMM寄存器,在對標量數據操做時,這些寄存器只保存浮點數,並只是用低32位(float)或64位(double)

浮點數傳送和轉換操做

傳送:

傳送實例:

其中都是對float類型數據進行賦值(傳遞)。之因此出現%rdi和%rsi這兩個整數寄存器,是由於它們記錄了某個浮點數的內存地址,這個內存地址是整數

轉換:

  • 通用寄存器即整數寄存器
  • 浮點數轉成整數,指令會執行截斷,並向0舍入
  • 雙操做數用來將浮點轉成整數
  • 三操做數用來將整數轉成浮點數。每每只須要源1和目的兩個操做數,這時將源2設成目的操做數便可

下面兩個代碼是GCC在單精度和雙精度作轉換所生成的,意圖不明:

過程當中的浮點代碼

結合浮點體系結構一節圖示,XMM寄存器就能夠處理向函數傳遞浮點參數,以及從函數返回浮點值

  • 利用%xmm0 ~ %xmm7最多能夠傳遞8個參數,更多參數依賴棧
  • 函數使用%xmm0返回浮點值
  • 全部寄存器都是調用者保存,被調用者能夠隨意修改
  • 參數包含指針、整數、浮點數時,指針和整數經過整數寄存器傳遞,浮點數經過XMM寄存器傳遞

浮點運算操做

實例:

經過實例觀察到:

  • 浮點數參數經過XMM寄存器傳遞,整數參數經過整數寄存器傳遞
  • 運算前須要將float和int轉爲double類型

浮點常量

  • 整數運算中,使用當即數做爲操做數,能夠很好的處理常量
  • 浮點運算中,AXV浮點操做沒法使用當即數爲操做符

因此浮點運算中,編譯器須要爲常量提早分配內存空間,以後將浮點指令會使用內存地址做爲操做符,來使用常量

上圖,編譯器在標號爲.LC2和.LC3的內存地址存儲了常量1.8和32.0

浮點數位操做

比較浮點數

上述浮點比較指令會設置三個條件碼:

  • 零標誌位ZF
  • 進位標誌位CF
  • 奇偶標誌位PF

無序狀況就是兩個操做數中存在NAN的狀況,這是會將PF設爲1

條件碼的設置條件爲:

小結

  • 機器級編程,寄存器和運行時棧都是可見的
  • 編譯器須要使用多條指令來產生和操做各類數據結構和控制結構
  • C語言確認邊界檢查,致使C程序容易出現緩衝區溢出而致使被攻擊
  • 現代編譯器使用各類手段爲運行時系統提供了安全保護
  • 本書只介紹了C到x86-64的映射,對C++也相似
  • java實現方式徹底不一樣,它的目標代碼是特殊的二進制代碼,即java字節碼。java字節碼經過虛擬機(軟件)解釋處理,而不是直接由硬件實現。它的優點在於可移植性
相關文章
相關標籤/搜索