#arm #sp #backtrace #lr
#arm #sp #обратная трассировка #lr
Вопрос:
В настоящее время я пытаюсь получить обратную трассировку на основе указателя стека и регистра ссылок на устройстве ARM64 с помощью программы C.
Ниже приведен пример того, как objdump bar() вызывает foo() с 240444: ebfffd68 bl 23f9ec <foo@@Base>
Я могу получить регистр ссылок (lr) и, получив 23f9ec, сохранить его в списке трассировки в качестве последней процедуры.
Мой вопрос: из приведенного ниже кода сборки с текущим lr 0023f9ec <foo@@Base>: как вычислить, чтобы получить предыдущую процедуру с lr 0023fe14 <bar@@Base>, используя язык C?
вот моя реализация, но ошибаюсь в предыдущем lr
int bt(void** backtrace, int max_size) {
unsigned long* sp = __get_SP();
unsigned long* ra = __get_LR();
int* funcbase = (int*)(int)amp;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 amp; 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, [pc, #84] ; 23fa68 <foo@@Base 0x7c>
23fa10: e3002256 movw r2, #598 ; 0x256
23fa14: e59f1050 ldr r1, [pc, #80] ; 23fa6c <foo@@Base 0x80>
23fa18: e7943003 ldr r3, [r4, r3]
23fa1c: e08f1001 add r1, pc, 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, [pc, #48] ; 23fa70 <foo@@Base 0x84>
23fa3c: e08f0000 add r0, pc, 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, r4, 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, r5, r6, r7, r9, pc}
23fa64: 003d5be0 eorseq r5, sp, r0, ror #23
23fa68: 000026c8 andeq r2, r0, r8, asr #13
23fa6c: 002b7ba6 eoreq r7, fp, r6, lsr #23
23fa70: 002b73e5 eoreq r7, fp, r5, ror #7
0023fe14 <bar@@Base>:
23fe14: e92d4ef0 push {r4, r5, r6, r7, r9, sl, fp, lr}
23fe18: e24dde16 sub sp, sp, #352 ; 0x160
23fe1c: e59f76a8 ldr r7, [pc, #1704] ; 2404cc <bar@@Base 0x6b8>
23fe20: e1a04000 mov r4, r0
23fe24: e59f66a4 ldr r6, [pc, #1700] ; 2404d0 <bar@@Base 0x6bc>
23fe28: e1a03000 mov r3, r0
23fe2c: e59f26a0 ldr r2, [pc, #1696] ; 2404d4 <bar@@Base 0x6c0>
23fe30: e08f7007 add r7, pc, r7
23fe34: e08f6006 add r6, pc, r6
23fe38: e3a00000 mov r0, #0
23fe3c: e08f2002 add r2, pc, r2
23fe40: e1a05001 mov r5, r1
23fe44: e3a01003 mov r1, #3
23fe48: e59f9688 ldr r9, [pc, #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, [pc, #264] ; 240558 <bar@@Base 0x744>
24044c: e3a01003 mov r1, #3
240450: e08f2002 add r2, pc, r2
240454: e1a05000 mov r5, r0
240458: e1a03000 mov r3, r0
24045c: e3a00000 mov r0, #0
Комментарии:
1. Я не понимаю вашего вопроса. Вы уже знаете об
lr
этом, так чего же вам не хватает?2. Я обновил вопрос. Допустим, мой lr равен 23f9ec, и сохраните его в списке трассировки как последнюю процедуру, теперь из приведенного выше ассемблерного кода, как вычислить, чтобы получить предыдущую процедуру с помощью lr is 23fe14, используя язык C.
Ответ №1:
Я не думаю, что есть простой способ сделать это.
Обычно регистр 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, r5, [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, r5, r7, pc}
Если вы разыменовываете r7
там, вы получаете пару указателей, второй из которых является lr
, а первый из которых является r7
вызывающей функцией, что позволяет повторять этот процесс, пока вы не достигнете нижней части стека.
Судя по опубликованной вами сборке, в кодовой базе, которую вы просматриваете, этого нет. Это означает, что единственный способ получить обратный адрес — это тот же способ, что и сам код: продвигайтесь вперед по каждой инструкции и анализируйте / интерпретируйте их, пока не дойдете до чего-то, что загружается pc
. Это, конечно, несовершенно, поскольку в вашем стеке вызовов могут быть функции, которые никогда не возвращаются, но вы мало что можете с этим поделать.
Может возникнуть соблазн выполнить поиск в обратном направлении, и хотя вы можете использовать эвристический подход и, вероятно, достичь с его помощью вполне разумных результатов, это еще менее надежно, чем поиск вперед, поскольку у вас нет абсолютно никакого способа определить, пришли ли вы к адресу X, перейдя вперед от предыдущей инструкции или явно перейдятам откуда-то еще.
Комментарии:
1. Как насчет того, чтобы в случае, если доступен fp, получить из него lr и предыдущий fp, как показано ниже: cl.cam.ac.uk /~fms27/teaching/2001-02/arm-project/02-sort/… Структура данных обратной трассировки стека имеет формат, показанный ниже: сохранить указатель кода [fp] <-fp указывает на здесь возвращает значение ссылки [fp, #-4] возвращает значение sp [fp, #-8] возвращает значение fp [fp, #-12]
2.@NgocHoangNguyen Если вы посмотрите
bar@@Base
, то увидите, что при сохраненииfp
он не создает новыйfp
. Такfp
что, если он не используется в качестве указателя кадра. Ничего нет.