處理器:棧結構

棧存放的是什麼?

棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。描述的是函數的調用關係

 

  一般來說,arm linux中的棧幀有三種結構,取決於在編譯時所使用的編譯選項

 

  1. APCS標準結構

     +——–+

 

     |   PC   |-不是我們一般所說的cpu pc指針(也就是R15寄存器),僅僅是壓棧時候的pc=sp最後的地址。

 

     +——–+

 

     |  LR   |

 

     +——–+

 

     |   SP   |-----》往往是暫存的ip值,不是幀的sp

 

     +——–+

 

     |  FP   |

 

     +——–+

 

     |  …  |

 

     +——–+

 

     如果在編譯中使用了-mapcs選項,那麼函數調用中的棧幀就爲該結構。對應的彙編代碼如下:

         00008458 :

 

             8458:    e1a0c00d    mov    ip, sp

 

             845c:    e92dd800    push   {fp, ip, lr, pc}

 

r0-r3都是可選的,是用於傳遞函數前面四個參數。

r4-r10也是可選的,是編譯器根據具體情況(使用局部變量的數目),來決定使用那個寄存器,就保存那些寄存器的。

上圖對應如下的stack frame layout

/*

 *Stack frame layout:(地址從低到高)

 *            optionally saved caller registers (r4- r10)

 *            saved fp

 *            saved sp

 *            saved lr

 *   frame => saved pc

 *            optionally saved arguments (r0 - r3)

 *saved sp => <next word>

 */

 

例如:AL850KE異常爲例:

kernel棧的stack

