#!/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 text_buffer = bytearray() data_buffer = bytearray() bss_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, bss_size global 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() bss_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 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_bss_entry(size: int, label: str = None): global bss_size, vaddr_bnd bss_start = bss_size bss_size += size # Виртуальный адрес начала блока virt_addr = vaddr_bnd + bss_start entries.append(( ".bss", # сегмент "BSS", # файловый адрес (специальное значение) f"0x{virt_addr:08x}", # виртуальный адрес "", # байт "", # уводящий адрес f".резб {size}", # исходная команда label if label else "", # приводящая метка "", "", "" )) """Добавляет запись для BSS в CSV """ """ def add_bss_entry(size: int, label: str = None): global bss_size, vaddr_bnd bss_start = bss_size bss_size += size bss_file_addr = "BSS" bss_file_addr = current_address 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 f"0x{bss_file_addr + offset:08x}", # 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) def add_word16(value: int, source: str = "", target=None): 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(): 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") add_word16(2, "e_type = ET_EXEC") add_word16(62, "e_machine = EM_X86_64") add_word32(1, "e_version") add_word64(0, "e_entry (stub)") add_word64(64, "e_phoff") add_word64(0, "e_shoff = 0") add_word32(0, "e_flags") add_word16(64, "e_ehsize") add_word16(56, "e_phentsize") 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(): 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") 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": # Разбиваем максимум на 4 части: DIRECTIVE, имя, секция, остальное parts = line.split(':', 3) 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, bss_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) 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') 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} 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', '') 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: 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: 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, vaddr_data, vaddr_bnd print("\n[DEBUG finalize_headers] Начало") text_offset = 0x1000 text_filesz = len(text_buffer) text_memsz = len(text_buffer) # Выравнивание для начала секции данных 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) # Вычисляем виртуальные адреса с учётом выравнивания на 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: new_bytes = struct.pack(' 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 bss_size > 0: bnd_vaddr = align_up(data_vaddr + len(data_buffer), PAGE_SIZE) print(f" .bnd: 0x{bnd_vaddr:x} - 0x{bnd_vaddr + bss_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: {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)}") print(f" релокаций: {len(relocations)}") print(f" записей: {len(entries)}") print(f" CSV: {csv_file}") if __name__ == "__main__": main()