陳 鑫
(航空工業西安航空計算技術研究所,陜西 西安 710068)
隨著ARJ21、C919等多個民用飛機型號的相繼研制,通用航空領域的不斷發展,我國機載軟件研制過程中,越來越廣泛地認可和使用代表國際先進水平的、來自美國和歐洲等航空發達國家的 RTCA DO-178B[1]/C[2]適航標準。該標準規定了軟件研制過程中的各項活動和多個目標,但沒有給出如何完成這些活動,也沒有說明如何達到這些目標。經過技術人員的不斷努力,國內已能應對標準中的大部分要求,但對標準中提到的軟硬件集成過程輸出的驗證、以及編譯鏈接過程和目標碼等內容,仍有不少困惑。
本文在簡述DO-178B/C標準對軟硬件集成過程輸出驗證的要求的基礎上,指出了該活動與目標碼覆蓋分析(OCA)活動的差別,并以GCC編譯器為例,結合國產機載嵌入式操作系統TM2,提出了一個軟硬件集成過程輸出驗證的詳細解決方案。
根據適航要求,機載軟件作為高安全高可靠軟件,軟件開發過程應該確保源代碼被正確編譯和鏈接、沒有引入錯誤[3];從驗證角度講,就是要對軟硬件集成過程的輸出進行評審和分析。DO-178B/C的6.3.5節提出應該檢查下列缺陷:
(1)編譯器告警[2];
(2)硬件地址錯誤[1,2];
(3)軟件部件遺漏[1,2];
(4)內存重疊[1,2];
考慮到目前軟件開發過程一般會采用集成開發環境(IDE),因此,驗證人員應該關注IDE提供的軟件編譯、鏈接和加載過程,要評審和分析編譯器、鏈接器和加載器等的參數和默認配置。
由于對軟硬件集成過程輸出的評審和分析活動與OCA活動都涉及目標碼,導致兩者容易混淆,實際上兩者差異很大。
軟硬件集成過程輸出的評審和分析活動的目標是確保該過程是完整的和正確的;活動針對 A、B和C級軟件,關心編譯器、鏈接器和加載器等的使用情況;對目標碼來說,該活動考慮的是目標碼文件中的各種符號(全局變量、函數等),并不考慮編譯過程中生成的無法追溯到源代碼的目標碼;該活動的具體細節在后文討論。
OCA活動的目標是確保編譯生成的目標碼序列的正確性;當軟件是 A級、軟件結構覆蓋分析(SCA)在源代碼級進行、且編譯過程生成了無法追溯到源代碼的目標碼時,才需要進行OCA;該活動先識別出哪些目標碼不能追溯到源代碼,然后再通過附加驗證來確認這些額外的目標碼能夠正確地運行、并且不會引入異常行為[4]。
下面先簡要介紹機載軟件開發常用的交叉編譯工具 GCC,再結合國產機載嵌入式操作系統 TM2(它使用GCC 3.4.4工具鏈),詳細討論軟硬件集成過程輸出的評審和分析活動的7個子活動。
GCC是一套面向嵌入式領域的交叉編譯工具,支持多種編程語言、多種優化選項、多種調試信息格式、多種反匯編方式、以及分步編譯,支持X86、ARM7、StrongARM、PPC4XX、MPC8XX、MIPS R3000等多種CPU[5]。
GCC編譯程序的基本過程見圖1所示。首先,core-gcc根據輸入文件的后綴來確定文件的類型,然后根據用戶的編譯選項(包括優化選項、調試信息選項等)將其編譯成相應的匯編臨時文件(后綴為.s);接著,dcore-as將該匯編文件編譯成目標文件(后綴為.o);最后,dcore-ld根據用戶的鏈接選項(包括指定鏈接命令文件等)將目標文件和各種庫鏈接起來生成可執行文件[5]。

