彙編器構造

来源:http://www.cnblogs.com/fanzhidongyzby/archive/2016/08/27/5812140.html
-Advertisement-
Play Games

彙編器構造 一、 彙編器簡介 前面介紹了編譯器構造和靜態鏈接器構造的具體方法,而且我們實現了一個將高級語言轉化為彙編語言的編譯器,同時又實現了一個將多個目標文件鏈接為一個可執行文件的鏈接器。現在需要一個連接這兩個模塊的功能模塊——彙編器,它能將一個單獨的彙編文件轉換為一個可重定位目標文件,如圖1-1 ...


 彙編器構造

 

一、             彙編器簡介

前面介紹了編譯器構造靜態鏈接器構造的具體方法,而且我們實現了一個將高級語言轉化為彙編語言的編譯器,同時又實現了一個將多個目標文件鏈接為一個可執行文件的鏈接器。現在需要一個連接這兩個模塊的功能模塊——彙編器,它能將一個單獨的彙編文件轉換為一個可重定位目標文件,如圖1-1反映出彙編器在整個編譯系統中的地位和功能。

 

 1-1  靜態編譯步驟

從本質上講,彙編器也是編譯器,只是它和我們熟知的編譯器的有略微的差別。彙編器處理的“高級語言”是彙編語言,輸出的是機器語言二進位形式。因此,對於彙編器的構造,實質上和編譯器大同小異,也都需要進行詞法分析、語法分析、語義處理、符號表管理和代碼生成(機器代碼)等階段。

對於編譯器來說,代碼生成階段只需要將解析的語法樹映射到彙編語言子模塊即可(當然還要考慮指令優化問題),而對於彙編器,將解析出的指令簡潔的映射到正確機器代碼相對比較複雜。另外,由於本彙編器處理的輸入文件為編譯器生成的彙編文件,經測試,編譯生成的彙編文件是正確的彙編文件,因此彙編器不需要考慮源文件會產生錯誤,因此它的語法分析的目的是識別出輸入語言的語法結構併進行解析引導機器代碼生成。

另外,彙編器和編譯器最大的不同是:彙編語言允許符號後置定義,因此通過一遍掃描無法保證獲得某個符號的準確定義信息,所以對於彙編器必須採用兩邊掃描的方式進行設計,彙編器的設計結構如圖1-2所示。

 

1-2 彙編器結構

從圖中可以看出彙編器的設計中,在語法分析模塊之前和前述編譯器的結構完全相同,只是語法分析時要進行兩遍的掃描過程,通過第一遍掃描獲取文件定義的所有的段的信息以及全部的符號信息,第二遍掃描根據最新的段表和符號表,將所有的重定位信息收集到重定位表中,然後通過指令生成模塊生成了代碼段數據。最後,從符號表中抽取有效數據定義形成數據段,符號導出到文件符號表段,再把所有的段按照elf文件的格式組裝起來,形成最終的可重定位目標文件*.o。下麵就按照上述路程具體說明彙編器設計的內容。

二、             文法定義

和編譯器設計過程相同,首先必須明確處理彙編語言的文法定義,按照符合LL1)文法的規則定義的待處理彙編語言的文法如表2-1所示: 

 

表2-1 彙編文法

上述彙編文法可以識別之前編譯器生成的所有代碼,從文法定義中,可以看出彙編語言的功能主要如下:

1)支持段聲明,全局符號聲明,數據定義db|dw|ddtimes關鍵字和equ巨集命令。

2)支持數據為整數和字元串格式,允許定義中引用符號,數據使用逗號分隔。

3)支持的指令數:雙操作數指令5條,單操作數指令17條,無操作數指令1條。

4)支持定址模式:寄存器定址,立即定址,寄存器間址,間接定址,基址+偏移定址,基址+變址定址。

明確彙編語言的文法後就可以構造分析程式識別語言的語法結構。

三、             詞法分析

彙編器的詞法分析過程和編譯器相同,也需要掃描器和解析器,區別在於字母表和詞法記號的差別。彙編語言的詞法記號如表3-1所示。

 3-1  詞法記號

從詞法記號表中可以看出彙編語言詞法記號的變化:

1)標識符可以用符號‘@’開頭。

2)增加一部分界符‘[’,‘]’,‘:’。

3)刪除了一部分界符‘*’,‘/’,‘=’,‘>’,‘<’,‘!’,‘;,‘(’,‘)’,‘{’,‘}’。

