GAS: объяснение .cfi_def_cfa_offset - PullRequest
45 голосов
/ 24 сентября 2011

Мне бы хотелось пояснить значения, используемые с директивами .cfi_def_cfa_offset в сборке, сгенерированной GCC.Я смутно знаю, что директивы .cfi участвуют в фреймах вызовов и раскручивании стека, но я хотел бы получить более подробное объяснение того, почему, например, значения 16 и 8 используются в сборке, выданной GCC, при компиляции следующей программы на Cна моей 64-битной машине с Ubuntu.

Программа на C:

#include <stdio.h>

int main(int argc, char** argv)
{
        printf("%d", 0);
        return 0;
}

Я вызвал GCC для исходного файла test.c следующим образом: gcc -S -O3 test.c.Я знаю, что -O3 позволяет нестандартную оптимизацию, но я хотел ограничить размер сгенерированной сборки для краткости.

Сгенерированная сборка:

        .file   "test.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "%d"
        .text
        .p2align 4,,15
.globl main
        .type   main, @function
main:
.LFB22:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        xorl    %edx, %edx
        movl    $.LC0, %esi
        movl    $1, %edi
        xorl    %eax, %eax
        call    __printf_chk
        xorl    %eax, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
            .cfi_endproc
.LFE22:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
        .section        .note.GNU-stack,"",@progbits

Почему значения 16и 8 используется для директив .cfi_def_cfa_offset в сгенерированной сборке?Кроме того, почему номер 22, используемый для локальной функции начала и метки конца функции?

Ответы [ 2 ]

72 голосов
/ 24 сентября 2011

Как указано в спецификации 6.4 DWARF :

[...] Кадр вызова идентифицируется по адресу в стеке.Мы называем этот адрес каноническим адресом кадра или CFA.Как правило, CFA определяется как значение указателя стека на сайте вызова в предыдущем кадре (которое может отличаться от его значения при входе в текущий кадр).

main() вызывается откуда-то еще (в коде поддержки времени выполнения libc C), и во время выполнения инструкции call %rsp будет указывать на верхнюю частьстека (который является самым низким адресом - стек растет вниз), что бы это ни было (в данном случае это не имеет значения):

:                :                              ^
|    whatever    | <--- %rsp                    | increasing addresses
+----------------+                              |

Значение %rsp на данный моментэто «значение указателя стека на сайте вызова», т. е. CFA, как определено в спецификации.

При выполнении инструкции call он будет выдавать 64-битный (8 байт) возвратадрес в стеке:

:                :
|    whatever    | <--- CFA
+----------------+
| return address | <--- %rsp == CFA - 8
+----------------+

Теперь мы запускаем код на main, который выполняет subq $8, %rsp, чтобы зарезервировать для себя еще 8 байтов стека:

:                :
|    whatever    | <--- CFA
+----------------+
| return address |
+----------------+
| reserved space | <--- %rsp == CFA - 16
+----------------+

изменение указателя стека объявляется в отладочной информации с использованием директивы .cfi_def_cfa_offset, и вы можете видеть, что CFA теперь смещен на 16 байтов от текущего указателя стека.

В конце функции, инструкция addq $8, %rsp снова изменяет указатель стека, поэтому вставляется еще одна директива .cfi_def_cfa_offset, указывающая, что CFA теперь смещен только на 8 байтов от указателя стека.

(Число «22» в метках - просто произвольное значение.Компилятор будет генерировать уникальные имена меток на основе некоторых деталей реализации, таких как внутренняя нумерация основных блоков.)

1 голос
/ 20 апреля 2017

Мне бы хотелось пояснить значения, используемые с директивами .cfi_def_cfa_offset в сборке, сгенерированной GCC.

Мэтью дал хорошее объяснение. Вот определение из Раздел 7.10 Директивы CFI в руководстве по ГАЗУ:

.cfi_def_cfa_offset изменяет правило для вычисления CFA. Регистр остается прежним, но смещение является новым. Обратите внимание, что это абсолютное смещение, которое будет добавлено в определенный регистр для вычисления адреса CFA.

А .cfi_adjust_cfa_offset:

То же, что и .cfi_def_cfa_offset, но смещение является относительным значением, которое добавляется / вычитается из предыдущего смещения.

...