1、背景介紹
本文着重從問題定位的角度來介紹如何定位嵌入式軟體系統中的問題,并結合AliOS Things提供的部分維測手段來介紹。
AliOS Things目前已經提供了豐富的Debug維測功能、離線的腳本解析,以及可視化的IDE工具來讓使用者快速上手使用我們的維測功能;相關的維測功能和工具也在實際開發項目中不斷完善。
完善的維測工具一直是AliOS Things的主要發展的方向;但是從另一個方面來說,工具本身的作用也是有限的,嵌入式系統出現的問題可以說是千變萬化;不同的開發者,對于工具提供的資訊了解也不盡相同。我們在熟悉使用基本的維測工具的同時,還需要了解底層問題定位的基礎知識,基本的定位手段和思路。解Bug的技術思路和維測工具的完善應該是相輔相成,互相促進的。
本文介紹了部分常用的嵌入式問題定位的基礎知識和基本的手段。其中涉及到的AliOS Things核心一些已有的工具,以及一些内容的拓展和深入,讀者可以自行了解和學習。
結合AliOS Things談嵌入式系統通用問題定位方法(2):核心相關基礎 結合AliOS Things談嵌入式系統通用問題定位方法(3):問題定位思路1.1、對象
嵌入式核心和驅動開發測試人員
協定棧、中間件開發測試人員
上層APP元件開發測試人員
1.2、目的
如何從底層軟體源頭上開始解bug
如何快速确認分流大量bug
從問題本身了解規範性代碼如何編寫
1.3、内容
結合AliOS Things核心來介紹:
解bug的必要知識點
解bug的通用方法、步驟、關鍵點
AliOS Things提供定位問題的手段、工具
本文介紹的内容主要以ARMv7架構為例,編譯器為gcc。
2、CPU相關基礎點
(以ARMv7a為例)
2.1、基礎架構
!
Monitor:安全非安全
Hypervisor: 虛拟化
PL1:特權模式
PL0:使用者模式
核心的核心态對應CPU的特權模式;
核心的使用者态對應CPU的使用者模式。
對于PL1,有7種子模式,我們主要關注:
SVC:核心正常運作模式
IRQ:中斷進入模式
UND: 指令異常
ABT:資料異常
CPU目前狀态可通過CPSR尾部的Mode狀态寄存器來擷取:
- .equ CPSR_Mode_USR, 0x10
- .equ CPSR_Mode_FIQ, 0x11
- .equ CPSR_Mode_IRQ, 0x12
- .equ CPSR_Mode_SVC, 0x13
- .equ CPSR_Mode_ABT, 0x17
- .equ CPSR_Mode_UND, 0x1B
- .equ CPSR_Mode_SYS, 0x1F
2.2、基礎寄存器
32bit ARM彙編有16個32位寄存器:
- r0-r3 主要用來傳遞函數調用第1到第4個參數(a0-a3),更多的參數須通過棧來傳遞。
- r0-r1也作為結果寄存器,儲存函數傳回結果;被調用的子程式在傳回前無須恢複這些寄存器的内容。
- r4-r9 為被調儲存(callee-save)寄存器,一般儲存内部局部變量(local variables)。
- r7 大部分情況用來儲存系統調用号(syscall number)。
- r9 某些變體可能當作特殊寄存器。
- r10(SL)被調儲存寄存器,Stack Limit。
- r11(FP)被調儲存寄存器, 幀指針(Flame Pointer)。
- r12(IP)特殊寄存器,棧寄存器(Intra Procedure)。
- r13(SP)特殊寄存器,棧指針,類似x86_64中的RSP。
- r14(LR)特殊寄存器。Link Register.
- r15(PC)特殊寄存器。Program Counter (like RIP in x86_64 & EIP in x86)
其中注意幾點:每個模式下SP(R13)獨立,PC隻有一個,LR獨立,SPSR自動儲存着進入該模式前的CPSR。Fiq比較特殊,R8~R14都是獨立的。
2.3、協處理器
ARM體系架構支援協處理器,用于擴充ARM處理器功能。協處理器指令用于通路協處理器。協處理器支援16個協處理器,編号0-15,使用CP0-CP15描述。
- CP15:提供系統控制功能。包括架構和特性ID,以及控制,狀态資訊和配置支援。
- CP14:提供硬體Debug功能。
- CP10,CP11:共同支援浮點運算和向量操作。控制和配置浮點和進階SIMD擴充架構。
- CP8,9,12,13:為ARM架構保留協處理器。
- CP0-7:由廠家定義協處理功能
2.4、基礎指令
ARM提供兩種指令編碼格式:Thumb(2)、ARM指令集
ARM指令集每條指令固定為4 位元組大小;
Thumb(2)指令集每條指令為2或者4位元組大小;
具體細節不在此贅述。
2.5、壓棧出棧相關
壓棧出棧指令可以用下面表格來概括:
其中LDM的字尾對應關系:
FA: full + add 滿增
FD:full + decrease 滿減
EA:empty + add 空增
ED:empty + decrease 空減
IA: increment after 先取值,SP後增加
IB: increment before SP先增加,後取值
DA: decrement after 先取值,SP後減小
DB: decrement before SP先減小,後取值
目前一般使用的是:
LDMIA :彈棧時,先取SP内資料,後SP位址增大
STMDB :壓棧時,SP位址先減小,後開始存放資料
2.6、異常
異常寄存器:
主要關注四個寄存器,來判斷通路哪個位址出現了何種異常:
DFSR:資料異常狀态寄存器
DFAR:導緻資料異常的位址
IFSR:指令異常狀态寄存器
IFAR:導緻指令異常的位址
2.7、MMU/MPU相關
對于MPU和MMU的細節,使用者可不需要關注。但是需要了解基本概念。
MPU針對ARM-M系列,可以設定特殊記憶體段的屬性是特權還是非特權通路,沒有虛拟和實體的映射;
MMU針對ARM-A系列,包含虛拟和實體的映射,以及各種權限的控制。
M系列的使用者态有特權和非特權之分;
A系列的使用者态即對應最小使用者權限模式。
對于通路位址的權限問題,有時需要檢視MMU頁表,去核對屬性。
容易遇到的問題:
- 通路權限問題,如代碼段資料段的權限問題
- 頁表覆寫導緻記憶體空間沖突問題
2.8、函數/資料斷點
函數和資料斷點是CPU提供的基本調式Debug功能。
對應gdb的對函數打斷點,watch監控某一塊記憶體的修改通路,以及進一步的單步調試功能。
2.9、編譯彙編連結相關
2.9.1、需關注編譯選項
Cpu相關:
-mcpu=cortex-a7 :指定cpu架構
-mfpu=neon-vfpv4 :浮點數、neno指令
-mfloat-abi=hard :軟硬浮點
emu相關:
-fshort-enums:
sizeof(enum1)不增加這個-fshort-enums選項的時候為4,增加後為該enum實際可以存放的最小位元組資料類型,如1位元組。
預設不設定,一旦設定就選用節省記憶體的enum長度
編譯告警相關:
-Wall : 編譯顯示所有告警
-Werror: 所有的警告當成錯誤進行處理
-Wfatal-errors:出現錯誤停止編譯
-w : 關閉告警
需要使用gdb檢視詳細的資料結構資訊:
加入-g選項
2.9.2、彙編注意點
函數跳轉指令(B、BL、BLX):
BL 和 BLX 指令可将下一個指令的位址複制到 LR(r14,連結寄存器)中。
BX 和 BLX 指令可将處理器的狀态從 ARM 更改為 Thumb,或從 Thumb 更改為 ARM。
BLX label 無論何種情況,始終會更改處理器的狀态。
BX Rm 和 BLX Rm 可從 Rm 的位 [0] 推算出目标狀态:
如果 Rm 的位 [0] 為 0,則處理器的狀态會更改為(或保持在)ARM 狀态
如果 Rm 的位 [0] 為 1,則處理器的狀态會更改為(或保持在)Thumb 狀态。
反彙編别名:
在檢視彙編時,通用寄存器會有特殊的别名,常用對應如下:
R10 sl 棧限制
R11 fp 桢指針
R12 ip
R13 sp 棧指針
R14 lr 連接配接寄存器
R15 pc 程式計數器
函數入參傳遞:
對于ARM-32位處理器,R0、R1、R2、R3寄存器分别作為函數的第0個至第3個入參,超出入參使用棧空間;
32位函數出參統一使用R0。
調用者報錯和被調用者儲存的寄存器:
被調用函數隻會儲存被自己上下文修改的R4開始的寄存器,R0~R3由調用棧按需儲存。
指令編碼:
每條指令都有固定的編碼格式,否則就是非法指令。動态加載/調用棧解析等會有對指令修改的操作,一般對定位問題有兩個小用途:
- 看開棧大小:主要有沒有定義過大的數組,通過sub sp的指令來搜尋立即數的大小;
e24dd014 sub sp, sp, #20
- 強制修改指令
在代碼段可修改的情況下,強制修改指令,達到某種如進入死循環,強制跳轉的目的
2.9.3、ELF結構相關
ELF内是比較複雜的資料結構,其中分可重定位檔案(.o)、共享目标檔案(.so)、以及可執行檔案(elf)。二進制bin檔案不屬于elf格式,而是沒有elf的頭部和相關符号等資訊,純碎裝載的可執行段的二進制檔案。内部結構比較複雜,隻介紹基本定位問題使用的。
Elf中的section:一般在ld中可見定義的描述,如下面即是一個section
- .data : {
- __data_start = .;
- *(.data)
- *(.data.*)
- __data_end = .;
- } > DRAM_ADDR
Elf中的段:在最後連結時,連結器會将各個section組合成最終的幾個段,當然可以在ld中顯示地關聯段。
Section一般存在于尚未連結的可重定位檔案,最終生成可執行的elf中都是組合的段資訊
基本檢視ELF資訊的編譯器工具:
- 檢視ELF頭部資訊、段組成
arm-none-eabi-readelf -lS ./[email protected]
結果示例:
- Elf轉彙編
localhost: arm-none-eabi-objdump -d ./[email protected]
- 查找符号
- localhost: arm-none-eabi-nm ./[email protected] | grep aos_init
- 080021f8 T aos_init
相關資訊也可通過map檔案,其同樣也是通過elf生成。
- addr2line
- localhost: arm-none-eabi-addr2line -e ./[email protected] 0x80021f8
- /workspace/aos_gerrit_new/core/osal/aos/rhino.c:815
通過指令位址,導出對應C的檔案名和行數
對于C++一般使用 –pfiCe選項
- C++函數名
彙編中C++的函數名非常複雜,可通過下面指令輸入正常的函數名:
- localhost: arm-none-eabi-c++filt [email protected] _ZN17GwMeshControlInfoD1Ev [email protected]
- GwMeshControlInfo::~GwMeshControlInfo()
開發者技術支援
如需更多技術支援,可加入釘釘開發者群,或者關注微信公衆号
更多技術與解決方案介紹,請通路阿裡雲AIoT首頁
https://iot.aliyun.com/