From 403b999d421a2d6fc4b3a943519329b0e4dc7a7c Mon Sep 17 00:00:00 2001 From: mzhoot Date: Mon, 25 May 2026 20:36:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20bss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvs_pass1.py | 196 +++-- инструменты/kvs_pass1 copy.py | 878 +++++++++++++++++++++++ тесты/тест_бнд2.квс | 45 ++ 3 files changed, 1049 insertions(+), 70 deletions(-) create mode 100644 инструменты/kvs_pass1 copy.py create mode 100644 тесты/тест_бнд2.квс diff --git a/kvs_pass1.py b/kvs_pass1.py index 293bb79..d0f00a5 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.py @@ -13,7 +13,7 @@ import sys import csv -import struct # <--- НОВЫЙ ИМПОРТ +import struct sys.path.insert(0, '.') from kvs_data import ( @@ -31,11 +31,11 @@ last_segment = None vaddr_text = text_vaddr_base vaddr_data = None vaddr_bnd = None -data_offset = 0x2000 # начальное значение, будет пересчитано в finalize_headers +data_offset = 0x2000 text_buffer = bytearray() data_buffer = bytearray() -bnd_size = 0 +bss_size = 0 labels = {} label_sections = {} @@ -52,7 +52,8 @@ relocations = [] def reset_state(): global entries, current_address, current_segment, last_segment - global text_buffer, data_buffer, bnd_size, vaddr_data, vaddr_bnd + global text_buffer, data_buffer, bss_size + global vaddr_data, vaddr_bnd global labels, label_sections, symbols, text_pos, data_pos, entry_point, relocations global pending_labels @@ -62,10 +63,10 @@ def reset_state(): last_segment = None text_buffer = bytearray() data_buffer = bytearray() - bnd_size = 0 + bss_size = 0 vaddr_data = None vaddr_bnd = None - data_offset = 0x2000 # добавить + data_offset = 0x2000 labels = {} label_sections = {} symbols = {} @@ -83,7 +84,7 @@ def set_segment(segment: str): def get_virtual_address(): """Возвращает виртуальный адрес для текущей позиции""" - global data_offset # добавить + global data_offset if current_segment == '.text': return vaddr_text + (current_address - 0x1000) @@ -104,7 +105,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 show_segment: last_segment = current_segment @@ -129,36 +130,59 @@ def add_byte(value: int, source: str = "", target=None, labels_list=None): pending_labels = [] +def add_bss_entry(size: int, label: str = None): + """Добавляет запись для BSS в CSV (БЕЗ увеличения файлового адреса)""" + global bss_size, vaddr_bnd + + bss_start = bss_size + bss_size += size + + bss_file_addr = "BSS" + + for offset in range(size): + virt_addr = vaddr_bnd + bss_start + offset + label_name = label if offset == 0 and label else "" + + # ПОРЯДОК ПОЛЕЙ ДОЛЖЕН СООТВЕТСТВОВАТЬ РАСПАКОВКЕ В save_to_csv: + # segment, addr, virt_addr, byte_str, target, source, label, cmd_with_values, calc_target, calc_byte + entries.append(( + ".bss", # segment + bss_file_addr, # addr + f"0x{virt_addr:08x}", # virt_addr + "0x00", # byte_str + "", # target (уводящий_адрес) - пусто + f".резб {size}" if offset == 0 else "", # source (исходная_команда) + label_name, # label (приводящая_метка) ← сюда имя метки! + "", # cmd_with_values + "", # calc_target + "0x00" # calc_byte + )) + + + def add_bytes(values, source: str = "", target=None): for i, v in enumerate(values): add_byte(v, source if i == 0 else "", target if i == 0 else None) -# ========== РЕФАКТОРИНГ: ИСПОЛЬЗУЕМ struct.pack ========== - def add_word16(value: int, source: str = "", target=None): - """Добавляет 16-битное значение (little-endian)""" add_bytes(struct.pack(' 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 @@ -452,7 +460,7 @@ def get_unresolved_labels(operands): def process_ast_line(line): - global text_pos, data_pos, vaddr_data, vaddr_bnd, bnd_size + global text_pos, data_pos, vaddr_data, vaddr_bnd, bss_size global text_buffer, data_buffer, entry_point, symbols, pending_labels line_type, fields = parse_ast_line(line) @@ -466,14 +474,16 @@ 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') + # Инициализируем vaddr_bnd когда входим в BSS + if vaddr_bnd is None: + data_vaddr = vaddr_data if vaddr_data else align_up(vaddr_text + text_pos, PAGE_SIZE) + vaddr_bnd = align_up(data_vaddr + data_pos, PAGE_SIZE) elif directive == '.глобал': entry_point = fields.get('label', '_start') @@ -530,7 +540,14 @@ def process_ast_line(line): elif directive in ('.резб', '.резс', '.рездс', '.резкс'): count = int(fields.get('count', '0')) multiplier = {'резб': 1, 'резс': 2, 'рездс': 4, 'резкс': 8} - bnd_size += count * multiplier.get(directive, 1) + size = count * multiplier.get(directive, 1) + + # Добавляем BSS в CSV + label_name = pending_labels[0] if pending_labels else None + add_bss_entry(size, label_name) + + if label_name and pending_labels: + pending_labels = [] elif line_type == "LABEL": label_name = fields.get('name', '') @@ -554,8 +571,9 @@ def process_ast_line(line): elif sec == '.бнд': if vaddr_bnd is None: - vaddr_bnd = align_up(vaddr_data + data_pos, PAGE_SIZE) - addr = vaddr_bnd + bnd_size + data_vaddr = vaddr_data if vaddr_data else align_up(vaddr_text + text_pos, PAGE_SIZE) + vaddr_bnd = align_up(data_vaddr + data_pos, PAGE_SIZE) + addr = vaddr_bnd + bss_size labels[label_name] = addr label_sections[label_name] = sec if label_name not in pending_labels: @@ -651,27 +669,62 @@ def process_ast_line(line): def finalize_headers(): - global entries, data_offset # добавить data_offset в global + global entries, data_offset, vaddr_data, vaddr_bnd + + print("\n[DEBUG finalize_headers] Начало") text_offset = 0x1000 text_filesz = len(text_buffer) text_memsz = len(text_buffer) - # ВЫЧИСЛЯЕМ ФАЙЛОВОЕ СМЕЩЕНИЕ ДЛЯ DATA ДИНАМИЧЕСКИ + # Выравнивание для начала секции данных data_offset = align_up(text_offset + text_filesz, PAGE_SIZE) + print(f"[DEBUG] text_filesz={text_filesz}, data_offset=0x{data_offset:x}") data_filesz = len(data_buffer) - data_memsz = len(data_buffer) + bnd_size - vaddr_data_final = align_up(vaddr_text + text_filesz, PAGE_SIZE) + # Вычисляем виртуальные адреса с учётом выравнивания на PAGE_SIZE + vaddr_data_start = align_up(vaddr_text + text_filesz, PAGE_SIZE) + vaddr_bss_start = align_up(vaddr_data_start + data_filesz, PAGE_SIZE) + + # Сохраняем в глобальные переменные + vaddr_data = vaddr_data_start + vaddr_bnd = vaddr_bss_start + + # memsz должен покрывать всё: от начала .data до конца .bss + # Важно: включаем выравнивание между .data и .bss! + data_memsz = (vaddr_bss_start + bss_size) - vaddr_data_start + + print(f"[DEBUG] vaddr_data_start=0x{vaddr_data_start:x}") + print(f"[DEBUG] vaddr_bss_start=0x{vaddr_bss_start:x}") + print(f"[DEBUG] data_filesz={data_filesz}, bss_size={bss_size}") + print(f"[DEBUG] data_memsz={data_memsz} (файл: {data_filesz}, память: {data_memsz})") + + # Обновляем виртуальные адреса для BSS записей в CSV + for i, entry in enumerate(entries): + if entry[0] == ".bss" and entry[1] == "BSS": + old_virt = int(entry[2], 16) + # Вычисляем смещение внутри BSS + bss_offset = old_virt - vaddr_bss_start if vaddr_bss_start else 0 + new_virt = vaddr_bss_start + bss_offset + entries[i] = (entry[0], entry[1], f"0x{new_virt:08x}", entry[3], + entry[4], entry[5], entry[6], entry[7], entry[8], entry[9]) + print(f"[DEBUG] BSS запись: virt 0x{old_virt:x} -> 0x{new_virt:x}") + entry_point_addr = labels.get(entry_point, vaddr_text) + print(f"[DEBUG] entry_point_addr=0x{entry_point_addr:x}") i = 0 while i < len(entries): segment, addr, virt_addr, byte_str, target, source, label, cmd_with_values, calc_target, calc_byte = entries[i] + # Пропускаем BSS-записи (они уже обработаны выше) + if segment == ".bss": + i += 1 + continue + + # Заполнение точки входа if 'e_entry (stub)' in source: - # Вставляем 8 байт (uint64_t) new_bytes = struct.pack(' 0: - print(f" .bnd: {bnd_size} байт, смещение 0x{bnd_start_offset:x} - 0x{bnd_end_offset-1:x}") + if bss_size > 0: + print(f" .bnd: {bss_size} байт (только в памяти, не в файле)") 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: + if bss_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}") + print(f" .bnd: 0x{bnd_vaddr:x} - 0x{bnd_vaddr + bss_size - 1:x}") def main(): @@ -856,7 +913,6 @@ def main(): finalize_headers() save_to_csv(csv_file) - # Добавить эту строку: print_section_info() vaddr_data_final = align_up(vaddr_text + len(text_buffer), PAGE_SIZE) @@ -865,7 +921,7 @@ def main(): 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}, offset=0x{align_up(0x1000 + len(text_buffer), PAGE_SIZE):x}") - print(f" .bnd: {bnd_size} байт, vaddr=0x{vaddr_bnd_final:x}") + print(f" .bnd: {bss_size} байт, vaddr=0x{vaddr_bnd_final:x} (только в памяти)") print(f" точка входа: {entry_point} = 0x{labels.get(entry_point, vaddr_text):x}") print(f" меток: {len(labels)}") print(f" констант: {len(symbols)}") diff --git a/инструменты/kvs_pass1 copy.py b/инструменты/kvs_pass1 copy.py new file mode 100644 index 0000000..293bb79 --- /dev/null +++ b/инструменты/kvs_pass1 copy.py @@ -0,0 +1,878 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Первый проход КВС (однопроходная версия с заглушками) +Генерирует CSV-таблицу напрямую из AST +- Неизвестные метки заменяются заглушками (0xCC) с пометкой в колонке 'уводящий_адрес' +- В колонке 'приводящая_метка' записываются имена меток, находящихся по данному адресу (через запятую) +- Колонка 'команда_со_значениями' оставлена пустой (для второго прохода) +- Колонки 'рассчитанный_уводящий_адрес' и 'рассчитанный_байт' оставлены для второго прохода +- Колонка 'виртуальный_адрес' содержит виртуальный адрес байта (для pass4) +""" + +import sys +import csv +import struct # <--- НОВЫЙ ИМПОРТ + +sys.path.insert(0, '.') +from kvs_data import ( + PAGE_SIZE, text_vaddr_base, align_up, + ALWAYS_STUB_INSTRUCTIONS +) +from kvs_encoder import encode_instruction + +# ========== ГЛОБАЛЬНОЕ СОСТОЯНИЕ ========== +entries = [] +current_address = 0 +current_segment = '.header' +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() +bnd_size = 0 + +labels = {} +label_sections = {} +symbols = {} + +pending_labels = [] + +text_pos = 0 +data_pos = 0 + +entry_point = "_start" +relocations = [] + + +def reset_state(): + global entries, current_address, current_segment, last_segment + global text_buffer, data_buffer, bnd_size, vaddr_data, vaddr_bnd + global labels, label_sections, symbols, text_pos, data_pos, entry_point, relocations + global pending_labels + + entries = [] + current_address = 0 + current_segment = '.header' + last_segment = None + text_buffer = bytearray() + data_buffer = bytearray() + bnd_size = 0 + vaddr_data = None + vaddr_bnd = None + data_offset = 0x2000 # добавить + labels = {} + label_sections = {} + symbols = {} + text_pos = 0 + data_pos = 0 + entry_point = "_start" + relocations = [] + pending_labels = [] + + +def set_segment(segment: str): + global current_segment + current_segment = segment + + +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 - data_offset) + else: + return current_address + + +def add_byte(value: int, source: str = "", target=None, labels_list=None): + global current_address, last_segment, pending_labels + + effective_labels = "" + if labels_list is not None and labels_list: + effective_labels = ','.join(labels_list) + elif pending_labels: + effective_labels = ','.join(pending_labels) + + show_segment = current_segment# if current_segment != last_segment else None + if show_segment: + last_segment = current_segment + + virt_addr = get_virtual_address() + byte_val = value & 0xFF + + entries.append(( + show_segment if show_segment else "", + current_address, + f"0x{virt_addr:08x}", + f"0x{byte_val:02x}", + target if target else "", + source, + effective_labels, + "", + "", + f"0x{byte_val:02x}" + )) + current_address += 1 + + if pending_labels: + pending_labels = [] + + +def add_bytes(values, source: str = "", target=None): + for i, v in enumerate(values): + add_byte(v, source if i == 0 else "", target if i == 0 else None) + + +# ========== РЕФАКТОРИНГ: ИСПОЛЬЗУЕМ struct.pack ========== + +def add_word16(value: int, source: str = "", target=None): + """Добавляет 16-битное значение (little-endian)""" + add_bytes(struct.pack(' 0: + pending_labels = [] + current_address += padding + + +def unescape_string(s): + result = [] + i = 0 + while i < len(s): + if s[i] == '\\' and i + 1 < len(s): + if s[i + 1] == 'n': + result.append('\n') + elif s[i + 1] == 't': + result.append('\t') + elif s[i + 1] == 'r': + result.append('\r') + else: + result.append(s[i + 1]) + i += 2 + else: + result.append(s[i]) + i += 1 + return ''.join(result) + + +def generate_elf_header(): + """Генерирует заголовок ELF (64 байта)""" + # ELF magic + add_bytes([0x7F, 0x45, 0x4C, 0x46], "ELF magic") + add_byte(2, "ELFCLASS64") + add_byte(1, "ELFDATA2LSB") + add_byte(1, "ELF version") + add_byte(0, "OS ABI") + add_byte(0, "ABI version") + add_bytes([0] * 7, "Padding") + + # e_type, e_machine, e_version + add_word16(2, "e_type = ET_EXEC") + add_word16(62, "e_machine = EM_X86_64") + add_word32(1, "e_version") + + # e_entry (stub - будет заполнен позже) + add_word64(0, "e_entry (stub)") + + # e_phoff, e_shoff + add_word64(64, "e_phoff") + add_word64(0, "e_shoff = 0") + + # e_flags, e_ehsize, e_phentsize + add_word32(0, "e_flags") + add_word16(64, "e_ehsize") + add_word16(56, "e_phentsize") + + # e_phnum, e_shentsize, e_shnum, e_shstrndx + add_word16(2, "e_phnum = 2") + add_word16(0, "e_shentsize = 0") + add_word16(0, "e_shnum = 0") + add_word16(0, "e_shstrndx") + + +def generate_program_headers(): + """Генерирует два программных заголовка (PT_LOAD)""" + # === Первый заголовок: TEXT (RX) === + add_word32(1, "p_type = PT_LOAD") + add_word32(5, "p_flags = RX") + add_word64(0, "p_offset (text stub)") # будет заполнено позже + add_word64(vaddr_text, "p_vaddr (text)") + add_word64(vaddr_text, "p_paddr (text)") + add_word64(0, "p_filesz (text stub)") # будет заполнено позже + add_word64(0, "p_memsz (text stub)") # будет заполнено позже + add_word64(PAGE_SIZE, "p_align") + + # === Второй заголовок: DATA (RW) === + add_word32(1, "p_type = PT_LOAD") + add_word32(6, "p_flags = RW") + add_word64(0, "p_offset (data stub)") # будет заполнено позже + add_word64(0, "p_vaddr (data stub)") # будет заполнено позже + add_word64(0, "p_paddr (data stub)") # будет заполнено позже + add_word64(0, "p_filesz (data stub)") # будет заполнено позже + add_word64(0, "p_memsz (data stub)") # будет заполнено позже + add_word64(PAGE_SIZE, "p_align") + + +def read_ast(input_file): + with open(input_file, 'r', encoding='utf-8') as f: + return [line.strip() for line in f] + + +def parse_ast_line(line): + if not line: + return None, {} + + line_type = line.split(':')[0] + + if line_type == "INSTR": + first = line.find(':') + second = line.find(':', first + 1) + third = line.find(':', second + 1) + + if first == -1 or second == -1 or third == -1: + return "INSTR", {} + + mnemonic = line[first + 1:second] + section = line[second + 1:third] + operands_str = line[third + 1:] + operands = operands_str.split(',') if operands_str else [] + + return "INSTR", { + 'mnemonic': mnemonic, + 'section': section, + 'operands': operands + } + + elif line_type == "DIRECTIVE": + parts = line.split(':') + directive = parts[1] if len(parts) > 1 else "" + + if directive in ('.текст', '.данные', '.бнд'): + return "DIRECTIVE", {'directive': directive} + elif directive == '.глобал': + return "DIRECTIVE", {'directive': directive, 'label': parts[2] if len(parts) > 2 else ""} + elif directive in ('.строка', '.строка_нуль'): + return "DIRECTIVE", { + 'directive': directive, + 'section': parts[2] if len(parts) > 2 else "", + 'string': parts[3] if len(parts) > 3 else "" + } + elif directive == '.константа': + return "DIRECTIVE", { + 'directive': directive, + 'name': parts[2] if len(parts) > 2 else "", + 'value': parts[3] if len(parts) > 3 else "" + } + elif directive == '.байт': + return "DIRECTIVE", { + 'directive': directive, + 'section': parts[2] if len(parts) > 2 else "", + 'bytes': parts[3] if len(parts) > 3 else "" + } + elif directive in ('.резб', '.резс', '.рездс', '.резкс'): + return "DIRECTIVE", { + 'directive': directive, + 'section': parts[2] if len(parts) > 2 else "", + 'count': parts[3] if len(parts) > 3 else "0" + } + else: + return "DIRECTIVE", {'directive': directive} + + elif line_type == "LABEL": + parts = line.split(':') + return "LABEL", { + 'name': parts[1] if len(parts) > 1 else "", + 'section': parts[2] if len(parts) > 2 else "" + } + + return line_type, {} + + +def get_stub_size(mnemonic, operands): + 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] + if reg in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', + 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'): + return 10 + elif reg in ('еаикс', 'ебикс', 'есикс', 'едикс'): + return 7 + else: + return 2 + elif mnemonic in ("загрузить", "сохранить", "загрузить_адрес"): + return 7 + elif mnemonic == "сравнить_с": + return 7 + elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно"): + return 7 + elif mnemonic in ("переместить_с_нулями", "переместить_со_знаком"): + return 7 + return 1 + + +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: + if not op: + continue + 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б', + 'спл', 'бпл', 'сил', 'дил'): + continue + if op.startswith('MEM:reg_indirect:'): + continue + if op.startswith('[') and op.endswith(']'): + inner = op[1:-1] + if inner in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай'): + continue + if inner.isdigit() or (inner.startswith('0x') and len(inner) > 2): + continue + if inner not in labels: + return True + continue + if op.isdigit() or (op.startswith('0x') and len(op) > 2): + continue + if op in symbols: + continue + if op not in labels: + return True + + return False + + +def get_unresolved_labels(operands): + unresolved = [] + for op in operands: + if not op: + continue + if op.startswith('[') and op.endswith(']'): + inner = op[1:-1] + if inner not in labels and not inner.isdigit() and not (inner.startswith('0x')): + if inner not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай'): + if inner not in symbols: + unresolved.append(inner) + elif op not in labels and not op.isdigit() and not (op.startswith('0x')): + if op not in ('раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', + 'еаикс', 'ебикс', 'есикс', 'едикс', + 'аикс', 'бикс', 'сикс', 'дикс', + 'ал', 'бл', 'кл', 'дл', + 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'): + if op not in symbols: + unresolved.append(op) + return unresolved + + +def process_ast_line(line): + global text_pos, data_pos, vaddr_data, vaddr_bnd, bnd_size + global text_buffer, data_buffer, entry_point, symbols, pending_labels + + line_type, fields = parse_ast_line(line) + + if line_type == "DIRECTIVE": + directive = fields.get('directive', '') + + if directive == '.текст': + set_segment('.text') + + 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') + + elif directive == '.глобал': + entry_point = fields.get('label', '_start') + + elif directive == '.константа': + name = fields.get('name', '') + value_str = fields.get('value', '') + if value_str.startswith('0x') or value_str.startswith('0X'): + value = int(value_str, 16) + else: + value = int(value_str) + symbols[name] = value + print(f" константа: {name} = {value}") + + elif directive in ('.строка', '.строка_нуль'): + s = fields.get('string', '') + real_s = unescape_string(s) + bstring = real_s.encode('utf-8') + if directive == '.строка_нуль': + bstring += b'\x00' + + source = f'{directive} "{s}"' + + for i, byte in enumerate(bstring): + labels_list = pending_labels if i == 0 else None + add_byte(byte, source if i == 0 else "", None, labels_list) + data_buffer.append(byte) + if labels_list: + pending_labels = [] + data_pos += len(bstring) + + elif directive == '.байт': + bytes_str = fields.get('bytes', '') + if bytes_str: + byte_values = bytes_str.split(',') + source = f'.байт {bytes_str}' + bstring = bytearray() + for byte_str in byte_values: + byte_str = byte_str.strip() + if byte_str.startswith('0x'): + val = int(byte_str, 16) + else: + val = int(byte_str) + bstring.append(val & 0xFF) + + for i, byte in enumerate(bstring): + labels_list = pending_labels if i == 0 else None + add_byte(byte, source if i == 0 else "", None, labels_list) + data_buffer.append(byte) + if labels_list: + pending_labels = [] + data_pos += len(bstring) + + elif directive in ('.резб', '.резс', '.рездс', '.резкс'): + count = int(fields.get('count', '0')) + multiplier = {'резб': 1, 'резс': 2, 'рездс': 4, 'резкс': 8} + bnd_size += count * multiplier.get(directive, 1) + + elif line_type == "LABEL": + label_name = fields.get('name', '') + sec = fields.get('section', '') + + if sec == '.text': + addr = vaddr_text + text_pos + labels[label_name] = addr + label_sections[label_name] = sec + if label_name not in pending_labels: + pending_labels.append(label_name) + + elif sec == '.data': + if vaddr_data is None: + vaddr_data = align_up(vaddr_text + text_pos, PAGE_SIZE) + addr = vaddr_data + data_pos + labels[label_name] = addr + label_sections[label_name] = sec + if label_name not in pending_labels: + pending_labels.append(label_name) + + elif sec == '.бнд': + if vaddr_bnd is None: + vaddr_bnd = align_up(vaddr_data + data_pos, PAGE_SIZE) + addr = vaddr_bnd + bnd_size + labels[label_name] = addr + label_sections[label_name] = sec + if label_name not in pending_labels: + pending_labels.append(label_name) + + else: + addr = current_address + labels[label_name] = addr + label_sections[label_name] = sec + if label_name not in pending_labels: + pending_labels.append(label_name) + + elif line_type == "INSTR": + mnemonic = fields.get('mnemonic', '') + sec = fields.get('section', '') + operands = fields.get('operands', []) + + source = f"{mnemonic} {', '.join(operands)}" if operands else mnemonic + + need_stub = has_unresolved_labels(operands, mnemonic) + + if need_stub: + stub_size = get_stub_size(mnemonic, operands) + unresolved = get_unresolved_labels(operands) + + if 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({ + 'address': current_address, + 'size': stub_size, + 'labels': unresolved if unresolved else [label_from_op] if label_from_op else [], + 'source': source, + 'mnemonic': mnemonic + }) + + for i in range(stub_size): + labels_list = pending_labels if i == 0 else None + add_byte(0xCC, source if i == 0 else "", reloc_text if i == 0 else None, labels_list) + if labels_list: + pending_labels = [] + + if sec == '.text': + text_buffer.extend([0xCC] * stub_size) + text_pos += stub_size + else: + data_buffer.extend([0xCC] * stub_size) + data_pos += stub_size + else: + try: + if sec == '.text': + current_pos = text_pos + else: + current_pos = data_pos + + encoded = encode_instruction( + mnemonic, operands, labels, label_sections, symbols, + vaddr_text, vaddr_data if vaddr_data else 0, + current_pos, vaddr_bnd if vaddr_bnd else 0 + ) + + for i, byte in enumerate(encoded): + labels_list = pending_labels if i == 0 else None + add_byte(byte, source if i == 0 else "", None, labels_list) + if labels_list: + pending_labels = [] + + if sec == '.text': + text_buffer.extend(encoded) + text_pos += len(encoded) + else: + data_buffer.extend(encoded) + data_pos += len(encoded) + + except Exception as e: + print(f"Ошибка кодирования {source}: {e}", file=sys.stderr) + labels_to_attach = pending_labels.copy() + add_byte(0xCC, source, f"ОШИБКА: {e}", labels_to_attach) + pending_labels.clear() + + if sec == '.text': + text_buffer.append(0xCC) + text_pos += 1 + else: + data_buffer.append(0xCC) + data_pos += 1 + + +def finalize_headers(): + global entries, data_offset # добавить data_offset в global + + text_offset = 0x1000 + 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 + + vaddr_data_final = align_up(vaddr_text + text_filesz, PAGE_SIZE) + entry_point_addr = labels.get(entry_point, vaddr_text) + + i = 0 + while i < len(entries): + segment, addr, virt_addr, byte_str, target, source, label, cmd_with_values, calc_target, calc_byte = entries[i] + + if 'e_entry (stub)' in source: + # Вставляем 8 байт (uint64_t) + new_bytes = struct.pack(' 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>") + sys.exit(1) + + ast_file = sys.argv[1] + csv_file = sys.argv[2] + + reset_state() + + set_segment('.header') + generate_elf_header() + generate_program_headers() + align_to(PAGE_SIZE) + + ast_lines = read_ast(ast_file) + for line in ast_lines: + process_ast_line(line) + + 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}, 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)}") + print(f" констант: {len(symbols)}") + print(f" релокаций: {len(relocations)}") + print(f" записей: {len(entries)}") + print(f" CSV: {csv_file}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/тесты/тест_бнд2.квс b/тесты/тест_бнд2.квс new file mode 100644 index 0000000..d80f2a2 --- /dev/null +++ b/тесты/тест_бнд2.квс @@ -0,0 +1,45 @@ +.текст +.глобал _start + +_start: + ; ============================================= + ; ТЕСТ 1: Абсолютная адресация в .бнд + ; ============================================= + переместить_имм раикс, 0x42 + сохранить [абс_перем], раикс + + ; ============================================= + ; ТЕСТ 2: Косвенная адресация в .бнд + ; ============================================= + переместить_имм рбикс, косв_перем + переместить_имм раикс, 0x99 + сохранить [рбикс], раикс + + ; ============================================= + ; ТЕСТ 3: Ещё одна косвенная адресация в .бнд + ; ============================================= + переместить_имм рсикс, ещё_перем + переместить_имм раикс, 0x77 + сохранить [рсикс], раикс + + ; ============================================= + ; Читаем всё обратно + ; ============================================= + загрузить р8, [абс_перем] + загрузить р9, [рбикс] + загрузить р10, [рсикс] + + ; ============================================= + ; ЗАВЕРШЕНИЕ: возвращаем из абс_перем + ; ============================================= + загрузить рдиай, [абс_перем] + переместить_имм раикс, 60 + вызов_системы + +.данные +пустые_данные: .байт 0 + +.бнд +абс_перем: .резб 1 +косв_перем: .резб 1 +ещё_перем: .резб 8 \ No newline at end of file