圖1 GCC 編譯鏈接的基本過程Fig.1 Basic process of the GCC compilation and link
從圖1可以看出,在源代碼到可執行碼的轉換過程中,交叉編譯和匯編、交叉鏈接等步驟均可能引入錯誤[3]。但是,由于編譯鏈接工具本身的復雜性,目前對它的完全驗證還存在非常困難的技術問題[6];因此工程項目中通常不對編譯鏈接工具做正確性假設,而是通過檢查編譯鏈接活動的輸入與輸出來確保該過程的完整性和正確性;對加載可執行碼到目標機這個步驟的驗證思路也是如此。換句話說,對軟硬件集成過程輸出的驗證可以通過評審和分析軟件編譯信息、代碼源文件、工程文件(MakeFile)、內存配置文件、目標文件、可執行文件等來進行。在這個活動中,主要應該檢查下列7類缺陷,其中,前4類對應DO-178B/C適航標準,后3類是對前4類的補充,對后3類的要求大多來自于項目經驗。
編譯器輸出的告警信息代表軟件源代碼在編譯過程中出現了故障[3]。檢查告警信息分下面兩步。
第一步:設置編譯器選項,輸出所有告警信息,方法是:在 TM2開發環境中,選擇“產生所有警告信息(-Wall)”;
第二步:重新構建 OS項目,人工分析所有構建輸出信息,確認編譯過程中不存在編譯錯誤和告警信息;如果有告警信息,開發方應提供解釋,驗證方需判斷解釋是否合理;最好由開發方修改源代碼以消除告警信息。
開發方提供硬件設備地址,驗證方檢查源代碼中的硬件地址是否與開發方提供的一致;不一致的,開發方需要改成一致。
軟件部件的評審和分析用于確認各個源文件(*.c、*.h)不多不少的被編譯成目標文件(*.o),又不多不少地被鏈接到可執行文件(*.elf、*.bin)中,主要檢查下列內容:
(1)正確的源文件是否被包含在工程文件(MakeFile)中;
(2)源代碼文件之間的依賴關系是否正確;
(3)所有源文件是否都生成目標文件;
(4)所有目標文件是否都連接到可執行文件中。
第1項檢查方法:根據開發方提供的源文件列表,在 TM2開發環境中查看工程文件(例如:makefile、source.mk、v_makefile.mk等),檢查源文件是否已經不多不少地包含在這些工程文件中。
第2項檢查方法:在TM2開發環境的make/src目錄中,查看subdir.mk文件,該文件中有進行編譯的每個源文件對應的目錄文件(*.d)和目標文件(*.o)的列表;根據 subdir.mk文件,依次查看每個.d文件,一個.d文件中列出了某個源文件編譯時編譯器使用的所有依賴文件;根據這個依賴文件列表,結合開發方提供的文件調用關系樹和源文件中的#include語句,檢查該源文件的依賴關系是否正確。
需要說明的是:
· *.d文件中的依賴文件列表有前后順序,這是由C語言本身的要求決定的;
· 編譯器自己使用的頭文件,在源文件中會用#include語句列出,但在*.d文件中不會列出;
· 根據開發方提供的文件調用關系樹,可以在代碼審查時先檢查一下文件依賴關系是否正確;
· 檢查依賴關系時,依次查看源文件中的每個#include語句,并查看每個被包含文件內再次包含的文件,以此類推,類似于深度優先算法,這樣就可以從源文件中的各個#include語句得到該源文件的完整的依賴文件列表;把這個列表和開發方提供的文件調用關系樹、以及*.d文件中的依賴文件列表進行比較,三者應該一致。
第3項檢查方法:根據開發方提供的源文件列表,檢查源文件與目標文件是否一一對應。
第4項檢查方法:在TM2開發環境中,查看工程文件 makefile,查找以“dcore-ld”開頭的鏈接器命令。一個鏈接器命令后面包括有鏈接了的*.o文件,多個鏈接器命令之間沒有直接關聯,鏈接器工作時順序執行這些命令。檢查所有鏈接器命令,列出命令后面包括的所有*.o文件,這些*.o文件與第3項得到的目標文件列表應該一致。
4.5.1 段間重疊檢查
檢查開發方提供的內存分配表和編譯器實際需要的各段的大小,要求分配的內存應大于或等于實際需要的內存,以此驗證內存各個段之間是否重疊。
在 TM2開發環境中,查看配置項目下的configRecord.xml文件,里面有為項目“.text”、“.rodata”、“.data”和“.bss”等段分配的內存空間的大小,這個分配應該和開發方提供的內存分配表一致;項目目錄下的catlinkcmds文件里面是編譯器實際需要的各段的大小;檢查這兩個文件中各段的空間大小,驗證各段之間是否重疊。
4.5.2 段內重疊檢查
可執行文件中的地址,即鏈接后產生的地址,是加載到目標機的地址(絕對地址)。驗證方通過反匯編的方法,列出可執行文件中各個符號(全局變量、函數等)的地址,檢查一個段內各個符號的地址之間是否重疊。
TM2開發環境在鏈接后可以生成兩種可執行文件:*.elf和*.bin。*.elf可以解析成文本文件,*.bin則只能是二進制文件;兩者只是格式差異,內容是一致的。驗證方可以從*.elf中抽出每個符號的一些信息,命令是:dcore-nm -s os.elf > osMap.txt;該命令從 os.elf文件中抽出數據并存到 osMap.txt文件中,其中的信息包括:符號名、類型、占用空間的大小(size)、起始地址、符號所在文件和行號等,見圖2所示。驗證方檢查這些數據,判斷一個段內各個符號的地址之間是否重疊。
需要說明的是:在TM2開發環境make目錄下的map.txt文件(內存映像文件)中有更多有關符號的信息。
驗證方檢查源代碼中是否存在內存動態分配(例如malloc()語句)。如果沒有內存動態分配,內存重疊的可能性較小;如果有內存動態分配,應檢查是否對動態分配空間的大小進行了約束,或者釋放了前面分配的內存空間(例如 free()語句);如果即沒有約束,也沒有釋放,則可能在軟件運行過程中出現內存重疊,這是不安全的。
需要說明的是:本項活動可以在代碼審查時進行。
開發方應該明確本項目的編譯鏈接選項,驗證方檢查開發環境中的編譯鏈接選項與開發方提供的是否一致;不一致的,開發方需要改成一致。
需要說明的是:
· 本項活動可以作為對軟硬件集成過程輸出的評審和分析活動的第一項子活動;
· 目前,還沒有針對某一個編譯器鏈接器各個選項在特定情況下是否適用的規范性或指導性文件,因此,這一步主要依據是開發方提供的編譯鏈接選項,只要開發環境中的編譯鏈接選項與其一致即可。
加載過程檢查可用于確認可執行文件被正確地加載到目標機中的正確位置,主要檢查下列內容:
(1)加載器對加載的數據是否進行了有效性檢查(例如CRC校驗);
(2)可執行文件是否被加載到目標機的正確位置;
(3)無效的軟件是否沒有被加載到目標機;
(4)數據加載過程是否沒有出現錯誤;
(5)數據加載后軟件是否正常運行;
(6)加載的軟件版本是否正確。
第1項檢查方法:驗證方了解加載過程,再判斷是否有安全保證。TM2開發環境的加載過程是把可執行文件分解成若干包,然后與目標機上的常駐代理軟件通信,一包一包地傳到目標機上的特定地址;在此過程中,使用CRC校驗保證通信正確。
需要說明的是:對于其他加載器,需要開發方或加載器提供方先說明加載器如何加載,以及加載時如何進行有效性檢查,驗證方再進行分析檢查。
第2項檢查方法:重新加載一次可執行文件,檢查可執行文件的加載起始地址是否與開發方給出的起始地址一致。
第3項檢查方法:在重新加載過程中,檢查加載了的可執行文件是否與開發方給出的需要加載的文件一致。
第4項檢查方法:在重新加載過程中,檢查數據加載過程是否正常結束,沒有出現錯誤。
第5項檢查方法:加載完畢后,啟動目標機,檢查軟件是否正常啟動和運行。TM2加載到目標機、目標機啟動后,能夠傳回版本信息和知識產權信息,并在宿主機端完整顯示出來,就可以認為軟件已經正常啟動和運行。
需要說明的是,本項活動不需要運行測試用例,只要目標機上的軟件能夠正常啟動,進入初始狀態即可。
第6項檢查方法:啟動目標機,檢查軟件顯示的版本信息是否與開發方提供的被測件版本信息一致。
如果上述6項檢查均通過,表明軟件可執行碼加載到目標機過程是正確的;如果有某項檢查沒有通過,驗證方應會同開發方再次驗證。
表1把上面7個子活動中開發方和驗證方的工作進行了小結。

