From 99579d2538a6db14f8e055e4e92ce2889963d95f Mon Sep 17 00:00:00 2001 From: mzhoot Date: Mon, 25 May 2026 18:19:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BC=D0=B0=D1=80=D0=BA=D0=B5=D1=80=D1=8B=20?= =?UTF-8?q?=D1=81=D0=B5=D0=BA=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvs_pass1.py | 55 ++++- kvs_pass4.py | 123 ++++------- инструменты/kvs_csv_builder.py | 237 ---------------------- инструменты/kvs_elf.py | 167 --------------- 4 files changed, 93 insertions(+), 489 deletions(-) delete mode 100644 инструменты/kvs_csv_builder.py delete mode 100644 инструменты/kvs_elf.py diff --git a/kvs_pass1.py b/kvs_pass1.py index 750eebb..293bb79 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.py @@ -31,6 +31,7 @@ last_segment = None vaddr_text = text_vaddr_base vaddr_data = None vaddr_bnd = None +data_offset = 0x2000 # начальное значение, будет пересчитано в finalize_headers text_buffer = bytearray() data_buffer = bytearray() @@ -64,6 +65,7 @@ def reset_state(): bnd_size = 0 vaddr_data = None vaddr_bnd = None + data_offset = 0x2000 # добавить labels = {} label_sections = {} symbols = {} @@ -81,12 +83,14 @@ def set_segment(segment: str): def get_virtual_address(): """Возвращает виртуальный адрес для текущей позиции""" + global data_offset # добавить + 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) + return vaddr_data + (current_address - data_offset) else: return current_address @@ -100,7 +104,7 @@ def add_byte(value: int, source: str = "", target=None, labels_list=None): elif pending_labels: effective_labels = ','.join(pending_labels) - show_segment = current_segment if current_segment != last_segment else None + show_segment = current_segment# if current_segment != last_segment else None if show_segment: last_segment = current_segment @@ -462,6 +466,11 @@ def process_ast_line(line): elif directive == '.данные': set_segment('.data') align_to(PAGE_SIZE) + # Пересчитываем data_offset, так как .text уже полностью обработан + global data_offset + data_offset = align_up(0x1000 + text_pos, PAGE_SIZE) + # Для отладки (можно убрать) + print(f" .data: пересчитан data_offset = 0x{data_offset:x} (text_pos={text_pos})") elif directive == '.бнд': set_segment('.bss') @@ -642,14 +651,15 @@ def process_ast_line(line): def finalize_headers(): - global entries + global entries, data_offset # добавить data_offset в global text_offset = 0x1000 - data_offset = 0x2000 - text_filesz = len(text_buffer) text_memsz = len(text_buffer) + # ВЫЧИСЛЯЕМ ФАЙЛОВОЕ СМЕЩЕНИЕ ДЛЯ DATA ДИНАМИЧЕСКИ + data_offset = align_up(text_offset + text_filesz, PAGE_SIZE) + data_filesz = len(data_buffer) data_memsz = len(data_buffer) + bnd_size @@ -794,6 +804,37 @@ def save_to_csv(filename: str): ]) + +def print_section_info(): + """Выводит информацию о секциях с учётом выравнивания""" + text_start_offset = 0x1000 + text_end_offset = text_start_offset + len(text_buffer) + text_end_aligned = align_up(text_end_offset, PAGE_SIZE) + + data_start_offset = text_end_aligned + data_end_offset = data_start_offset + len(data_buffer) + data_end_aligned = align_up(data_end_offset, PAGE_SIZE) + + bnd_start_offset = data_end_aligned + bnd_end_offset = bnd_start_offset + bnd_size + + print(f"\n=== Информация о размещении секций (файловые смещения) ===") + print(f" .text: {len(text_buffer)} байт, смещение 0x{text_start_offset:x} - 0x{text_end_offset-1:x}") + print(f" выровнено: следующий сектор начнётся с 0x{text_end_aligned:x}") + print(f" .data: {len(data_buffer)} байт, смещение 0x{data_start_offset:x} - 0x{data_end_offset-1:x}") + print(f" выровнено: следующий сектор начнётся с 0x{data_end_aligned:x}") + if bnd_size > 0: + print(f" .bnd: {bnd_size} байт, смещение 0x{bnd_start_offset:x} - 0x{bnd_end_offset-1:x}") + + print(f"\n=== Виртуальные адреса ===") + print(f" .text: 0x{vaddr_text:x} - 0x{vaddr_text + len(text_buffer) - 1:x}") + data_vaddr = align_up(vaddr_text + len(text_buffer), PAGE_SIZE) + print(f" .data: 0x{data_vaddr:x} - 0x{data_vaddr + len(data_buffer) - 1:x}") + if bnd_size > 0: + bnd_vaddr = align_up(data_vaddr + len(data_buffer), PAGE_SIZE) + print(f" .bnd: 0x{bnd_vaddr:x} - 0x{bnd_vaddr + bnd_size - 1:x}") + + def main(): if len(sys.argv) != 3: print("Использование: python kvs_pass1.py <вход.аст> <выход.csv>") @@ -815,13 +856,15 @@ def main(): finalize_headers() save_to_csv(csv_file) + # Добавить эту строку: + print_section_info() 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) print(f"\n=== Первый проход ===") print(f" .text: {len(text_buffer)} байт, vaddr=0x{vaddr_text:x}") - print(f" .data: {len(data_buffer)} байт, vaddr=0x{vaddr_data_final:x}") + print(f" .data: {len(data_buffer)} байт, vaddr=0x{vaddr_data_final:x}, offset=0x{align_up(0x1000 + len(text_buffer), PAGE_SIZE):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" меток: {len(labels)}") diff --git a/kvs_pass4.py b/kvs_pass4.py index a253742..1924a14 100644 --- a/kvs_pass4.py +++ b/kvs_pass4.py @@ -53,7 +53,7 @@ def parse_command(cmd_str): return mnemonic, operands -def detect_section_addresses(entries, headers): +def detect_section_addresses(entries, headers, log_file): """ Определяет виртуальные адреса секций из CSV. """ @@ -82,17 +82,17 @@ def detect_section_addresses(entries, headers): if segment == '.text' and not found_text: vaddr_text = virt_addr found_text = True - print(f" .text: 0x{vaddr_text:x}") + log_file.write(f" .text: 0x{vaddr_text:x}\n") elif segment == '.data' and not found_data: vaddr_data = virt_addr found_data = True - print(f" .data: 0x{vaddr_data:x}") + log_file.write(f" .data: 0x{vaddr_data:x}\n") elif segment == '.bss' and not found_bnd: vaddr_bnd = virt_addr found_bnd = True - print(f" .bss: 0x{vaddr_bnd:x}") + log_file.write(f" .bss: 0x{vaddr_bnd:x}\n") # Проверяем, что все нужные секции найдены if not found_text: @@ -100,11 +100,11 @@ def detect_section_addresses(entries, headers): sys.exit(1) if not found_data: - print(" Предупреждение: секция .data не найдена в CSV") + log_file.write(" Предупреждение: секция .data не найдена в CSV, используем 0x402000\n") vaddr_data = 0x402000 # fallback -def collect_labels_from_csv(entries, headers): +def collect_labels_from_csv(entries, headers, log_file): """ Собирает метки из CSV, используя колонку 'виртуальный_адрес' """ @@ -129,29 +129,11 @@ def collect_labels_from_csv(entries, headers): if label_name: labels[label_name] = virt_addr label_sections[label_name] = '' - - -def get_section_for_address(virt_addr): - """ - Определяет секцию по виртуальному адресу - """ - if vaddr_text is not None and virt_addr >= vaddr_text: - # Простая эвристика: если адрес в диапазоне .text (первые 0x1000 байт) - if virt_addr < vaddr_text + PAGE_SIZE: - return '.text' - if vaddr_data is not None and virt_addr >= vaddr_data: - if virt_addr < vaddr_data + PAGE_SIZE: - return '.data' - - if vaddr_bnd is not None and virt_addr >= vaddr_bnd: - if virt_addr < vaddr_bnd + PAGE_SIZE: - return '.bss' - - return '.unknown' + log_file.write(f"Найдено меток: {len(labels)}\n") -def reencode_instructions(entries, headers): +def reencode_instructions(entries, headers, log_file): try: source_with_values_idx = headers.index('команда_со_значениями') calc_byte_idx = headers.index('рассчитанный_байт') @@ -189,30 +171,12 @@ def reencode_instructions(entries, headers): try: mnemonic, operands = parse_command(cmd) if mnemonic: - # ОТЛАДОЧНЫЙ ВЫВОД (только для переходов) - if mnemonic.startswith('короткий_переход') or mnemonic.startswith('переход'): - print(f"\n=== ОТЛАДКА ===") - print(f" Адрес в CSV: 0x{file_addr:08x}") - print(f" Виртуальный адрес инструкции: 0x{instr_virt_addr:08x}") - print(f" Команда: {cmd}") - 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}") + # Для отладки: записываем в лог все инструкции переместить_имм + if mnemonic == 'переместить_имм' and operands and '0x00403014' in cmd: + log_file.write(f"\nDEBUG MOV:\n") + log_file.write(f" Адрес в CSV: 0x{file_addr:08x}\n") + log_file.write(f" Команда: {cmd}\n") + log_file.write(f" Операнды: {operands}\n") # Вычисляем current_pos как смещение в секции section = row[segment_idx].strip() @@ -224,7 +188,7 @@ def reencode_instructions(entries, headers): elif section == '.bss': current_pos = instr_virt_addr - vaddr_bnd if vaddr_bnd else 0 else: - # Fallback: определяем по диапазону адресов + # Fallback: определяем по диапазону адресов - ФУФЛО, УБРАТЬ if vaddr_text and 0x401000 <= instr_virt_addr < 0x402000: current_pos = instr_virt_addr - vaddr_text elif vaddr_data and 0x402000 <= instr_virt_addr < 0x403000: @@ -238,9 +202,10 @@ def reencode_instructions(entries, headers): 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" РЕЗУЛЬТАТ: УСПЕШНО") + # Для отладки: записываем результат + if mnemonic == 'переместить_имм' and operands and '0x00403014' in cmd: + log_file.write(f" Закодировано: {[f'0x{b:02x}' for b in encoded]}\n") + log_file.write(f" Длина: {len(encoded)} байт\n") for j, byte in enumerate(encoded): if i + j < len(entries): @@ -251,12 +216,10 @@ def reencode_instructions(entries, headers): continue except Exception as 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}") + log_file.write(f"\n=== ОШИБКА ===\n") + log_file.write(f" Команда: {cmd}\n") + log_file.write(f" Мнемоника: {mnemonic if 'mnemonic' in locals() else '?'}\n") + log_file.write(f" Ошибка: {e}\n") error_count += 1 row[calc_byte_idx] = "0xcc" i += 1 @@ -293,34 +256,36 @@ def main(): sys.exit(1) csv_file = sys.argv[1] + log_file_name = csv_file.replace('.csv', '_pass4.log') print(f"\n=== Четвёртый проход КВС ===") print(f"Чтение CSV: {csv_file}") - headers, entries = read_csv(csv_file) - - print(f"\n--- Определение адресов секций из CSV ---") - detect_section_addresses(entries, headers) - - print(f"\n--- Сбор меток из CSV ---") - collect_labels_from_csv(entries, headers) - 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--- Перекодирование инструкций ---") - reencoded_count, error_count = reencode_instructions(entries, headers) + with open(log_file_name, 'w', encoding='utf-8') as log_file: + log_file.write(f"=== Четвёртый проход КВС ===\n") + log_file.write(f"CSV: {csv_file}\n\n") + + headers, entries = read_csv(csv_file) + + log_file.write(f"--- Определение адресов секций из CSV ---\n") + detect_section_addresses(entries, headers, log_file) + + log_file.write(f"\n--- Сбор меток из CSV ---\n") + collect_labels_from_csv(entries, headers, log_file) + + log_file.write(f"\n--- Перекодирование инструкций ---\n") + reencoded_count, error_count = reencode_instructions(entries, headers, log_file) + + log_file.write(f"\n--- Статистика ---\n") + log_file.write(f" Меток в словаре: {len(labels)}\n") + log_file.write(f" Перекодировано инструкций: {reencoded_count}\n") + log_file.write(f" Ошибок: {error_count}\n") print_statistics(reencoded_count, error_count, len(labels)) save_csv(csv_file, headers, entries) print(f"\nCSV сохранён: {csv_file}") - print(f"Колонка 'рассчитанный_байт' обновлена") + print(f"Лог сохранён: {log_file_name}") if __name__ == "__main__": diff --git a/инструменты/kvs_csv_builder.py b/инструменты/kvs_csv_builder.py deleted file mode 100644 index 08c983f..0000000 --- a/инструменты/kvs_csv_builder.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -""" -Генератор CSV-таблицы для программы на КВС (функциональная версия) -Экспериментальный однопроходный ассемблер - с корректными заглушками ELF -""" - -import sys -import csv -from typing import List, Optional, Tuple - -# Глобальное состояние -entries = [] # Список записей: (address, byte, target, source, segment) -current_address = 0 -current_segment = '.header' -last_segment = None -relocations = [] # Список отложенных ссылок: (pos, label, type, source) - -def reset_state(): - """Сбросить состояние генератора""" - global entries, current_address, current_segment, last_segment, relocations - entries = [] - current_address = 0 - current_segment = '.header' - last_segment = None - relocations = [] - -def set_segment(segment: str): - """Переключить текущий сегмент""" - global current_segment - current_segment = segment - -def add_byte(value: int, source: str = "", target: Optional[int] = None): - """Добавить один байт""" - global current_address, last_segment - - # Показываем сегмент только при смене - show_segment = current_segment if current_segment != last_segment else None - if show_segment: - last_segment = current_segment - - entries.append(( - current_address, - value & 0xFF, - target, - source, - show_segment - )) - current_address += 1 - -def add_bytes(values: List[int], source: str = ""): - """Добавить последовательность байтов""" - for v in values: - add_byte(v, source) - -def add_word32(value: int, source: str = ""): - """Добавить 32-битное слово (little-endian)""" - add_bytes([ - (value >> 0) & 0xFF, - (value >> 8) & 0xFF, - (value >> 16) & 0xFF, - (value >> 24) & 0xFF, - ], source) - -def add_word64(value: int, source: str = ""): - """Добавить 64-битное слово (little-endian)""" - add_bytes([ - (value >> 0) & 0xFF, - (value >> 8) & 0xFF, - (value >> 16) & 0xFF, - (value >> 24) & 0xFF, - (value >> 32) & 0xFF, - (value >> 40) & 0xFF, - (value >> 48) & 0xFF, - (value >> 56) & 0xFF, - ], source) - -def align_to(alignment: int): - """Выровнять адрес до границы (просто двигаем адрес)""" - global current_address - padding = (alignment - (current_address % alignment)) % alignment - current_address += padding - -def add_relocation(pos: int, label: str, reloc_type: str, source: str = ""): - """Запомнить место для отложенной подстановки""" - relocations.append((pos, label, reloc_type, source)) - -def generate_elf_header(): - """Сгенерировать корректный ELF заголовок с заглушками""" - # EI_MAGIC - add_bytes([0x7F, 0x45, 0x4C, 0x46], "ELF magic") - - # ei_class: 64-bit (2) - add_byte(2, "ELFCLASS64") - - # ei_data: little-endian (1) - add_byte(1, "ELFDATA2LSB") - - # ei_version: 1 - add_byte(1, "ELF version") - - # ei_osabi: System V (0) - add_byte(0, "OS ABI") - - # ei_abiversion: 0 - add_byte(0, "ABI version") - - # ei_pad: 7 байт паддинга - add_bytes([0] * 7, "Padding") - - # e_type: ET_EXEC (2) - add_word32(2, "e_type = ET_EXEC") - - # e_machine: EM_X86_64 (62) - add_word32(62, "e_machine = EM_X86_64") - - # e_version: 1 - add_word32(1, "e_version") - - # e_entry: точка входа (заглушка 0x401000) - add_word64(0x401000, "e_entry (stub: _start)") - - # e_phoff: смещение до program headers (64 байта) - add_word64(64, "e_phoff") - - # e_shoff: смещение до section headers (0 = нет секций) - add_word64(0, "e_shoff = 0") - - # e_flags: 0 - add_word32(0, "e_flags") - - # e_ehsize: размер ELF заголовка (64) - add_word32(64, "e_ehsize") - - # e_phentsize: размер program header entry (56) - add_word32(56, "e_phentsize") - - # e_phnum: количество program headers (2) - add_word32(2, "e_phnum = 2") - - # e_shentsize: размер section header entry (0) - add_word32(0, "e_shentsize = 0") - - # e_shnum: количество section headers (0) - add_word32(0, "e_shnum = 0") - - # e_shstrndx: индекс секции строк (0) - add_word32(0, "e_shstrndx") - -def generate_program_headers(): - """Сгенерировать program headers с корректными выравниваниями""" - - # Смещения в файле (должны быть кратны 4096) - text_offset = 0x1000 # 4096 - data_offset = 0x2000 # 8192 - - # Program Header 1: TEXT (RX) - add_word32(1, "p_type = PT_LOAD") - add_word32(5, "p_flags = RX") - add_word64(text_offset, "p_offset (text)") - add_word64(0x401000, "p_vaddr (text)") - add_word64(0x401000, "p_paddr (text)") - add_word64(0, "p_filesz (stub)") # Заглушка - add_word64(0, "p_memsz (stub)") # Заглушка - add_word64(0x1000, "p_align (4KB)") - - # Program Header 2: DATA (RW) - add_word32(1, "p_type = PT_LOAD") - add_word32(6, "p_flags = RW") - add_word64(data_offset, "p_offset (data)") - add_word64(0x402000, "p_vaddr (data)") - add_word64(0x402000, "p_paddr (data)") - add_word64(0, "p_filesz (stub)") # Заглушка - add_word64(0, "p_memsz (stub)") # Заглушка - add_word64(0x1000, "p_align (4KB)") - -def generate_empty_program(): - """Сгенерировать пустую программу с корректным ELF скелетом""" - reset_state() - - # Генерируем заголовки - set_segment('.header') - generate_elf_header() - generate_program_headers() - - # Выравнивание до .text секции - align_to(0x1000) - - # .text секция - set_segment('.text') - add_byte(0xCC, ".text section start (INT3)") - - # Выравнивание до .data секции - align_to(0x1000) - - # .data секция - set_segment('.data') - add_byte(0x00, ".data section start") - - # Вывод информации - print(f"\n=== Сгенерирован скелет программы ===", file=sys.stderr) - print(f"ELF заголовок: 64 байта", file=sys.stderr) - print(f"Program headers: 112 байт (2 * 56)", file=sys.stderr) - print(f".text: 1 байт по адресу 0x401000", file=sys.stderr) - print(f".data: 1 байт по адресу 0x402000", file=sys.stderr) - print(f"\nПроверка readelf:", file=sys.stderr) - print(f" e_phoff = 64 (0x40) - верно", file=sys.stderr) - print(f" p_offset(text) = 4096 (0x1000) - кратно p_align", file=sys.stderr) - print(f" p_offset(data) = 8192 (0x2000) - кратно p_align", file=sys.stderr) - -def save_to_csv(filename: str): - """Сохранить таблицу в CSV""" - with open(filename, 'w', newline='', encoding='utf-8') as f: - writer = csv.writer(f, delimiter=';') - writer.writerow(['адрес', 'байт', 'целевой_адрес', 'исходная_команда', 'сегмент']) - - for addr, byte, target, source, segment in entries: - writer.writerow([ - f"0x{addr:08x}", - f"0x{byte:02x}", - f"0x{target:08x}" if target is not None else "", - source, - segment if segment else "" - ]) - - print(f"CSV сохранен: {filename}", file=sys.stderr) - -def main(): - if len(sys.argv) < 2: - print("Usage: python3 kvs_csv_builder.py ") - sys.exit(1) - - generate_empty_program() - save_to_csv(sys.argv[1]) - print("\nГотово!", file=sys.stderr) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/инструменты/kvs_elf.py b/инструменты/kvs_elf.py deleted file mode 100644 index 5ab14b5..0000000 --- a/инструменты/kvs_elf.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -kvs_elf.py - Генерация ELF заголовков для КВС -Содержит функции для создания ELF64 заголовка и программных заголовков -""" - -import struct -from kvs_data import PAGE_SIZE, text_vaddr_base - - -class ELFHeader: - """Генератор ELF64 заголовков""" - - # ELF константы - EI_MAG0 = 0x7F - EI_MAG1 = ord('E') - EI_MAG2 = ord('L') - EI_MAG3 = ord('F') - EI_CLASS64 = 2 - EI_DATA2LSB = 1 - EI_VERSION = 1 - ET_EXEC = 2 - EM_X86_64 = 62 - EV_CURRENT = 1 - PT_LOAD = 1 - PF_R = 0x4 - PF_W = 0x2 - PF_X = 0x1 - - def __init__(self): - self.entry_point = 0 # будет заполнено позже - - def get_header_bytes(self): - """Возвращает байты ELF заголовка""" - # e_ident (16 байт) - e_ident = bytes([ - self.EI_MAG0, self.EI_MAG1, self.EI_MAG2, self.EI_MAG3, # magic - self.EI_CLASS64, # ELFCLASS64 - self.EI_DATA2LSB, # ELFDATA2LSB - self.EI_VERSION, # EV_CURRENT - 0, # OS ABI (none) - 0, # ABI version - 0, 0, 0, 0, 0, 0, 0 # padding - ]) - - # Собираем заголовок - header = bytearray(e_ident) - header.extend(struct.pack('