C編譯器入門~想懂低階系統從自幹編譯器開始~
  • 譯者序
  • 前言
    • 符號與規範
    • 本書的開發環境
    • 關於作者
    • 結束前言之前
  • 機械語言與組譯器
    • CPU 與記憶體
    • 什麼是組譯器
    • C程式和所對應的組合語言
      • 簡單的範例
      • 包含呼叫函式的範例
    • 本章小結
  • 創造計算機等級的語言
    • 第1步:創造能編譯1個整數的語言
    • 第2步:製作可以算加減法的編譯器
    • 第3步:加入標記解析器(tokenizer)
    • 第4步:改良錯誤訊息
    • 文法的記法與遞迴下降分析法
      • 將文法結構表示為樹(tree)
      • 以生成規則定義文法
      • 以 BNF 描述生成規則
      • 簡單的生成規則
      • 以生成規則描述運算子的優先順序
      • 包含遞迴的生成規則
      • 遞迴下降語法分析
    • 堆疊機
      • 堆疊機的概念
      • 編譯成堆疊機指令
      • 以x86-64實作堆疊機的方法
    • 第5步:製作可進行四則運算的編譯器
    • 第6步:單項加與單項減
    • 第7步:比較運算子
      • 修改標記解析器
      • 新的文法
      • 產生組合語言指令
  • 分離編譯與連結
    • 分離編譯
      • 分離編譯與其必要性
      • 標頭檔的必要性與其內容
      • 連結錯誤
      • 全域變數的宣告與定義
    • 第8步:分割檔案與修改 Makefile
      • 分割檔案
      • 修改 Makefile
  • 函式與區域變數
    • 第9步:1個字的區域變數
      • 堆疊上的變數空間
      • 修改標記解析器
      • 修改分析器
      • 左邊值與右邊值
      • 從任意的記憶體位址取得其值
      • 修改指令產生器
      • 修改主函式
    • 第10步:複數文字的區域變數
    • 第11步:return
    • 1973年的C編譯器
Powered by GitBook
On this page

Was this helpful?

  1. 機械語言與組譯器

什麼是組譯器

由於機械語言是直接給 CPU 看的語言,是站在 CPU 的角度所設計,並沒有考慮到人類讀寫的容易與否。想要透過二進制編輯器來讀寫機械語也不是不可能,但也是非常痛苦的作業。組合語言就是為了這個問題才被發明出來。組合語言和機械語言幾乎是一對一對應的語言,但是對人類來說,組合語言要容易讀寫多了。

一般非直譯式或透過虛擬機器(譯注:如 Java 的 JVM),直接輸出二進制執行檔的編譯器,通常都是輸出組合語言。有些編譯器看起來是直接輸出機械語言,其實多半也是先輸出組合語言,再在背後執行組譯器。本書所做的C編譯器也是輸出組合語言。

把組合語言轉換成機械語言也可以說是「編譯」的一種,但是為了強調輸入是組合語言,又被稱作「組譯」。

讀者可能以前也在別的地方看過組合語言也說不定。如果還沒見過的話,現在正好是認識一下組合語言的好機會。用objdump指令,隨便找一個執行檔反組譯看看,看一下該執行檔機械語言轉回組合語言的結果吧。以下是ls指令反組譯的結果:

$ objdump -d -M intel /bin/ls
/bin/ls:     file format elf64-x86-64

Disassembly of section .init:

0000000000003d58 <_init@@Base>:
  3d58:  48 83 ec 08           sub    rsp,0x8
  3d5c:  48 8b 05 7d b9 21 00  mov    rax,QWORD PTR [rip+0x21b97d]
  3d63:  48 85 c0              test   rax,rax
  3d66:  74 02                 je     366a <_init@@Base+0x12>
  3d68:  ff d0                 call   rax
  3d6a:  48 83 c4 08           add    rsp,0x8
  3d6e:  c3                    ret
...

在筆者的環境下,ls指令約含有2萬個以上的機械語言指令,反組譯就會變成大概有2萬行篇幅的怪物,上面只放了開頭的一部份。

組合語言基本上是一行一個機械語言指令。舉例來說我們來看下面這行:

3d58:  48 83 ec 08           sub    rsp,0x8

這行是什麼意思呢?3d58表示的是存放該機械語言的記憶體位址。也就是說,執行ls指令的時候,該行指令會被放到記憶體的 0x3d58 號位置,等程式計數器跑到 0x3d58 時就會執行該行指令。緊接著的四個16進制數字是實際的機械語言的長相。CPU 把這些值讀進去,然後將其作為指令執行。sub rsp,0x8則是對應該機械語言指令的組合語言。關於 CPU 的指令集我們會逐章解釋,上面這個指令是把 RSP 這個暫存器的數值減掉(sub 是 subtract,減去的英文的開頭)8的指令。

PreviousCPU 與記憶體NextC程式和所對應的組合語言

Last updated 5 years ago

Was this helpful?