упростить рассчет смещений

This commit is contained in:
mzhoot 2026-05-23 18:56:43 +03:00
parent bb80efac8d
commit 84646f02c6
6 changed files with 604 additions and 370 deletions

View file

@ -23,14 +23,14 @@
- **Полностью русский синтаксис** — все инструкции, регистры и директивы на кириллице - **Полностью русский синтаксис** — все инструкции, регистры и директивы на кириллице
- **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску - **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску
- **Четырёхпроходная сборка** — традиционная схема с разрешением меток, констант и перекодированием - **Четырёхпроходная сборка** — традиционная схема с разрешением меток, подстановкой констант и перекодированием
- **Поддержка секций** — разделение на `.текст` (код), `.данные` (данные) и `.бнд` (неинициализированные данные) - **Поддержка секций** — разделение на `.текст` (код), `.данные` (данные) и `.бнд` (неинициализированные данные)
- **Гибкая адресация памяти**: - **Гибкая адресация памяти**:
- абсолютная адресация по меткам `[переменная]` - абсолютная адресация по меткам `[переменная]`
- абсолютная адресация по числовому адресу `[0x402000]` - абсолютная адресация по числовому адресу `[0x402000]`
- косвенная адресация через регистр `[раикс]` - косвенная адресация через регистр `[раикс]`
- всё с RIP-relative кодированием для 64-битных перемещений - всё с RIP-relative кодированием для 64-битных перемещений
- **CSV-логирование** — детальный файл с соответствием адресов, байтов и исходных команд - **CSV-логирование** — детальный файл с соответствием адресов, байтов, меток и исходных команд
- **Более 50 инструкций** — полный набор основных команд x86-64 - **Более 50 инструкций** — полный набор основных команд x86-64
- **Минимализм зависимостей** — только стандартная библиотека Python - **Минимализм зависимостей** — только стандартная библиотека Python
@ -48,9 +48,9 @@
AST-представление (.аст) AST-представление (.аст)
│ │
▼ (kvs_pass1.py) ← ПРОХОД 1: генерация CSV с заглушками (0xCC) ▼ (kvs_pass1.py) ← ПРОХОД 1: генерация CSV с заглушками (0xCC)
CSV-файл (.csv) ← содержит все байты программы, метки, заглушки CSV-файл (.csv) ← содержит все байты программы, метки, виртуальные адреса
│ │
▼ (kvs_pass2.py) ← ПРОХОД 2: разрешение меток (замена заглушек) ▼ (kvs_pass2.py) ← ПРОХОД 2: разрешение меток
│ │
▼ (kvs_pass3.py) ← ПРОХОД 3: подстановка констант (.константа) ▼ (kvs_pass3.py) ← ПРОХОД 3: подстановка констант (.константа)
│ │
@ -68,24 +68,24 @@ ELF-файл (.elf)
| Файл | Назначение | | Файл | Назначение |
|------|-------------| |------|-------------|
| `kvs_build.py` | Главный сборочный скрипт (запускает все этапы по порядку) | | `kvs_build.py` | Главный сборочный скрипт (запускает все этапы по порядку) |
| `kvs_lexer.py` | Лексический анализ, разбор на токены (с поддержкой `[]`, `+`, `-`, `*`) | | `kvs_lexer.py` | Лексический анализ, разбор на токены (поддерживает `[]`, `+`, `-`, `*`) |
| `kvs_parser.py` | Синтаксический анализ, построение AST (директивы, метки, инструкции) | | `kvs_parser.py` | Синтаксический анализ, построение AST (директивы, метки, инструкции) |
| `kvs_data.py` | Общие данные: регистры, таблица инструкций, вспомогательные функции | | `kvs_data.py` | Общие данные: регистры, таблица инструкций, вспомогательные функции |
| `kvs_encoder.py` | Фасад кодировщика (реэкспорт из fixsize + mutsize) | | `kvs_encoder.py` | Фасад кодировщика (объединяет fixsize и mutsize) |
| `kvs_encoder_fixsize.py` | Кодировщик инструкций фиксированного размера | | `kvs_encoder_fixsize.py` | Кодировщик инструкций фиксированного размера |
| `kvs_encoder_mutsize.py` | Кодировщик инструкций переменного размера + диспетчер | | `kvs_encoder_mutsize.py` | Кодировщик инструкций переменного размера + диспетчер |
| `kvs_pass1.py` | Первый проход — вычисление размеров секций, адресов меток, генерация CSV с заглушками | | `kvs_pass1.py` | Первый проход — генерация CSV с заглушками для неизвестных меток |
| `kvs_pass2.py` | Второй проход — разрешение меток | | `kvs_pass2.py` | Второй проход — разрешение меток (замена заглушек на реальные адреса) |
| `kvs_pass3.py` | Третий проход — подстановка констант | | `kvs_pass3.py` | Третий проход — подстановка констант из директив `.константа` |
| `kvs_pass4.py` | Четвёртый проход — перекодирование инструкций | | `kvs_pass4.py` | Четвёртый проход — перекодирование инструкций с подставленными значениями |
| `kvs_builder.py` | Сборка ELF: чтение CSV, заполнение байтов по адресам, установка прав на исполнение | | `kvs_builder.py` | Сборка ELF: чтение CSV, заполнение байтов по адресам, установка прав на исполнение |
### Почему 4 прохода? ### Почему 4 прохода?
1. **Первый проход** — неизвестно, какие адреса будут у меток (метка может использоваться до определения). Вместо остановки ставим заглушки `0xCC` и продолжаем. 1. **Первый проход** — неизвестно, какие адреса будут у меток (метка может использоваться до определения). Вместо остановки ставим заглушки `0xCC` и продолжаем.
2. **Второй проход** — после того как все адреса меток известны, можно заменить заглушки на реальные значения. 2. **Второй проход** — после того как все адреса меток известны, заменяем заглушки на реальные значения.
3. **Третий проход** — константы могут ссылаться друг на друга и на метки, поэтому их подстановка делается отдельно. 3. **Третий проход** — константы могут ссылаться друг на друга и на метки, поэтому их подстановка делается отдельно.
4. **Четвёртый проход** — после подстановки чисел адреса/констант нужно перегенерировать машинный код (разные операнды могут менять длину инструкции). 4. **Четвёртый проход** — после подстановки чисел (адресов/констант) нужно перегенерировать машинный код, так как разные операнды могут менять длину инструкции.
### Структура CSV-файла ### Структура CSV-файла
@ -93,6 +93,7 @@ ELF-файл (.elf)
|---------|-------------| |---------|-------------|
| `сегмент` | Имя секции (`.header`, `.text`, `.data`, `.bss`) | | `сегмент` | Имя секции (`.header`, `.text`, `.data`, `.bss`) |
| `адрес` | Файловое смещение байта | | `адрес` | Файловое смещение байта |
| `виртуальный_адрес` | Виртуальный адрес байта в памяти (для вычисления смещений) |
| `байт` | Исходное значение байта (из pass1) | | `байт` | Исходное значение байта (из pass1) |
| `приводящая_метка` | Имена меток, находящихся на этом адресе (через запятую) | | `приводящая_метка` | Имена меток, находящихся на этом адресе (через запятую) |
| `уводящий_адрес` | Строка `ЗАГЛУШКА имя` для неразрешённых меток | | `уводящий_адрес` | Строка `ЗАГЛУШКА имя` для неразрешённых меток |
@ -475,12 +476,11 @@ strings программа.elf
После компиляции создаётся файл `программа.csv` со структурой: После компиляции создаётся файл `программа.csv` со структурой:
| адрес | байт | целевой_адрес | исходная_команда | | адрес | виртуальный_адрес | байт | приводящая_метка | исходная_команда |
|-------|------|---------------|------------------| |-------|-------------------|------|------------------|------------------|
| 0x401000 | 48 | | переместить_имм раикс, 1 | | 0x1000 | 0x401000 | 48 | _start | переместить_имм раикс, 10 |
| 0x401001 | B8 | | | | 0x1001 | 0x401001 | B8 | | |
| 0x401002 | 01 | | | | ... | ... | ... | ... | ... |
| ... | ... | ... | ... |
### Трассировка системных вызовов ### Трассировка системных вызовов
@ -524,7 +524,6 @@ gdb ./программа.elf
- Адресация со смещением `[reg + disp]` - Адресация со смещением `[reg + disp]`
- Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация - Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация
- Инструкции для работы со стеком (PUSH, POP, CALL, RET) — кроме базовых PUSH/POP reg
- Поддержка плавающей запятой (FPU/SSE) - Поддержка плавающей запятой (FPU/SSE)
- Многомодульная компиляция - Многомодульная компиляция
- Макросы - Макросы
@ -544,7 +543,8 @@ gdb ./программа.elf
- Поддержка числовых адресов `[0x402000]` - Поддержка числовых адресов `[0x402000]`
- Поддержка косвенной адресации `[регистр]` - Поддержка косвенной адресации `[регистр]`
- Секция `.бнд` для неинициализированных данных - Секция `.бнд` для неинициализированных данных
- Рефакторинг: выделение кодировщика в отдельный модуль - Вынесение кодировщика в отдельный модуль
- Колонка `виртуальный_адрес` в CSV для точного вычисления смещений
### В планах (v3.0) ### В планах (v3.0)

