Как работает ассемблер?

#object #assembly #linker #machine-code

#объект #сборка #компоновщик #машинный код

Вопрос:

Я ищу краткое описание использования ассемблера при создании машинного кода.

Итак, я знаю, что ассемблер — это перевод машинного кода в соотношении 1: 1. Но я начинаю путаться в объектном коде и компоновщиках и в том, как они в него помещаются.

Мне не нужен сложный ответ, достаточно простого

Ответ №1:

И ассемблер, и компилятор преобразуют исходные файлы в объектные файлы.

Объектные файлы фактически являются промежуточным этапом перед окончательным исполняемым результатом (генерируемым компоновщиком).

Компоновщик принимает указанные объектные файлы и библиотеки (которые являются пакетами объектных файлов) и разрешает записи перемещения (или «исправления»).

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

Допустим, вы хотите, чтобы программа выводила на экран сообщение, разделенное на два исходных файла, и вы хотите собрать их отдельно и связать (пример использования системных вызовов Linux x86-64) —

main.asm :

 bits 64
section .text
extern do_message
global _start
_start:
    call do_message
    mov rax, 1
    int 0x80 
  

сообщение.asm :

 bits 64
section .text
global do_message
do_message:
    mov rdi, message
    mov rcx, dword -1
    xor rax, rax
    repnz scasb
    sub rdi, message
    mov rax, 4
    mov rbx, 1
    mov rcx, message
    mov rdx, rdi
    int 0x80
    ret

section .data
message: db "hello world",10,0
  

Если вы соберете их и посмотрите на выходные данные объектного файла main.asm (например, objdump -d main.o), вы заметите, что ‘call do_message’ имеет адрес 00 00 00 00 — что недопустимо.

 0000000000000000 <_start>:
   0:   e8 00 00 00 00          callq  5 <_start 0x5>
   5:   48 c7 c0 01 00 00 00    mov    $0x1,%rax
   c:   cd 80                   int    $0x80
  

Но для 4 байт адреса создается запись перемещения :

 $ objdump -r main.o
main.o:     file format elf64-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE 
0000000000000001 R_X86_64_PC32     do_message 0xfffffffffffffffc
000000000000000d R_X86_64_32       .data
  

Смещение равно ‘1’, а тип ‘R_X86_64_PC32’, который сообщает компоновщику разрешить эту ссылку и поместить разрешенный адрес в указанное смещение.

Когда вы связываете конечную программу с ‘ld -o program main.o message.o’, все перемещения разрешены, и если ничего не осталось неразрешенным, вы остаетесь с исполняемым файлом.

Когда мы ‘objdump -d’ создаем исполняемый файл, мы можем видеть разрешенный адрес :

 00000000004000f0 <_start>:
  4000f0:   e8 0b 00 00 00          callq  400100 <do_message>
  4000f5:   48 c7 c0 01 00 00 00    mov    $0x1,%rax
  4000fc:   cd 80                   int    $0x80
  

Такие же перемещения используются как для переменных, так и для функций.
Тот же процесс происходит, когда вы связываете свою программу с несколькими большими библиотеками, такими как libc — вы определяете функцию с именем ‘main’, на которую libc имеет внешнюю ссылку — затем libc запускается перед вашей программой и вызывает вашу функцию ‘main’ при запуске исполняемого файла.

Комментарии:

1. asm -> объект -> компоновка … маленький аккуратный пример.

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

3. @gavlaaaaaaa : смещение ссылки на перемещение do_message в main.o равно 1, потому что код начинается с 0, а смещение 0 — это код операции ‘e8’, который является инструкцией ‘callq’, смещение 1 — это начало следующего 32-разрядного значения, которое является операндом для инструкции ‘callq’.

Ответ №2:

Простое объяснение:

Как только язык ассемблера собран в объектный код, компоновщик используется для преобразования объектного кода в исполняемый файл команд, который компьютер может понять и выполнить. Сгенерированный машинный код может быть интерпретирован контроллером центрального процессора.