Как бы вы объяснили этот список разборки? - PullRequest
2 голосов
/ 17 января 2020

У меня есть простая функция на языке C, в отдельной строке файла. c:

void var_init(){
    char *hello = "Hello";
}

, скомпилированная с:

gcc -ffreestanding -c string.c -o string.o

И затем я использую команду

objdump -d string.o

чтобы увидеть список дизассемблирования. Я получил следующее:

string.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <var_init>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # b <var_init+0xb>
   b:   48 89 45 f8             mov    %rax,-0x8(%rbp)
   f:   90                      nop
  10:   5d                      pop    %rbp
  11:   c3                      retq

Я потерял понимание этого списка. Книга «Написание ОС с нуля» говорит кое-что о старых разборках и немного раскрывает тайну, но их список совершенно другой, и я даже не вижу, чтобы данные интерпретировались как код в моем, как говорит автор.

Ответы [ 2 ]

2 голосов
/ 17 января 2020

В дополнение к объяснению от @VladfromMoscow, я подумал, что для автора может быть полезно увидеть, что происходит, когда вы компилируете в сборку, а не используете objdump, чтобы увидеть ее, так как данные можно увидеть более просто (IMO) и относительная адресация RIP может иметь немного больше смысла.

gcc -S x.s

Выход

    .file   "x.c"
    .text
    .section    .rodata
.LC0:
    .string "Hello"
    .text
    .globl  var_init
    .type   var_init, @function
var_init:
.LFB0:
    pushq   %rbp
    movq    %rsp, %rbp
    leaq    .LC0(%rip), %rax
    movq    %rax, -8(%rbp)
    nop
    popq    %rbp
    ret
.LFE0:
    .size   var_init, .-var_init
    .ident  "GCC: (Alpine 8.3.0) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

2 голосов
/ 17 января 2020

Эта команда

lea    0x0(%rip),%rax

сохраняет адрес строкового литерала в регистре rax.

И эта команда

mov    %rax,-0x8(%rbp)

копирует адрес из регистр rax в выделенную память стека. Адрес занимает 8 байт, как видно из смещения в стеке -0x8.

Это хранилище вообще происходит только потому, что вы скомпилировали его в режиме отладки; это обычно было бы оптимизировано далеко. Следующее, что происходит, это то, что локальные переменные (в ниже указателя стека) эффективно отбрасываются, так как функция разрушает свой кадр стека и возвращает.

Материал, который вы Вы, вероятно, рассматриваете включенный sub $16, %rsp или аналогичный для выделения места для местных жителей ниже RBP, затем освобождаете это пространство позже; ABI System V системы x86-64 не нуждается в этом в конечных функциях (которые не вызывают никаких других функций); они могут просто использовать зону чтения. (См. Также Где именно находится красная зона на x86-64? ). Или скомпилируйте с gcc -mno-red-zone, который вы, вероятно, в любом случае захотите для автономного кода: Почему код ядра не может использовать красную зону

Затем он восстанавливает сохраненное значение RBP вызывающего абонента (который был ранее установлен как указатель кадра; обратите внимание, что пространство для локальных адресов было адресовано относительно RBP).

pop    %rbp

и завершает работу, фактически вставляя адрес возврата в RIP

retq
...