表1 開發方需要提供的信息和驗證方檢查的缺陷對應表Tab.1 Information afforded by developer anddefects checked by verifier
在機載嵌入式設備開發過程中,為了滿足 DO-178B/C適航標準提出的、驗證軟硬件集成過程輸出的要求,本文詳細討論了對編譯器告警、硬件地址錯誤、軟件部件遺漏、內存重疊、內存動態分配失控、編譯鏈接選項錯誤、加載過程錯誤等7類缺陷的檢查方法和注意事項。文中提到了國產機載嵌入式操作系統TM2,只是為了說明方便;實際上,只要使用了GCC交叉編譯工具,不同項目在編譯器、鏈接器和加載器的使用方面差別不大,均可參考本文討論的方法進行軟硬件集成過程輸出的驗證活動。
[1] RTCA/DO-178B Software Considerations in Airborne Systems and Equipment Certification[S]. 1992.
[2] RTCA/DO-178C Software Considerations in Airborne Systems and Equipment Certification[S]. 2011.
[3] 宮偉祥, 趙婳. 民用機載軟件集成過程的技術研究[J]. 計算機系統應用, 2016, 25(7).
[4] 童岳威, 劉建方. 民用飛機A級別機載軟件項目源代碼到目標碼追溯性分析研究[J]. 科技視界, 2016, 20.
[5] GNU工具用戶手冊[Z].
[6] 任建國. 適航認證中的目標碼覆蓋率分析工具VerOCode[J].航空制造技術, 2012, 22.