来自链接寄存器的 ARM64 回溯

问题描述

我目前正在尝试使用 C 程序在 ARM64 设备上基于堆栈指针和链接寄存器获取回溯。

下面是objdump的例子 bar() 使用 240444 调用 foo():ebfffd68 bl 23f9ec

我可以获取链接寄存器 (lr),然后从中获取 23f9ec,将其保存到回溯列表作为最后一个例程。

我的问题:从下面与当前 lr 0023f9ec 的汇编代码,如何使用 lr 计算获得以前的例程是 0023fe14 使用 C 语言?

这是我的实现,但之前的 lr 出错

int bt(void** backtrace,int max_size) {
    unsigned long* sp = __get_SP();
    unsigned long* ra = __get_LR();
    int* funcbase = (int*)(int)&bt;
    int spofft = (short)((*funcbase));
    sp = (char*)sp-spofft;
    unsigned long* wra = (unsigned long*)ra;
    int spofft; 
    int depth = 0;
    while(ra) {
        wra = ra;
        while((*wra >> 16) != 0xe92d) {
            wra--;
        }
        if(wra == 0)
            return 0;
        
            spofft = (short)(*wra & 0xffff);
        
        if(depth < max_size)
            backtrace[depth] = ra;
        else 
            break;
     
        ra =(unsigned long *)((unsigned long)ra + spofft);
        sp =(unsigned long *)((unsigned long)sp + spofft);

        
        depth++;
    }
    return 1;
}
0023f9ec <foo@@Base>:
  23f9ec:   e92d42f3    push    {r0,r1,r4,r5,r6,r7,r9,lr}
  23f9f0:   e1a09001    mov r9,r1
  23f9f4:   e1a07000    mov r7,r0
  23f9f8:   ebfffff9    bl  23f9e4 <__get_SP@@Base>
  23f9fc:   e59f4060    ldr r4,[pc,#96]   ; 23fa64 <foo@@Base+0x78>
  23fa00:   e08f4004    add r4,pc,r4
  23fa04:   e1a05000    mov r5,r0
  23fa08:   ebfffff3    bl  23f9dc <__get_LR@@Base>
  23fa0c:   e59f3054    ldr r3,#84]   ; 23fa68 <foo@@Base+0x7c>
  23fa10:   e3002256    movw    r2,#598    ; 0x256
  23fa14:   e59f1050    ldr r1,#80]   ; 23fa6c <foo@@Base+0x80>
  23fa18:   e7943003    ldr r3,[r4,r3]
  23fa1c:   e08f1001    add r1,r1
  23fa20:   e5934000    ldr r4,[r3]
  23fa24:   e1a03005    mov r3,r5
  23fa28:   e6bf4074    sxth    r4,r4
  23fa2c:   e58d4004    str r4,[sp,#4]
  23fa30:   e1a06000    mov r6,r0
  23fa34:   e58d0000    str r0,[sp]
  23fa38:   e59f0030    ldr r0,#48]   ; 23fa70 <foo@@Base+0x84>
  23fa3c:   e08f0000    add r0,r0
  23fa40:   ebfd456d    bl  190ffc <printf@plt>
  23fa44:   e1a03009    mov r3,r9
  23fa48:   e1a02007    mov r2,r7
  23fa4c:   e1a01006    mov r1,r6
  23fa50:   e0640005    rsb r0,r5
  23fa54:   ebffff70    bl  23f81c <get_prev_sp_ra2@@Base>
  23fa58:   e3a00000    mov r0,#0
  23fa5c:   e28dd008    add sp,sp,#8
  23fa60:   e8bd82f0    pop {r4,pc}
  23fa64:   003d5be0    eorseq  r5,r0,ror #23
  23fa68:   000026c8    andeq   r2,r8,asr #13
  23fa6c:   002b7ba6    eoreq   r7,fp,lsr #23
  23fa70:   002b73e5    eoreq   r7,ror #7

0023fe14 <bar@@Base>:
  23fe14:   e92d4ef0    push    {r4,sl,lr}
  23fe18:   e24dde16    sub sp,#352    ; 0x160
  23fe1c:   e59f76a8    ldr r7,#1704] ; 2404cc <bar@@Base+0x6b8>
  23fe20:   e1a04000    mov r4,r0
  23fe24:   e59f66a4    ldr r6,#1700] ; 2404d0 <bar@@Base+0x6bc>
  23fe28:   e1a03000    mov r3,r0
  23fe2c:   e59f26a0    ldr r2,#1696] ; 2404d4 <bar@@Base+0x6c0>
  23fe30:   e08f7007    add r7,r7
  23fe34:   e08f6006    add r6,r6
  23fe38:   e3a00000    mov r0,#0
  23fe3c:   e08f2002    add r2,r2
  23fe40:   e1a05001    mov r5,r1
  23fe44:   e3a01003    mov r1,#3
  23fe48:   e59f9688    ldr r9,#1672] ; 2404d8 <bar@@Base+0x6c4>
.....................................................................
  24043c:   e3a0100f    mov r1,#15
  240440:   e1a0000a    mov r0,sl
  240444:   ebfffd68    bl  23f9ec <foo@@Base>
  240448:   e59f2108    ldr r2,#264]  ; 240558 <bar@@Base+0x744>
  24044c:   e3a01003    mov r1,#3
  240450:   e08f2002    add r2,r2
  240454:   e1a05000    mov r5,r0
  240458:   e1a03000    mov r3,r0
  24045c:   e3a00000    mov r0,#0

解决方法

我认为没有什么简单的方法可以做到这一点。

通常任何操作系统的寄存器 ABI 都包含一个“帧指针”寄存器。例如,在 Apple 的 armv7 ABI 上,这是 r7

0x10006fc0      b0b5           push {r4,r5,r7,lr}
0x10006fc2      02af           add r7,sp,8
0x10006fc4      0448           ldr r0,[0x10006fd8]
0x10006fc6      d0e90c45       ldrd r4,[r0,0x30]
0x10006fca      0020           movs r0,0
0x10006fcc      fff7a6ff       bl 0x10006f1c
0x10006fd0      0019           adds r0,r0,r4
0x10006fd2      6941           adcs r1,r5
0x10006fd4      b0bd           pop {r4,pc}

如果在那里取消引用 r7,则会得到一对指针,其中第二个是 lr,第一个是调用函数的 r7,允许你重复这个过程,直到你到达堆栈的底部。

根据您发布的程序集来看,您正在查看的代码库没有那个。这意味着获取返回地址的唯一方法与代码本身的方法相同:向前执行每条指令并解析/解释它们,直到到达加载到 pc 中的内容。这当然是不完美的,因为您的调用堆栈中可能有些函数永远不会返回,但您对此无能为力。

向后搜索可能很诱人,虽然您可以使用启发式方法并可能用它获得相当合理的结果,但这比向前搜索更不可靠,因为您完全无法判断是否到达通过从前一条指令向前或从其他地方显式跳转到 X 来寻址。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...