linux 函数调用栈帧,函数调用栈帧回溯

本文以Linux + arm64平台上的测试程序为例,讲解函数调用的栈帧回溯基本原理。

1. Overview

相关的函数调用规范,可参考arm官方的aapcs64文档

undefined

2. Demo

2.1 堆栈

Thread 9 (LWP 1386):

#0 0x0000007faec8fd28 in ?? ()

#1 0x0000007faf208190 in osal_memcpy (dst=0x7f0e6cc727, src=0x7f08024180, count=32477) at vdi/linux/vdi_osal.c:335

#2 0x0000007faf205278 in vdi_write_memory (core_idx=3, dst_addr=17924376359, src_data=0x7f08024180 "", len=32477, endian=16) at vdi/linux/vdi.c:1300

... ...

#11 0x000000000044e9b8 in bmMonkey::VPUTask::VideoCapture::read (this=0x7f917f90e8, frame=0x7f08013e40) at /jenkins/projects/AI_BSP_bmMonkey_daily_build/bmMonkey/src/bmMonkey.cpp:279

#12 0x000000000044ef3c in bmMonkey::VPUTask::run (this=0x4b8900, seq=2) at /jenkins/projects/AI_BSP_bmMonkey_daily_build/bmMonkey/src/bmMonkey.cpp:326

... ...

#19 0x0000007faeee73bc in ?? ()

Backtrace stopped: previous frame identical to this frame (corrupt stack?)

这里就以#12和#11栈帧为例

2.2 查看#12栈帧(caller)

(gdb) f 12

#12 0x000000000044ef3c in bmMonkey::VPUTask::run (this=0x4b8900, seq=2) at

(gdb) i r

x0 0x7f0e6cc727 545702856487

x1 0x7f08024a79 545595214457

x2 0x7594 30100

x3 0x7f0e6ccfe0 545702858720

x4 0x7f0802c05d 545595244637

x5 0x7f0e6d4604 545702888964

x6 0xced9c821daa46d83 -3541579584222499453

x7 0xae3ab9e9ba49762c -5892192748956977620

x8 0x882dfd5053b622c8 -8633966389155716408

x9 0x10f14ede4ad483f6 1220843690639262710

x10 0x544ccab1c46ccb2 379653303891840178

x11 0xe324afdbaeaae7b 1022962518989647483

x12 0xb051c1241528b35f -5741595689202699425

x13 0xd87b19b469eae132 -2847654076719898318

x14 0x7 7

x15 0x2dc62c8656e5 50329173776101

x16 0x7faf233440 548399166528

x17 0x7faec8fc10 548393253904

x18 0x0 0

x19 0x7f08007df8 545595096568

x20 0x4b8900 4950272

x21 0x7fc88517de 548825012190

x22 0x7fc88517df 548825012191

x23 0x0 0

x24 0x4bc170 4964720

x25 0x1000 4096

x26 0x1 1

x27 0x7fb201f030 548447318064

x28 0x7fc88517e8 548825012200

x29 0x7f917f90b0 547901903024

x30 0x44ef3c 4517692

sp 0x7f917f90b0 0x7f917f90b0

pc 0x44ef3c 0x44ef3c <:vputask::run long>

cpsr 0x20000000 [ EL=0 C ]

fpsr 0x10 16

fpcr 0x0 0

反汇编当前函数

(gdb) disas

Dump of assembler code for function bmMonkey::VPUTask::run(unsigned long):