4)註釋由分號引導的單行註釋。

5)關鍵字表有重大變化,所有的彙編助記符、寄存器、彙編器操作符都是關鍵字。

很明顯,隨著彙編語法結構的相對簡化,詞法記號的識別的複雜度也有所降低。另外由於由編譯器生成的彙編語言文件是經過測試正確的,因此不需要進行異常處理。

四、             語法分析

語法分析是彙編器設計的核心,從圖1-2就可以看出語法分析模塊的重要地位。彙編器的語法分析模塊不需要進行錯誤處理和修複的操作,但是必須正確識別並處理每一個關鍵的語法模塊。彙編語言有兩大類型的語法模塊:數據和指令。數據語法模塊要能識別所有類型的符號並存儲到符號表,供指令模塊和重定位表使用。指令語法模塊要填充臨時數據結構,供指令生成模塊生成正確的操作碼和操作數二進位信息。

簡單的說,語法分析的目的是填充系統需要的三張表:段表、符號表、重定位表。通過第一遍掃描將輸入文件的所有的段信息收集到段表中,所有的符號信息收集到符號表中,然後第二面掃描在產生重定位的地方生成重定位項,填充重定位表。這三張表是輸出文件信息的核心,下邊就按照這三張表的構造流程逐個說明。

4.1  段表

彙編語言使用section關鍵字聲明段開始,直到下一個段聲明或者文件結束位置結束,整個中間部分都屬於section聲明的段的內容。具體的說,由於編譯器生成的彙編文件共有三個段:.text.data.bss。又因為我們使用兩邊掃描源文件的方式,因此,在第二遍掃描之前(第一遍.bss段結束後)彙編器就可以獲得所有的段信息。參考鏈接器設計中elf文件Elf32_Shdr的數據結構可以看出段表項信息的最關鍵的信息是:段名、偏移、大小。段名在每次section聲明時候記錄下來即可,偏移計算之前必須知道上一個段的大小,因此段的大小計算是關鍵中的關鍵。為此彙編器使用一個全局變數curAddr記錄了相對於當前段的起始偏移,每次彙編語言定義一個需要地址空間存儲的語法模塊,這個curAddr就會累加當前語法模塊的大小,直到段聲明結束時記錄了整個段的大小。至於每個語法模塊的大小如何計算,在後邊符號表中再具體介紹。另外,由於.bss的特殊性,它的物理大小為0,但是虛擬大小需要計算。比如編譯器只使用.bss存儲了輔助棧供64k位元組,因此虛擬大小為64k,但是占用磁碟空間大小為0

另外還需要註意的是段的偏移並不是簡單的累加段的大小計算,因為還涉及另一個概念——段對齊。這裡和鏈接器類似,段的開始位置必須是一個數的整數倍(一般重定位目標文件是按照4位元組對齊),因此在每次累加段偏移的時候需要考慮段對齊的影響。圖4-1給出了一個構造段表項一個例子:

 

4-1 段表構造實例

下麵給出了段表項的相關代碼: 

void Table::switchSeg()
{
    if(scanLop==1)
    {
        dataLen+=(4-dataLen%4)%4;
        obj.addShdr(curSeg,lb_record::curAddr);//新建一個段
        if(curSeg!=".bss")
            dataLen+=lb_record::curAddr;
    }
    curSeg="";curSeg+=id;//切換下一個段名
    lb_record::curAddr=0;//清0段偏移
}
void Elf_file::addShdr(string sh_name,int size)
{
    int off=52+dataLen;
    if(sh_name==".text")
    {
        addShdr(sh_name,SHT_PROGBITS,SHF_ALLOC|SHF_EXECINSTR,0,off,size,0,0,4,0);
    }
    else if(sh_name==".data")
    {
        addShdr(sh_name,SHT_PROGBITS,SHF_ALLOC|SHF_WRITE,0,off,size,0,0,4,0);
    }
    else if(sh_name==".bss")
    {
        addShdr(sh_name,SHT_NOBITS,SHF_ALLOC|SHF_WRITE,0,off,size,0,0,4,0);
    }
}

 

函數switchSeg在每次段聲明的位置被調用,但是只是在第一次掃描時候生成段表項,每次調用後都會記錄當前段名到curSeg,並清零curAddrdataLen記錄了當前的段偏移,添加段表項之前都會將之按照4位元組對齊後在加上52elf文件頭大小)作為真正的段偏移添加到段表。另外,.bss段聲明結束後是不累加段偏移的,這就反映了.bss無物理空間的含義。最後,addShdr按照段名分別生成具體的段表項,記錄到段表。

