摘 要:采用反射和開放編譯技術(shù),設(shè)計并實現(xiàn)了一個面向?qū)ο蟪绦蜢o態(tài)分析器,對開放編譯器OpenC++進行了擴展與改進。通過實驗研究表明,該程序靜態(tài)分析器可以得到全面的系統(tǒng)依賴信息,可以有效地輔助程序理解。
關(guān)鍵詞:面向?qū)ο螅怀绦蚶斫猓荒嫦蚬こ蹋环瓷洌婚_放編譯;靜態(tài)分析
中圖分類號:TP311.5 文獻標志碼:A
文章編號:1001-3695(2008)09-2706-03
Static analyzer of objectoriented system based on open compiler
LIU Pengfei,HU Shengming,CHEN Ping,LIANG Ruoying
(School of Computer Science Technology, Xidian University, Xi’an 710071, China)
Abstract:This paper designed and implemented a static analyzer of objectoriented system based on the techniques of reflection and open compiler.The experiments show that the analyzer can get whole static dependency information and do well in assistant on program understanding.
Key words:objectoriented; program understanding; reverse engineering; reflection; open compiler; static analysis
隨著軟件技術(shù)的發(fā)展和社會需求的增長,軟件系統(tǒng)變得越來越復雜,更新速度也越來越快。軟件維護、遺產(chǎn)系統(tǒng)[1](legacy system)增多以及軟件的再工程問題均是軟件產(chǎn)業(yè)面臨的重要課題。以上這些問題的有效解決在很大程度上都依賴于程序理解。程序理解是軟件維護、遺留系統(tǒng)的現(xiàn)代化改造以及軟件的再工程等領(lǐng)域的重要技術(shù)。全面深入的靜態(tài)信息是程序理解和逆向工程的前提和關(guān)鍵。程序理解是一項相當復雜的工作。方法上需要將傳統(tǒng)的人工閱讀方法與分析工具或自動化轉(zhuǎn)換手段有機地融合,技術(shù)上則要求所研發(fā)的工具具有較強的壯健性,能處理應付多樣化的事務描述。從具體實現(xiàn)方式講,分為靜態(tài)分析工具和動態(tài)分析工具。動態(tài)方法雖然能清晰地體現(xiàn)對象交互的時序性,卻由于植入的信息量大,運行植入后的目標系統(tǒng)的低效成了該方法的瓶頸;其次,動態(tài)方法得不到完整的程序依賴信息,無法把握系統(tǒng)的整體結(jié)構(gòu)。
傳統(tǒng)的靜態(tài)分析工具如Sniff+[2]、MOOSE[3]、ATT的CIA++[4,5](C++ information abstraction)等也能快速地得到程序依賴信息,但所得程序依賴缺少條件、循環(huán)、跳轉(zhuǎn)等精確的控制信息。本文根據(jù)C++語言的靜態(tài)類型特點,提出利用開放編譯器OpenC++對目標代碼進行深入的靜態(tài)分析,以得到目標程序完整的程序依賴信息,輔助程序理解。
1 反射和開放編譯
反射是關(guān)于并作用于計算系統(tǒng)本身的推理過
程,這一活動涉及訪問并可以部分地改變整個系統(tǒng),從而影響自身的計算。反射作用的目的在于動態(tài)地修改計算系統(tǒng)的內(nèi)部結(jié)構(gòu),使得計算更為有效[6]。反射的實現(xiàn)通常將系統(tǒng)細分為兩層,即基層和元層。應用的功能在基層實現(xiàn)。元層完成觀測、操縱基層的結(jié)構(gòu)和行為的功能。面向?qū)ο笙到y(tǒng)通過引入元對象來實現(xiàn)反射。一個對象被和一個元對象聯(lián)系在一起時,這個元對象表示該對象的元信息[7]。
開放編譯思想是將編譯過程用元對象協(xié)議MOP(meta object protocol)向用戶開放,允許用戶自己通過編制實現(xiàn)元對象的程序片段來干預編譯過程,以達到特定的應用目的。MOP是在面向?qū)ο笙到y(tǒng)中實現(xiàn)反射技術(shù)的一種體系結(jié)構(gòu),是一種API接口,它開放了語言的頂層,允許用戶為了特殊需要去調(diào)整語言的設(shè)計和實現(xiàn)。按照實現(xiàn)的反射類型,MOP可以分為計算反射和結(jié)構(gòu)反射兩大類;如果從反射的時機來看,可以區(qū)分為編譯時MOP和運行時MOP兩類;如果從元接口的類型來分,可以分為基于對象的MOP和基于類的MOP兩類。編譯時MOP是在編譯基層程序時調(diào)用元對象進行翻譯,所得到的新的基層程序?qū)⑻娲谐绦颉T獙ο笾淮嬖谟诰幾g時,在運行時并不存在。元層信息在運行時構(gòu)造和存在的MOP稱為運行時MOP。相對于編譯時MOP,運行時MOP會帶來動態(tài)創(chuàng)建、查詢元對象的開銷,性能上的損失較大,且也無法利用編譯器在性能優(yōu)化上的便利。本文使用OpenC++的MOP是實現(xiàn)結(jié)構(gòu)反射的、基于類的、編譯時的MOP[8,9]。
2 靜態(tài)分析器的實現(xiàn)技術(shù)
2.1 OpenC++的工作原理
OpenC++編譯器的工作過程由三個階段組成:a)預處理, 從OpenC++到C++的源到源(source to source)轉(zhuǎn)換;b)標準C++編譯器的編譯;c)鏈接[10]。OpenC++ MOP是在第二階段控制轉(zhuǎn)換的接口,指明OpenC++的擴展特征如何轉(zhuǎn)換成普通的C++代碼,如圖1所示。
在OpenC++中,常規(guī)的C++程序稱為基級程序(baselevel program);通過MOP接口操縱基級程序元信息的程序稱為元級程序(metalevel program)。在圖1中,基級程序經(jīng)C++預處理器處理后,被分割成許多小的代碼片段(codefragment)。這些代碼片段由類元對象轉(zhuǎn)換后,再重新組合成一個完整的C++程序。最后OpenC++將轉(zhuǎn)換后的C++程序傳遞給標準的C++編譯器,如GNU C++。這種過程給了用戶選擇后端C++編譯器的方便。
在OpenC++中,代碼片段由Ptree元對象以分析樹的形式表示。雖然元對象與C++中的對象類似,但它們只存在于編譯器,并且代表著基級程序的元狀態(tài),因此稱它們?yōu)樵獙ο螅荒芎唵蔚胤Q為對象。
2.2 C++對靜態(tài)解析的支持
OpenC++沒有提供可直接獲取OO系統(tǒng)靜態(tài)信息的接口。分析OpenC++對源程序進行解析(parsing)和轉(zhuǎn)換(translation)的過程后發(fā)現(xiàn):通過在其轉(zhuǎn)換路徑上的一些關(guān)鍵點上添加代碼,可以實現(xiàn)獲取靜態(tài)信息這一功能。
OpenC++讀入一個源文件后,逐段逐段地對其掃描。這里的段(fragment)是按聲明(declaration)區(qū)分的。OpenC++總共設(shè)置了七類聲明,即空聲明、TEMPLATE、METACLASS、EXTERN、NAMESPACE、USING和常規(guī)聲明。類定義、函數(shù)定義、非內(nèi)聯(lián)方法的定義均屬于常規(guī)聲明的范疇。
經(jīng)過詞法分析、語法分析后,OpenC++為每個段構(gòu)造出相應的分析樹。接著,這棵分析樹在元對象的控制下,由根開始一層一層的、在元對象的控制下、按照后序的方式被轉(zhuǎn)換。整個過程如圖2所示。
如果利用OpenC++來獲取目標系統(tǒng)的動態(tài)信息,則可以在轉(zhuǎn)換時進行植入等自定義動作。由于只需要捕獲目標系統(tǒng)的靜態(tài)信息,無須對目標系統(tǒng)源碼進行轉(zhuǎn)換。這時的OpenC++和普通的 C++一樣,在需要轉(zhuǎn)換時提取目標代碼的靜態(tài)信息即可。
2.2.1 獲取函數(shù)/方法調(diào)用關(guān)系的實現(xiàn)
OpenC++為函數(shù)/方法調(diào)用提供了一個ClassWalker::TranslateFuncall()接口,允許開發(fā)者重載該方法以實現(xiàn)對方法調(diào)用語義的修改或擴充。下面以一個簡單的例子說明OpenC++處理一個函數(shù)/方法調(diào)用的過程:
class A{…};
void f(){…};
void main(){
A obj;(1)
obj.M(100);(2)
fn();(3)
}
main()的部分分析樹如圖3所示。圖3中A、B等表示非葉子節(jié)點;{“fn”,2}等表示葉子節(jié)點,其中第一個域表示字符串首地址,第二個域表示字符長度; PtreeBlock等表示某一特定類型的非葉子節(jié)點。
當元對象Walker(Walker子類ClassWalker的一個對象,用于遍歷分析樹)走到對應(3)的PtreeExprStatement節(jié)點下的PtreeFuncallExpr節(jié)點后,會調(diào)用ClassWalker::TranslateFuncall(…)進行轉(zhuǎn)換(通過Visitor設(shè)計模式)。由此可知ClassWalker::TranslateFuncall(…)是處理函數(shù)/方法調(diào)用的關(guān)鍵所在。
2.2.2 獲取循環(huán)、分支、跳轉(zhuǎn)信息的實現(xiàn)
由上可知獲取目標系統(tǒng)靜態(tài)信息的關(guān)鍵在于找到元對象對由目標系統(tǒng)源代碼所生成的分析樹進行轉(zhuǎn)換的關(guān)鍵點。OpenC++針對循環(huán)、分支、跳轉(zhuǎn)信息分別定義有元對象PtreeXXXStatement(For,While,If,Break,Do)。這些元對象的轉(zhuǎn)換由Walker::TranslateXXX(Ptree*) 負責,故此接口也就是獲取此類信息的關(guān)鍵點所在。
2.3 靜態(tài)信息抽取算法
從OpenC++的工作原理可知,OpenC++對目標程序構(gòu)造相應的分析樹,然后利用元對象Walker或其子類對分析樹進行遍歷,因此OpenC++ MOP 只需在編譯時提供對C++中一些關(guān)鍵特性的控制能力即可,而不必在如何進行語法分析、如何構(gòu)造語法樹這樣的細節(jié)上花費太多的精力。針對對象的每一個基本動作,如方法調(diào)用、數(shù)據(jù)讀寫、對象創(chuàng)建以及條件、循環(huán)、跳轉(zhuǎn)等控制信息,元對象都有對應的方法用于定制對象的這一種行為。其算法如下:
輸入:目標系統(tǒng)TS=(S1,S2,…,Sn)
輸出:XML格式的靜態(tài)信息文件SF
步驟:
foreach Si=rProgram() do//對程序聲明段進行分段分析
Ptree* root = rFunctionBody();
//分析函數(shù)體的定義體, root指向生成的分析樹AST的根節(jié)點
Walker w;
Ptree* root2=w.Translate(root);
//root2指向轉(zhuǎn)換后分析樹AST'的根節(jié)點
do//調(diào)用w.Translate(root)
root—>Translate(w);
do//調(diào)用w.Translate(w)
w.TranslateFunctionImplementation(root2);
do//調(diào)用w.TranslateFunctionImplementation()
foreach Ptree* node E AST do
case
PtreeFuncallExpr://函數(shù)調(diào)用節(jié)點
do//調(diào)用w.TranslateFuncall()
生成消息節(jié)點加入XML文件
od
PtreeForStatement:
PtreeWhileStatement:
PtreeBreakStatement:
PtreeDoStatement:
PtreeIfStatement:
do//調(diào)用w.TranslateIf()
if含有else
生成含有else的消息節(jié)點加入XML文件
else if 不含有else
生成不含有else的消息節(jié)點加入XML文件
od
PtreeXXX://其他類型XXX節(jié)點:
//調(diào)用父類的
//ClassWalker::TranslateXXX()
//Walker::TranslateXXX()
esac//End of case
od//End of foreach
od//End of 調(diào)用w.TranslateFunctionImp()
od//End of 調(diào)用w.Translate(w)
od//End of 調(diào)用w.Translate(root)
od//End of foreach
3 實驗研究
根據(jù)本文設(shè)計實現(xiàn)的面向?qū)ο蟪绦蜢o態(tài)分析器已在本文開發(fā)的逆向工程的工具XDRE(XiDian reverse engineering)中得到應用。下面通過對一個例子程序的靜態(tài)信息提取,來測試該分析器的有效性。采用的用例是一個面向?qū)ο蟮耐\噲龇抡嫦到y(tǒng)。它是基于Windows平臺,使用VC++開發(fā)的基于MFC的工程。共有45個C++源文件(大小共102 KB),21個VC++類。
所得靜態(tài)信息文件存儲在CallNodeRelation.xml中,共得到函數(shù)/方法調(diào)用節(jié)點97個。限于篇幅下面僅列出包含條件的靜態(tài)信息片段。其中callnode_id表示所得依賴信息的序號。此靜態(tài)信息片段對應的代碼片斷如下:
void CCarGenerator::setHorizontalCarDistance(CCar* acar, CCar* another) {
//如果兩輛車均在水平方向
if((acar—>m_icurrentx) >= (another—>m_icurrentx)){another—>doWaiting();//后面水平車輛停下
}
else if((acar—>m_icurrentx) < (another—>m_icurrentx)) {
acar—>doWaiting();//后面水平車輛停下
}
}
從實驗結(jié)果可以看出,所得語句級別的程序依賴包括了帶有嵌套的條件、循環(huán)、跳轉(zhuǎn)等精確的控制信息。深入函數(shù)、方法內(nèi)部,表現(xiàn)出了函數(shù)間的全面、深層次調(diào)用關(guān)系,而不僅僅是簡單函數(shù)依賴關(guān)系,為得到符合UML2.0的序列圖提供了足夠的信息。
4 結(jié)束語
靜態(tài)信息是程序理解和逆向工程的前提和關(guān)鍵。傳統(tǒng)的靜態(tài)分析缺少條件、循環(huán)、跳轉(zhuǎn)等控制信息,對程序的控制流描述不夠精確;動態(tài)分析恢復出的信息過少,且存在目標系統(tǒng)運行效率低下的瓶頸問題。基于上述問題,本文提出了基于反射和開放編譯技術(shù)的靜態(tài)分析方式,可以得到全面深入的靜態(tài)信息,有利于對目標程序的程序理解和逆向工程。由于沒有改寫底層的系統(tǒng)調(diào)用,沒有采用平臺相關(guān)的技術(shù),使得本工具具有優(yōu)秀的可移植性,只需重新編譯即可運行于Linux、Windows和Solaris等支持標準C/C++的系統(tǒng)。開放編譯技術(shù)的引入則增強了本分析器的自動化程度,無須修改源代碼,減輕了開發(fā)人員的負擔。
本文給出的程序靜態(tài)分析器以C++語言為基礎(chǔ)。隨著開放編譯技術(shù)應用面的擴展,許多主流的面向?qū)ο笳Z言也都支持開放編譯,如Java語言,因此開放編譯的機制在面向?qū)ο蟪绦蜢o態(tài)解析方面具有一定通用性。而利用開放編譯器技術(shù)也有助于軟件測試和度量方面的進一步研究[11]。
參考文獻:
[1]BENNETT K.Legacy systems:coping with success[J].IEEE Journal,1995,12(1):19-22.
[2]BELLAY B,GALL H.A comparison of four reverse engineering tools[C]//Proc of the 4th WCRE. Amsterdam:[s.n.],1997.
[3]RICHNER T,STEPHANE.Recovering highlevel views of objectoriented applications from static and dynamic information[C]//Proc of ICSM.Oxford:[s.n.],1999.
[4]CHEN Y F,GANSNER E R,KOUTSOFIOS E.A C++ data model supporting reachability analysis and dead code detection[C]//Proc of the 6th European Software Engineering Conference. Zurich:[s.n.],1998:682-694.
[5]GRASS J.Objectoriented design archeology with CIA++[J].Computing Systems,1992,5(1):5-67.
[6]陳平.反射結(jié)構(gòu)和對象標志的研究[D].西安:西安電子科技大學,1991.
[7]ELIANE M A,ROSA C A.A fault injection approach based on reflective programming[C]//Proc of Dependable Systems and Networks. New York:[s.n.],2000.
[8]SHIGERU C.A metaobject protocol for C++[C]//Proc of OOPSLA. New York:[s.n.],1995.
[9]SHIGERU C.A stuty of compiletime metaobject protocol[D].Tokyo:Graduate School of Science,The University of Tokyo,1996.
[10]SHIGERU C.OpenC++ tutorial[EB/OL].(200110)[2002-05].http://www.csg.is.titech.ac.jp/~chiba/openc++.html.
[11]孫青巖, 陳平.基于開放編譯器的內(nèi)存泄露檢測機制[J].計算機工程, 2004,30(20):42-44.