diff --git a/objdump_readelf/kvs_.py b/objdump_readelf/kvs_.py new file mode 100644 index 0000000..4db16f9 --- /dev/null +++ b/objdump_readelf/kvs_.py @@ -0,0 +1,560 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +КВС - язык программирования +версия minimal.py (обновлённая, с полной поддержкой ELF) +без использования re +""" + +import sys +import struct + +# Глобальная переменная для имени исходного файла +source_filename = "" + +def safe_close(f): + if f is not None: + f.close() + +# === Глобальные данные === +INSTRUCTIONS = { + "переместить_имм": {"code": None, "type": "reg_imm"}, + "сравнить_с": {"code": None, "type": "cmp_reg_imm"}, + "переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"}, + "вызов_системы": {"code": b"\x0F\x05", "type": "none"}, + "переход": {"code": b"\xE9", "type": "jmp"}, +} + +REGISTERS = { + "раикс": 0, # RAX + "рдикс": 2, # RDX + "рсиай": 6, # RSI + "рдиай": 7, # RDI +} + +# Глобальное состояние ассемблера +labels = {} +label_sections = {} +symbols = {} +sections = {".text": bytearray(), ".data": bytearray()} +current_section = ".text" +entry_point = "_start" +position = {".text": 0, ".data": 0} +pass_num = 0 + +# === ELF layout (глобальные переменные для второго прохода и записи) === +PAGE_SIZE = 0x1000 +elf_base_vaddr = 0x400000 # первая страница — заголовки +text_vaddr_base = 0x401000 # вторая страница — .text +data_vaddr_base = 0x402000 # третья страница — .data + +# Смещения и адреса (вычисляются после первого прохода) +text_size = 0 +data_size = 0 +offset_text = 0 +offset_data = 0 +shstrtab_offset = 0 +shdr_offset = 0 +vaddr_text = 0 +vaddr_data = 0 + +def align_up(x, align): + return (x + align - 1) & ~(align - 1) + +def get_reg_info(reg_name): + return {"size": 64, "index": REGISTERS[reg_name]} + +# === Функции формирования ошибок === + +def make_error_msg(line_num, line_text, detail): + return f"Ошибка в файле {source_filename}, строка {line_num}:\n {line_text}\n{detail}" + +def error_invalid_operand_count(mnemonic, expected, got): + return f"Инструкция '{mnemonic}':\n ожидается {expected} операнд(а/ов), получено: {got}" + +def error_unknown_mnemonic(mnemonic): + return f"Неизвестная инструкция: '{mnemonic}'" + +def error_invalid_register(op_name, reg_name, mnemonic): + return f"Инструкция '{mnemonic}':\n операнд '{op_name}' = '{reg_name}' не является допустимым регистром.\n Допустимые регистры: {', '.join(list(REGISTERS.keys()))}" + +def error_invalid_number_format(s): + return f"Недопустимый формат числа: '{s}'" + +def error_unknown_directive(word): + return f"Неизвестная директива: '{word}'" + +def error_missing_label(label): + return f"Метка не найдена: '{label}'" + +def error_unexpected_string_in_byte(): + return ".байт требует числовые значения, не строки" + +def error_unterminated_string(): + return "Незакрытая кавычка в строке" + +# === Лексер === + +def tokenize_line(line): + semi = line.find(';') + if semi != -1: + line = line[:semi] + line = line.rstrip() + if not line: + return [] + tokens = [] + i = 0 + n = len(line) + while i < n: + ch = line[i] + if ch.isspace(): + i += 1 + continue + if ch == '"': + i += 1 + s = '' + while i < n and line[i] != '"': + if line[i] == '\\' and i + 1 < n: + i += 1 + esc = line[i] + if esc == 'n': + s += '\n' + elif esc == 't': + s += '\t' + elif esc == '"': + s += '"' + elif esc == '\\': + s += '\\' + else: + s += '\\' + esc + i += 1 + else: + s += line[i] + i += 1 + if i >= n: + raise ValueError(error_unterminated_string()) + i += 1 + tokens.append(('string', s)) + continue + if ch == ',': + tokens.append(('comma', ',')) + i += 1 + continue + if ch == ':': + tokens.append(('colon', ':')) + i += 1 + continue + j = i + while j < n and not (line[j].isspace() or line[j] in ',:;'): + j += 1 + word = line[i:j] + if word: + tokens.append(('word', word)) + i = j + return tokens + +# === Парсинг === + +def parse_operand(operand): + if operand.isdigit(): + return int(operand) + if operand.startswith("0x"): + return int(operand, 16) + if operand in labels: + section = label_sections[operand] + if section == ".text": + return labels[operand] + vaddr_text + elif section == ".data": + return labels[operand] + vaddr_data + else: + raise ValueError(f"Метка в неизвестной секции: {section}") + if operand in symbols: + return symbols[operand] + raise ValueError(f"Неизвестный операнд: {operand}") + +def parse_number_token(token): + if token[0] != 'word': + raise ValueError(f"Ожидалось число, получено: {token}") + s = token[1] + if s.isdigit(): + return int(s) + if s.startswith("0x"): + return int(s, 16) + raise ValueError(error_invalid_number_format(s)) + +def encode_instruction(mnemonic, operands, line_num, line_text): + instr = INSTRUCTIONS[mnemonic] + code = bytearray() + itype = instr["type"] + + if itype == "none": + code.extend(instr["code"]) + + elif itype == "reg_imm": + if len(operands) != 2: + raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) + reg_info = get_reg_info(operands[0]) + if reg_info is None: + raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) + reg = reg_info["index"] + imm = parse_operand(operands[1]) + + if 0 <= reg <= 7: + code.extend(b'\x48') + code.append(0xB8 + reg) + code.extend(struct.pack('= 8: + rex |= 0x01 + code.append(rex) + code.append(0x81) + modrm = 0xF8 | (reg & 7) + code.append(modrm) + code.extend(struct.pack(' 0x7FFFFFFF: + raise ValueError(f"Цель слишком далеко для 32-битного смещения в инструкции '{mnemonic}': {offset} (должно быть в [-2^31, 2^31-1])") + code.extend(struct.pack(' 0x7FFFFFFF: + raise ValueError(f"Цель условного перехода слишком далеко: {offset}") + code.extend(struct.pack('= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon': + label = tokens[0][1] + labels[label] = position[current_section] + label_sections[label] = current_section + rest = tokens[2:] + if rest: + parse_instruction_or_directive(rest, line_num, line_text) + return + parse_instruction_or_directive(tokens, line_num, line_text) + +def parse(source): + global pass_num, text_size, data_size + global offset_text, offset_data, shstrtab_offset, shdr_offset + global vaddr_text, vaddr_data + global sections, position + + lines = source.split('\n') + # === ПРОХОД 1: анализ === + pass_num = 1 + position[".text"] = 0 + position[".data"] = 0 + sections[".text"] = bytearray() + sections[".data"] = bytearray() + labels.clear() + label_sections.clear() + + for line_num, line in enumerate(lines, start=1): + original_line = line.rstrip() + try: + tokens = tokenize_line(line) + parse_tokens(tokens, line_num, original_line) + except Exception as e: + error_full = make_error_msg(line_num, original_line, str(e)) + print(error_full, file=sys.stderr) + sys.exit(1) + + text_size = position[".text"] + data_size = position[".data"] + + # === ВЫЧИСЛЕНИЕ РАЗМЕЩЕНИЯ === + elf_header_size = 64 + ph_size = 56 + ph_num = 3 + ph_table_size = ph_num * ph_size + + offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE) + offset_data = align_up(offset_text + text_size, PAGE_SIZE) + + vaddr_text = text_vaddr_base + vaddr_data = data_vaddr_base + + # Section headers + shstrtab_content = b"\x00.text\x00.data\x00.shstrtab\x00" + shstrtab_size = len(shstrtab_content) + shstrtab_offset = align_up(offset_data + data_size, 8) + shdr_size = 64 + shdr_num = 4 + shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16) + + # === ПРОХОД 2: генерация === + pass_num = 2 + position[".text"] = 0 + position[".data"] = 0 + sections[".text"] = bytearray() + sections[".data"] = bytearray() + for line_num, line in enumerate(lines, start=1): + original_line = line.rstrip() + try: + tokens = tokenize_line(line) + parse_tokens(tokens, line_num, original_line) + except Exception as e: + error_full = make_error_msg(line_num, original_line, str(e)) + print(error_full, file=sys.stderr) + sys.exit(1) + +def create_elf(filename): + global sections, entry_point, labels, vaddr_text, vaddr_data + global offset_text, offset_data, shstrtab_offset, shdr_offset + + text = sections[".text"] + data = sections[".data"] + + # Точка входа + entry_addr = vaddr_text + labels.get(entry_point, 0) + + # .shstrtab + shstrtab_content = b"\x00.text\x00.data\x00.shstrtab\x00" + shstrtab_size = len(shstrtab_content) + + # Размеры + elf_header_size = 64 + ph_size = 56 + ph_num = 3 + shdr_size = 64 + shdr_num = 4 + + file_size = shdr_offset + shdr_num * shdr_size + elf_data = bytearray(file_size) + + # === 1. ELF-заголовок === + e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8 + elf_header = struct.pack( + '<16sHHIQQQIHHHHHH', + e_ident, + 2, # ET_EXEC + 0x3e, # EM_X86_64 + 1, # version + entry_addr, # entry + elf_header_size,# phoff + shdr_offset, # shoff + 0, # flags + elf_header_size, + ph_size, + ph_num, + shdr_size, + shdr_num, + 3 # .shstrtab index + ) + elf_data[0:64] = elf_header + + # === 2. Program Headers === + def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz): + return struct.pack('") + sys.exit(1) + +source_file = sys.argv[1] +source_filename = source_file +if not source_file.endswith('.квс'): + print("Ошибка: файл должен иметь расширение .квс") + sys.exit(1) + +f_in = None +try: + f_in = open(source_file, "r", encoding="utf-8") + source = f_in.read() + f_in.close() +except FileNotFoundError: + safe_close(f_in) + print(f"Ошибка: файл '{source_file}' не найден.") + sys.exit(1) +except UnicodeDecodeError as e: + safe_close(f_in) + print(f"Ошибка кодировки в файле '{source_file}': {e}") + sys.exit(1) + +try: + parse(source) + elf_file = source_file[:-4] + ".elf" + create_elf(elf_file) + print(f"ELF-файл создан: {elf_file}") + print("Запустить: ./" + elf_file) + print(f"Проверить: objdump -D {elf_file}") + print(f"Проверить: eu-readelf -a {elf_file}") + print(f"Проверить: readelf -l {elf_file}") + print(f"Проверить: file {elf_file}") +except SystemExit: + raise +except Exception as e: + print(f"Внутренняя ошибка ассемблера: {e}", file=sys.stderr) + sys.exit(1) \ No newline at end of file