0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ARMv7-A那些事-棧回溯淺析

嵌入式那些事 ? 來源:嵌入式那些事 ? 2023-11-21 15:51 ? 次閱讀

嵌入式開發(fā)過程中,經(jīng)常需要對(duì)代碼進(jìn)行調(diào)試來解決各種各樣的問題,常用的調(diào)試手段有:

(1)、開發(fā)環(huán)境搭配硬件仿真器進(jìn)行在線調(diào)試。優(yōu)點(diǎn):調(diào)試過程中能夠清楚的知道各個(gè)寄存器的值以及各個(gè)變量的值,程序的執(zhí)行流程也能夠一目了然。缺點(diǎn):板卡需要引出硬件仿真器的連接口,并且需要購買硬件仿真器。

(2)、通過調(diào)試串口打印信息梳理程序的執(zhí)行流程,結(jié)合代碼分析問題產(chǎn)生的原因。優(yōu)點(diǎn):足夠簡單,通過增加較多的打印信息來分析問題出現(xiàn)的位置,再結(jié)合代碼分析問題產(chǎn)生的原因。缺點(diǎn):沒法準(zhǔn)確的定位問題產(chǎn)生的位置和原因。

(3)、在應(yīng)用或者操作系統(tǒng)死機(jī)的時(shí)候,根據(jù)操作系統(tǒng)輸出的異常棧信息進(jìn)行分析,再結(jié)合鏡像或者應(yīng)用的反匯編代碼進(jìn)行定位。通常這種方法和方法(2)結(jié)合使用。

本文主要簡單的講講?;厮荩瑢?duì)于以后去理解操作系統(tǒng)的異常棧處理打個(gè)基礎(chǔ)吧。

ARM處理器的棧回溯主要有兩種方式:一種是基于棧幀寄存器(FP)的?;厮?,另一種是unwind形式的?;厮?。本文主要講講基于棧幀寄存器(FP)的?;厮荨?/p>

?;厮菹嚓P(guān)寄存器

在棧回溯過程中,主要涉及如下寄存器:

R15:又叫程序計(jì)數(shù)器(Program Counter)PC,PC主要用于存放CPU取指的地址。

R14:又叫鏈接寄存器(Link register)LR,LR主要用于存放函數(shù)的返回地址,即當(dāng)函數(shù)返回時(shí),知道自己該回到哪兒去繼續(xù)運(yùn)行。

R13:又叫堆棧指針寄存器(Stack pointer)SP,SP通常用于保存堆棧地址,在使用入棧和出棧指令時(shí),SP中的堆棧地址會(huì)自動(dòng)的更新。

R12:又叫內(nèi)部過程調(diào)用暫存寄存器(Intra-Procedure-call scratch register)IP,主要用于暫存SP。

R11:又叫幀指針寄存器(Frame pointer)FP,通常指向一個(gè)函數(shù)的棧幀底部,表示一個(gè)函數(shù)棧的開始位置。

ARM棧幀結(jié)構(gòu)

依據(jù)AAPCS (ARM Archtecture Procedure Call Standard)規(guī)范,當(dāng)調(diào)用子函數(shù)時(shí),子函數(shù)一開始的代碼總是會(huì)執(zhí)行壓棧操作來保留父函數(shù)的相關(guān)信息,壓棧步驟示例如下所示:

movip,sp
push{fp,ip,lr,pc}
subfp,ip,#4
subsp,sp,#16
...

每個(gè)函數(shù)都有自己的棧空間,這一部分稱為棧幀。棧幀在函數(shù)被調(diào)用的時(shí)候創(chuàng)建,在函數(shù)返回后銷毀。每個(gè)函數(shù)的棧幀是由SP寄存器和FP寄存器來界定的,ARM棧幀結(jié)構(gòu)典型示意圖如下所示:

6422d2b4-8842-11ee-939d-92fbcf53809c.png

ARMv7-A架構(gòu)-ARM棧幀結(jié)構(gòu)

上圖描述的棧幀,main函數(shù)和func1函數(shù)的示意代碼如下:

intfunc1(intp1,intp2,intp3,intp4,intp5)
{
inti;
intj;

i=0xf3;
j=0xf6;

return0;
}

intmain(intargc,char*argv[])
{
inti;
intj;

i=0x33;
j=0x66;
func1(0xa1,0xa2,0xa3,0xa4,0xa5);

return0;
}