View file

@ -33,7 +33,6 @@ if __name__ == "__main__":
("kvs_pass2.py", [csv_file], "2 проход"), ("kvs_pass2.py", [csv_file], "2 проход"),
("kvs_pass3.py", [ast_file, csv_file], "3 проход"), ("kvs_pass3.py", [ast_file, csv_file], "3 проход"),
("kvs_pass4.py", [csv_file], "4 проход"), ("kvs_pass4.py", [csv_file], "4 проход"),
#("kvs_pass2.py", [csv_file], "Второй проход: разрешение меток"),
("kvs_builder.py", [csv_file, elf_file], "Сборка ELF"), ("kvs_builder.py", [csv_file, elf_file], "Сборка ELF"),
] ]

View file

@ -6,8 +6,9 @@
Генерирует CSV-таблицу напрямую из AST Генерирует CSV-таблицу напрямую из AST
- Неизвестные метки заменяются заглушками (0xCC) с пометкой в колонке 'уводящий_адрес' - Неизвестные метки заменяются заглушками (0xCC) с пометкой в колонке 'уводящий_адрес'
- В колонке 'приводящая_метка' записываются имена меток, находящихся по данному адресу (через запятую) - В колонке 'приводящая_метка' записываются имена меток, находящихся по данному адресу (через запятую)
- Колонка 'команда_со_значениями' оставлена пустой (для возможного расширения) - Колонка 'команда_со_значениями' оставлена пустой (для второго прохода)
- Колонки 'рассчитанный_уводящий_адрес' и 'рассчитанный_байт' оставлены для второго прохода - Колонки 'рассчитанный_уводящий_адрес' и 'рассчитанный_байт' оставлены для второго прохода
- Колонка 'виртуальный_адрес' содержит виртуальный адрес байта (для pass4)
""" """
import sys import sys
@ -18,7 +19,7 @@ from kvs_data import PAGE_SIZE, text_vaddr_base, align_up
from kvs_encoder import encode_instruction from kvs_encoder import encode_instruction
# ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ========== # ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ==========
entries = [] # (segment, address, byte, target, source, label, source_with_values, calc_target, calc_byte) entries = [] # (segment, address, virt_addr, byte, target, source, label, cmd_with_values, calc_target, calc_byte)
current_address = 0 current_address = 0
current_segment = '.header' current_segment = '.header'
last_segment = None last_segment = None
@ -31,12 +32,11 @@ text_buffer = bytearray()
data_buffer = bytearray() data_buffer = bytearray()
bnd_size = 0 bnd_size = 0
labels = {} # имя метки -> адрес labels = {}
label_sections = {} # имя метки -> секция label_sections = {}
symbols = {} # имя константы -> значение symbols = {}
# Для сбора нескольких меток на один адрес pending_labels = []
pending_labels = [] # список имён меток, ожидающих привязки к следующему байту
text_pos = 0 text_pos = 0
data_pos = 0 data_pos = 0
@ -44,6 +44,34 @@ data_pos = 0
entry_point = "_start" entry_point = "_start"
relocations = [] relocations = []
# Список инструкций, которые всегда должны получать заглушки в первом проходе
ALWAYS_STUB_INSTRUCTIONS = {
"переход",
"короткий_переход",
"вызвать",
"цикл",
"переход_если_равно",
"переход_если_неравно",
"переход_если_меньше",
"переход_если_больше",
"переход_если_меньше_или_равно",
"переход_если_больше_или_равно",
"переход_если_перенос",
"переход_если_нет_переноса",
"переход_если_ноль",
"переход_если_не_ноль",
"короткий_переход_если_равно",
"короткий_переход_если_неравно",
"короткий_переход_если_меньше",
"короткий_переход_если_больше",
"короткий_переход_если_меньше_или_равно",
"короткий_переход_если_больше_или_равно",
"короткий_переход_если_перенос",
"короткий_переход_если_нет_переноса",
"короткий_переход_если_ноль",
"короткий_переход_если_не_ноль",
}
def reset_state(): def reset_state():
global entries, current_address, current_segment, last_segment global entries, current_address, current_segment, last_segment
@ -75,11 +103,21 @@ def set_segment(segment: str):
current_segment = segment current_segment = segment
def get_virtual_address():
"""Возвращает виртуальный адрес для текущей позиции"""
if current_segment == '.text':
return vaddr_text + (current_address - 0x1000)
elif current_segment == '.data':
if vaddr_data is None:
return current_address
return vaddr_data + (current_address - 0x2000)
else:
return current_address
def add_byte(value: int, source: str = "", target=None, labels_list=None): def add_byte(value: int, source: str = "", target=None, labels_list=None):
"""Добавить один байт в текущую позицию"""
global current_address, last_segment, pending_labels global current_address, last_segment, pending_labels
# Если есть отложенные метки, объединяем их через запятую
effective_labels = "" effective_labels = ""
if labels_list is not None and labels_list: if labels_list is not None and labels_list:
effective_labels = ','.join(labels_list) effective_labels = ','.join(labels_list)
@ -90,20 +128,23 @@ def add_byte(value: int, source: str = "", target=None, labels_list=None):
if show_segment: if show_segment:
last_segment = current_segment last_segment = current_segment
virt_addr = get_virtual_address()
byte_val = value & 0xFF
entries.append(( entries.append((
show_segment if show_segment else "", # segment show_segment if show_segment else "",
current_address, # address current_address,
value & 0xFF, # byte f"0x{virt_addr:08x}", # виртуальный_адрес
target if target else "", # target (уводящий_адрес) f"0x{byte_val:02x}", # байт (в виде hex-строки)
source, # source (исходная_команда) target if target else "",
effective_labels, # label (приводящая_метка) - несколько через запятую source,
"", # source_with_values (команда_со_значениями) - всегда пусто effective_labels,
"", # calc_target "", # команда_со_значениями (заполняется во втором проходе)
"" # calc_byte "", # рассчитанный_уводящий_адрес
f"0x{byte_val:02x}" # рассчитанный_байт (изначально = байт)
)) ))
current_address += 1 current_address += 1
# После добавления байта сбрасываем отложенные метки
if pending_labels: if pending_labels:
pending_labels = [] pending_labels = []
@ -114,7 +155,6 @@ def add_bytes(values, source: str = "", target=None):
def add_word16(value: int, source: str = "", target=None): def add_word16(value: int, source: str = "", target=None):
"""Добавить 16-битное слово (little-endian)"""
add_bytes([ add_bytes([
(value >> 0) & 0xFF, (value >> 0) & 0xFF,
(value >> 8) & 0xFF, (value >> 8) & 0xFF,
@ -122,7 +162,6 @@ def add_word16(value: int, source: str = "", target=None):
def add_word32(value: int, source: str = "", target=None): def add_word32(value: int, source: str = "", target=None):
"""Добавить 32-битное слово (little-endian)"""
add_bytes([ add_bytes([
(value >> 0) & 0xFF, (value >> 0) & 0xFF,
(value >> 8) & 0xFF, (value >> 8) & 0xFF,
@ -132,7 +171,6 @@ def add_word32(value: int, source: str = "", target=None):
def add_word64(value: int, source: str = "", target=None): def add_word64(value: int, source: str = "", target=None):
"""Добавить 64-битное слово (little-endian)"""
add_bytes([ add_bytes([
(value >> 0) & 0xFF, (value >> 0) & 0xFF,
(value >> 8) & 0xFF, (value >> 8) & 0xFF,
@ -174,48 +212,29 @@ def unescape_string(s):
def generate_elf_header(): def generate_elf_header():
# EI_MAGIC
add_bytes([0x7F, 0x45, 0x4C, 0x46], "ELF magic") add_bytes([0x7F, 0x45, 0x4C, 0x46], "ELF magic")
# EI_CLASS, EI_DATA, EI_VERSION
add_byte(2, "ELFCLASS64") add_byte(2, "ELFCLASS64")
add_byte(1, "ELFDATA2LSB") add_byte(1, "ELFDATA2LSB")
add_byte(1, "ELF version") add_byte(1, "ELF version")
# EI_OSABI, EI_ABIVERSION
add_byte(0, "OS ABI") add_byte(0, "OS ABI")
add_byte(0, "ABI version") add_byte(0, "ABI version")
# EI_PAD
add_bytes([0] * 7, "Padding") add_bytes([0] * 7, "Padding")
# e_type (2 байта)
add_word16(2, "e_type = ET_EXEC") add_word16(2, "e_type = ET_EXEC")
# e_machine (2 байта)
add_word16(62, "e_machine = EM_X86_64") add_word16(62, "e_machine = EM_X86_64")
# e_version (4 байта)
add_word32(1, "e_version") add_word32(1, "e_version")
# e_entry (8 байт, заглушка)
add_word64(0, "e_entry (stub)") add_word64(0, "e_entry (stub)")
# e_phoff (8 байт)
add_word64(64, "e_phoff") add_word64(64, "e_phoff")
# e_shoff (8 байт)
add_word64(0, "e_shoff = 0") add_word64(0, "e_shoff = 0")
# e_flags (4 байта)
add_word32(0, "e_flags") add_word32(0, "e_flags")
# e_ehsize (2 байта)
add_word16(64, "e_ehsize") add_word16(64, "e_ehsize")
# e_phentsize (2 байта)
add_word16(56, "e_phentsize") add_word16(56, "e_phentsize")
# e_phnum (2 байта)
add_word16(2, "e_phnum = 2") add_word16(2, "e_phnum = 2")
# e_shentsize (2 байта)
add_word16(0, "e_shentsize = 0") add_word16(0, "e_shentsize = 0")
# e_shnum (2 байта)
add_word16(0, "e_shnum = 0") add_word16(0, "e_shnum = 0")
# e_shstrndx (2 байта)
add_word16(0, "e_shstrndx") add_word16(0, "e_shstrndx")
def generate_program_headers(): def generate_program_headers():
# Program Header 1: TEXT (RX)
add_word32(1, "p_type = PT_LOAD") add_word32(1, "p_type = PT_LOAD")
add_word32(5, "p_flags = RX") add_word32(5, "p_flags = RX")
add_word64(0, "p_offset (text stub)") add_word64(0, "p_offset (text stub)")
@ -225,7 +244,6 @@ def generate_program_headers():
add_word64(0, "p_memsz (text stub)") add_word64(0, "p_memsz (text stub)")
add_word64(PAGE_SIZE, "p_align") add_word64(PAGE_SIZE, "p_align")
# Program Header 2: DATA (RW)
add_word32(1, "p_type = PT_LOAD") add_word32(1, "p_type = PT_LOAD")
add_word32(6, "p_flags = RW") add_word32(6, "p_flags = RW")
add_word64(0, "p_offset (data stub)") add_word64(0, "p_offset (data stub)")
@ -258,7 +276,6 @@ def parse_ast_line(line):
mnemonic = line[first + 1:second] mnemonic = line[first + 1:second]
section = line[second + 1:third] section = line[second + 1:third]
operands_str = line[third + 1:] operands_str = line[third + 1:]
operands = operands_str.split(',') if operands_str else [] operands = operands_str.split(',') if operands_str else []
return "INSTR", { return "INSTR", {
@ -313,7 +330,17 @@ def parse_ast_line(line):
def get_stub_size(mnemonic, operands): def get_stub_size(mnemonic, operands):
if mnemonic == "переместить_имм" and len(operands) >= 2: if mnemonic in ("переход", "вызвать"):
return 5
elif mnemonic == "короткий_переход":
return 2
elif mnemonic.startswith("переход_если") and not mnemonic.startswith("короткий"):
return 6
elif mnemonic.startswith("короткий_переход_если"):
return 2
elif mnemonic == "цикл":
return 2
elif mnemonic == "переместить_имм" and len(operands) >= 2:
reg = operands[0] reg = operands[0]
if reg in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', if reg in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'): 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'):
@ -322,46 +349,88 @@ def get_stub_size(mnemonic, operands):
return 7 return 7
else: else:
return 2 return 2
elif mnemonic in ("переход", "вызвать"):
return 5
elif mnemonic.startswith("короткий_переход"):
return 2
elif mnemonic.startswith("переход_если"):
return 6
elif mnemonic.startswith("короткий_переход_если"):
return 2
elif mnemonic in ("загрузить", "сохранить", "загрузить_адрес"): elif mnemonic in ("загрузить", "сохранить", "загрузить_адрес"):
return 7 return 7
elif mnemonic == "сравнить_с":
return 7
elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно"):
return 7
elif mnemonic in ("переместить_с_нулями", "переместить_со_знаком"):
return 7
return 1 return 1
def has_unresolved_labels(operands): def extract_label_from_operands(operands):
"""Извлекает первую метку из операндов инструкции"""
for op in operands:
if not op:
continue
# Пропускаем регистры
if op in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'еаикс', 'ебикс', 'есикс', 'едикс',
'аикс', 'бикс', 'сикс', 'дикс',
'ал', 'бл', 'кл', 'дл',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'):
continue
# Пропускаем числа
if op.isdigit() or (op.startswith('0x') and len(op) > 2):
continue
# Пропускаем косвенную адресацию через регистр
if op.startswith('MEM:reg_indirect:'):
continue
# Извлекаем метку из [метка]
if op.startswith('[') and op.endswith(']'):
inner = op[1:-1]
if inner not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай'):
if not inner.isdigit() and not inner.startswith('0x'):
return inner
# Обычная метка
elif op not in symbols:
return op
return None
def has_unresolved_labels(operands, mnemonic=""):
if mnemonic in ALWAYS_STUB_INSTRUCTIONS:
for op in operands:
if op and not op.isdigit() and not op.startswith('0x'):
if op not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'еаикс', 'ебикс', 'есикс', 'едикс',
'аикс', 'бикс', 'сикс', 'дикс',
'ал', 'бл', 'кл', 'дл'):
return True
for op in operands: for op in operands:
if not op: if not op:
continue continue
if op in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', if op in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15',
'еаикс', 'ебикс', 'есикс', 'едикс', 'еаикс', 'ебикс', 'есикс', 'едикс',
'аикс', 'бикс', 'сикс', 'дикс', 'аикс', 'бикс', 'сикс', 'дикс',
'ал', 'бл', 'кл', 'дл'): 'ал', 'бл', 'кл', 'дл',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15',
'р8д', 'р9д', 'р10д', 'р11д', 'р12д', 'р13д', 'р14д', 'р15д',
'р8в', 'р9в', 'р10в', 'р11в', 'р12в', 'р13в', 'р14в', 'р15в',
'р8б', 'р9б', 'р10б', 'р11б', 'р12б', 'р13б', 'р14б', 'р15б',
'спл', 'бпл', 'сил', 'дил'):
continue
if op.startswith('MEM:reg_indirect:'):
continue continue
if op.startswith('[') and op.endswith(']'): if op.startswith('[') and op.endswith(']'):
inner = op[1:-1] inner = op[1:-1]
if inner in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай'): if inner in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай'):
continue continue
if inner in labels: if inner.isdigit() or (inner.startswith('0x') and len(inner) > 2):
continue
if inner.isdigit() or (inner.startswith('0x')):
continue continue
if inner not in labels:
return True return True
elif op in labels:
continue continue
elif op.isdigit() or (op.startswith('0x')): if op.isdigit() or (op.startswith('0x') and len(op) > 2):
continue continue
else:
if op in symbols: if op in symbols:
continue continue
if op not in labels:
return True return True
return False return False
@ -380,7 +449,8 @@ def get_unresolved_labels(operands):
if op not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', if op not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай',
'еаикс', 'ебикс', 'есикс', 'едикс', 'еаикс', 'ебикс', 'есикс', 'едикс',
'аикс', 'бикс', 'сикс', 'дикс', 'аикс', 'бикс', 'сикс', 'дикс',
'ал', 'бл', 'кл', 'дл'): 'ал', 'бл', 'кл', 'дл',
'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'):
if op not in symbols: if op not in symbols:
unresolved.append(op) unresolved.append(op)
return unresolved return unresolved
@ -505,17 +575,25 @@ def process_ast_line(line):
source = f"{mnemonic} {', '.join(operands)}" if operands else mnemonic source = f"{mnemonic} {', '.join(operands)}" if operands else mnemonic
source_with_values = "" need_stub = has_unresolved_labels(operands, mnemonic)
if has_unresolved_labels(operands): if need_stub:
stub_size = get_stub_size(mnemonic, operands) stub_size = get_stub_size(mnemonic, operands)
unresolved = get_unresolved_labels(operands) unresolved = get_unresolved_labels(operands)
if unresolved:
reloc_text = f"ЗАГЛУШКА {', '.join(unresolved)}" reloc_text = f"ЗАГЛУШКА {', '.join(unresolved)}"
else:
label_from_op = extract_label_from_operands(operands)
if label_from_op:
reloc_text = f"ЗАГЛУШКА {label_from_op}"
else:
reloc_text = "ЗАГЛУШКА (неизвестная метка)"
relocations.append({ relocations.append({
'address': current_address, 'address': current_address,
'size': stub_size, 'size': stub_size,
'labels': unresolved, 'labels': unresolved if unresolved else [label_from_op] if label_from_op else [],
'source': source, 'source': source,
'mnemonic': mnemonic 'mnemonic': mnemonic
}) })
@ -560,9 +638,9 @@ def process_ast_line(line):
except Exception as e: except Exception as e:
print(f"Ошибка кодирования {source}: {e}", file=sys.stderr) print(f"Ошибка кодирования {source}: {e}", file=sys.stderr)
labels_list = pending_labels labels_to_attach = pending_labels.copy()
add_byte(0xCC, source, None, f"ОШИБКА: {e}", labels_list) add_byte(0xCC, source, f"ОШИБКА: {e}", labels_to_attach)
pending_labels = [] pending_labels.clear()
if sec == '.text': if sec == '.text':
text_buffer.append(0xCC) text_buffer.append(0xCC)
@ -589,113 +667,113 @@ def finalize_headers():
i = 0 i = 0
while i < len(entries): while i < len(entries):
segment, addr, byte, target, source, label, source_with_values, calc_target, calc_byte = entries[i] segment, addr, virt_addr, byte_str, target, source, label, cmd_with_values, calc_target, calc_byte = entries[i]
# e_entry - 8 байт
if 'e_entry (stub)' in source: if 'e_entry (stub)' in source:
new_bytes = [(entry_point_addr >> j*8) & 0xFF for j in range(8)] new_bytes = [(entry_point_addr >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{entry_point_addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"e_entry = 0x{entry_point_addr:x}", f"e_entry = 0x{entry_point_addr:x}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_offset (text) - 8 байт
elif 'p_offset (text stub)' in source: elif 'p_offset (text stub)' in source:
new_bytes = [(text_offset >> j*8) & 0xFF for j in range(8)] new_bytes = [(text_offset >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_offset (text) = 0x{text_offset:x}", f"p_offset (text) = 0x{text_offset:x}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_filesz (text) - 8 байт
elif 'p_filesz (text stub)' in source: elif 'p_filesz (text stub)' in source:
new_bytes = [(text_filesz >> j*8) & 0xFF for j in range(8)] new_bytes = [(text_filesz >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_filesz (text) = {text_filesz}", f"p_filesz (text) = {text_filesz}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_memsz (text) - 8 байт
elif 'p_memsz (text stub)' in source: elif 'p_memsz (text stub)' in source:
new_bytes = [(text_memsz >> j*8) & 0xFF for j in range(8)] new_bytes = [(text_memsz >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_memsz (text) = {text_memsz}", f"p_memsz (text) = {text_memsz}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_offset (data) - 8 байт
elif 'p_offset (data stub)' in source: elif 'p_offset (data stub)' in source:
new_bytes = [(data_offset >> j*8) & 0xFF for j in range(8)] new_bytes = [(data_offset >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_offset (data) = 0x{data_offset:x}", f"p_offset (data) = 0x{data_offset:x}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_vaddr (data) - 8 байт
elif 'p_vaddr (data stub)' in source: elif 'p_vaddr (data stub)' in source:
new_bytes = [(vaddr_data_final >> j*8) & 0xFF for j in range(8)] new_bytes = [(vaddr_data_final >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{vaddr_data_final + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_vaddr (data) = 0x{vaddr_data_final:x}", f"p_vaddr (data) = 0x{vaddr_data_final:x}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_paddr (data) - 8 байт
elif 'p_paddr (data stub)' in source: elif 'p_paddr (data stub)' in source:
new_bytes = [(vaddr_data_final >> j*8) & 0xFF for j in range(8)] new_bytes = [(vaddr_data_final >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{vaddr_data_final + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_paddr (data) = 0x{vaddr_data_final:x}", f"p_paddr (data) = 0x{vaddr_data_final:x}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_filesz (data) - 8 байт
elif 'p_filesz (data stub)' in source: elif 'p_filesz (data stub)' in source:
new_bytes = [(data_filesz >> j*8) & 0xFF for j in range(8)] new_bytes = [(data_filesz >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_filesz (data) = {data_filesz}", f"p_filesz (data) = {data_filesz}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
# p_memsz (data) - 8 байт
elif 'p_memsz (data stub)' in source: elif 'p_memsz (data stub)' in source:
new_bytes = [(data_memsz >> j*8) & 0xFF for j in range(8)] new_bytes = [(data_memsz >> j*8) & 0xFF for j in range(8)]
for j, nb in enumerate(new_bytes): for j, nb in enumerate(new_bytes):
entries[i + j] = (segment, addr + j, nb, target, virt = f"0x{addr + j:08x}"
byte_hex = f"0x{nb:02x}"
entries[i + j] = (segment, addr + j, virt, byte_hex, target,
f"p_memsz (data) = {data_memsz}", f"p_memsz (data) = {data_memsz}",
label if j == 0 else "", label if j == 0 else "",
"", "", "", "", byte_hex)
f"0x{nb:02x}")
i += 8 i += 8
continue continue
@ -705,19 +783,20 @@ def finalize_headers():
def save_to_csv(filename: str): def save_to_csv(filename: str):
with open(filename, 'w', newline='', encoding='utf-8') as f: with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, delimiter=';') writer = csv.writer(f, delimiter=';')
writer.writerow(['сегмент', 'адрес', 'байт', 'приводящая_метка', 'уводящий_адрес', writer.writerow(['сегмент', 'адрес', 'виртуальный_адрес', 'байт', 'приводящая_метка',
'исходная_команда', 'команда_со_значениями', 'уводящий_адрес', 'исходная_команда', 'команда_со_значениями',
'рассчитанный_уводящий_адрес', 'рассчитанный_байт']) 'рассчитанный_уводящий_адрес', 'рассчитанный_байт'])
for segment, addr, byte, target, source, label, source_with_values, calc_target, calc_byte in entries: for segment, addr, virt_addr, byte_str, target, source, label, cmd_with_values, calc_target, calc_byte in entries:
writer.writerow([ writer.writerow([
segment, segment,
f"0x{addr:08x}", f"0x{addr:08x}",
f"0x{byte:02x}", virt_addr,
byte_str,
label, label,
target, target,
source, source,
source_with_values, cmd_with_values,
calc_target, calc_target,
calc_byte calc_byte
]) ])
@ -748,16 +827,16 @@ def main():
vaddr_data_final = align_up(vaddr_text + len(text_buffer), PAGE_SIZE) vaddr_data_final = align_up(vaddr_text + len(text_buffer), PAGE_SIZE)
vaddr_bnd_final = align_up(vaddr_data_final + len(data_buffer), PAGE_SIZE) vaddr_bnd_final = align_up(vaddr_data_final + len(data_buffer), PAGE_SIZE)
print(f"\n=== Первый проход (однопроходная генерация с заглушками) ===") print(f"\n=== Первый проход ===")
print(f" .text: {len(text_buffer)} байт, виртуальный адрес=0x{vaddr_text:x}") print(f" .text: {len(text_buffer)} байт, vaddr=0x{vaddr_text:x}")
print(f" .data: {len(data_buffer)} байт, виртуальный адрес=0x{vaddr_data_final:x}") print(f" .data: {len(data_buffer)} байт, vaddr=0x{vaddr_data_final:x}")
print(f" .bnd: {bnd_size} байт, виртуальный адрес=0x{vaddr_bnd_final:x}") print(f" .bnd: {bnd_size} байт, vaddr=0x{vaddr_bnd_final:x}")
print(f" точка входа: {entry_point} = 0x{labels.get(entry_point, vaddr_text):x}") print(f" точка входа: {entry_point} = 0x{labels.get(entry_point, vaddr_text):x}")
print(f" меток: {len(labels)}") print(f" меток: {len(labels)}")
print(f" констант: {len(symbols)}") print(f" констант: {len(symbols)}")
print(f" релокаций: {len(relocations)}") print(f" релокаций: {len(relocations)}")
print(f" записей в CSV: {len(entries)}") print(f" записей: {len(entries)}")
print(f" CSV сохранен: {csv_file}") print(f" CSV: {csv_file}")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -2,13 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Второй проход КВС Второй проход КВС (упрощённый)
- Собирает адреса всех приводящих меток (поддерживает несколько меток через запятую) - Собирает адреса всех приводящих меток из колонки 'виртуальный_адрес'
- Заменяет заглушки в уводящих адресах на реальные адреса - Заменяет заглушки в уводящих адресах на реальные адреса
- Записывает рассчитанные значения в колонку 'рассчитанный_уводящий_адрес' (файловое смещение) - Записывает рассчитанные значения в колонку 'рассчитанный_уводящий_адрес' (файловое смещение)
- Формирует команду с подставленными адресами в колонку 'команда_со_значениями' (виртуальный адрес) - Формирует команду с подставленными виртуальными адресами в колонку 'команда_со_значениями'
- Пропускает заглушки, которые не найдены (возможно это константы)
- Выводит статистику по неразрешённым заглушкам
""" """
import sys import sys
@ -30,55 +28,17 @@ def read_csv(csv_file):
return headers, entries return headers, entries
def get_virtual_addresses(entries, headers): def collect_labels(entries, headers):
"""
Извлекает виртуальные адреса секций из заголовка CSV
Возвращает (vaddr_text, vaddr_data)
"""
vaddr_text = 0x401000
vaddr_data = 0x402000
try:
source_idx = headers.index('исходная_команда')
calc_target_idx = headers.index('рассчитанный_уводящий_адрес')
except ValueError:
return vaddr_text, vaddr_data
for row in entries:
source = row[source_idx].strip()
calc_target = row[calc_target_idx].strip()
if 'p_vaddr (text)' in source and calc_target:
parts = calc_target.split('=')
if len(parts) >= 2:
addr_str = parts[1].strip()
if addr_str.startswith('0x'):
vaddr_text = int(addr_str, 16)
elif 'p_vaddr (data)' in source and calc_target:
parts = calc_target.split('=')
if len(parts) >= 2:
addr_str = parts[1].strip()
if addr_str.startswith('0x'):
vaddr_data = int(addr_str, 16)
return vaddr_text, vaddr_data
def collect_labels(entries, headers, vaddr_text, vaddr_data):
""" """
Собирает адреса всех приводящих меток. Собирает адреса всех приводящих меток.
Возвращает два словаря: Использует колонку 'виртуальный_адрес' для получения адреса метки.
- labels_file: имя метки -> файловое смещение Возвращает словарь: имя метки -> виртуальный адрес
- labels_virt: имя метки -> виртуальный адрес
""" """
labels_file = {} # имя метки -> файловое смещение labels = {}
labels_virt = {} # имя метки -> виртуальный адрес
try: try:
label_idx = headers.index('приводящая_метка') label_idx = headers.index('приводящая_метка')
addr_idx = headers.index('адрес') virt_addr_idx = headers.index('виртуальный_адрес')
segment_idx = headers.index('сегмент')
except ValueError as e: except ValueError as e:
print(f"Ошибка: не найдена нужная колонка в CSV: {e}") print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
sys.exit(1) sys.exit(1)
@ -86,33 +46,33 @@ def collect_labels(entries, headers, vaddr_text, vaddr_data):
for row in entries: for row in entries:
label_str = row[label_idx].strip() label_str = row[label_idx].strip()
if label_str: if label_str:
addr_str = row[addr_idx].strip() virt_addr_str = row[virt_addr_idx].strip()
if addr_str.startswith('0x'): if virt_addr_str.startswith('0x'):
file_addr = int(addr_str, 16) virt_addr = int(virt_addr_str, 16)
segment = row[segment_idx].strip()
# Вычисляем виртуальный адрес
if segment == '.text':
virt_addr = vaddr_text + (file_addr - 0x1000)
elif segment == '.data':
virt_addr = vaddr_data + (file_addr - 0x2000)
else:
virt_addr = file_addr
# Разделяем несколько меток через запятую
for label_name in label_str.split(','): for label_name in label_str.split(','):
label_name = label_name.strip() label_name = label_name.strip()
if label_name: if label_name:
labels_file[label_name] = file_addr labels[label_name] = virt_addr
labels_virt[label_name] = virt_addr
return labels_file, labels_virt # Выводим статистику собранных меток
print(f"\n{'='*80}")
print(f"СОБРАННЫЕ ПРИВОДЯЩИЕ МЕТКИ (всего: {len(labels)})")
print(f"{'='*80}")
print(f"\n{'Имя метки':<35} {'Виртуальный адрес':<22}")
print(f"{'-'*57}")
for name in sorted(labels.keys()):
virt_addr = labels[name]
print(f"{name:<35} 0x{virt_addr:08x} ({virt_addr})")
return labels
def resolve_targets(entries, headers, labels_file, labels_virt): def resolve_targets(entries, headers, labels):
""" """
Заменяет уводящие адреса-заглушки на реальные адреса Заменяет уводящие адреса-заглушки на реальные адреса
- рассчитанный_уводящий_адрес: файловое смещение - рассчитанный_уводящий_адрес: файловое смещение (берём из колонки 'адрес')
- команда_со_значениями: виртуальный адрес - команда_со_значениями: виртуальный адрес
""" """
try: try:
@ -120,12 +80,13 @@ def resolve_targets(entries, headers, labels_file, labels_virt):
calc_target_idx = headers.index('рассчитанный_уводящий_адрес') calc_target_idx = headers.index('рассчитанный_уводящий_адрес')
source_idx = headers.index('исходная_команда') source_idx = headers.index('исходная_команда')
source_with_values_idx = headers.index('команда_со_значениями') source_with_values_idx = headers.index('команда_со_значениями')
addr_idx = headers.index('адрес')
except ValueError as e: except ValueError as e:
print(f"Ошибка: не найдена нужная колонка в CSV: {e}") print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
sys.exit(1) sys.exit(1)
resolved = [] # (имя_метки, файловый_адрес, виртуальный_адрес) resolved = []
unresolved = [] # имя_метки unresolved = []
for row in entries: for row in entries:
target = row[target_idx].strip() target = row[target_idx].strip()
@ -133,17 +94,22 @@ def resolve_targets(entries, headers, labels_file, labels_virt):
if target and target.startswith('ЗАГЛУШКА '): if target and target.startswith('ЗАГЛУШКА '):
label_name = target.replace('ЗАГЛУШКА ', '').strip() label_name = target.replace('ЗАГЛУШКА ', '').strip()
if label_name in labels_file: if label_name in labels:
file_addr = labels_file[label_name] virt_addr = labels[label_name]
virt_addr = labels_virt[label_name]
file_hex = f"0x{file_addr:08x}"
virt_hex = f"0x{virt_addr:08x}" virt_hex = f"0x{virt_addr:08x}"
# Файловое смещение в рассчитанный_уводящий_адрес # Файловое смещение берём из колонки 'адрес' текущей строки
addr_str = row[addr_idx].strip()
if addr_str.startswith('0x'):
file_addr = int(addr_str, 16)
file_hex = f"0x{file_addr:08x}"
row[calc_target_idx] = file_hex row[calc_target_idx] = file_hex
resolved.append((label_name, file_addr, virt_addr)) resolved.append((label_name, file_addr, virt_addr))
else:
row[calc_target_idx] = ""
resolved.append((label_name, 0, virt_addr))
# Виртуальный адрес в команда_со_значениями # Подставляем виртуальный адрес в команду
source_cmd = row[source_idx].strip() source_cmd = row[source_idx].strip()
if source_cmd: if source_cmd:
cmd_with_addr = source_cmd.replace(label_name, virt_hex) cmd_with_addr = source_cmd.replace(label_name, virt_hex)
@ -152,6 +118,46 @@ def resolve_targets(entries, headers, labels_file, labels_virt):
unresolved.append(label_name) unresolved.append(label_name)
row[calc_target_idx] = "" row[calc_target_idx] = ""
# Выводим статистику разрешённых заглушек
print(f"\n{'='*80}")
print(f"РАЗРЕШЁННЫЕ ЗАГЛУШКИ (всего: {len(resolved)})")
print(f"{'='*80}")
if resolved:
unique_resolved = {}
for name, file_addr, virt_addr in resolved:
if name not in unique_resolved:
unique_resolved[name] = (file_addr, virt_addr)
print(f"\nУникальных меток: {len(unique_resolved)}")
print(f"\n{'Имя метки':<35} {'Файловое смещение':<22} {'Виртуальный адрес':<22}")
print(f"{'-'*79}")
for name in sorted(unique_resolved.keys()):
file_addr, virt_addr = unique_resolved[name]
if file_addr:
print(f"{name:<35} 0x{file_addr:08x} ({file_addr:<10}) 0x{virt_addr:08x} ({virt_addr})")
else:
print(f"{name:<35} {'(не известно)':<22} 0x{virt_addr:08x} ({virt_addr})")
else:
print("\n--- НЕТ РАЗРЕШЁННЫХ ЗАГЛУШЕК ---")
# Выводим статистику неразрешённых заглушек
print(f"\n{'='*80}")
print(f"НЕРАЗРЕШЁННЫЕ ЗАГЛУШКИ (всего: {len(unresolved)})")
print(f"{'='*80}")
if unresolved:
unresolved_counts = Counter(unresolved)
print(f"\nУникальных неразрешённых имён: {len(unresolved_counts)}")
print(f"\n{'Имя метки':<35} {'Количество вхождений':<20}")
print(f"{'-'*55}")
for name, count in sorted(unresolved_counts.items(), key=lambda x: x[1], reverse=True):
print(f"{name:<35} {count}")
else:
print("\n--- НЕТ НЕРАЗРЕШЁННЫХ ЗАГЛУШЕК ---")
return resolved, unresolved return resolved, unresolved
@ -163,36 +169,6 @@ def save_csv(csv_file, headers, entries):
writer.writerows(entries) writer.writerows(entries)
def print_statistics(labels_count, resolved, unresolved, vaddr_text, vaddr_data):
"""Выводит статистику по разрешению меток"""
print(f"\n--- Статистика разрешения меток ---")
print(f" Виртуальный адрес .text: 0x{vaddr_text:x}")
print(f" Виртуальный адрес .data: 0x{vaddr_data:x}")
print(f" Всего приводящих меток: {labels_count}")
print(f" Разрешено заглушек: {len(resolved)}")
print(f" Не разрешено заглушек (возможно константы): {len(unresolved)}")
if resolved:
unique_resolved = {}
for name, file_addr, virt_addr in resolved:
if name not in unique_resolved:
unique_resolved[name] = (file_addr, virt_addr)
print(f"\n--- Разрешённые заглушки (первые 20) ---")
for i, (name, (file_addr, virt_addr)) in enumerate(list(unique_resolved.items())[:20]):
print(f" {name} -> файл:0x{file_addr:08x}, вирт:0x{virt_addr:08x}")
if len(unique_resolved) > 20:
print(f" ... и ещё {len(unique_resolved) - 20}")
if unresolved:
unresolved_counts = Counter(unresolved)
print(f"\n--- Неразрешённые заглушки (возможно константы) ---")
for name, count in sorted(unresolved_counts.items(), key=lambda x: x[1], reverse=True)[:20]:
print(f" {name}: {count} раз(а)")
if len(unresolved_counts) > 20:
print(f" ... и ещё {len(unresolved_counts) - 20} различных имён")
def main(): def main():
if len(sys.argv) != 2: if len(sys.argv) != 2:
print("Использование: python kvs_pass2.py <входной.csv>") print("Использование: python kvs_pass2.py <входной.csv>")
@ -200,33 +176,27 @@ def main():
csv_file = sys.argv[1] csv_file = sys.argv[1]
print(f"\n=== Второй проход КВС ===") print(f"\n=== ВТОРОЙ ПРОХОД КВС (упрощённый) ===")
print(f"Чтение CSV: {csv_file}") print(f"Чтение CSV: {csv_file}")
headers, entries = read_csv(csv_file) headers, entries = read_csv(csv_file)
vaddr_text, vaddr_data = get_virtual_addresses(entries, headers) print(f"\n--- СБОР ПРИВОДЯЩИХ МЕТОК ---")
print(f"Виртуальный адрес .text: 0x{vaddr_text:x}") labels = collect_labels(entries, headers)
print(f"Виртуальный адрес .data: 0x{vaddr_data:x}")
print(f"\n--- Сбор приводящих меток ---") print(f"\n--- РАЗРЕШЕНИЕ УВОДЯЩИХ АДРЕСОВ ---")
labels_file, labels_virt = collect_labels(entries, headers, vaddr_text, vaddr_data) resolved, unresolved = resolve_targets(entries, headers, labels)
print(f"Найдено приводящих меток: {len(labels_file)}")
if labels_file: print(f"\n{'='*80}")
print(f"\n--- Примеры приводящих меток (первые 20) ---") print(f"ОБЩАЯ СТАТИСТИКА")
for i, name in enumerate(list(labels_file.keys())[:20]): print(f"{'='*80}")
print(f" {name} -> файл:0x{labels_file[name]:08x}, вирт:0x{labels_virt[name]:08x}") print(f" Приводящих меток в CSV: {len(labels)}")
if len(labels_file) > 20: print(f" Разрешено заглушек: {len(resolved)}")
print(f" ... и ещё {len(labels_file) - 20}") print(f" Не разрешено заглушек: {len(unresolved)}")
print(f"{'='*80}")
print(f"\n--- Разрешение уводящих адресов и формирование команд ---")
resolved, unresolved = resolve_targets(entries, headers, labels_file, labels_virt)
print_statistics(len(labels_file), resolved, unresolved, vaddr_text, vaddr_data)
save_csv(csv_file, headers, entries) save_csv(csv_file, headers, entries)
print(f"\nCSV сохранен: {csv_file}") print(f"\nCSV сохранён: {csv_file}")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -4,13 +4,13 @@
""" """
Четвёртый проход КВС Четвёртый проход КВС
- Перекодирует инструкции, используя подставленные значения - Перекодирует инструкции, используя подставленные значения
- Смотрит колонки 'команда_со_значениями' и 'рассчитанный_уводящий_адрес' - Смотрит колонку 'команда_со_значениями'
- Записывает перекодированные байты в колонку 'рассчитанный_байт' - Записывает перекодированные байты в колонку 'рассчитанный_байт'
- Использует виртуальные адреса из CSV для вычисления смещений
""" """
import sys import sys
import csv import csv
import struct
sys.path.insert(0, '.') sys.path.insert(0, '.')
from kvs_data import PAGE_SIZE, text_vaddr_base, align_up from kvs_data import PAGE_SIZE, text_vaddr_base, align_up
@ -18,16 +18,15 @@ from kvs_encoder import encode_instruction
# ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ========== # ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ==========
vaddr_text = text_vaddr_base vaddr_text = text_vaddr_base
vaddr_data = None vaddr_data = 0x402000
vaddr_bnd = None vaddr_bnd = None
labels = {} # имя метки -> адрес labels = {}
label_sections = {} # имя метки -> секция label_sections = {}
symbols = {} # имя константы -> значение symbols = {}
def read_csv(csv_file): def read_csv(csv_file):
"""Читает CSV файл и возвращает список записей и заголовки"""
entries = [] entries = []
headers = [] headers = []
@ -41,7 +40,6 @@ def read_csv(csv_file):
def parse_command(cmd_str): def parse_command(cmd_str):
"""Разбирает команду вида 'мнемоника операнд1, операнд2, ...'"""
if not cmd_str: if not cmd_str:
return None, [] return None, []
@ -50,75 +48,44 @@ def parse_command(cmd_str):
operands = [] operands = []
if len(parts) > 1 and parts[1]: if len(parts) > 1 and parts[1]:
# Разбираем операнды, разделённые запятыми
operands = [op.strip() for op in parts[1].split(',')] operands = [op.strip() for op in parts[1].split(',')]
return mnemonic, operands return mnemonic, operands
def collect_labels_from_csv(entries, headers): def collect_labels_from_csv(entries, headers):
"""Собирает метки из CSV для перекодирования""" """
global labels, label_sections, vaddr_data, vaddr_bnd Собирает метки из CSV, используя колонку 'виртуальный_адрес'
"""
global labels, label_sections
try: try:
label_idx = headers.index('приводящая_метка') label_idx = headers.index('приводящая_метка')
addr_idx = headers.index('адрес') virt_addr_idx = headers.index('виртуальный_адрес')
segment_idx = headers.index('сегмент')
byte_idx = headers.index('байт')
except ValueError as e: except ValueError as e:
print(f"Ошибка: не найдена нужная колонка в CSV: {e}") print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
sys.exit(1) sys.exit(1)
# Сначала нужно определить виртуальные адреса секций
max_addr = 0
for row in entries:
addr_str = row[addr_idx].strip()
if addr_str.startswith('0x'):
addr = int(addr_str, 16)
if addr > max_addr:
max_addr = addr
segment = row[segment_idx].strip()
# Определяем vaddr_data по первому адресу в .data
if segment == '.data' and vaddr_data is None:
vaddr_data = addr
# Определяем vaddr_bnd по первому адресу в .bss
elif segment == '.bss' and vaddr_bnd is None:
vaddr_bnd = addr
if vaddr_data is None:
vaddr_data = align_up(vaddr_text + max_addr, PAGE_SIZE)
if vaddr_bnd is None:
vaddr_bnd = align_up(vaddr_data + max_addr, PAGE_SIZE)
# Собираем метки
for row in entries: for row in entries:
label_str = row[label_idx].strip() label_str = row[label_idx].strip()
if label_str: if label_str:
addr_str = row[addr_idx].strip() virt_addr_str = row[virt_addr_idx].strip()
if addr_str.startswith('0x'): if virt_addr_str.startswith('0x'):
addr = int(addr_str, 16) virt_addr = int(virt_addr_str, 16)
for label_name in label_str.split(','): for label_name in label_str.split(','):
label_name = label_name.strip() label_name = label_name.strip()
if label_name: if label_name:
labels[label_name] = addr labels[label_name] = virt_addr
# Определяем секцию по адресу label_sections[label_name] = ''
if addr >= vaddr_text and addr < vaddr_text + PAGE_SIZE:
label_sections[label_name] = '.text'
elif vaddr_data and addr >= vaddr_data and addr < vaddr_data + PAGE_SIZE:
label_sections[label_name] = '.data'
elif vaddr_bnd and addr >= vaddr_bnd:
label_sections[label_name] = '.bss'
def reencode_instructions(entries, headers): def reencode_instructions(entries, headers):
"""Перекодирует инструкции, используя подставленные значения"""
try: try:
source_with_values_idx = headers.index('команда_со_значениями') source_with_values_idx = headers.index('команда_со_значениями')
calc_target_idx = headers.index('рассчитанный_уводящий_адрес')
calc_byte_idx = headers.index('рассчитанный_байт') calc_byte_idx = headers.index('рассчитанный_байт')
addr_idx = headers.index('адрес') addr_idx = headers.index('адрес')
segment_idx = headers.index('сегмент') virt_addr_idx = headers.index('виртуальный_адрес')
byte_idx = headers.index('байт') byte_idx = headers.index('байт')
except ValueError as e: except ValueError as e:
print(f"Ошибка: не найдена нужная колонка в CSV: {e}") print(f"Ошибка: не найдена нужная колонка в CSV: {e}")
@ -127,7 +94,6 @@ def reencode_instructions(entries, headers):
reencoded_count = 0 reencoded_count = 0
error_count = 0 error_count = 0
# Проходим по всем строкам
i = 0 i = 0
while i < len(entries): while i < len(entries):
row = entries[i] row = entries[i]
@ -136,76 +102,86 @@ def reencode_instructions(entries, headers):
i += 1 i += 1
continue continue
addr = int(addr_str, 16) file_addr = int(addr_str, 16)
segment = row[segment_idx].strip()
# Берём виртуальный адрес инструкции из CSV
virt_addr_str = row[virt_addr_idx].strip()
if virt_addr_str.startswith('0x'):
instr_virt_addr = int(virt_addr_str, 16)
else:
instr_virt_addr = file_addr
# Проверяем, есть ли команда для перекодирования
cmd = row[source_with_values_idx].strip() cmd = row[source_with_values_idx].strip()
calc_target = row[calc_target_idx].strip()
# Определяем, нужно ли перекодировать
need_reencode = False
if cmd: if cmd:
# Есть команда с подставленными константами
need_reencode = True
elif calc_target and calc_target.startswith('0x'):
# Есть рассчитанный адрес для заглушки
# Нужно взять исходную команду и подставить адрес
source_idx = headers.index('исходная_команда')
source = row[source_idx].strip()
if source:
# Заменяем ЗАГЛУШКА имя на реальный адрес
target_addr = int(calc_target, 16)
cmd = source.replace('ЗАГЛУШКА', '').strip()
# Добавляем адрес как операнд
cmd = f"{cmd} 0x{target_addr:08x}"
need_reencode = True
if need_reencode and cmd:
try: try:
mnemonic, operands = parse_command(cmd) mnemonic, operands = parse_command(cmd)
if mnemonic: if mnemonic:
# Вычисляем текущую позицию в секции # ОТЛАДОЧНЫЙ ВЫВОД (только для переходов)
if segment == '.text': if mnemonic.startswith('короткий_переход') or mnemonic.startswith('переход'):
current_pos = addr - vaddr_text print(f"\n=== ОТЛАДКА ===")
elif segment == '.data' and vaddr_data: print(f" Адрес в CSV: 0x{file_addr:08x}")
current_pos = addr - vaddr_data print(f" Виртуальный адрес инструкции: 0x{instr_virt_addr:08x}")
else: print(f" Команда: {cmd}")
current_pos = 0 print(f" Мнемоника: {mnemonic}")
print(f" Операнды: {operands}")
# Кодируем инструкцию if operands and len(operands) > 0:
target_str = operands[0]
if target_str.startswith('0x'):
target_addr = int(target_str, 16)
# Определяем размер инструкции
if mnemonic.startswith('короткий'):
instr_size = 2
else:
instr_size = 5
offset = target_addr - (instr_virt_addr + instr_size)
print(f" Целевой адрес: 0x{target_addr:08x}")
print(f" Размер инструкции: {instr_size}")
print(f" RIP = 0x{instr_virt_addr + instr_size:08x}")
print(f" Смещение = {offset} (0x{offset & 0xFFFFFFFF:08x})")
print(f" В диапазоне [-128..127]: {-128 <= offset <= 127}")
# Вычисляем current_pos как смещение в секции
# Определяем секцию по виртуальному адресу
if 0x401000 <= instr_virt_addr < 0x402000:
current_pos = instr_virt_addr - vaddr_text
elif 0x402000 <= instr_virt_addr < 0x403000:
current_pos = instr_virt_addr - vaddr_data
else:
current_pos = instr_virt_addr
# Преобразуем операнды: если строка с 0x, оставляем как есть
# Кодировщик сам разберётся с isdigit()
encoded = encode_instruction( encoded = encode_instruction(
mnemonic, operands, labels, label_sections, symbols, mnemonic, operands, labels, label_sections, symbols,
vaddr_text, vaddr_data if vaddr_data else 0, vaddr_text, vaddr_data, current_pos, vaddr_bnd
current_pos, vaddr_bnd if vaddr_bnd else 0
) )
# Записываем байты в колонку рассчитанный_байт if mnemonic.startswith('короткий_переход') or mnemonic.startswith('переход'):
# Для много-байтовых инструкций записываем в последовательные строки print(f" Закодировано: {[f'0x{b:02x}' for b in encoded]}")
print(f" РЕЗУЛЬТАТ: УСПЕШНО")
for j, byte in enumerate(encoded): for j, byte in enumerate(encoded):
if i + j < len(entries): if i + j < len(entries):
entries[i + j][calc_byte_idx] = f"0x{byte:02x}" entries[i + j][calc_byte_idx] = f"0x{byte:02x}"
else:
# Не хватает строк, создаём новую
# (в нормальной ситуации не должно происходить)
pass
reencoded_count += 1 reencoded_count += 1
# Пропускаем следующие строки, принадлежащие этой инструкции
# (обычно в CSV одна строка = один байт)
i += len(encoded) i += len(encoded)
continue continue
except Exception as e: except Exception as e:
print(f"Ошибка перекодирования '{cmd}': {e}") print(f"\n=== ОТЛАДКА (ОШИБКА) ===")
print(f" Команда: {cmd}")
print(f" Мнемоника: {mnemonic if 'mnemonic' in locals() else '?'}")
print(f" Операнды: {operands if 'operands' in locals() else '?'}")
print(f" Виртуальный адрес инструкции: 0x{instr_virt_addr:08x}")
print(f" ОШИБКА: {e}")
error_count += 1 error_count += 1
# Записываем INT3 как заглушку
row[calc_byte_idx] = "0xcc" row[calc_byte_idx] = "0xcc"
i += 1 i += 1
else: else:
# Если нет команды для перекодирования, просто копируем исходный байт # Если нет команды, копируем исходный байт
byte_str = row[byte_idx].strip() byte_str = row[byte_idx].strip()
if byte_str.startswith('0x'): if byte_str.startswith('0x'):
row[calc_byte_idx] = byte_str row[calc_byte_idx] = byte_str
@ -215,19 +191,20 @@ def reencode_instructions(entries, headers):
def save_csv(csv_file, headers, entries): def save_csv(csv_file, headers, entries):
"""Сохраняет обновлённый CSV файл"""
with open(csv_file, 'w', newline='', encoding='utf-8') as f: with open(csv_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, delimiter=';') writer = csv.writer(f, delimiter=';')
writer.writerow(headers) writer.writerow(headers)
writer.writerows(entries) writer.writerows(entries)
def print_statistics(reencoded_count, error_count): def print_statistics(reencoded_count, error_count, labels_count):
"""Выводит статистику перекодирования"""
print(f"\n--- Статистика перекодирования ---") print(f"\n--- Статистика перекодирования ---")
print(f" Меток в словаре: {labels_count}")
print(f" Перекодировано инструкций: {reencoded_count}") print(f" Перекодировано инструкций: {reencoded_count}")
if error_count: if error_count:
print(f" Ошибок: {error_count}") print(f" Ошибок: {error_count}")
else:
print(f" Ошибок: нет")
def main(): def main():
@ -240,21 +217,26 @@ def main():
print(f"\n=== Четвёртый проход КВС ===") print(f"\n=== Четвёртый проход КВС ===")
print(f"Чтение CSV: {csv_file}") print(f"Чтение CSV: {csv_file}")
# Читаем CSV
headers, entries = read_csv(csv_file) headers, entries = read_csv(csv_file)
print(f"\n--- Сбор меток из CSV ---") print(f"\n--- Сбор меток из CSV ---")
collect_labels_from_csv(entries, headers) collect_labels_from_csv(entries, headers)
print(f"Найдено меток: {len(labels)}") print(f"Найдено меток: {len(labels)}")
if labels:
print(f"\n--- Примеры меток (виртуальные адреса) ---")
for i, name in enumerate(sorted(labels.keys())[:10]):
print(f" {name} -> 0x{labels[name]:08x}")
if len(labels) > 10:
print(f" ... и ещё {len(labels) - 10}")
print(f"\n--- Перекодирование инструкций ---") print(f"\n--- Перекодирование инструкций ---")
reencoded_count, error_count = reencode_instructions(entries, headers) reencoded_count, error_count = reencode_instructions(entries, headers)
print_statistics(reencoded_count, error_count) print_statistics(reencoded_count, error_count, len(labels))
# Сохраняем результат
save_csv(csv_file, headers, entries) save_csv(csv_file, headers, entries)
print(f"\nCSV сохранен: {csv_file}") print(f"\nCSV сохранён: {csv_file}")
print(f"Колонка 'рассчитанный_байт' обновлена") print(f"Колонка 'рассчитанный_байт' обновлена")

View file

@ -0,0 +1,204 @@
; сложный_тест9.квс
; Тест: запутанные переходы, крякмис
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_не_ноль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_не_ноль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 1: "Два шага вперед" - короткий_переход через мертвый_код_2 и мертвый_код_3
; Прыгаем к проверка_4, минуя мертвый_код_2 и мертвый_код_3
короткий_переход проверка_4
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться из-за прыжка выше
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мертвый_код_3:
; ЖИВОЙ КОД 3: Этот код выполнится благодаря прыжку "назад"
; Выведем сообщение, чтобы убедиться, что он выполнился
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_live_from_3
переместить_имм рдикс, len_msg_live_from_3
вызов_системы
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 3: "Четыре вперед" - переход через мертвый_код_4, 5, 6, 7
; Цель слишком далеко для короткого перехода. Используем длинный.
; Прыгаем к проверка_8
переход проверка_8
; ---------------------
проверка_4:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимо_мертвого_кода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимо_мертвого_кода_4:
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 2: "Один назад" - короткий_переход к мертвому_коду_3 (делая его живым)
короткий_переход мертвый_код_3
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
проверка_8:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимо_мертвого_кода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимо_мертвого_кода_8:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 9, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимо_мертвого_кода_9
; --- МЕРТВЫЙ КОД 9 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_9
переместить_имм рдикс, len_msg_dead_9
вызов_системы
; ---------------------
мимо_мертвого_кода_9:
; ДЕВЯТЫЙ ПЕРЕХОД: короткий_переход (безусловный)
; Просто прыгаем мимо мертвого кода 10.
короткий_переход мимо_мертвого_кода_10
; --- МЕРТВЫЙ КОД 10 ---
; Этот код НЕ должен выполниться (так как jmp сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_10
переместить_имм рдикс, len_msg_dead_10
вызов_системы
; ---------------------
мимо_мертвого_кода_10:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Живой из 3!\n" = 8 букв * 2 + 1 (!) + 2 (\n) = 16 + 3 = 19 байт на содержимое. len = 19
msg_live_from_3: .строка_нуль "Живой из 3!\n"
.константа len_msg_live_from_3 = 19
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строка_нуль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 10!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_10: .строка_нуль "Мертвый код 10!"
.константа len_msg_dead_10 = 18
; "Мертвый код 9!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_9: .строка_нуль "Мертвый код 9!"
.константа len_msg_dead_9 = 18
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строка_нуль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строка_нуль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строка_нуль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строка_нуль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строка_нуль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строка_нуль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строка_нуль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строка_нуль "Мертвый код 1!"
.константа len_msg_dead_1 = 18