4.2  符號表

符號表是所有表中最重要的,段表使用它計算自身大小,重定位需要它識別重定位符號,最終的數據段和符號表段還需要符號表進行導出。符號表相關的有三個最重要的數據結構:lb_recordInstTable。顧名思義,lb_record記錄了當前分析出來的符號,Inst記錄了當前分析出來的指令,Table是對所有符號的記錄,即傳統意義上的符號表,不過這裡把Inst也作為符號表數據結構的一部分看待。下麵首先給出這三種數據結構的定義:

首先說明符號數據結構:

struct lb_record//符號聲明記錄
{
  static int curAddr;//一個段內符號的偏移累加量
  string segName;//隸屬於的段名,三種:.text .data .bss
  string lbName;//符號名
  bool isEqu;//是否是L equ 1
  bool externed;//是否是外部符號,內容是1的時候表示為外部的,此時curAddr不累加
  int addr;//符號段偏移
  int times;//定義重覆次數
  int len;//符號類型長度:db-1 dw-2 dd-4
  int *cont;//符號內容數組
  int cont_len;//符號內容長度
  lb_record(string n,bool ex);//L:或者創建外部符號(ex=true:L dd @e_esp)
  lb_record(string n,int a);//L equ 1
  lb_record(string n,int t,int l,int c[],int c_l);//L times 5 dw 1,"abc",L2 或者 L dd 23
  void write();//輸出符號內容
};

(1)       curAddr:當前段偏移的靜態變數。

(2)       segName:符號隸屬於的段名。

(3)       lnName:符號名。

(4)       isEqu:符號是否是equ定義的常量。

(5)       externed:符號是否是外部符號。

(6)       addr:符號的段偏移,若isEqutrue則表示符號的值。

(7)       times:符號定義重覆次數,不帶times關鍵字預設為1equ和外部符號為0

(8)       len:符號類型長度,dbdwdd分別為124位元組,無類型為0

(9)       cont

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天博客的主題是Keychain, 在本篇博客中會通過一個登陸的Demo將用戶名密碼存入到KeyChain中,並且查看一下KeyChain中存的是什麼東西,把這些內容給導出來。當然本篇博客的重點不是如何使用Keychain來存儲你的用戶名和密碼了。不過今天的博客中會用到這些知識。Apple的開發著文 ...
  • View滑動是自定義ViewGroup中十分常見的一個功能。Android提供了多種View滑動的方法。 1. layout方法 2. offsetLeftAndRight()與offsetTopAndBottom方法 3. LayoutParams方法 4. scrollTo 與scrollBy方 ...
  • 一、安裝JDK 1、用戶可以在Oracle JDK的官網下載相應版本的JDK,本例以JDK 1.6為例,官網地址為http://www.oracle.com/tech-network/java/javase/downloads/index.html。 2、配置環境變數,在/etc/profile增加 ...
  • 1、環境準備 1)準備6台ubuntu虛擬機 2)配置好IP(192.168.1.36,192.168.1.37,192.168.1.38, 192.168.1.40,192.168.1.41,192.168.1.42 三台master 三台slave) 3)安裝好openssh-server 2、 ...
  • 表大小 慢的sql select a.city, a.agent_id, a.username, a.real_name, phone, zgy_name, login_count, user_count, count(distinct b.invest_id) user_invested, sum ...
  • 一個學習性任務:每個人有不同次數的成績,統計出每個人的最高成績。 這個問題應該還是相對簡單,其實就用聚合函數就好了。 select id,name,max(score) from Student group by id,name order by name 上邊這種情況只適用id 和name是一一對 ...
  • 1.安裝MySql 目前MySQL有兩種形式的文件,一個是msi格式,一個是zip格式的。msi格式的直接點擊setup.exe就好,按照步驟進行。但是很多人下了zip格式的解壓發現沒有setup.exe,本人下載的也是這樣的,不知道怎麼安裝,點哪裡都沒有反應。只能尋求度娘幫助,然後才瞭解到,這種文 ...
  • 方法一: 直接(手動)去修改資料庫名稱,資料庫表名稱,資料庫列名稱、列屬性 方法二: 使用SQL語句去修改 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...