每個(gè)函數(shù)的棧幀中都會(huì)保存調(diào)用該函數(shù)之前的PC、LR、SP、FP寄存器的值;如果函數(shù)具有參數(shù)并且函數(shù)內(nèi)部使用了局部變量,那么函數(shù)棧幀中也會(huì)保存函數(shù)的參數(shù)和局部變量;如果被調(diào)用的子函數(shù)參數(shù)過多,那么多余的參數(shù)會(huì)通過父函數(shù)的棧進(jìn)行傳遞。比如func1函數(shù)的參數(shù)p5通過main函數(shù)的棧幀進(jìn)行傳遞的。(注:編譯器的版本不同,函數(shù)棧幀中參數(shù)和局部變量的壓棧順序可能不同,PC,LR,SP和FP這4個(gè)寄存器的壓棧順序一般是固定的)

函數(shù)棧幀中的PC和LR均指向代碼段,PC表示執(zhí)行入棧指令時(shí)CPU正在取指的地址,LR表示當(dāng)前函數(shù)返回后繼續(xù)執(zhí)行的地址。

?;厮菰?/p>

在?;厮莸倪^程中,我們主要利用FP寄存器進(jìn)行?;厮荨Mㄟ^FP就可以知道當(dāng)前函數(shù)的棧底,從而可以找到存儲(chǔ)在棧幀中的LR寄存器的數(shù)據(jù),這個(gè)數(shù)據(jù)就是函數(shù)的返回地址。同時(shí)也可以找到保存在函數(shù)棧幀中的上一級(jí)函數(shù)FP的數(shù)據(jù),這個(gè)數(shù)據(jù)指向了上一級(jí)函數(shù)的棧底,按照同樣的方法可以找出上一級(jí)函數(shù)棧幀中存儲(chǔ)的LR和FP數(shù)據(jù),就知道哪個(gè)函數(shù)調(diào)用了上一級(jí)函數(shù)以及這個(gè)函數(shù)的棧底地址。這就是?;厮莸牧鞒蹋麄€(gè)流程以FP為核心,依次找出每個(gè)函數(shù)棧幀中存儲(chǔ)的LR和FP數(shù)據(jù),計(jì)算出函數(shù)返回地址和上一級(jí)函數(shù)棧底地址,從而找出每一級(jí)函數(shù)調(diào)用關(guān)系。

?;厮菥幾g選項(xiàng)

當(dāng)gcc的編譯選項(xiàng)帶有-mapcs-frame時(shí),編譯出來的代碼能夠?qū)C,LR,SP和FP寄存器的值壓入函數(shù)的棧幀中。默認(rèn)情況下gcc的編譯選項(xiàng)為-mno-apcs-frame ,此時(shí)編譯出來的代碼不一定會(huì)將PC,LR,SP和FP這四個(gè)寄存器的值壓入函數(shù)的棧幀中,可能只會(huì)將LR和FP寄存器的值壓入函數(shù)的棧幀中。關(guān)于-mapcs-frame選項(xiàng),gcc的手冊(cè)描述如下:

Generateastackframethatiscompliant
withtheARMProcedureCallStandardfor
allfunctions,evenifthisisnotstrictly
necessaryforcorrectexecutionofthecode.
Specifying‘-fomit-frame-pointer’withthis
optioncausesthestackframesnottobe
generatedforleaffunctions.Thedefault
is‘-mno-apcs-frame’.
Thisoptionisdeprecated.

我這里使用的gcc信息如下:

$arm-none-eabi-gcc-v
...
gccversion10.3.120210824(release)(GNUArmEmbeddedToolchain10.3-2021.10)

雖然gcc手冊(cè)上說-mapcs-frame選項(xiàng)被廢棄了,但是只有添加了該選項(xiàng),編譯出來的代碼才會(huì)將PC,LR,SP和FP寄存器的值壓入函數(shù)的棧幀中。

我這里編譯代碼仍然使用-mapcs-frame選項(xiàng),有知道該選項(xiàng)對(duì)應(yīng)的新的棧幀配置選項(xiàng)的兄弟可以告知我一下。

棧回溯示例

根據(jù)前面的內(nèi)容,這里簡單的寫了一個(gè)?;厮莸氖纠?,函數(shù)調(diào)用流程為:main -> test_a -> test_b -> test_c。

函數(shù)的源代碼如下:

inttest_a(intarg0,intarg1,intarg2,intarg3,intarg4)
{
inta;

a=0xff11;

test_b(0xbb00);

returna;
}

inttest_b(intarg0)
{
intb;

b=0xff22;

test_c(0xcc00);

returnb;
}