0x000000000044ec6c : stp x29, x30, [sp,#-352]!

0x000000000044ec70 : mov x29, sp

0x000000000044ec74 : stp x19, x20, [sp,#16]

0x000000000044ec78 : str x0, [x29,#40]

0x000000000044ec7c : str x1, [x29,#32]

0x000000000044ec80 : ldr x0, [x29,#40]

0x000000000044ec84 : bl 0x449484 <:task::getname const>

... ...

0x000000000044ef1c : and w0, w0, #0xff

0x000000000044ef20 : eor w0, w0, #0x1

0x000000000044ef24 : and w0, w0, #0xff

0x000000000044ef28 : cmp w0, #0x0

0x000000000044ef2c : b.eq 0x44f070 <:vputask::run long>

0x000000000044ef30 : ldr x1, [x29,#48]

0x000000000044ef34 : add x0, x29, #0x38

0x000000000044ef38 : bl 0x44e850 <:vputask::videocapture::read>

=> 0x000000000044ef3c : and w0, w0, #0xff

0x000000000044ef40 : strb w0, [x29,#335]

0x000000000044ef44 : ldrb w0, [x29,#335]

0x000000000044ef48 : eor w0, w0, #0x1

0x000000000044ef4c : and w0, w0, #0xff

0x000000000044ef50 : cmp w0, #0x0

... ...

0x000000000044f114 : ldp x19, x20, [sp,#16]

0x000000000044f118 : ldp x29, x30, [sp],#352

0x000000000044f11c : ret

End of assembler dump.

先看栈帧保存操作

0x000000000044ec6c : stp x29, x30, [sp,#-352]!

0x000000000044ec70 : mov x29, sp

0x000000000044ec74 : stp x19, x20, [sp,#16]

目前获取的信息如下

12保存的寄存器有: fp, lr, x19, x20

当前fp=0x7f917f90b0

下一条待执行的指令地址为0x44ef3c

最后函数退出前,会再恢复

0x000000000044f114 : ldp x19, x20, [sp,#16]

0x000000000044f118 : ldp x29, x30, [sp],#352

0x000000000044f11c : ret

2.3 查看#11栈帧(callee)

(gdb) f 11

#11 0x000000000044e9b8 in bmMonkey::VPUTask::VideoCapture::read (this=0x7f917f90e8, frame=0x7f08013e40) at

(gdb) i r

x0 0x7f0e6cc727 545702856487

x1 0x7f08024a79 545595214457

x2 0x7594 30100

x3 0x7f0e6ccfe0 545702858720

x4 0x7f0802c05d 545595244637

x5 0x7f0e6d4604 545702888964

x6 0xced9c821daa46d83 -3541579584222499453

x7 0xae3ab9e9ba49762c -5892192748956977620

x8 0x882dfd5053b622c8 -8633966389155716408

x9 0x10f14ede4ad483f6 1220843690639262710

x10 0x544ccab1c46ccb2 379653303891840178

x11 0xe324afdbaeaae7b 1022962518989647483

x12 0xb051c1241528b35f -5741595689202699425

x13 0xd87b19b469eae132 -2847654076719898318

x14 0x7 7

x15 0x2dc62c8656e5 50329173776101

x16 0x7faf233440 548399166528

x17 0x7faec8fc10 548393253904

x18 0x0 0

x19 0x7f08007df8 545595096568

x20 0x4b8900 4950272

x21 0x7fc88517de 548825012190

x22 0x7fc88517df 548825012191

x23 0x0 0

x24 0x4bc170 4964720

x25 0x1000 4096

x26 0x1 1

x27 0x7fb201f030 548447318064

x28 0x7fc88517e8 548825012200

x29 0x7f917f9040 547901902912

x30 0x44e9b8 4516280

sp 0x7f917f9040 0x7f917f9040

pc 0x44e9b8 0x44e9b8 <:vputask::videocapture::read>

cpsr 0x20000000 [ EL=0 C ]

fpsr 0x10 16

fpcr 0x0 0

反汇编当前函数

(gdb) disas

Dump of assembler code for function bmMonkey::VPUTask::VideoCapture::read(AVFrame*):

0x000000000044e850 : stp x29, x30, [sp,#-112]!

0x000000000044e854 : mov x29, sp

0x000000000044e858 : str x19, [sp,#16]

0x000000000044e85c : str x0, [x29,#40]

0x000000000044e860 : str x1, [x29,#32]

0x000000000044e864 : ldr x0, [x29,#40]

0x000000000044e868 : ldr x2, [x0,#96]

0x000000000044e86c : ldr x0, [x29,#40]

0x000000000044e870 : add x0, x0, #0x8

0x000000000044e874 : mov x1, x0

0x000000000044e878 : mov x0, x2

0x000000000044e87c : bl 0x447350

... ...

0x000000000044e9ac : mov x1, x0

0x000000000044e9b0 : mov x0, x2

0x000000000044e9b4 : bl 0x446cc0

=> 0x000000000044e9b8 : str w0, [x29,#108]

0x000000000044e9bc : ldr w0, [x29,#108]

0x000000000044e9c0 : cmp w0, #0x0

... ...

0x000000000044ec60 : ldr x19, [sp,#16]

0x000000000044ec64 : ldp x29, x30, [sp],#112

0x000000000044ec68 : ret

End of assembler dump.

先看栈帧保存操作

0x000000000044e850 : stp x29, x30, [sp,#-112]!

0x000000000044e854 : mov x29, sp

0x000000000044e858 : str x19, [sp,#16]

这里保存了fp/lr/x19三个寄存器值

最后函数退出前,会再恢复

0x000000000044ec60 : ldr x19, [sp,#16]

0x000000000044ec64 : ldp x29, x30, [sp],#112

0x000000000044ec68 : ret

11栈帧里, 存储了caller也就是#12栈帧的部分信息

(gdb) x/14xg 0x7f917f9040

0x7f917f9040: 0x0000007f917f90b0 0x000000000044ef3c

0x7f917f9050: 0x0000007f08007df8 0x0000007f917f9090

0x7f917f9060: 0x0000007f08013e40 0x0000007f917f90e8

0x7f917f9070: 0x0000007f917f9090 0x000000050000ffff

0x7f917f9080: 0x0000007f917f90b0 0x000000000044ef1c

0x7f917f9090: 0x0000000000989680 0x00000000004b8928

0x7f917f90a0: 0x0000000500000005 0x00000000004b8928

前面三个64bit值分别保存的是

0x0000007f917f90b0是#12栈帧的fp

0x000000000044ef3c是#12栈帧里待执行的下一条指令地址

0x0000007f08007df8是#12栈帧里的x19

也就是,我们可以通过#11推导出#12. 以此类推,#12也可以推导出#13等。

3. 总结

默认情况下,arm64平台的每个栈帧都会保存fp(x29)和lr(x30)两个寄存器. 通过递归这两个寄存器,可以得到整个backtrace.

-fomit-frame-pointer编译选项可以优化掉fp

程序员自我修养(ID: dumphex)

你可能感兴趣的:(linux,函数调用栈帧)