劉曉楠,趙榮彩,龐建民,魏振方
(1.信息工程大學(xué)四院,河南 鄭州450002;2.國家科技部 數(shù)學(xué)工程與先進(jìn)計算國家重點實驗室,河南 鄭州450002)
二進(jìn)制翻譯[1-5]是一種特殊的編譯技術(shù),與正向編譯不同,它處理的是二進(jìn)制形式的可執(zhí)行程序,這樣的程序是一系列指令操作和操作所需數(shù)據(jù)的集合。以二進(jìn)制翻譯形式實現(xiàn)的程序移植,不但要實現(xiàn)不同指令集架構(gòu) (instruction set architecture,ISA)之間程序語義的完全模擬,同時也必須在目標(biāo)體系架構(gòu)上原樣恢復(fù)程序運行時所需的所有數(shù)據(jù)。
使用C語言編寫的程序是由一個個函數(shù) (也稱過程)構(gòu)成的,經(jīng)過編譯后,會生成相應(yīng)的可執(zhí)行程序。從宏觀上講,對這樣的可執(zhí)行程序進(jìn)行數(shù)據(jù)恢復(fù),實質(zhì)上是對其包含的用戶過程和庫過程中的參數(shù)、變量、常量等數(shù)據(jù)的全面移植。從微觀角度看,可執(zhí)行程序是由一條條指令構(gòu)成,針對指令的數(shù)據(jù)恢復(fù)就轉(zhuǎn)變?yōu)樵谀繕?biāo)平臺上原樣映射源平臺上的內(nèi)存中和寄存器中的數(shù)據(jù)。
信息工程大學(xué)開發(fā)的IA64到Alpha的靜態(tài)二進(jìn)制翻譯器提出基于IA64ABI(application binary interface)的參數(shù)數(shù)據(jù)的恢復(fù)方法,但并未涉及IA32架構(gòu)?,F(xiàn)有的數(shù)據(jù)恢復(fù)手段[6]一般以構(gòu)造完整的控制流圖為前提,再輔以對過程實施活躍變量分析等處理。但是這類技術(shù)本身存在先天劣勢,即通過靜態(tài)分析手段不能保證獲得完備的程序控制流圖。除此以外,上述方法還存在一定的局限性,眾所周知,常用的C編譯器一般都提供不同級別的編譯優(yōu)化選項優(yōu)化,以常用的GCC編譯器為例,使用O3級別的優(yōu)化選項編譯生成的二進(jìn)制程序中已經(jīng)很難再找到原有的過程參數(shù)傳遞方式的痕跡。當(dāng)然采用這樣處理方式生成的二進(jìn)制程序,其內(nèi)存數(shù)據(jù)的棧楨布局都會與ABI的約定有所不同。因此僅僅通過控制流和數(shù)據(jù)流分析等手段不一定能解決過程調(diào)用中參數(shù)和變量的恢復(fù)問題。
用戶自定義過程的參數(shù)和變量在某些情況下是不能被完全恢復(fù)的,其恢復(fù)的程度與源代碼編譯時被優(yōu)化的程度,以及馮諾依曼體系中數(shù)據(jù)與指令混存方式有關(guān)[7,8]。因此對于用戶自定義過程,本文提出了一種不同的處理方式,以達(dá)到恢復(fù)過程訪問的所有數(shù)據(jù)的目的。
過程所訪問的數(shù)據(jù)主要有常量、變量,過程間傳遞的參數(shù),其中關(guān)于常量的恢復(fù)比較簡單,本節(jié)主要描述變量和參數(shù)與數(shù)據(jù)的關(guān)系。
在高級語言中 (以C語言為例),變量如果按照定義的位置可以分為3類:全局變量、局部變量和過程參數(shù)。這3類數(shù)據(jù)在二進(jìn)制翻譯中處理各不相同,本文從編譯的角度做簡要介紹:
(1)全局變量是程序高級編程語言提供的一種數(shù)據(jù)處理方式,可在當(dāng)前執(zhí)行程序的所有過程中對它進(jìn)行訪問。通過編譯器編譯之后,全局變量所對應(yīng)的存儲單元將被分配一個特定的地址空間,該空間通常處于可執(zhí)行二進(jìn)制文件的數(shù)據(jù)段中。
(2)與全局變量不同,局部變量的訪問范圍局限于一個過程之內(nèi)。編譯器通常將局部變量放在過程調(diào)用的棧幀結(jié)構(gòu)中。局部變量的生命期隨著過程調(diào)用開始而開始,隨著過程調(diào)用結(jié)束而終結(jié)。
(3)過程參數(shù)是過程調(diào)用方 (caller)與被調(diào)用方(callee)之間傳遞數(shù)據(jù)的主要手段。它通常由caller設(shè)置,編譯器按照ABI規(guī)范處理,并傳遞給callee使用。
本文在討論數(shù)據(jù)恢復(fù)方案時,有幾個相關(guān)問題需要單獨聲明:
(1)關(guān)于動態(tài)鏈接和堆中的數(shù)據(jù)遷移,本文所涉及的二進(jìn)制翻譯系統(tǒng)在目標(biāo)端采用函數(shù)封裝的方式,即使用目標(biāo)平臺上同名的C 庫函數(shù)來模擬源平臺的C 庫函數(shù)調(diào)用,因此不必考慮動態(tài)鏈接和程序運行時堆數(shù)據(jù)的分配與回收。
(2)內(nèi)存指針問題,本文所述二進(jìn)制翻譯實現(xiàn)是基于匯編級映射的,即源二進(jìn)制文件經(jīng)過反匯編后,再經(jīng)過中間表示映射到目標(biāo)平臺的匯編指令,進(jìn)而實現(xiàn)在目標(biāo)平臺的翻譯的。因此不涉及內(nèi)存指針的處理。
(3)大小端問題,本文所述二進(jìn)制翻譯系統(tǒng)的源和目標(biāo)平臺恰好都為小端模式,所以數(shù)據(jù)處理方式類同。即便假設(shè)存在大小端不同的情況,也可以在每次數(shù)據(jù)裝載和寫入時進(jìn)行簡單轉(zhuǎn)換。因此該問題不影響本文的技術(shù)實現(xiàn)。
(4)虛擬內(nèi)存和內(nèi)存頁大小問題,關(guān)于二進(jìn)制翻譯訪存范圍,本文所述系統(tǒng)采取與X86完全一致的低4G 內(nèi)存映射方案,并且程序是匯編級別的。源和目標(biāo)平臺也都是運行Linux操作系統(tǒng)。因此本文討論問題也不涉及虛擬內(nèi)存的分配,內(nèi)存頁大小也對本文所述方法無影響。
本文討論的有關(guān)用戶自定義過程的數(shù)據(jù)恢復(fù)問題是在以下前提下提出的:
(1)在二進(jìn)制翻譯過程中,源文件已經(jīng)被正確解碼,并已經(jīng)用中間表示手段呈現(xiàn)。
(2)本文的數(shù)據(jù)恢復(fù)是在二進(jìn)制翻譯過程中當(dāng)源體系架構(gòu)的程序執(zhí)行空間已經(jīng)映射到目標(biāo)體系架構(gòu)的相應(yīng)地址空間之后討論的。
(3)筆者所參與的二進(jìn)制翻譯系統(tǒng)是一個多源,單目標(biāo)的動靜混合的翻譯器。下文所述例子將選取該系統(tǒng)支持的一種源體系結(jié)構(gòu)IA32,目標(biāo)選取RISC 架構(gòu)的某國產(chǎn)CPU (下文簡稱OUR-RISC)。
(4)本文分析例子基于的C 程序源碼如圖1所示。該程序在IA32\Linux下編譯生成可執(zhí)行文件,通過二進(jìn)制翻譯可以在OUR-RISC上執(zhí)行。
圖1 示例程序的源碼
通過深入分析用戶自定義過程中參數(shù)和變量在二進(jìn)制翻譯過程中的特性,可知不管翻譯的源和目標(biāo)如何變化,被翻譯程序的語義是必須被保持的。即數(shù)據(jù)的訪問方式和存儲空間可能不容易恢復(fù),但程序在過程調(diào)用中指令語義卻一定可以通過相關(guān)技術(shù)手段復(fù)原。因此在進(jìn)行數(shù)據(jù)恢復(fù)的時候,并不進(jìn)行參數(shù)和變量的準(zhǔn)確識別和區(qū)分。而是在指令語義層面完成目標(biāo)對源的精確模擬,同時還需要將指令執(zhí)行過程中與數(shù)據(jù)暫存、傳遞、讀取、寫入等操作相關(guān)的寄存器和內(nèi)存空間也同樣在目標(biāo)端模擬。對于過程動態(tài)調(diào)用時內(nèi)存數(shù)據(jù)的恢復(fù)則輔助以棧幀動態(tài)維護(hù)策略。這樣處理后,目標(biāo)機(jī)器中對寄存器和內(nèi)存的訪問時機(jī)和動作均與源平臺嚴(yán)格對應(yīng)。
針對用戶自定義過程在執(zhí)行中指令訪問數(shù)據(jù)的3種可能性,即:源機(jī)器上的數(shù)據(jù)均處于程序執(zhí)行時訪問的寄存器、可執(zhí)行程序的數(shù)據(jù)段以及執(zhí)行時的內(nèi)存楨棧中,提出了具體的應(yīng)對策略:
(1)寄存器中數(shù)據(jù)的處理
對于寄存器尋址,本文采用精確映射的策略:針對源機(jī)器和目標(biāo)機(jī)器的全部寄存器,如果目標(biāo)機(jī)器提供的同類型寄存器數(shù)量多于源機(jī)器,為提高翻譯后程序的執(zhí)行效率,對寄存器進(jìn)行一對一精確映射。映射的時候需要綜合考慮不同指令集架構(gòu)ISA 寄存器的類型和屬性。如果目標(biāo)機(jī)器寄存器數(shù)量少于源機(jī)器,在部分寄存器采用精確映射的基礎(chǔ)上,其余寄存器可以通過內(nèi)存模擬 (在內(nèi)存中開辟固定區(qū)域,對被映射的寄存器內(nèi)容的全部變化完全模擬,稱作全態(tài)模擬);若源和目的機(jī)器的寄存器數(shù)量相同,由于不同機(jī)器對寄存器的設(shè)定差異,一般也應(yīng)該采用精確映射與全態(tài)模擬相結(jié)合的策略。
在二進(jìn)制翻譯中,不同的ISA 通常使用不同的參數(shù)傳遞策略,例如:多數(shù)64位ISA 選擇通過寄存器傳遞前幾個參數(shù),超過約定參數(shù)個數(shù)后,將使用內(nèi)存?zhèn)鲄⒌姆绞?;?2位的X86就只使用內(nèi)存來傳遞參數(shù)。實際應(yīng)用中需要靈活處理。
(2)數(shù)據(jù)段中數(shù)據(jù)的處理
可執(zhí)行程序數(shù)據(jù)段中的數(shù)據(jù)屬于靜態(tài)數(shù)據(jù)。靜態(tài)數(shù)據(jù)一般是源代碼經(jīng)過編譯后被有組織的放在程序的數(shù)據(jù)段中,包括被靜態(tài)初始化的全局變量、數(shù)組等。針對數(shù)據(jù)段中的數(shù)據(jù),在二進(jìn)制翻譯中,可以采用數(shù)據(jù)段克隆的方式在二進(jìn)制的目標(biāo)文件中重建。
(3)運行時內(nèi)存數(shù)據(jù)的處理
動態(tài)數(shù)據(jù),通常指存在于程序被執(zhí)行時堆中和棧中的數(shù)據(jù)。堆中的數(shù)據(jù)一般是通過調(diào)用內(nèi)存申請庫函數(shù)而在程序執(zhí)行時獲得空間并使用的。因此對于堆中的動態(tài)數(shù)據(jù),不需要進(jìn)行恢復(fù)。但是對于棧中的數(shù)據(jù),該部分?jǐn)?shù)據(jù)與可執(zhí)行程序運行時過程的調(diào)用和返回息息相關(guān),必須通過全態(tài)模擬與棧幀動態(tài)維護(hù)相結(jié)合的方式進(jìn)行數(shù)據(jù)的恢復(fù)。
寄存器中的數(shù)據(jù)在程序運行時,一般是通過寄存器名稱來訪問的,如語句 “sub esp,18h”。在設(shè)計中間語言時,通過對寄存器進(jìn)行編號,前邊的匯編語句就變成 “r28=r28-24”。最終翻譯到目標(biāo)平臺就變成了 “l(fā)di$sp,-24($sp)”。針對寄存器類型的數(shù)據(jù),需要采用上文提到的精確映射的解決策略。
而對于存儲在數(shù)據(jù)段中的數(shù)據(jù),則通常是根據(jù)編譯程序分配的固定內(nèi)存地址來訪問,如語句 “push x”(其中的x是一個在.data段的全局變量x,初值是3,其數(shù)據(jù)段的描述為 “.data:08049450public x .data:08049450x dd 3)”。轉(zhuǎn)換 成 中 間 語 言 就 變 成 了 “m [r28-4]:=m[0x8049450]”。最終翻譯到目標(biāo)平臺,由后邊的4條語句完成“l(fā)di$5,0x8049450;ldw $5,0 ($5);stw $5,-4($sp);ldi $sp,-4 ($sp);”。可見程序執(zhí)行時是根據(jù)編譯器分配的固定地址0x8049450來讀取處于數(shù)據(jù)段中的全局變量x的值3,然后再把數(shù)值3壓棧。針對這種對數(shù)據(jù)的訪問形式,需要采用上文提到的數(shù)據(jù)段克隆的解決策略。
基于語義鏡像的數(shù)據(jù)恢復(fù)就是采用了精確映射和數(shù)據(jù)段克隆結(jié)合的手段,只要保證鏡像后目標(biāo)平臺的指令在語義層面與源平臺完全等價,就可以準(zhǔn)確完成數(shù)據(jù)的恢復(fù)。接下來對語義鏡像的具體實現(xiàn)通過實例給出介紹。
3.1.1 針對寄存器的精確映射
寄存器的映射是二進(jìn)制翻譯過程中的重要步驟,其映射的過程絕對不是簡單的牽線對應(yīng),而是建立在對源和目標(biāo)機(jī)器ISA 深刻理解的基礎(chǔ)上,精確映射方案的確定需要通盤考慮準(zhǔn)確性和效率。想要在目標(biāo)機(jī)器準(zhǔn)確模擬源機(jī)器的行為,可以將源機(jī)器的寄存器與目標(biāo)機(jī)器的寄存器建立映射關(guān)系。當(dāng)然由于源和目標(biāo)ISA 的差異有時難以彌合,在有些時候也可以選擇將源機(jī)器的寄存器映射到目標(biāo)機(jī)器的某段內(nèi)存空間,通過內(nèi)存模擬的方式消除較大的體系結(jié)構(gòu)差異??紤]到程序運行中頻繁訪存的開銷,只要目標(biāo)機(jī)器的寄存器數(shù)量和資源較多,絕大多數(shù)情況下都采取寄存器間的精確映射方式[9,10]。
在我們的實例中,源機(jī)器采用最為常見的32 位X86(也就是IA32架構(gòu)),目標(biāo)機(jī)器為某RISC 架構(gòu)處理器,提供了豐富的寄存器資源,完全可以勝任針對X86處理器的精確映射,精確映射策略是二進(jìn)制翻譯獲得較高性能的首選做法。
示例分析:
IA32架構(gòu)的基本寄存器如圖2所示。通用寄可以整體32位使用,也可以單獨使用其16位或者8位,方式比較靈活,如圖3所示。
(1)IA32通用寄存器精確映射
如表1所示在中間表示層和目標(biāo)處理器層均對寄存器標(biāo)稱了相應(yīng)的編號。工程應(yīng)用中,開發(fā)者應(yīng)該明確寄存器編號與物理寄存器的對應(yīng)關(guān)系。其中序號的起止并不是重要的問題,選擇序號的起止范圍與工程實踐中具體細(xì)節(jié)有關(guān),其方式也比較靈活,表1也只是給出一種示例方案。
圖2 IA32通用寄存器
圖3 通用寄存器的復(fù)用
表1 精確映射方案
寄存器Caller-save和Callee-saved屬性的處理:在精確映射過程中特別需要注意調(diào)用者/被調(diào)用者保存寄存器(caller/callee saved registers),IA32 中 兩 種 寄 存 器 屬 性 的分布見表1。需要注意的是主調(diào)過程caller在過程調(diào)用前不保存callee-saved寄存器的值,這一點在IA32程序正常執(zhí)行的時候不存在任何問題,編譯器和操作系統(tǒng)會進(jìn)行自動的處理。但是在二進(jìn)制翻譯過程中,目標(biāo)平臺的程序調(diào)用約定不同。假如被調(diào)用過程是庫函數(shù),如果被調(diào)用的庫函數(shù)改變了本應(yīng)在IA32 平臺下被callee-saved處理的寄存器值,勢必導(dǎo)致二進(jìn)制翻譯程序的執(zhí)行錯誤。因此必須在精確映射時將callee-saved寄存器映射到目標(biāo)平臺的受保護(hù)寄存器 (preserved across procedure calls),本所所針對目標(biāo)平臺的受保護(hù)寄存器與X86平臺 “callee-saved”在功能和含義上都是一致的。
針對需要由調(diào)用者保存的寄存器,其保存動作一般可以通過指令語義分析很容得的獲得。以X86架構(gòu)為例,過程調(diào)用者會在調(diào)用發(fā)生之前通過一組組的數(shù)據(jù)壓棧指令完成對寄存器的保存操作。下文要提及的全態(tài)模擬處理方式可以將顯式的指令操作在目標(biāo)機(jī)器重現(xiàn),所以源機(jī)器的caller-saved寄存器在映射時不需要區(qū)分目標(biāo)寄存器的類型。
特殊功能寄存器的處理:源平臺的ESP寄存器一般在堆棧尋址時使用,所以被特別精確映射到目標(biāo)平臺功能相近的$sp寄存器中;而擴(kuò)展棧指針寄存器ESP同樣被精確映射到目標(biāo)平臺的功能相近$fp寄存器中。
IA32架構(gòu)通用寄存器可以部分訪問的處理:IA32架構(gòu)為了保持對歷史架構(gòu)的兼容等原因,對于EAX 等寄存器允許訪問低16位AX 寄存器,以及8位的AH 和AL 寄存器的情況。而目標(biāo)平臺的RISC架構(gòu)只允許對寄存器整體的訪問,因此還需要額外使用目標(biāo)平臺的移位、置位、異或等指令做相應(yīng)處理以彌合不同體系結(jié)構(gòu)之間的差異。這些功能被稱為精確映射的配套處理過程。
(2)IA32架構(gòu)的EFLAGS
關(guān)于EFLAGS寄存器在二進(jìn)制翻譯中的處理是比較復(fù)雜的,相關(guān)文獻(xiàn) [11,12]中有對應(yīng)的論述。筆者參與的二進(jìn)制翻譯系統(tǒng)中,除了對該寄存器采取精確映射和活躍狀態(tài)模擬之外,還使用了大量的優(yōu)化、模擬,以及化簡算法,其涉及的內(nèi)容已經(jīng)超出本文討論的范圍。而與精確映射有關(guān)的就是需要指定目標(biāo)平臺的一個寄存器來承擔(dān)EFLAGS寄存器的功能。
(3)其它寄存器的處理
由于源和目標(biāo)架構(gòu)存在地址空間轉(zhuǎn)換問題,所以在指令解碼階段提前將指令指針寄存器EIP 的偏移尋址的地址轉(zhuǎn)化為目標(biāo)平臺可用的固定地址,因此也就不需要對EIP進(jìn)行精確映射和模擬了。同時考慮到在IA32架構(gòu)下高級語言編制的可執(zhí)行程序在反匯編解碼后,不會出現(xiàn)對段寄存器的操作,所以無需對6 個16 位的段寄存器進(jìn)行任何處理。而對于浮點寄存器,我們采用在中間代碼生成時就通過多條中間代碼對IA32架構(gòu)中棧式的浮點寄存器變化過程做了全態(tài)模擬,所以浮點寄存器也只需要進(jìn)行一對一的精確映射即可:ST(0)-ST(7)依次映射到目標(biāo)平臺的$f2-$f9這8個受保護(hù)的浮點寄存器。
3.1.2 數(shù)據(jù)段克隆
數(shù)據(jù)段克隆是指為降低不同平臺間二進(jìn)制翻譯數(shù)據(jù)移植的難度,采取特定處理方法和映射策略將源平臺可執(zhí)行程序的數(shù)據(jù)段整體克隆到目標(biāo)平臺的相應(yīng)可執(zhí)行程序中。下面通過簡單的示例來大體說明其具體做法,示例程序為IA32\Linux環(huán)境下的ELF32格式的可執(zhí)行文件作。
示例分析:
可執(zhí)行文件格式 (executable and linking format,ELF)文件是Linux 操作系統(tǒng)下的一種常用目標(biāo)文件 (object file)格式,如圖4所示。
圖4 ELF文件結(jié)構(gòu)
通過分析ELF32文件的程序頭部中為每個section保存的段描述符,我們可以區(qū)分section類型,定位與數(shù)據(jù)存儲相關(guān)的幾個section在文件中的偏移、大小,以及需要被映射到的虛擬地址。據(jù)此可以把section中的數(shù)據(jù)按需提取出來,作為數(shù)據(jù)段克隆的源數(shù)據(jù)。
由于源平臺的程序在編譯后,生成的可執(zhí)行二進(jìn)制程序中section會被映射到 “固定”的虛擬空間 (既段的虛擬地址不變,比如例子中的.rodata 就被映射到虛擬地址0x8048430,大小為8個字節(jié),如圖5 所示)。ELF 程序?qū)?shù)據(jù)段中數(shù)據(jù)的訪問也是通過相應(yīng)的虛擬地址給出:對函數(shù)printf的格式字符參數(shù) “z=%d”的訪問,就是通過給出標(biāo)號format(該標(biāo)號對應(yīng)虛擬地址0x8048438)的方式訪問。
圖5 示例文件中.rodata段內(nèi)容
由此可見,如果將提取出的數(shù)據(jù)段源數(shù)據(jù) “克隆”到目標(biāo)機(jī)器中,也就是保證翻譯后的程序在目標(biāo)機(jī)編譯后,仍然可以將.rodata等數(shù)據(jù)段加載到與源機(jī)器同樣的虛擬地址處,我們采用的方式可用圖6的偽代碼描述。同時將可執(zhí)行程序的運行地址空間范圍原樣克隆到的目標(biāo)機(jī)對應(yīng)虛擬空間中,就可以保證翻譯得到的目標(biāo)代碼根據(jù)虛擬地址直接訪問這些數(shù)據(jù)。
圖6 實現(xiàn)數(shù)據(jù)段克隆的偽代碼
圖6中對源體系架構(gòu)處于數(shù)據(jù)段中的數(shù)據(jù)通過在中翻譯程序使用字符數(shù)組,并配合內(nèi)存數(shù)據(jù)移動的手段實現(xiàn)了對預(yù)案體系架構(gòu)數(shù)據(jù)段的克隆操作。但是這種處理方法在應(yīng)用程序的數(shù)據(jù)段容量極大時是存在缺陷的,因為有的高級語言對字符數(shù)組的大小是有限制的。因此需要進(jìn)行一定的調(diào)整。即首先在二進(jìn)制翻譯時將源二進(jìn)制文件中數(shù)據(jù)段寫入文件。然后當(dāng)被翻譯程序執(zhí)行時,提前將被寫入文件的數(shù)據(jù)加載到特定的內(nèi)存空間。被翻譯程序在執(zhí)行時則通過數(shù)據(jù)偏移的方式按需訪問這些靜態(tài)數(shù)據(jù)即可。
3.2.1 棧幀布局
程序運行時的重要數(shù)據(jù)結(jié)構(gòu)——棧幀 (stack frames),主要用于過程調(diào)用的數(shù)據(jù)保存和恢復(fù),通常用它來保存臨時變量,過程調(diào)用需要的傳遞參數(shù)和返回值等數(shù)據(jù),以及提供一些需要臨時分配的空間。
承接上文的示例,我們?nèi)匀灰訧A32 架構(gòu)的Stack Frame來舉例,包含一個調(diào)用者棧幀 (caller stack frame)和一個被調(diào)用過程的棧幀 (也就是當(dāng)前執(zhí)行過程的棧幀current stack frame),如圖7所示。在圖7中有兩個重要的指針,分別是棧指針,其值為寄存器ESP 的值 (stack pointer,SP)和幀指針,其值為寄存器EBP 的值 (frame pointer,F(xiàn)P)。
3.2.2 棧幀動態(tài)維護(hù)
圖7 棧幀內(nèi)存布局
過程調(diào)用中參數(shù)的傳遞和調(diào)用結(jié)束后的返回都是通過棧的方式完成。如果callee要訪問參數(shù)數(shù)據(jù),caller要訪問返回值數(shù)據(jù),需要首先根據(jù)要訪問數(shù)據(jù)相對于棧頂?shù)钠朴嬎愠銎浯鎯Φ刂?,然后再訪問。由于各體系架構(gòu)ABI規(guī)定不同,在發(fā)生過程調(diào)用時系統(tǒng)對過程返回地址保存方法也不盡相同。以IA32架構(gòu)的處理器為例,系統(tǒng)在callee被調(diào)用前會將其返回地址以4 個字節(jié)的大小壓入棧頂,當(dāng)callee執(zhí)行結(jié)束需要返回caller時則根據(jù)事先壓入的返回地址完成控制流的轉(zhuǎn)移。而與此不同的是OUR-RISC 平臺在過程調(diào)用返回時,使用專用寄存器來保存返回值。也就是說其過程調(diào)用時不會為返回值而自動改變SP值,也不會顯式的將返回地址壓棧。針對這種情況,如果僅僅是在翻譯過程中簡單的進(jìn)行指令的翻譯,則意味著目標(biāo)平臺的棧指針偏移與源平臺存在差異,因為它并不需要經(jīng)過程返回地址壓棧。即如果不對該差異加以處理,勢必會造成翻譯后程序在反問過程參數(shù)時出錯。因此這種由體系結(jié)構(gòu)差異造成的棧指針偏差必須在二進(jìn)制翻譯過程中修正。針對本文所述方法,只需將在目標(biāo)端發(fā)生過程調(diào)用時自動將棧頂指針做算術(shù)加減操作即可,以此保證源和目標(biāo)ISA 的不同特性不會帶來的棧幀失衡。
針對這種情況,本文提出了一個以棧幀動態(tài)維護(hù)來解決棧幀失衡的方法:在OUR-RISC 被調(diào)用過程callee的開始階段添加一個棧指針減4的操作,同時在過程返回前添加一個棧指針加4的動作。以此來模擬IA32可執(zhí)行程序在過程調(diào)用時幀棧變換的全部狀態(tài)。
上文提到為了保證幀棧平衡而添加的指針加減4操作,相當(dāng)于在目標(biāo)平臺的用戶自定義函數(shù)開始時申請了4個字節(jié)的??臻g,而在過程返回前釋放這4 個字節(jié)的??臻g。需要指出的是,我們在具體的工程實踐中并沒有浪費這4個字節(jié)的空間。由于目標(biāo)平臺的返回地址寄存器是需要callee保護(hù)的,所以可以假設(shè)如果callee在運行過程中,其內(nèi)部又發(fā)生了一次過程調(diào)用,導(dǎo)致原來的callee身份成為了新的caller(稱為new_caller)。這就意味著callee要翻譯到原來的caller的返回地址可能會被新的過程調(diào)用所覆蓋,導(dǎo)致返回出錯,程序執(zhí)行失敗。上述假設(shè)在目標(biāo)平臺正常的程序執(zhí)行時是不會發(fā)生的,因為返回寄存器的保存由操作系統(tǒng)實現(xiàn)。但是如果是通過二進(jìn)制翻譯后的程序在目標(biāo)機(jī)器上執(zhí)行,就有可能出現(xiàn)假設(shè)中的情況。因此我們利用了為了實現(xiàn)幀棧平衡而冗余的幀棧加減4操作,除了調(diào)整棧指針值外,還切實的將用戶自定義過程的返回地址壓棧。經(jīng)過這樣的處理,就可以很完善的解決IA32 和OUR-RISC的ISA 差異給程序訪問動態(tài)數(shù)據(jù)所帶來的問題。
本文提出的用戶過程數(shù)據(jù)恢復(fù)方案在課題組的二進(jìn)制翻譯系統(tǒng)中得到應(yīng)用,且針對Spec2006測試集的所有程序都實現(xiàn)了IA32到OUR-RISC的二進(jìn)制翻譯。翻譯后的程序經(jīng)過編譯后,可以在OUR-RISC\Linux正確的執(zhí)行。測試結(jié)果見表2。測試內(nèi)容包括翻譯程序執(zhí)行輸出結(jié)果與Spec2006源程序在IA32\Linux環(huán)境下直接執(zhí)行結(jié)果對比。運行時間比率:源程序執(zhí)行時間/翻譯程序執(zhí)行時間=比率。
表2 實驗結(jié)果
本文針對二進(jìn)制翻譯中的難點——數(shù)據(jù)恢復(fù)的問題展開分析和討論,對用戶過程中涉及的函數(shù)參數(shù)、局部變量、全局變量以及返回值等數(shù)據(jù)在目標(biāo)平臺上完成了相應(yīng)的恢復(fù)。實驗結(jié)果表明,該方法即可以保證翻譯程序運行結(jié)果正確,同時又能使得被翻譯程序具有較高的執(zhí)行效率,實現(xiàn)了IA32程序所用數(shù)據(jù)移植到OUR-RISC 處理器的目標(biāo)。下一步的研究工作將集中在對二進(jìn)制翻譯生成程序執(zhí)行效率的進(jìn)一步提升之上。
[1]ZHANG Yaoxue.Thoughts on China’s infrastructure software and development of China’s CPU [R].Hangzhou:China Computer Federation,2010 (in Chinese).[張堯?qū)W.關(guān)于我國基礎(chǔ)軟件與CPU 發(fā)展的幾點思考 [R].杭州:中國計算機(jī)學(xué)會,2010.]
[2]CAI Songsong,LIU Qi,WANG Jian,et al.Optimization of binary translator based on godson CPU [J].Computer Engineering,2009,35 (7):280-282 (in Chinese). [蔡嵩松,劉奇,王劍,等.基于龍芯處理器的二進(jìn)制翻譯器優(yōu)化 [J].計算機(jī)工程,2009,35 (7):280-282.]
[3]ZHANG Ji,LI Ningbo.Key technology research of simulator based on binary translation [J].Computer Engineering,2010,36 (16):246-248 (in Chinese).[張激,李寧波.基于二進(jìn)制翻譯的仿真器關(guān)鍵技術(shù)研究 [J].計算機(jī)工程,2010,36(16):246-248.]
[4]Wondracek G,Comparetti PM,Kruegel C,et al.Automatic network protocol analysis[C]//Proceedings of the 15th Annual Network and Distributed System Security Symposium,2008.
[5]Wang Z,Jiang X,Cui W,et al.ReFormat:Automatic reverse engineering of encrypted messages[C]//Proceedings of the European Symposium on Research in Computer Security,2009.
[6]FANG Xia,YIN Qing,JIANG Liehui,et al.Register parameter recovery method based on dataflow analysis [J].Computer Engineering,2009,35 (22):62-64 (in Chinese). [方霞,尹青,蔣烈輝,等.基于數(shù)據(jù)流分析的寄存器參數(shù)恢復(fù)方法 [J].計算機(jī)工程,2009,35 (22):62-64.]
[7]ZHANG Youwei,LIU Xiaochun,WANG Yonghong.Study of recognition algorithm for program section [J].Computer Engineering,2009,35 (5):72-73 (in Chinese). [張有為,劉小春,汪永紅.程序段識別算法研究 [J].計算機(jī)工程,2009,35 (5):72-73.]
[8]Mike Van Emmerik.Static single assignment for decompilation[M].USA:The University of Queensland School of Information Technology and Electrical Engineering,2007.
[9]WEN Yanhua,TANG Daguo,QI Fengbin.Register mapping and register function cutting out implementation in binary translation [J].Journal of Software,2009,20 (zk):1-7 (in Chinese).[文延華,唐大國,漆鋒濱.二進(jìn)制翻譯中的寄存器映射與剪裁的實現(xiàn) [J].軟件學(xué)報,2009,20 (zk):1-7.]
[10]LIAO Yin,SUN Guangzhong,JIANG Haitao,et al.All registers direct mapping method in dynamic binary translation[J].Computer Applications and Software,2011,28 (11):21-24 (in Chinese). [廖銀,孫廣中,姜海濤,等.動態(tài)二進(jìn)制翻譯中全寄存器直接映射方法 [J].計算機(jī)應(yīng)用與軟件,2011,28 (11):21-24.]
[11] WANG Ronghua,MENG Jianyi,CHEN Zhijian,et al.Condition code optimization in dynamic binary translation [J].Journal of Zhejiang University(Engineering Science),2014,48 (1):124-129 (in Chinese).[王榮華,孟建熠,陳志堅,等.動態(tài)二進(jìn)翻譯中的標(biāo)志位優(yōu)化算法 [J].浙江大學(xué)學(xué)報(工學(xué)版),2014,48 (1):124-129.]
[12]TANG Feng,WU Chenggang,F(xiàn)ENG Xiaobing,et al.Ef-LA algorithm based on dynamic feedback [J].Journal of Software,2007,18 (7):1603-1611 (in Chinese). [唐鋒,武成崗,馮曉兵,等.基于動態(tài)反饋的標(biāo)志位線性分析算法[J].軟件學(xué)報,2007,18 (7):1603-1611.]