Чтение исполняемого файла и листинга
Контекст: исполняемый ELF под Linux x86-64. Инструменты:
readelf,objdump, отладчик. Обзор RE — в истории ассемблера.
Дизассемблирование ELF - чтение бинарника
Исходника нет — остаётся дизассемблирование: восстановление мнемоник из байт .text. Даже со своим кодом листинг полезен: увидеть, что NASM и линкер реально положили в файл, где PLT, выравнивание, RIP-relative адреса.
Из чего состоит ELF (упрощённо)
| Секция | Содержимое |
|---|---|
.text | машинные инструкции |
.rodata | константы (строки, таблицы) |
.data | инициализированные глобальные данные |
.bss | нулевые данные при загрузке (в файле только размер) |
.symtab / .dynsym | имена функций и переменных |
.rela.* | как переписать адреса при загрузке (PIC, линковка с libc) |
Заголовок ELF описывает архитектуру (x86-64), точку входа (e_entry) и таблицу секций.
readelf -h ./app # заголовок
readelf -S ./app # секции
readelf -s ./app # символы
Дизассемблирование
objdump -d -M intel ./app
-M intel — синтаксис как в NASM (dest, src), а не AT&T.
Фрагмент может выглядеть так:
0000000000401000 <_start>:
401000: b8 01 00 00 00 mov eax,0x1
401005: bf 01 00 00 00 mov edi,0x1
...
Слева — виртуальный адрес после загрузки, справа — шестнадцатеричные байты и мнемоники.
С исходником рядом:
objdump -d -M intel -S ./app # если есть отладочные символы (-g при сборке)
Сопоставление с NASM-исходником
| В исходнике | В листинге |
|---|---|
mov rax, 1 | mov rax,0x1 — может быть другая длина инструкции |
lea rdi, [rel msg] | RIP-relative: lea rdi,[rip+0x...] |
call printf | часто call *...@plt — прыжок через таблицу процедур линковки |
section .bss | в файле секция пустая, размер в заголовке |
локальная метка .loop | может отсутствовать в символах; только адреса |
Не каждая метка попадает в символьную таблицу — локальные и static в C скрыты.
Таблица символов
nm ./app
Буквы состояния:
T/t— код в.text(глобальный / локальный)D/d— инициализированные данныеB/b— BSSU— неразрешённый (ожидает libc при линковке)
Имя _start или main — точка входа. Несколько U на printf — программа слинкована с динамической libc.
Отладочная информация
Сборка с символами:
gcc -g main.c add.o -o app
В GDB: disassemble /s my_add, info registers, x/s msg.
Без -g остаются только публичные символы и догадки по вызовам API.
Типичные артефакты, которые путают новичков
- NOP-заполнение — выравнивание входа функции на 16 байт.
- Две метки на один адрес — оптимизация линкера / альтернативные имена символов.
- Адреса 0x7f... — позиционно-независимый код (PIE); при каждом запуске смещение (ASLR).
- Строка в .rodata — в листинге не видна; смотрят
objdump -s -j .rodataилиstrings ./app.
Минимальный чек-лист разбора
readelf -h— 64-bit, ELF, entry.readelf -S— есть.text,.rodata, размеры разумны.objdump -d -M intel— логика_start/main.nm— какие функции экспортированы, что тянется из libc.- При расхождении с ожиданием — пересобрать с
-gи сравнить в GDB по шагам.
Дальше
Практика отладки — справочник, раздел про GDB. Строковые циклы REP — отдельная статья. SIMD и float в листинге — 15.md. Бинарник Windows (PE) — 16.md.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Полный отказ от высокоуровневых языков нецелесообразен. Поэтому большинство компиляторов поддерживают встроенный ассемблер — механизм вставки ассемблерных инструкций непосредственно в код на C/C++. %macro, %define и %if в NASM — шаблоны инструкций без дублирования исходного текста. Разделение программы на .asm-файлы, global и extern, сборка объектников и линковка в ELF. Вызов ассемблерных функций из C и наоборот — System V AMD64 ABI, выравнивание стека, сборка. REP, MOVS, SCAS, STOS, флаг DF и доступ к данным по индексу через таблицу. SSE2 для float и double, регистры XMM, выравнивание; кратко про стек x87 и AVX. Microsoft x64 calling convention, shadow space, вывод в консоль и файлы через API вместо syscall. Основы ассемблера - синтаксис Intel/AT&T, базовые инструкции и принципы низкоуровневого программирования. Архитектура ассемблерных программ - взаимодействие с ОС, вызовы библиотек и организация низкоуровневого кода. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и команды процессора в ассемблере - регистр команд, переходы и управление потоком исполнения. Команды и подпрограммы в ассемблере - передача параметров, соглашения вызовов и работа со стеком.История ассемблерных языков
Макросы и условная сборка
Несколько модулей и линковка
Взаимодействие с C и C++
Строковые инструкции и таблицы поиска
Числа с плавающей точкой и SIMD
Windows x64, WinAPI и отличия от Linux
Основы ассемблера
Архитектура ассемблерных программ
Типы данных и регистры
Управляющие конструкции и команды процессора
Команды и подпрограммы