Dwarf DW_AT_location objdump и dwarfdump несовместимы - PullRequest
3 голосов
/ 01 апреля 2020

Я играю с CPython и пытаюсь понять, как работает отладчик. В частности, я пытаюсь получить местоположение последнего PyFrameObject, чтобы я мог пройти его и получить Python обратную трассировку.

В файле ceval.c строка 689 содержит определение функции. :

PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)

Меня интересует расположение f в стеке. При выгрузке двоичного файла с помощью dwarfdump я получаю, что f находится на $rbp-824, но если я выгружаю двоичный файл с помощью objdump, я получаю, что местоположение равно $rbp-808 - несоответствие 16. Кроме того, при отладке с ГБД, я понимаю, что правильный ответ $rbp-808, как objdump дает мне. Почему расхождение и почему dwarfdump неверно? Что я не понимаю?

Как технически воссоздать проблему: Загрузите python-2.7.17.tgz с Python веб-сайта. Извлечение.

Я скомпилировал python -2.7.17 из источника с символами отладки (./configure --enable-pydebug && make). Выполните следующие команды в результирующем двоичном файле python:

dwarfdump Python-2.7.17/python имеет следующий вывод:

                        DW_AT_name                  f           
                        DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                        DW_AT_decl_line             0x000002b1                         
                        DW_AT_type                  <0x00002916>
                        DW_AT_location              len 0x0003: 91c879: DW_OP_fbreg -824

Я знаю, что это правильный f, потому что строка переменной является заявлено на 689 (0x2b1). Как вы можете видеть, расположение:

DW_AT_location len 0x0003: 91c879: DW_OP_fbreg -824: значение $rbp-824.

Выполнение команды objdump -S Python-2.7.17/python имеет следующий вывод:

PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
   f7577:       55                      push   %rbp
   f7578:       48 89 e5                mov    %rsp,%rbp
   f757b:       41 57                   push   %r15
   f757d:       41 56                   push   %r14
   f757f:       41 55                   push   %r13
   f7581:       41 54                   push   %r12
   f7583:       53                      push   %rbx
   f7584:       48 81 ec 38 03 00 00    sub    $0x338,%rsp
   f758b:       48 89 bd d8 fc ff ff    mov    %rdi,-0x328(%rbp)
   f7592:       89 b5 d4 fc ff ff       mov    %esi,-0x32c(%rbp)
   f7598:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
   f759f:       00 00 
   f75a1:       48 89 45 c8             mov    %rax,-0x38(%rbp)
   f75a5:       31 c0                   xor    %eax,%eax

Отладка этот вывод покажет вам, что соответствующая строка: f758b: 48 89 bd d8 fc ff ff mov %rdi,-0x328(%rbp), где вы можете ясно видеть, что f загружается из -0x328(%rbp), что составляет $rbp-808. Кроме того, GDB поддерживает этот вывод.

Опять же, вопрос в том, чего мне не хватает и почему 16-байтовая несоответствие между dwarfdump и реальностью?

Спасибо

Редактировать: dwarfdump, включая указанную выше функцию:

< 1><0x00004519>    DW_TAG_subprogram
                      DW_AT_external              yes(1)
                      DW_AT_name                  PyEval_EvalFrameEx
                      DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                      DW_AT_decl_line             0x000002b1
                      DW_AT_prototyped            yes(1)
                      DW_AT_type                  <0x00000817>
                      DW_AT_low_pc                0x000f7577
                      DW_AT_high_pc               <offset-from-lowpc>53969
                      DW_AT_frame_base            len 0x0001: 9c: DW_OP_call_frame_cfa
                      DW_AT_GNU_all_tail_call_sites yes(1)
                      DW_AT_sibling               <0x00005bbe>
< 2><0x0000453b>      DW_TAG_formal_parameter
                        DW_AT_name                  f
                        DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                        DW_AT_decl_line             0x000002b1
                        DW_AT_type                  <0x00002916>
                        DW_AT_location              len 0x0003: 91c879: DW_OP_fbreg -824

В соответствии с ответом ниже, DW_OP_fbreg смещено от основания кадра - в моем случае DW_OP_call_frame_cfa. У меня возникли проблемы с определением основы кадра. Мои регистры следующие:

(gdb) info registers
rax            0xfffffffffffffdfe       -514
rbx            0x7f6a4887d040   140094460121152
rcx            0x7f6a48e83ff7   140094466441207
rdx            0x0      0
rsi            0x0      0
rdi            0x0      0
rbp            0x7ffd24bcef00   0x7ffd24bcef00
rsp            0x7ffd24bceba0   0x7ffd24bceba0
r8             0x7ffd24bcea50   140725219813968
r9             0x0      0
r10            0x0      0
r11            0x246    582
r12            0x7f6a48870df0   140094460071408
r13            0x7f6a48874b58   140094460087128
r14            0x1      1
r15            0x7f6a48873794   140094460082068
rip            0x5559834e99c0   0x5559834e99c0 <PyEval_EvalFrameEx+46153>
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

Как указано выше, я уже знаю, что %rbp-808 работает. Как правильно сделать это с моими регистрами?

Редактировать: Я наконец понял ответ. Мне нужно было развернуть еще одну функцию и найти место, где была вызвана моя функция. Там переменная, которую я искал, действительно была в $rsp и $rsp-824 была правильной

1 Ответ

3 голосов
/ 02 апреля 2020

DW_OP_fbreg -824: значение $rbp-824

Это означает , а не . Это означает, что смещение -824 от кадра базы (виртуального) регистра, которое необязательно (и обычно) не равно $rbp.

Вам нужно искать DW_AT_frame_base для знать, что является основой кадра в текущей функции.

Скорее всего, она определена как DW_OP_call_frame_cfa, что является значением $RSP непосредственно перед вызовом текущей функции и равно до $RBP-16 (8 байтов для адреса возврата, сохраненного инструкцией CALL, и 8 байтов для предыдущего $RBP, сохраненного первой инструкцией вашей функции).

...