謝文光,李琪,馬春燕,*,汪克念,尹偉,張濤
1. 中國民航大學(xué) 適航學(xué)院 民航航空器適航審定技術(shù)重點實驗室, 天津 300300
2. 航空工業(yè)無線電電子研究所, 上海 200241
3. 西北工業(yè)大學(xué) 軟件學(xué)院, 西安 710072
目前,電信、軍事以及工業(yè)嵌入式應(yīng)用領(lǐng)域均要求確保機器碼程序和C源程序的功能一致性,以防止編譯過程中插入非C源程序中要求的例外功能,影響嵌入式系統(tǒng)可靠性和安全性[1]。例如民用飛機機載軟件通常采用RTCA/DO-178C作為適航符合性方法。對于A級軟件(即安全水平最高的研制保證等級軟件)[2],RTCA/DO-178C要求實現(xiàn)機器碼程序到源程序的追溯性。
PowerPC P2020[3]處理器在航空嵌入式軟件系統(tǒng)和其他嵌入式領(lǐng)域中被廣泛應(yīng)用。C語言應(yīng)用程序需要在PC平臺X86處理器上通過C語言編譯器進(jìn)行編譯[4],生成可以在PowerPC P2020處理器上執(zhí)行的機器碼程序。本文以RTCA/DO-178C中的適航要求[5]作為研究的出發(fā)點,以PowerPC P2020處理器的機器碼程序作為溯源的需求,假設(shè)編譯器在編譯過程中可能會插入例外代碼,通過追溯機器碼程序和C源程序的溯源關(guān)系,降低安全關(guān)鍵軟件存在的風(fēng)險和不穩(wěn)定因素。
目前溯源方法需要程序員人工分析并建立映射關(guān)系,分析難度高、效率低。提升機器碼程序和C源程序溯源關(guān)系的自動化程度是目前研究的熱點之一。Brauer等[6]通過判斷機器碼和源代碼的控制流圖是否同構(gòu),提出了一種使用抽象解釋驗證部分機器碼和源代碼可追溯性分析的技術(shù)。Boccardo等[7]利用調(diào)用圖的大小、函數(shù)數(shù)量、控制流圖的頂點和邊的數(shù)量4個特性,提出了一種使用人工神經(jīng)網(wǎng)絡(luò)來關(guān)聯(lián)源代碼和機器碼的方法,但是,該方法實現(xiàn)的機器碼的溯源關(guān)系精確性有待提高。文獻(xiàn)[8]采用程序分析技術(shù)建立知識庫,并對源代碼進(jìn)行分析,以揭示所使用的變量、算術(shù)操作、邏輯操作、關(guān)系操作和控制結(jié)構(gòu)等參數(shù)。Rapita 公司和GmbH 公司[9-10]分別研制了源碼到機器碼追溯的可視化工具,靜態(tài)分析C代碼和匯編代碼程序,從分支、函數(shù)、內(nèi)存分配等角度,驗證源代碼到機器碼的追溯關(guān)系。文獻(xiàn)[11]討論了從源代碼到機器碼追溯涉及的相關(guān)技術(shù),但未給出具體追溯方法。文獻(xiàn)[12]根據(jù)源代碼的典型語法結(jié)構(gòu)及代碼子集,生成源碼、匯編代碼交叉對照列表,但未給出函數(shù)體中機器碼和源代碼的代碼行追溯方法。文獻(xiàn)[13]對惡意代碼可執(zhí)行文件進(jìn)行反匯編及分詞,利用Word2Vec對反匯編后的十六進(jìn)制碼進(jìn)行矢量化,搭建TextCNN深度學(xué)習(xí)模型進(jìn)行家族譜判定,但該方法僅根據(jù)已有的樣本來識別變體,可能導(dǎo)致溯源工作低效甚至無效。文獻(xiàn)[14]研究目前提出的惡意代碼的溯源技術(shù),指出具有更高級威脅的惡意代碼檢測存在缺陷。文獻(xiàn)[15]將源代碼與機器碼的每一個語句映射到向量,利用神經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu),逐句分析源代碼及機器碼之間的關(guān)系。文獻(xiàn)[16-17]介紹了一種驗證MC68020機器碼正確性的形式化方法,但是該研究并未涉及浮點型程序的驗證。文獻(xiàn)[18]將GCC編譯器源代碼中的函數(shù)分為會修改輸入數(shù)據(jù)的函數(shù)和不會修改輸入數(shù)據(jù)的函數(shù),人工對比輸入輸出數(shù)據(jù)檢測是否被插入惡意代碼,但效率較低。
基于以上研究背景,本文提出從文件、函數(shù)名、函數(shù)體代碼行3層次實現(xiàn)PowerPC P2020處理器機器碼程序與C源程序之間的溯源方法,與現(xiàn)有工作相比,具有以下優(yōu)點:
1) 方法更具有通用性,適用于不同語言、不同處理器型號之間溯源關(guān)系的研究,只需將 2.4.1 節(jié)中語言語法結(jié)構(gòu)到匯編語言指令序列映射規(guī)則的定義修改為相關(guān)語言及處理器匯編語言指令序列映射規(guī)則的定義即可。
2) 方法分析機器碼代碼行和源碼的溯源關(guān)系時,不局限于機器碼子集、關(guān)鍵語法特征、分支或函數(shù)等。
3) 方法可以提升溯源的自動化程度,并研制落地的實驗環(huán)境實現(xiàn)溯源對比的目標(biāo),節(jié)省人工勞動。
4) 方法利用源代碼和機器碼之間語法和語義的等價性原理進(jìn)行溯源,更為精確可靠。
自頂向下分解C語言源碼和PowerPC P2020機器碼之間的溯源關(guān)系,可將C語言源碼和PowerPC P2020機器碼之間的映射需求分為3部分:
1) PowerPC P2020機器碼文件主名應(yīng)與C語言源碼文件主名溯源。
在編譯過程[19]中,編譯器會根據(jù)C源程序文件主名生成相同主名的PowerPC P2020機器碼文件。在映射函數(shù)及函數(shù)體前,需要先對生成的機器碼文件(.o文件)進(jìn)行檢查,建立文件之間的映射關(guān)系,以防在鏈接時引入未知的文件模塊。
2) PowerPC P2020機器碼文件中函數(shù)名應(yīng)與C語言源碼文件中的函數(shù)名溯源。
在文件主名一一映射的前提下,需要先對生成的機器碼文件中所有的函數(shù)名進(jìn)行檢查,建立函數(shù)名之間的映射關(guān)系,以防在鏈接時引入未知的函數(shù)模塊。
3) PowerPC P2020機器碼文件中的函數(shù)體代碼應(yīng)和C語言源碼文件中的函數(shù)體代碼溯源。
在文件主名與函數(shù)名一一映射的前提下,對生成的機器碼中所有的函數(shù)體進(jìn)行檢查,防止在編譯時引入未知的代碼行。
論文提出的溯源算法 CodeTraceBack的偽代碼如算法1。根據(jù)第1節(jié)的3層次映射需求,溯源算法CodeTraceBack包括3個子算法,即文件的溯源子算法、函數(shù)聲明的溯源子算法和函數(shù)體的溯源子算法。
算法1 溯源算法CodeTraceBack偽代碼
1) 文件溯源(偽代碼1~25行):C源程序與PowerPC P2020機器碼程序經(jīng)預(yù)處理模塊處理后,獲取C源程序文件主名列表與PowerPC P2020機器碼程序文件主名列表,對比得出C源程序與PowerPC P2020機器碼程序文件主名的溯源關(guān)系,詳見2.2節(jié)。
2) 函數(shù)聲明溯源(偽代碼26~62行):通過遍歷C源程序抽象語法樹[20]獲取函數(shù)名列表;通過遍歷PowerPC P2020機器碼程序匯編碼獲取函數(shù)名列表。對比兩項函數(shù)列表分析得出C源程序與PowerPC P2020機器碼程序函數(shù)名的溯源關(guān)系,詳見2.3節(jié)。對于函數(shù)可變參數(shù)的情況或其他錯誤情況,如果導(dǎo)致函數(shù)聲明中形式參數(shù)和形式參數(shù)個數(shù)出現(xiàn)溯源問題,由于形式參數(shù)會在函數(shù)體中的代碼行中使用,函數(shù)體溯源時會發(fā)現(xiàn)與機器碼匯編指令集無法匹配的問題。
3) 函數(shù)體溯源(偽代碼63~103行):根據(jù)C源程序生成的抽象語法樹,結(jié)合論文定義的抽象語法樹節(jié)點與匯編指令集的映射規(guī)則,生成以函數(shù)為單位的期望匯編指令序列。同時,以函數(shù)為單位獲取機器碼程序?qū)?yīng)的實際匯編代碼序列。對比期望的匯編指令序列和編譯器生成的匯編代碼序列,即可得出C源程序與機器碼程序函數(shù)體的溯源關(guān)系,詳見2.4節(jié)。
C源程序與PowerPC P2020機器碼程序文件主名之間的溯源包括經(jīng)預(yù)處理獲取PowerPC P2020機器碼程序文件主名名稱、C源程序文件主名名稱、建立追溯關(guān)系3個步驟。
1) 針對PowerPC P2020機器碼程序,預(yù)處理模塊解析其文件主名,去除文件名后綴,生成機器碼主名列表,標(biāo)記為targetFileNameSet,對應(yīng)偽代碼中第3行。
2) 針對C源程序,預(yù)處理模塊解析其文件主名,去除文件名后綴,生成源文件主名列表,標(biāo)記為sourceFileNameSet,對應(yīng)偽代碼中第2行。
3) 建立targetFileNameSet中文件主名到sourceFileNameSet中文件主名的追溯關(guān)系,標(biāo)記為ResultOfFileName。分析機器碼文件主名列表targetFileNameSet和源文件列表sourceFileNameSet中的文件主名是否相等。若匹配成功,將該文件主名對應(yīng)的文件目錄和文件主名存儲在ResultOfFileName中;若匹配失敗,則添加該文件主名至ResultOfFileName中,并標(biāo)注其未能追溯的結(jié)果,對應(yīng)偽代碼中4~23行。ResultOfFileName表格包含3列,即PowerPC P2020機器碼程序文件主名所在的目錄、C源程序文件主名所在的目錄、文件主名和結(jié)果標(biāo)注。
C源程序與PowerPC P2020機器碼程序函數(shù)名之間的溯源分為3個步驟,包括反匯編機器碼程序獲取機器碼函數(shù)名列表、通過語法分析獲取C源程序函數(shù)名列表,以及對比獲取機器碼函數(shù)名列表和C源程序函數(shù)名列表的溯源關(guān)系。
1) 針對PowerPC P2020機器碼程序,生成其所有函數(shù)名列表(偽代碼49~61行)。
偽代碼第50行通過編譯器套件中提供的“powerpc-linux-gnu-objdump-t”命令對PowerPC P2020機器碼程序進(jìn)行反匯編操作。PowerPC P2020機器碼程序文件遵循ELF格式,偽代碼51~59行解析PowerPC P2020機器碼程序ELF格式中的符號表段(.symtab段),識別函數(shù)名、函數(shù)名所在的行號以及該函數(shù)名所在的機器碼文件主名,構(gòu)造PowerPC P2020機器碼程序所有函數(shù)聲明的列表targetFuncNameSet(偽代碼第60行)。列表中的每個元素包括3個屬性信息:函數(shù)名、行號、機器碼文件主名。其中,機器碼文件主名是2.2節(jié)中機器碼程序成功映射的文件主名。
2) 針對C源程序,基于抽象語法樹生成其所有函數(shù)名列表(偽代碼31~48行)。
語法分析模塊可以利用開源工具Pycparser或其他自主研制的類似工具,生成C源程序文件的抽象語法樹(偽代碼第44行)。每個抽象語法樹的節(jié)點包含了語法結(jié)構(gòu)的名稱、子節(jié)點信息及其在C源程序中的行號信息。偽代碼第46行調(diào)用的getSourceFunc函數(shù)(偽代碼31~42行)遍歷抽象語法樹,搜索抽象語法樹中所有FuncDef節(jié)點(函數(shù)聲明節(jié)點),根據(jù)節(jié)點的屬性信息,構(gòu)造C源程序所有函數(shù)聲明的列表sourceFuncNameSet(偽代碼第47行)。列表中的每個元素包括3個屬性信息:函數(shù)名、行號、源文件主名。其中,源文件主名是2.2節(jié)中機器碼程序成功映射的文件主名。
3) 建立PowerPC P2020機器碼程序函數(shù)名和C源程序函數(shù)名之間的追溯關(guān)系(偽代碼第62行)。
針對targetFuncNameSet和sourceFuncNameSet中機器碼文件主名和源文件主名一致的2個元素,利用偽代碼中的compareSets函數(shù)計算這2個元素的函數(shù)名是否相等。建立PowerPC P2020機器碼程序函數(shù)名和C源程序函數(shù)名之間的追溯關(guān)系列表,標(biāo)記為ResultOfFuncName,將匹配成功的函數(shù)名信息存入該表格中,表格包含6列,即targetFuncNameSet列表中的每個元素和sourceFuncNameSet列表中的每個元素,同時將未匹配成功的進(jìn)行標(biāo)注。
C源程序與PowerPC P2020機器碼程序的函數(shù)體之間的溯源分為3個步驟,包括結(jié)合C源程序?qū)?yīng)的抽象語法樹與映射規(guī)則生成期望匯編指令序列、反匯編機器碼程序獲取實際的匯編代碼序列,以及獲取匯編代碼序列和期望匯編指令序列的溯源關(guān)系。其中,確定C源程序抽象語法樹節(jié)點生成期望匯編語言指令序列的規(guī)則是本文的核心關(guān)鍵技術(shù)。
本文建立了抽象語法樹節(jié)點映射到P2020期望匯編語言指令序列的規(guī)則庫,偽代碼第70行即當(dāng)遍歷到抽象語法樹節(jié)點時則調(diào)用該規(guī)則庫,利用規(guī)則庫中抽象語法樹節(jié)點與期望匯編指令序列之間關(guān)系的規(guī)則定義,生成源程序的期望匯編指令序列。2.4.1節(jié)將詳細(xì)闡述該抽象語法樹節(jié)點映射到PowerPC P2020匯編語言指令序列的規(guī)則庫定義。
2.4.1 C程序語法結(jié)構(gòu)到PowerPC P2020匯編
語言指令序列映射規(guī)則的定義
抽象語法樹節(jié)點包含23類,由于論文篇幅所限,本文僅列出函數(shù)類、流程控制類、跳轉(zhuǎn)類、運算類等較為常用的抽象語法樹節(jié)點,詳細(xì)闡述它們到PowerPC P2020匯編語言指令序列映射規(guī)則的定義。完整的語法結(jié)構(gòu)、語義及對應(yīng)的P2020匯編代碼序列的映射規(guī)則上傳https:∥gitee.com/angelavor/cto,便于讀者查看。
1) 函數(shù)類
① 函數(shù)調(diào)用節(jié)點funcCall
對于C源程序的抽象語法樹的函數(shù)調(diào)用節(jié)點funcCall,生成的匯編語言指令的規(guī)則為
(1)若調(diào)用語句中不存在實參:
生成一條條件跳轉(zhuǎn)指令:bl。
(2)若調(diào)用語句中存在實參:
(a)識別C語言函數(shù)調(diào)用語句中的實參對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列:若參數(shù)類型為整型或者長整型:lwz;若參數(shù)類型為字符型:lbz、clrlwi、mr;若參數(shù)類型為短整型:lhz、extsh、mr;若參數(shù)類型為浮點型:lwz、efdcfs、evmergehi、mr;若參數(shù)類型為雙精度浮點型:lwz、lwz、mr、mr;若參數(shù)類型為數(shù)組型:addi、mr。
(b)生成一條函數(shù)調(diào)用指令:crclr addres。
(c)一條條件跳轉(zhuǎn)指令:bl。
② 函數(shù)聲明節(jié)點funcDecl
對于C源程序的抽象語法樹的函數(shù)聲明節(jié)點funcDecl,生成的匯編語言指令序列規(guī)則為
(1)函數(shù)體入口指令序列:stwu、stw、mr。
(2)識別C語言函數(shù)體中語句序列對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
(3)函數(shù)體出口指令序列:lwz、mr、blr。
2) 控制語句相關(guān)節(jié)點
① if控制節(jié)點
對于C源程序的抽象語法樹的if節(jié)點,生成的匯編語言指令序列規(guī)則為
(1)根據(jù)if語句的判斷條件語句函數(shù)塊信息生成對應(yīng)的匯編指令序列:
(a)如果第1個子樹是constant或者id節(jié)點:不添加匯編指令。
(b)如果第1個子樹是binaryOp,且binaryOp的兩個子樹均為constant節(jié)點:不添加匯編指令。
(c)其他情況:遍歷該抽象語法樹,根據(jù)各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
(2)生成1條比較指令:
(a)如果第1個子樹是constant或者id節(jié)點:不添加匯編指令。
(b)如果第1個子樹是binaryOp且binaryOp的兩個子樹均為constant節(jié)點:不添加匯編指令。
(c)如果第1個子樹是binaryOp,且binaryOp的判斷條件是“||”,binaryOp的兩個子樹存在一個constant節(jié)點:不添加匯編指令。
(d)如果第1個子樹binaryOp的兩個子樹節(jié)點均為id:cmpw。
(e)如果第1個子樹binaryOp的兩個子樹有且只有一個constant節(jié)點:cmpwi。
(3)生成1條條件跳轉(zhuǎn)指令:
(a)若第1個子樹是id:beq。
(b)若第1個子樹binaryOp的比較符號為“==”:若變量為浮點型:ble;其他情況:bne。
(c)若第1個子樹binaryOp的比較符號為“!=”:若變量為浮點型:bgt;其他情況:beq。
(d)若第1個子樹binaryOp的比較符號為“>=”:若均為變量且變量均為浮點型:ble;若均為變量:blt;若存在一個常量(即其他情況):ble。
(e)若第1個子樹binaryOp的比較符號為“>”:ble。
(f)若第1個子樹binaryOp的比較符號為“<=”:若變量為浮點型:ble;其他情況:bgt。
(g)若第1個子樹binaryOp的比較符號為“<”:若均為變量且變量均為浮點型:ble;若均為變量:bge;若存在一個常量(即其他情況):bgt。
(4)根據(jù)if語句條件為true情況下函數(shù)塊信息生成對應(yīng)的匯編指令序列:遍歷該抽象語法樹,根據(jù)各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
(5)若存在第3個子樹else函塊,則生成1條無條件跳轉(zhuǎn)指令:b。
(6)根據(jù)if語句條件為false情況下函數(shù)塊信息生成對應(yīng)的匯編指令:遍歷該抽象語法樹,根據(jù)各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
圖1給出了if節(jié)點翻譯為匯編指令序列排列的邏輯原理。
圖1 if節(jié)點映射規(guī)則圖Fig.1 Diagram of correspondece of if node to rule
② for循環(huán)節(jié)點
對于C源程序的抽象語法樹的for節(jié)點,簡式標(biāo)記為“for(A;B;C){D}”,生成的匯編語言指令序列規(guī)則為
(1)根據(jù)代碼塊A信息生成對應(yīng)的匯編指令序列:
(a)A為變量:lwz。
(b)A為其他數(shù)據(jù)類型:根據(jù)A節(jié)點類型對應(yīng)的規(guī)則生成程序塊A對應(yīng)的匯編指令序列。
(2)一條跳轉(zhuǎn)指令:b。
(3)若存在代碼塊D:根據(jù)D節(jié)點類型對應(yīng)的規(guī)則生成程序塊D對應(yīng)的匯編指令序列;否則不產(chǎn)生匯編指令。
(4)若存在代碼塊C:根據(jù)C節(jié)點類型對應(yīng)的規(guī)則生成代碼塊C對應(yīng)的匯編指令序列。
(5)若存在代碼塊B:根據(jù)B節(jié)點類型對應(yīng)的規(guī)則生成代碼塊B對應(yīng)的匯編指令序列。
3) 跳轉(zhuǎn)類節(jié)點
① 返回節(jié)點return
對于C源程序的抽象語法樹的return節(jié)點,生成的匯編語言指令序列規(guī)則為
根據(jù)函數(shù)體返回類型信息生成對應(yīng)的匯編語言指令序列:
(1)若函數(shù)體返回類型為void:
(a)若返回值為空:li、stw。
(b)若返回值不為空:不添加匯編指令。
(2)若函數(shù)體返回類型為整型:
(a)若返回值為常量:li。
(b)返回值為變量:lwz。
(3)若函數(shù)體返回類型為字符型:lbz,clrlwi。
(4)若函數(shù)體返回類型為浮點型:lwz,mtctr。
② 循環(huán)終止節(jié)點break
對于C源程序的抽象語法樹的break節(jié)點,規(guī)則為生成一條b指令,表示無條件跳轉(zhuǎn)。
4) 運算類節(jié)點
① 二元運算符binaryOp
對于C源程序的抽象語法樹的binaryOp節(jié)點,簡式為:“A?B”。其中“?”代表“>”“ < ”“ ==”“!=”“ +”“ -”“ *”“/”等運算符號。生成的匯編語言指令序列步驟為:
步驟1識別binaryOp節(jié)點中存儲的運算符,如果是“>”“ <”“ ==”“ !=”則跳轉(zhuǎn)至步驟2,執(zhí)行步驟2~步驟4;如果為“+”“ -”“ *”“/”則跳轉(zhuǎn)至步驟5~步驟7。
步驟2識別程序中B對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
步驟3識別程序中A對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
步驟4一條比較指令cmpw。
步驟5識別程序中A對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
步驟6識別程序中B對應(yīng)的抽象語法樹,遍歷該抽象語法樹的各個節(jié)點類型,生成相應(yīng)的匯編語言指令序列。
步驟7識別binaryOp節(jié)點中存儲的運算符,如果是“+”則添加一條add指令;如果是“-”則添加一條subf指令;如果是“*”則添加一條mullw指令;如果是“/”則添加一條divw指令。
② 單目運算節(jié)點unaryOp
C源程序中單目運算符生成匯編語言指令集合的規(guī)則。對于C源程序的抽象語法樹的unaryOp節(jié)點,簡式為:“a++”“ --a”“ &a”“ -a”“ !a”“ ~a”“ *a”,生成的匯編語言指令序列應(yīng)為:
步驟1識別unaryOp節(jié)點中存儲的運算符,若為“*”或“&”,直接跳轉(zhuǎn)至步驟4。
步驟2一條lwz指令,用于將a加載進(jìn)寄存器。
步驟3識別unaryOp節(jié)點中存儲的運算符,如果為“++”“--”則添加一條add指令,它是“addi”和“add”的統(tǒng)一表示,用于進(jìn)行自增自減運算;如果為“-”,增添一條neg指令;若為“~”,增添一條not指令;若為“!”,不處理。
步驟4一條st指令,用于將寄存器中的結(jié)果加載到棧中。
2.4.2 建立PowerPC P2020機器碼程序函數(shù)體語句序列與C源程序函數(shù)體語句之間的追溯關(guān)系
基于2.4.1節(jié)中 C程序語法結(jié)構(gòu)到PowerPC P2020匯編語言指令序列映射規(guī)則的定義,建立函數(shù)體之間的追溯關(guān)系分以下3個步驟。
1) 針對于C源程序,利用pycpaser開源庫生成其抽象語法樹ast(偽代碼第50行)。偽代碼95~102行通過遍歷ast的抽象語法樹節(jié)點,并結(jié)合2.4.1節(jié)中構(gòu)建的規(guī)則庫生成期望匯編指令序列,并以函數(shù)體為單位將生成的期望指令序列存儲在sourceFuncAssemSet數(shù)組中。期望匯編指令序列中子項的節(jié)點格式為[cmd, coord],其中cmd存儲期望匯編語言指令序列助記符,coord中存儲對應(yīng)C源程序中的代碼行號信息。
2) 針對PowerPC P2020機器碼程序,偽代碼第50行利用“powerpc-linux-gnu-objdump-d-lfilename”命令解析機器碼程序文件,提取出函數(shù)名、匯編語言指令序列以及對應(yīng)C源程序中的代碼行號信息。偽代碼第70~94行以函數(shù)體為單位,將編譯器生成的相應(yīng)函數(shù)的匯編語言指令序列存儲在targetFuncAssemSet數(shù)組中。
3)建立PowerPC P2020機器碼程序函數(shù)體語句序列與C源程序函數(shù)體語句之間的追溯關(guān)系:
① 為方便計算其匹配率,算法將每一個匯編指令用唯一的ASCII碼值替換,并保存匯編指令與ASCII碼轉(zhuǎn)換的映射字符表(偽代碼第103行)。
② 對比實際匯編指令序列以及期望匯編指令序列對應(yīng)的字符串,若完全匹配,則匹配率為100%;若存在不匹配的字符,則匹配率為匹配的字符個數(shù)/字符總個數(shù)。
③ 根據(jù)映射字符表,回溯對應(yīng)的匯編指令。字符串中互相匹配的字符即為實際匯編指令序列與期望的匯編指令序列中互相匹配的匯編指令。
1) 動態(tài)鏈接庫函數(shù)的溯源。對于動態(tài)鏈接的庫函數(shù),首先需要存放常用動態(tài)鏈接庫的源碼,當(dāng)ELF文件頭中表明所依賴動態(tài)鏈接庫時,需要根據(jù)對應(yīng)動態(tài)鏈接庫的源碼,按照2.4節(jié)機器碼文件函數(shù)體代碼與源文件函數(shù)體代碼的映射方法進(jìn)行匹配即可,以驗證該動態(tài)鏈接庫是否存在問題。
2) 靜態(tài)鏈接庫函數(shù)的溯源。當(dāng)機器碼文件函數(shù)體內(nèi)部存在連續(xù)的匯編代碼行不能與期望匯編指令序列匹配時,需要驗證該段代碼是否為直接展開的庫函數(shù)。對于直接展開的庫函數(shù),需要預(yù)先加載當(dāng)前C源程序引用的函數(shù)庫。匹配方法與2.4節(jié)函數(shù)體匹配相似,但不需要建立C程序語法結(jié)構(gòu)到PowerPC P2020匯編語言指令序列映射規(guī)則,只需要對展開前函數(shù)庫的二進(jìn)制代碼進(jìn)行反匯編作為期望匯編指令序列,然后與機器碼文件中未能匹配的連續(xù)匯編代碼行(即內(nèi)嵌的庫函數(shù))進(jìn)行匹配即可,以驗證編譯器是否在靜態(tài)鏈接時對匯編指令進(jìn)行修改。
本文采用ubuntu20.04和python3.8,根據(jù)圖2的實驗流程,編程實現(xiàn)用于實驗驗證的實驗環(huán)境(命名為:NwpuSrcTrace)。NwpuSrcTrace以C源程序文件和PowerPC P2020機器碼文件作為輸入,通過可視化界面輸出機器碼和C源程序在文件、函數(shù)聲明和函數(shù)體代碼的追溯結(jié)果。
圖2 實驗驗證流程Fig.2 Experimental verification process
1) 文件主名溯源測試用例
為驗證文件溯源方法的有效性,論文設(shè)計了245個C源程序文件和與345個PowerPC P2020機器碼文件,其中100個機器碼文件設(shè)計為無法匹配到C語言源文件。
2) 函數(shù)名溯源測試用例
為驗證函數(shù)聲明溯源方法的有效性,論文設(shè)計了1 111個C語言函數(shù)聲明與1 273個PowerPC P2020機器碼函數(shù)聲明,其中162個機器碼函數(shù)聲明無法找到對應(yīng)的C語言源文件中的函數(shù)聲明。
3) 函數(shù)體溯源測試用例
為驗證函數(shù)體代碼溯源的有效性,論文覆蓋C語言程序的23類抽象語法樹節(jié)點,分別設(shè)計了20個C源程序文件和相應(yīng)的20個PowerPC P2020機器碼程序文件,共計460個測試用例進(jìn)行函數(shù)體代碼溯源實驗。表1列舉了函數(shù)聲明decl、if、for和while等部分抽象語法樹節(jié)點對應(yīng)的C源程序測試用例。為便于讀者查看完整的測試用例,詳見https:∥gitee.com/angelavor/cto。
表1 部分節(jié)點C源程序?qū)嶒瀸ο?/p>
1) 導(dǎo)入源文件和機器碼文件
將C源程序文件存儲在指定文件夾中,利用風(fēng)河Workbench嵌入式開發(fā)平臺GCC編譯器(航空領(lǐng)域應(yīng)用常用編譯器)對C源程序文件進(jìn)行編譯,得到對應(yīng)的PowerPC P2020機器碼程序文件,并存儲在相應(yīng)工程文件夾中。運行實驗環(huán)境NwpuSrcTrace中的home.py程序,啟動可視化界面,導(dǎo)入C源程序文件與PowerPC P2020機器碼程序文件。
2) 機器碼文件溯源
點擊溯源菜單, “文件主名溯源”“函數(shù)名溯源”和“函數(shù)體溯源”功能可以分別輸出文件名(如圖3所示)、函數(shù)名(如圖4所示)和函數(shù)體代碼(如圖5所示)的追溯清單。
文件主名溯源界面(圖3)中,上方為源文件列表與機器碼文件列表。下方的文件主名溯源結(jié)果顯示匹配信息與文件主名。
圖3 文件溯源結(jié)果輸出界面Fig.3 File traceability output interface
函數(shù)名溯源界面(圖4)中,右側(cè)上方顯示為機器碼函數(shù)的匯編指令列表。下方的函數(shù)名溯源結(jié)果顯示匹配信息、函數(shù)名以及函數(shù)所在文件主名。
圖4 函數(shù)聲明溯源結(jié)果輸出界面Fig.4 Output interface of function definition tracing results
函數(shù)體溯源界面(圖5)中,右側(cè)上方為機器碼函數(shù)對應(yīng)的匯編指令列表。界面下方的函數(shù)體溯源結(jié)果顯示以函數(shù)體為單位的匹配率。
圖5 函數(shù)體溯源結(jié)果輸出界面Fig.5 Output interface of function body tracing results
1) 文件溯源結(jié)果
圖6為系統(tǒng)導(dǎo)出的共590個文件溯源結(jié)果的部分截圖,其中包括匹配成功/失敗信息、C源程序文件主名信息和PowerPC P2020程序文件主名信息。目前文件主名溯源方法已達(dá)到100%準(zhǔn)確率。
圖6 文件溯源測試結(jié)果展示Fig.6 Display of file traceability test results
2) 函數(shù)聲明溯源結(jié)果
圖7為系統(tǒng)導(dǎo)出的共2 384個函數(shù)名溯源方法的部分測試結(jié)果,其中包括匹配成功/失敗信息、該函數(shù)名所在C源程序文件主名或該函數(shù)名所在PowerPC P2020程序文件主名信息。目前函數(shù)聲明溯源已達(dá)到100%的準(zhǔn)確率。
圖7 函數(shù)名溯源測試結(jié)果展示Fig.7 Display of function name traceability test results
3) 函數(shù)體代碼行溯源結(jié)果
表2給出了覆蓋23類抽象語法樹節(jié)點的C程序溯源測試結(jié)果:第1列為抽象語法樹節(jié)點,第2列為節(jié)點含義的描述,第3列為測試用例介紹,第4列為C語言函數(shù)體與機器碼函數(shù)體部分代碼示例,第5列為函數(shù)體代碼溯源的匹配率。實驗結(jié)果表明,除少數(shù)節(jié)點類型匹配率較低外,其余節(jié)點的匹配率較高,函數(shù)體代碼的平均成功匹配率為97.22%。匹配率低的節(jié)點涉及到復(fù)雜數(shù)據(jù)類型,如長整型long、浮點型float和雙精度浮點型double。編譯器對浮點和雙精度數(shù)據(jù)類型的處理是動態(tài)的并依賴于計算機的硬件環(huán)境,同時編譯器會對部分運算進(jìn)行合并優(yōu)化操作。例如:針對“b=a*2”語句,根據(jù)抽象語法樹節(jié)點對應(yīng)規(guī)則,BinaryOp節(jié)點的運算符號為“*”,對應(yīng)的匯編語言指令為“mullw”,但是在實際編譯過程中,編譯器會將其編譯為加法運算,即“+”對應(yīng)的“addi”指令。這些情況會導(dǎo)致匹配度無法到達(dá)預(yù)期的情況,仍需要人工對溯源結(jié)果進(jìn)行審核。
表2 函數(shù)體代碼行的溯源測試結(jié)果Table 2 Traceability test results for function body code
實驗環(huán)境NwpuSrcTrace可以實現(xiàn)自動化溯源文件、函數(shù)聲明和函數(shù)體代碼,能較好地實現(xiàn)C源碼和編譯后PowerPC P2020機器碼的可追溯性,詳細(xì)展示機器碼和源碼的匹配情況,縮減人工追溯C源碼和編譯后的PowerPC P2020機器碼關(guān)系的時間與經(jīng)濟(jì)成本。
本文提出了一種由PowerPC P2020處理器平臺的機器碼工程文件到C語言源代碼工程文件的追溯性分析方法,用戶可以從文件映射、函數(shù)聲明映射、函數(shù)體模塊映射3個方面獲取PowerPC P2020機器碼工程文件到C源代碼工程文件的追溯關(guān)系,函數(shù)體代碼的平均追溯匹配率達(dá)97.22%,文件和函數(shù)聲明溯源的追溯匹配率達(dá)100%。
續(xù)表7
續(xù)表7
本文研究目標(biāo)是驗證編譯器在編譯過程中是否插入異常代碼,在工程實踐過程中,可以將得到機器碼程序的過程分為編譯過程、驗證過程、裁剪過程等3個過程。在編譯過程中,通過參數(shù)控制編譯器生成帶符號表的ELF文件,即本文中的.o文件。在驗證過程中,使用本文所述方法驗證生成程序的正確性。在裁剪過程中,使用裁剪工具對驗證后的程序進(jìn)行裁剪。因為裁剪過程是在追溯機器碼程序的正確性后再進(jìn)行,因此在資源受限的嵌入式領(lǐng)域,生成的二進(jìn)制文件即使經(jīng)過裁剪,以及溯源所用的符號表可能會被刪除,也不會影響到本文的驗證方法。所以本文方法在實踐中是可行的。
在現(xiàn)有成果的基礎(chǔ)上,未來可以從以下4個方面繼續(xù)進(jìn)行深入研究:
1) 針對浮點數(shù)和雙精度帶來的部分節(jié)點匹配率低問題,未來將通過分析源碼、存儲符號及類型的辦法解決。
2) 目前實驗結(jié)果是覆蓋23類抽象語法樹節(jié)點的C程序單元測試的溯源,未來工作采用嵌入式航空領(lǐng)域包含C語言復(fù)雜嵌套數(shù)據(jù)結(jié)構(gòu)的常用函數(shù)庫和相關(guān)開源軟件作為集成測試集,對提出的方法進(jìn)行集成測試,進(jìn)一步驗證方法的有效性。
3) 在C程序語義分析的基礎(chǔ)上進(jìn)一步探索不同編譯優(yōu)化選項影響下的代碼溯源問題。目前編譯器的優(yōu)化選項有O0、O1、O2、O3、Os 這5種,編譯器默認(rèn)使用的是O2優(yōu)化選項,本文的實驗結(jié)論也是基于O2優(yōu)化選項。如果修改優(yōu)化選項,會對論文目前的實驗結(jié)果產(chǎn)生一定的影響,對于修改優(yōu)化選項后未能溯源的代碼,需要通過人工方式進(jìn)行溯源分析。本文方法仍能輔助人工完成大部分機器碼溯源任務(wù),降低全部機器碼溯源所花費的時間,滿足機載軟件適航要求。
4) C/C++是目前機載軟件開發(fā)的主要語言選擇項,后續(xù)將針對C/C++中更復(fù)雜的嵌套數(shù)據(jù)結(jié)構(gòu)和函數(shù),開展進(jìn)一步研究,并根據(jù)實驗環(huán)境已留出的選擇不同處理器及編程語言的開放接口,使提出的溯源方法推廣適配到其他平臺處理器以及C++編程語言。