APCS的棧數據結構是:FD 滿遞減,所以高地址是棧首(也就是PC在高地址,順序自高到低就是PClr,ip,,,,,rxxx

[  278.554080]-(1)[861:Compiler]Modules linkedin:

[  278.554098]-(1)[861:Compiler]CPU: 1 PID: 861Comm: Compiler Tainted: G        W    3.10.48 #1

[  278.554108]-(1)[861:Compiler]task: de1fa980ti: dd404000 task.ti: dd404000

[  278.554118]-(1)[861:Compiler]PC is at update_curr+0x17c/0x1f0

[  278.554126]-(1)[861:Compiler]LR is at 0x0

[  278.554137]-(1)[861:Compiler]pc : [<c009ff1c>]    lr : [<00000000>]    psr: 80030193

[  278.554137]sp : dd405e58  ip : 0000003f fp : dd405ea4

[  278.554148]-(1)[861:Compiler]r10:155a5456  r9 : dd404018  r8 : de1fa9b8

[  278.554158]-(1)[861:Compiler]r7 :00000000  r6 : 00002241  r5 : 00000001 r4 : 4d77d4d1

[  278.554167]-(1)[861:Compiler]r3 :00000000  r2 : dd404000  r1 : 00000003 r0 : dd405e58

[  278.554178]-(1)[861:Compiler]Flags: Nzcv  IRQs off FIQs on  Mode SVC_32  ISA ARM Segment user

[  278.554187]-(1)[861:Compiler]Control:10c5383d  Table: 5d91c06a  DAC: 00000015

………………...

[861:Compiler]Stack: (0xdd405e58 to 0xdd406000)//從5e58(不把包括5e58)開始dump,也就是 5e59

因爲是32位機器,一個地址4個字節,所以每個地址都是+4,如下:

[861:Compiler]5e40:  5e44      5e48        5e4c       5e50     5e54     5e58         c009cb20 c00a2de8

[861:Compiler]5e60: 00013edf 00000000 00000000 0000003fde989240 de9dd9b8 de9dd9c0 c0cc9b38(R4)

[861:Compiler]5e80: c0ca3cc0(R5) de98f840(R6) c0cf9988(r7) de1fa980(r8) c18c5cc0(r9) de1fa9b8(sl) dd405f1c(fp) dd405ea8(ip)

[861:Compiler]5ea0: c00a3878(lr) c009fdac(pc) dd405ef4 dd405eb8 c0092634c0009278 dd405ed4 00000002

[861:Compiler]5ec0:00000000 dda7a000 dd405ef4 dda7a000 00000002 c0db1510 c0cc9b38 c0ca3cc0

[861:Compiler]5ee0:dd405efc dd405ef0 c08f787c c009872c dd405f1c de1fac8c 00000001 c0cc9b38

[861:Compiler]5f00: dd404020 de1fa980 c18c5cc0 00000047dd405f7c dd405f20 c08f6074(LR) c00a34b4(PC)

[861:Compiler]5f20:15595153 0000003f 00000001 00000001 c0ca3cc0 c0ca3cc0 c0ca3cc0 c0ca3cc0

[861:Compiler]5f40:c0ca3cc0 c0cc9240 c0ca3cc0 c0ca3cc0 c0e02848 dd404010 00000000 dd404000

[861:Compiler]5f60:dd405fb0 00000000 dd404000 00000047 dd405f8c dd405f80 c08f66ec c08f5f10

[861:Compiler]5f80:dd405fac dd405f90 c00124d8 c08f66b8 400f0764 60030010 f0222000 00000000

[861:Compiler]5fa0:00000000 dd405fb0 c000e840 c00124b0 00000001 00000000 0000001f 00000000

[861:Compiler]5fc0:403243ec 00000000 00000000 00000000 00000002 60e1b060 00000047 68a9a270

[861:Compiler]5fe0:40323ef4 60e1afe8 4031cb25 400f0764 60030010 ffffffff 00000000 00000000

[861:Compiler]Backtrace:

[861:Compiler][<c009fda0>] (update_curr+0x0/0x1f0) from[<c00a3878>](put_prev_task_fair+0x3d0/0x5b8)

[861:Compiler][<c00a34a8>](put_prev_task_fair+0x0/0x5b8) from [<c08f6074>](__schedule+0x170/0x7a8)

[861:Compiler][<c08f5f04>](__schedule+0x0/0x7a8) from [<c08f66ec>](schedule+0x40/0x80)

[861:Compiler][<c08f66ac>](schedule+0x0/0x80) from [<c00124d8>] (do_work_pending+0x34/0xac)

[861:Compiler][<c00124a4>](do_work_pending+0x0/0xac) from [<c000e840>] (work_pending+0xc/0x20)

[861:Compiler]r7:00000000 r6:f0222000 r5:60030010 r4:400f0764

下面進行棧回溯:

首先PC is at update_curr+0x17c/0x1f0  找到所在的函數幀:

:(使用標準的APCS),紅色部分代碼是壓棧的彙編指令代碼:

c009fda0<update_curr>:

c009fda0:        e1a0c00d        mov        ip,sp   ---ip=sp

c009fda4:        e92ddff0        push        {r4,r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}這11 個寄存器都保存在棧中

c009fda8:        e24cb004        sub        fp,ip, #4                         --->fp=ip-4

c009fdac:        e24dd024        sub        sp,sp, #36        ; 0x24---sp = sp-36 擴展36個字節=9*4字節

c009fdb0:        e52de004        push        {lr}                ;(str lr, [sp, #-4]!)

c009fdb4:        ebfdbacd        bl        c000e8f0<__gnu_mcount_nc>

c009fdb8:        e5908030        ldr        r8,[r0, #48]        ; 0x30

c009fdbc:        e1a09000        mov        r9,r0

c009fdc0:        e590309c        ldr        r3,[r0, #156]        ; 0x9c

c009fdc4:        e3580000        cmp        r8,#0

c009fdc8:        e593a4b0        ldr        sl,[r3, #1200]        ; 0x4b0

c009fdcc:        e593c4b4        ldr        ip,[r3, #1204]        ; 0x4b4

c009fdd0:        0a000027        beq        c009fe74<update_curr+0xd4>

c009fdd4:        e5980020        ldr        r0,[r8, #32]

c009fdd8:        e05a0000        subs        r0,sl, r0

……

…..出棧指令:

c009ff84:        0affffc3        beq        c009fe98<update_curr+0xf8>

c009ff88:        eb215a85        bl        c08f69a4<preempt_schedule>

c009ff8c:        eaffffc1        b        c009fe98<update_curr+0xf8>

 

因此知道//棧大小=11+9 --20個字,且其dump stack的起始地址是0xdd405e58 ,所以從

[861:Compiler]5e40:  5e44      5e48        5e4c       5e50     5e54     5e58         c009cb20(起始) c00a2de8 ,20個字(32bit

則update_curr函數幀一直到[861:Compiler]5ea0: c00a3878(lr) c009fdac(pc)……...如上黑體部分爲update_curr函數的當前棧幀結構。大小是20個字節。從幀數據中得知 LRc00a3878

 

然後接着根據update_curr函數幀的c00a3878(lr) 去查看這個地址出於那個函數幀:(推算下來與Backtrace是對應的)

Grep -rin "c00a3878"  vmlinux.dis 

得知位於put_prev_task_fair的函數幀:

首先看該函數彙編,壓棧部分:

c00a34a8 <put_prev_task_fair>:(put_prev_task_fair+0x3d0/0x5b8)>>>>c00a34a8 ++0x3d0 等於update_curr裏面的lrc00a3878

c00a34a8:        e1a0c00d        mov        ip,sp

c00a34ac:        e92ddff0        push        {r4,r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}

c00a34b0:        e24cb004        sub        fp,ip, #4

c00a34b4:        e24dd04c        sub        sp,sp, #76        ; 0x4c

c00a34b8:        e52de004        push        {lr}                ;(str lr, [sp, #-4]!)

c00a34bc:        ebfdad0b        bl        c000e8f0<__gnu_mcount_nc>

c00a34c0:        e291a038        adds        sl,r1, #56        ; 0x38

c00a34c4:        0a0000ec        beq        c00a387c<put_prev_task_fair+0x3d4>

c00a34c8:        e3097988        movw        r7,#39304        ; 0x9988

 

put_prev_task_fair棧大小76/4+11 =30個字。得出該函數put_prev_task_fair的函數幀:以上綠色部分。

因此得知該幀的lr=c08f6074

 

同樣道理,根據c08f6074,去查看該地址出於那個函數幀,依次循環。

 

問題:爲何“PC is atupdate_curr+0x17c/0x1f0 ”與  update_curr函數棧幀裏面的c009fdac(pc) 兩者地址不一樣的呢?

因爲》》

前者:PC is at update_curr+0x17c 是當前cpu在執行的指令地址,這個地址就是update_curr函數內的指令,取自:cpuR15寄存器,代碼是:   __show_regs(regs);

後者:是這個函數在壓棧時候的填充的pc值,往往指向sp,這個值是存在棧空間。不是我們一般所說的cpu pc指針(也就是R15寄存器)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章