inttest_c(intarg0)
{
intc;

c=0xff33;

returnc;
}

intmain(void)
{
intval;

val=0xff00;

test_a(0xaa00,0xaa11,0xaa22,0xaa33,0xaa44);

return0;
}

上述函數(shù)的反匯編內(nèi)容如下:

80002164:

inttest_a(intarg0,intarg1,intarg2,intarg3,intarg4)
{
80002164:e1a0c00dmovip,sp
80002168:e92dd800push{fp,ip,lr,pc}
8000216c:e24cb004subfp,ip,#4
80002170:e24dd018subsp,sp,#24
80002174:e50b0018strr0,[fp,#-24];0xffffffe8
80002178:e50b101cstrr1,[fp,#-28];0xffffffe4
8000217c:e50b2020strr2,[fp,#-32];0xffffffe0
80002180:e50b3024strr3,[fp,#-36];0xffffffdc
inta;

a=0xff11;
80002184:e30f3f11movwr3,#65297;0xff11
80002188:e50b3010strr3,[fp,#-16]

test_b(0xbb00);
8000218c:e3a00cbbmovr0,#47872;0xbb00
80002190:eb000003bl800021a4

returna;
80002194:e51b3010ldrr3,[fp,#-16]
}
80002198:e1a00003movr0,r3
8000219c:e24bd00csubsp,fp,#12
800021a0:e89da800ldmsp,{fp,sp,pc}

800021a4:

inttest_b(intarg0)
{
800021a4:e1a0c00dmovip,sp
800021a8:e92dd800push{fp,ip,lr,pc}
800021ac:e24cb004subfp,ip,#4
800021b0:e24dd010subsp,sp,#16
800021b4:e50b0018strr0,[fp,#-24];0xffffffe8
intb;

b=0xff22;
800021b8:e30f3f22movwr3,#65314;0xff22
800021bc:e50b3010strr3,[fp,#-16]

test_c(0xcc00);
800021c0:e3a00b33movr0,#52224;0xcc00
800021c4:eb000003bl800021d8

returnb;
800021c8:e51b3010ldrr3,[fp,#-16]
}
800021cc:e1a00003movr0,r3
800021d0:e24bd00csubsp,fp,#12
800021d4:e89da800ldmsp,{fp,sp,pc}

800021d8:

inttest_c(intarg0)
{
800021d8:e1a0c00dmovip,sp
800021dc:e92dd800push{fp,ip,lr,pc}
800021e0:e24cb004subfp,ip,#4
800021e4:e24dd010subsp,sp,#16
800021e8:e50b0018strr0,[fp,#-24];0xffffffe8
intc;

c=0xff33;
800021ec:e30f3f33movwr3,#65331;0xff33
800021f0:e50b3010strr3,[fp,#-16]

returnc;
800021f4:e51b3010ldrr3,[fp,#-16]
}
800021f8:e1a00003movr0,r3
800021fc:e24bd00csubsp,fp,#12
80002200:e89da800ldmsp,{fp,sp,pc}

80002204
: intmain(void) { 80002204:e1a0c00dmovip,sp 80002208:e92dd800push{fp,ip,lr,pc} 8000220c:e24cb004subfp,ip,#4 80002210:e24dd010subsp,sp,#16 intval; val=0xff00; 80002214:e3a03cffmovr3,#65280;0xff00 80002218:e50b3010strr3,[fp,#-16] test_a(0xaa00,0xaa11,0xaa22,0xaa33,0xaa44); 8000221c:e30a3a44movwr3,#43588;0xaa44 80002220:e58d3000strr3,[sp] 80002224:e30a3a33movwr3,#43571;0xaa33 80002228:e30a2a22movwr2,#43554;0xaa22 8000222c:e30a1a11movwr1,#43537;0xaa11 80002230:e3a00caamovr0,#43520;0xaa00 80002234:ebffffcabl80002164 return0; 80002238:e3a03000movr3,#0 } 8000223c:e1a00003movr0,r3 80002240:e24bd00csubsp,fp,#12 80002244:e89da800ldmsp,{fp,sp,pc}

當(dāng)程序運(yùn)行到test_c()函數(shù)的return c;代碼處時(shí),F(xiàn)P的值為0x9FDFFF94,此時(shí)內(nèi)存數(shù)據(jù)如下:

6441f4be-8842-11ee-939d-92fbcf53809c.png

Snipaste_2023-08-30_15-56-26

test_c()函數(shù)的棧底為0x9FDFFF94,可以得到test_c()函數(shù)棧幀中LR為0x800021C8、FP為0x9FDFFFB4,LR是test_c()函數(shù)執(zhí)行完成后的返回地址,與反匯編代碼中test_b()函數(shù)調(diào)用完test_c()之后的下一個(gè)執(zhí)行地址一致:

800021c0:e3a00b33movr0,#52224;0xcc00
800021c4:eb000003bl800021d8

returnb;
800021c8:e51b3010ldrr3,[fp,#-16]//test_c()函數(shù)返回后繼續(xù)執(zhí)行的地址

FP為0x9FDFFFB4表示test_b()函數(shù)的棧底為0x9FDFFFB4,有了test_b()函數(shù)的棧底就可以得到test_b()函數(shù)棧幀中LR為0x80002194、FP為0x9FDFFFDC,從而知道test_b()函數(shù)執(zhí)行完成后的返回地址以及test_a()函數(shù)的棧底,依次逐級(jí)回溯,就可以知道程序的整個(gè)運(yùn)行流程了。

在?;厮莸倪^程中我們可以利用addr2line工具輔助我們對(duì)程序執(zhí)行流程的分析。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5359

    瀏覽量

    120818
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2259

    瀏覽量

    94817
  • ARM處理器
    +關(guān)注

    關(guān)注

    6

    文章

    361

    瀏覽量

    41834

原文標(biāo)題:ARMv7-A 那些事 - 7.棧回溯淺析

文章出處:【微信號(hào):嵌入式那些事,微信公眾號(hào):嵌入式那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    armv7 generic timer使用筆記

    armv7-A架構(gòu)中每個(gè)CPU核心都包含自己的私有定時(shí)器,所有cpu的定時(shí)器共享一個(gè)System counter, System counter負(fù)責(zé)產(chǎn)生計(jì)數(shù),傳遞到每個(gè)核心的私有定時(shí)器
    的頭像 發(fā)表于 09-27 15:10 ?2749次閱讀
    <b class='flag-5'>armv7</b> generic timer使用筆記

    ARMv7-A架構(gòu)的實(shí)現(xiàn)中SIMD和VFP的擴(kuò)展實(shí)現(xiàn)是可選的嗎?如何了解某控制器是否支持這些實(shí)現(xiàn)?

    ,如果更換其他ARMv7-A實(shí)現(xiàn)的內(nèi)核(如Cortex A8),如何確定該實(shí)現(xiàn)是否支持?第一次提問,如果表述不清楚請(qǐng)見諒!謝謝!
    發(fā)表于 07-01 16:33

    ARMv8-A AArch32主要特性

    Cortex-A32產(chǎn)品介紹ARMv8-A AArch32主要特性ARMv7-M與AArch32的不同之處軟件從ARMv7-M移植到ARMv7-A
    發(fā)表于 02-19 06:20

    淺析ARMv7-A體系架構(gòu)下的MMU的基本原理

    。MMU 主要功能之一是虛擬地址到物理地址的轉(zhuǎn)換,這個(gè)需要軟件和硬件配合完成,軟件需要針對(duì)不同的硬件進(jìn)行策略。這里主要分析 ARMv7-A 體系架構(gòu)下的 MMU 的基本原理。VMSA 是針對(duì)
    發(fā)表于 05-24 16:54

    如何在Armv7-A系列芯片上根據(jù)錯(cuò)誤調(diào)用來debug呢

    本周解決了兩個(gè)在 Armv7-A 系列芯片上出現(xiàn)的錯(cuò)誤,包括一個(gè)由于軟浮點(diǎn)配置導(dǎo)致的未定義指令錯(cuò)誤以及一個(gè)由于數(shù)據(jù)未對(duì)齊導(dǎo)致的 data abort 錯(cuò)誤,通過解決這兩個(gè)問題,學(xué)會(huì)了如何在 A 系列
    發(fā)表于 06-13 17:42

    Armv8-A構(gòu)架中Armv8.6-A引進(jìn)的最新功能介紹

    :細(xì)化的trap從Armv7-A開始引入虛擬化以來,arm持續(xù)改進(jìn)虛擬化的支持。一個(gè)虛擬化關(guān)鍵的支持是trap虛擬機(jī)執(zhí)行的一些操作。目的是為了虛擬化這些操作或是讓hypervisor充當(dāng)Guest
    發(fā)表于 07-29 15:29

    ARM體系結(jié)構(gòu)參考手冊(cè)ARMv7-AARMv7-R版本

    提前(AOT)編譯的特定支持。 ·決定處理器如何運(yùn)行的模式和狀態(tài),包括當(dāng)前的執(zhí)行特權(quán)和安全性。 ·例外模式。 ·內(nèi)存模型,定義內(nèi)存排序和內(nèi)存管理: -ARMv7-A架構(gòu)配置文件定義虛擬內(nèi)存系統(tǒng)架構(gòu)
    發(fā)表于 08-12 07:46

    ARM Cortex-A系列ARMv8-A程序員指南

    。 GNU和Linux文檔(Redhat和Fedora發(fā)行版除外)有時(shí)將AArch64稱為ARM64。 因?yàn)?b class='flag-5'>ARMv8-A體系結(jié)構(gòu)的許多概念都與ARMv7-A體系結(jié)構(gòu)相同,所以這里不涉及所有這些概念的細(xì)節(jié)
    發(fā)表于 08-22 07:22

    在基于ARMv7的平臺(tái)1.0版上使用CSAT進(jìn)行低級(jí)調(diào)試

    Armv7-A 平臺(tái)上執(zhí)行某些調(diào)試操作。 側(cè)重于平臺(tái)推移硅、 FPGA 和硬件模擬環(huán)境的用戶如果想要測試其調(diào)試設(shè)計(jì)的某些方面, 可能會(huì)發(fā)現(xiàn)此調(diào)試操作是有用的 。 此教程覆蓋的調(diào)試操作包括: ? 使用調(diào)試
    發(fā)表于 08-28 06:50

    如何將軟件應(yīng)用程序從ARMv5遷移到ARMv7-A/R

    5。 本應(yīng)用筆記還假設(shè)您具有ARMv5的軟件開發(fā)經(jīng)驗(yàn)。 假設(shè)主目標(biāo)平臺(tái)是圍繞ARMv7-A處理器構(gòu)建的。 由于ARMv7-AARMv7-R有許多重疊的區(qū)域,本文檔的一部分也適用于
    發(fā)表于 08-29 06:51

    一文詳解Linux內(nèi)核的回溯與妙用

    網(wǎng)上或多或少都能找到回溯的一些文章,但是講的都并不完整,沒有將內(nèi)核回溯的功能用于實(shí)際的內(nèi)核、應(yīng)用程序調(diào)試,這是本篇文章的核心:盡可能引導(dǎo)讀者將
    的頭像 發(fā)表于 10-05 10:02 ?5420次閱讀
    一文詳解Linux內(nèi)核的<b class='flag-5'>棧</b><b class='flag-5'>回溯</b>與妙用

    ARMv7ARMv7的體系結(jié)構(gòu)參考手冊(cè)免費(fèi)下載

    ARM? Architecture Reference Manual ARMv7-A and ARMv7-R edition
    發(fā)表于 09-28 08:00 ?27次下載
    <b class='flag-5'>ARMv7</b>和<b class='flag-5'>ARMv7</b>的體系結(jié)構(gòu)參考手冊(cè)免費(fèi)下載

    Cortex A7 MPCore的技術(shù)參考手冊(cè)

    Cortex-A7 MPCore處理器是實(shí)現(xiàn)ARMv7-a架構(gòu)的高性能、低功耗處理器。Cortex-A7 MPCore處理器在一個(gè)多處理器設(shè)備中有一到四個(gè)處理器,一個(gè)一級(jí)緩存子系統(tǒng),一個(gè)可選的集成GIC和一個(gè)可選的二級(jí)緩存控制器
    發(fā)表于 12-09 08:00 ?9次下載
    Cortex <b class='flag-5'>A7</b> MPCore的技術(shù)參考手冊(cè)

    ARMv7-A工作模式介紹

    級(jí)(非安全模式)的應(yīng)用就不能訪問高等級(jí)(安全模式)的資源,以此來保證敏感資源的安全性。 ARMv7-A 工作模式 以前的 A
    的頭像 發(fā)表于 09-11 16:31 ?1010次閱讀
    <b class='flag-5'>ARMv7-A</b>工作模式介紹

    RVBacktrace RISC-V極簡回溯組件

    RVBacktrace組件簡介一個(gè)極簡的RISC-V回溯組件。功能在需要的地方調(diào)用組件提供的唯一API,開始當(dāng)前環(huán)境的回溯支持輸出addr2line需要的命令,使用addr2lin
    的頭像 發(fā)表于 09-15 08:12 ?428次閱讀
    RVBacktrace RISC-V極簡<b class='flag-5'>棧</b><b class='flag-5'>回溯</b>組件