反汇编器源码剖析
?之前我們有對一匯編器進行源碼剖析,詳見《匯編器源碼剖析》,并且《實現一個匯編器》。本文,我們繼續之前的工作,對反匯編器進行源碼剖析,之后我們會根據反匯編器的實現原理,實現一個自己版本的反匯編器。本文剖析的反匯編器代碼詳見:source code。
???????? 源碼中反匯編器對應的文件為sdasm.c。
???????? sdasm.c文件中包含匯編指令集的定義op_desc,其類型為字符串數組。
const char *op_desc[] = {"HALT", "IN", "OUT", "ADD", "SUB", "MUL", "DIV","DUP","LD", "ST", "LDC", "JLT", "JLE", "JGT", "JGE", "JEQ", "JNE", "JMP", 0};? ? ? ? ?最為主要的是dasm_output函數,其功能即是針對輸入參數const Instruction* insts二進制指令,將其轉換為對應的匯編指令。并將結果輸出到const char* file文件中。其具體轉換過程是一次掃描insts,將insts[].op二進制指令對應的匯編指令輸出到文件中,進而檢測insts[].op對應的操作數個數,如果操作數個數大于0,則將指令對應的操作數也輸出到文件中。一條指令占一行。
void dasm_output( const char *file, const Instruction *insts, int size ){FILE *fp;char f[256];int i;strcpy( f, file );strcat( f, ".dasm" );fp = fopen( f, "w" );for( i = 0; i < size && insts[i].op != opHalt; ++ i ){fprintf( fp, "%3d\t%s", i, op_desc[insts[i].op] );if( get_operand_count( insts[i].op ) > 0 ){fprintf( fp, "\t%d", insts[i].arg );}fputc( '\n', fp );}fclose( fp );}? ? ? ? ?Instruction的定義還是沿用sm.h中的定義:
typedef struct Instruction{int op;int arg;} Instruction;? ? ? ? ?另外,程序extern了一個外部函數:
extern int get_operand_count( int op );其用于根據二進制指令獲取該指令對應的操作數個數。
?
???????? 以上是整個反匯編器的框架,通過對反匯編器的剖析,我們可以知道反匯編器的精髓在于二進制指令集到匯編指令集的映射。
???????? 接下來我們將根據反匯編器的實現原理,做一個自己版本的反匯編器。并總結匯編器和反匯編器之間的關系和聯系。
? ? 上文《反匯編器源碼剖析》,我們對一反匯編器源碼進行了學習,了解了反匯編器的實現原理。反匯編是匯編的逆過程,其也是包含三個主要部分:
- 匯編指令集
- 二進制指令集
- 二進制指令到匯編指令的映射
???????? 有了這三部分之后,我們就可以對二進制指令,將其翻譯成匯編指令,也就完成了反匯編過程。
? ? ? ? ?我們的二進制指令集和匯編指令集還是沿用之前的指令集。
???????? 下面我們先給出實現的反匯編器,然后對相關代碼進行解釋。
// 實現一個反匯編器 #include <iostream> #include <sstream> #include <string> #include <vector> #include <map> using namespace std;enum BinIns;// 二進制指令結構體 // 指令碼+操作數 struct Instruction {BinIns op; // 指令碼只占一個字節int arg; // 操作數,占四個字節 };// 枚舉類型的二進制指令集 enum BinIns {binHalt, binIn, binOut, binAdd, binSub, binMul, binDiv,binDup,binLd, binSt, binLdc, binJlt, binJle, binJgt, binJge, binJeq, binJne, binJmp,binInvalid }; // 枚舉類型說明: // enum后面定義的是枚舉類型名 // 花括號內部是該枚舉類型可以取的值// 初始化匯編指令集 void InitAssembleInstructions(vector<string>& assIns) {assIns.clear();assIns.push_back("HALT");assIns.push_back("IN");assIns.push_back("OUT");assIns.push_back("ADD");assIns.push_back("SUB");assIns.push_back("MUL");assIns.push_back("DIV");assIns.push_back("DUP");assIns.push_back("LD");assIns.push_back("ST");assIns.push_back("LDC");assIns.push_back("JLT");assIns.push_back("JLE");assIns.push_back("JGT");assIns.push_back("JGE");assIns.push_back("JEQ");assIns.push_back("JNE");assIns.push_back("JMP"); }// 初始化 // 指令-參數個數 void InitInstrctionArgNumber(map<BinIns, int>& insArgNum) {insArgNum.clear();insArgNum[binHalt] = 0;insArgNum[binIn] = 0;insArgNum[binOut] = 0;insArgNum[binAdd] = 0;insArgNum[binSub] = 0;insArgNum[binMul] = 0;insArgNum[binDiv] = 0;insArgNum[binDup] = 0;insArgNum[binLd] = 0;insArgNum[binSt] = 0;insArgNum[binLdc] = 1;insArgNum[binJlt] = 1;insArgNum[binJle] = 1;insArgNum[binJgt] = 1;insArgNum[binJge] = 1;insArgNum[binJeq] = 1;insArgNum[binJne] = 1;insArgNum[binJmp] = 1;insArgNum[binInvalid] = 0; }// 建立二進制指令到匯編指令的映射 // 初始化 void InitBinaryToAssemble(const vector<string>& assIns, map<BinIns, string>& binToIns) {binToIns.clear();for (auto i = 0; i != assIns.size(); ++i){// assIns和BinIns的指令次序一致binToIns[static_cast<BinIns>(i)] = assIns[i];} }// 讀入二進制指令 void ReadBinary(vector<string>& bin) {bin.clear();string line;while (getline(cin, line)){bin.push_back(line);} }// 顯示二進制指令 void Display(const vector<string>& bar) {for (auto i = 0; i != bar.size(); ++i){cout << bar[i] << endl;} }// 將讀入的二進制指令轉換為Instruction形式 void BinaryToAssemble(const vector<string>& bin,vector<string>& ass,const map<BinIns, string>& binToIns,map<BinIns, int>& insArgNum) {ass.clear();string binLine;for (auto i = 0; i != bin.size(); ++i){binLine += bin[i] + '\t';}cout << binLine << endl;istringstream sin(binLine);string strOp, strArg;string op;string arg;string assIns;BinIns opBin;while (sin >> strOp){opBin = static_cast<BinIns>(atoi(strOp.c_str()));auto cit = binToIns.find(opBin);if (cit == binToIns.end()){// 非法二進制指令// 忽略處理 ;break;}op = cit->second;int argNum = insArgNum[cit->first];if (argNum > 0){sin >> strArg;arg = strArg;}else{arg = "";}assIns = op + '\t' + arg;ass.push_back(assIns);} }// 二進制字符串為十進制字符串 string StringToNum(const string& str) {string ret;int num = 0;for (auto i = 0; i != str.size(); ++i){num = num * 2 + str[i] - '0';}char tmp[101];itoa(num, tmp, 10);ret = tmp;return ret; }// 二進制指令轉換為十進制指令 // 針對輸入的二進制指令為二進制編碼形式的情況 void BinaryToDec(vector<string>& bin) {for(auto i = 0; i != bin.size(); ++i){istringstream sin(bin[i]);string tmp, ins;while (sin >> tmp){ins += StringToNum(tmp) + '\t';}bin[i] = ins;} }int main() {// 匯編指令集vector<string> assIns;InitAssembleInstructions(assIns);// 二進制指令-操作數個數map<BinIns, int> insArgNum;InitInstrctionArgNumber(insArgNum);// 匯編指令到二進制的映射map<BinIns, string> binToAss;InitBinaryToAssemble(assIns, binToAss);vector<string> bin; // 保存讀入的二進制指令 ReadBinary(bin);cout << endl;Display(bin);cout << endl;vector<string> ass; // 保存轉換后的匯編指令 BinaryToAssemble(bin, ass, binToAss, insArgNum);Display(ass);cout << endl;return 0; }? ? ? ? ?測試用例:
? ? ? ? ?反匯編器的實現與匯編器實現整體框架基本一致。二進制指令集和匯編指令集完全一樣,不同點在于匯編器是從匯編指令到二進制指令的轉換,反匯編器是從二進制指令到匯編指令的轉換。
???????? 我們對輸入的二進制指令先將其保存,然后將其逐個掃描,將其解析出對應的二進制值,找到該指令對應的匯編指令,如果該指令具有操作數,則繼續將操作數讀取出來,并入到匯編指令中。順序掃描整個二進制指令,即得到其對應的匯編指令。
???????? 另外,默認情況下,我們認為輸入的二進制指令其表示形式為十進制的,如果輸入的是二進制的形式,那么可以調用BinaryToDec函數,將二進制形式的指令轉換為十進制形式的指令,進而進行反匯編操作。
?
???????? 到此為止,我們剖析了匯編器和反匯編器的源碼,了解了匯編器和反匯編器的原理,并實現了簡單的匯編器和反匯編器。
???????? 匯編器和反匯編器其主要包含三部分:二進制指令集的定義、匯編指令集的定義、二進制指令集和匯編指令集之間的相互轉換關系。
???????? 下面我們給出匯編器和反匯編器的簡單模型:
?
???????? 接下來,我們將繼續學習stack_machine源代碼。此外,學習一些C++方面的東西。
?
???????? 2013.10.6 0:33 國慶假期 于家中
總結
- 上一篇: 实现一个汇编器
- 下一篇: 基于堆栈的虚拟机实现