From 54b1de926ec2c3e0c391d30b436b3dd68f09b67e Mon Sep 17 00:00:00 2001 From: mzhoot Date: Sun, 7 Jun 2026 13:17:07 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20=D1=81=20readelf=20objdu?= =?UTF-8?q?mp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvs_build.py | 1 + kvs_pass1.py | 23 +- kvs_pass5.py | 732 ++++++++++++++++++ тесты/cleaner.py | 2 +- .../сравнитель/compare_elf.sh | 149 ++++ тесты/тест_бнд2.квс | 2 + 6 files changed, 897 insertions(+), 12 deletions(-) create mode 100644 kvs_pass5.py create mode 100755 тесты/сравнитель/compare_elf.sh diff --git a/kvs_build.py b/kvs_build.py index 572fa3d..5838b1b 100644 --- a/kvs_build.py +++ b/kvs_build.py @@ -33,6 +33,7 @@ if __name__ == "__main__": ("kvs_pass2.py", [csv_file], "2 проход"), ("kvs_pass3.py", [ast_file, csv_file], "3 проход"), ("kvs_pass4.py", [csv_file], "4 проход"), + ("kvs_pass5.py", [csv_file], "5 проход"), ("kvs_builder.py", [csv_file, elf_file], "Сборка ELF"), ] diff --git a/kvs_pass1.py b/kvs_pass1.py index d0f00a5..031ac8d 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.py @@ -131,13 +131,14 @@ def add_byte(value: int, source: str = "", target=None, labels_list=None): def add_bss_entry(size: int, label: str = None): - """Добавляет запись для BSS в CSV (БЕЗ увеличения файлового адреса)""" + """Добавляет запись для BSS в CSV """ 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 @@ -146,16 +147,16 @@ def add_bss_entry(size: int, label: str = None): # ПОРЯДОК ПОЛЕЙ ДОЛЖЕН СООТВЕТСТВОВАТЬ РАСПАКОВКЕ В 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 + ".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 )) diff --git a/kvs_pass5.py b/kvs_pass5.py new file mode 100644 index 0000000..7701af5 --- /dev/null +++ b/kvs_pass5.py @@ -0,0 +1,732 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Пятый проход КВС — генерация и добавление таблицы секций в CSV +Сканирует CSV, собирает информацию о секциях, обновляет ELF-заголовок +ПЕРЕСОЗДАЁТ ПРОГРАММНЫЕ ЗАГОЛОВКИ на основе реальных размеров секций +и добавляет таблицу секций в конец файла +ДОБАВЛЕНА НУЛЕВАЯ СЕКЦИЯ (индекс 0) +ДОБАВЛЕНА СЕКЦИЯ .comment со строкой "Сборщик КВС" +РАЗМЕЩЕНИЕ СЕКЦИЙ КАК В kvs_8.py +ПРАВИЛЬНЫЕ ИНДЕКСЫ В .shstrtab (с учётом наличия .bss) +""" + +import sys +import csv +import struct + +PAGE_SIZE = 0x1000 +ELF_BASE_VADDR = 0x400000 + +DEBUG_PRINTS = True + +def align_up(value, alignment): + """Выравнивание вверх""" + return ((value + alignment - 1) // alignment) * alignment + + +def analyze_csv(csv_file): + """Анализирует CSV и собирает информацию о секциях""" + + sections = { + '.text': {'start': None, 'end': None, 'size': 0, 'virt_start': None, 'virt_end': None}, + '.data': {'start': None, 'end': None, 'size': 0, 'virt_start': None, 'virt_end': None}, + } + + bss_blocks = [] + max_addr = 0 + + with open(csv_file, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f, delimiter=';') + + for row in reader: + seg = row['сегмент'] + addr_str = row['адрес'] + virt_addr_str = row['виртуальный_адрес'] + label = row['приводящая_метка'] + source = row['исходная_команда'] + + if not seg: + continue + + # Обновляем максимальный файловый адрес (только числовые адреса) + if addr_str and addr_str != 'BSS' and addr_str != '': + try: + addr = int(addr_str, 16) + if addr > max_addr: + max_addr = addr + except: + pass + + # BSS секция + if seg == '.bss': + if source and source.startswith('.резб'): + size = int(source.split()[1]) + virt_start = int(virt_addr_str, 16) + bss_blocks.append({ + 'virt_start': virt_start, + 'size': size, + 'label': label, + 'source': source + }) + continue + + # Обычные секции + if addr_str == 'BSS' or addr_str == '': + continue + + try: + addr = int(addr_str, 16) + virt_addr = int(virt_addr_str, 16) + except: + continue + + if seg == '.text': + if sections['.text']['start'] is None: + sections['.text']['start'] = addr + sections['.text']['virt_start'] = virt_addr + sections['.text']['end'] = addr + sections['.text']['virt_end'] = virt_addr + sections['.text']['size'] += 1 + + elif seg == '.data': + if sections['.data']['start'] is None: + sections['.data']['start'] = addr + sections['.data']['virt_start'] = virt_addr + sections['.data']['end'] = addr + sections['.data']['virt_end'] = virt_addr + sections['.data']['size'] += 1 + + # Пересчёт размеров секций из адресов + if sections['.text']['start'] and sections['.text']['end']: + sections['.text']['size'] = sections['.text']['end'] - sections['.text']['start'] + 1 + + if sections['.data']['start'] and sections['.data']['end']: + sections['.data']['size'] = sections['.data']['end'] - sections['.data']['start'] + 1 + + return sections, bss_blocks, max_addr + + +def generate_comment_data(): + """Генерирует данные для секции .comment (UTF-8 строка 'Сборщик КВС' с нуль-терминатором)""" + comment_str = 'Сборщик КВС' + return comment_str.encode('utf-8') + b'\x00' + + +def generate_shstrtab_binary(has_bss): + """Генерирует бинарную таблицу имён секций (как в kvs_8.py)""" + # Формат: \0.text\0.data\0.comment\0.shstrtab\0 + # (и .bss если есть) + if has_bss: + # .text, .data, .bss, .comment, .shstrtab + names = ['', '.text', '.data', '.bss', '.comment', '.shstrtab'] + else: + # .text, .data, .comment, .shstrtab + names = ['', '.text', '.data', '.comment', '.shstrtab'] + + result = b'' + for name in names: + result += name.encode('ascii') + b'\x00' + + return result + + +def create_null_section_entry(shdr_offset): + """Создаёт нулевую секцию (индекс 0) — все поля нулевые (sh_addralign=1 как в эталоне)""" + entries = [] + # 64 байта нулей для нулевой секции + for j in range(64): + if 48 <= j <= 55: + byte_val = 0x01 if j == 48 else 0x00 + else: + byte_val = 0x00 + entries.append({ + 'сегмент': '.shdr', + 'адрес': f"0x{shdr_offset + j:08x}", + 'виртуальный_адрес': '0x00000000', + 'байт': f"0x{byte_val:02x}", + 'приводящая_метка': 'NULL' if j == 0 else '', + 'уводящий_адрес': '', + 'исходная_команда': '.shdr NULL' if j == 0 else '', + 'команда_со_значениями': '', + 'рассчитанный_уводящий_адрес': '', + 'рассчитанный_байт': f"0x{byte_val:02x}" + }) + return entries + + +def create_section_table_entries(sections, has_bss, shdr_offset, shstrtab_offset, comment_offset, comment_size, shstrtab_data): + """Создаёт записи таблицы секций для CSV (с нулевой секцией, .comment и .shstrtab)""" + + if DEBUG_PRINTS: + print("\n[DEBUG] create_section_table_entries") + print(f" has_bss = {has_bss}") + print(f" shdr_offset = 0x{shdr_offset:x}") + print(f" shstrtab_offset = 0x{shstrtab_offset:x}") + print(f" comment_offset = 0x{comment_offset:x}") + print(f" comment_size = {comment_size}") + + entries = [] + + # Сначала добавляем НУЛЕВУЮ СЕКЦИЮ (индекс 0) + null_entries = create_null_section_entry(shdr_offset) + entries.extend(null_entries) + + if DEBUG_PRINTS: + print(f" Нулевая секция: {len(null_entries)} байт по адресу 0x{shdr_offset:x}") + + # Смещение для следующей секции (после нулевой) + next_offset = shdr_offset + 64 + + if DEBUG_PRINTS: + print(f" next_offset = 0x{next_offset:x} (начало заголовков секций)") + + # Вычисляем индексы имён в .shstrtab в зависимости от наличия BSS + if has_bss: + idx_text = 1 + idx_data = 7 + idx_bss = 13 + idx_comment = 17 + idx_shstrtab = 22 + else: + idx_text = 1 + idx_data = 7 + idx_comment = 13 + idx_shstrtab = 22 + + if DEBUG_PRINTS: + print(f" Индексы в .shstrtab:") + print(f" idx_text = {idx_text}") + print(f" idx_data = {idx_data}") + if has_bss: + print(f" idx_bss = {idx_bss}") + print(f" idx_comment = {idx_comment}") + print(f" idx_shstrtab = {idx_shstrtab}") + + # 1. Заголовок для .text (индекс 1) + header_text = struct.pack('") + sys.exit(1) + + csv_file = sys.argv[1] + + print(f"\n📊 Анализ CSV: {csv_file}") + + sections, bss_blocks, max_addr = analyze_csv(csv_file) + has_bss = len(bss_blocks) > 0 + print_section_info(sections, has_bss, max_addr) + + # Вычисляем смещения (как в kvs_8.py) + text_offset = sections['.text']['start'] + data_offset = sections['.data']['start'] + text_size = sections['.text']['size'] + data_size = sections['.data']['size'] + + # Размеры данных + comment_data = generate_comment_data() + comment_size = len(comment_data) + + # Порядок размещения (как в kvs_8.py): + # 1. .text (0x1000) + # 2. .data (0x2000) + # 3. .comment (сразу после .data) + comment_offset = data_offset + data_size + print(f" comment_offset = 0x{comment_offset:x}") + + # 4. .shstrtab (выравнивание на 8 байт после .comment) + shstrtab_data = generate_shstrtab_binary(has_bss) + shstrtab_size = len(shstrtab_data) + shstrtab_offset = align_up(comment_offset + comment_size, 8) + print(f" shstrtab_offset = 0x{shstrtab_offset:x}") + print(f" shstrtab_size = {shstrtab_size}") + + # 5. Таблица секций (выравнивание на 16 байт после .shstrtab) + # Количество секций: NULL + .text + .data + .comment + .shstrtab (+ .bss если есть) + num_sections = 5 # NULL + .text + .data + .comment + .shstrtab + if has_bss: + num_sections += 1 # + .bss + + # Таблица секций размещается после .shstrtab + shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16) + print(f" shdr_offset = 0x{shdr_offset:x}") + + print(f"\n📍 Размещение секций (как в kvs_8.py):") + print(f" .data: 0x{data_offset:08x}") + print(f" .comment: 0x{comment_offset:08x} (размер {comment_size} байт)") + print(f" .shstrtab: 0x{shstrtab_offset:08x} (размер {shstrtab_size} байт)") + print(f" Таблица секций: 0x{shdr_offset:08x}") + print(f" Количество секций: {num_sections}") + print(f" Наличие BSS: {has_bss}") + + # ПЕРЕСОЗДАЁМ ПРОГРАММНЫЕ ЗАГОЛОВКИ + regenerate_program_headers_in_csv(csv_file, sections, has_bss, text_offset, data_offset) + + # Генерируем записи таблицы секций + entries, num_headers = create_section_table_entries( + sections, has_bss, shdr_offset, shstrtab_offset, comment_offset, comment_size, shstrtab_data + ) + + # Генерируем данные для .comment + comment_entries = add_comment_data(comment_offset) + + # Генерируем данные для .shstrtab + shstrtab_entries = add_shstrtab_data(shstrtab_offset, shstrtab_data) + + e_shoff = shdr_offset + e_shnum = num_headers + e_shentsize = 64 + + print(f"\n📝 Обновление ELF-заголовка:") + print(f" e_shoff = 0x{e_shoff:08x}") + print(f" e_shnum = {e_shnum}") + print(f" e_shentsize = {e_shentsize}") + + # Обновляем ELF-заголовок в CSV + update_elf_header_in_csv(csv_file, e_shoff, e_shnum, e_shentsize) + + # Добавляем таблицу секций, .comment и .shstrtab в CSV + merge_csv_with_section_table(csv_file, entries, comment_entries, shstrtab_entries) + + print(f"\n💾 Таблица секций добавлена в {csv_file}") + print(f" Добавлено {len(entries)} записей заголовков") + print(f" Добавлено {len(comment_entries)} записей .comment") + print(f" Добавлено {len(shstrtab_entries)} записей .shstrtab") + print(f"\n✅ Готово! Размещение секций как в kvs_8.py:") + print(f" .comment сразу после .data") + print(f" .shstrtab выровнен на 8 байт") + print(f" Таблица секций выровнена на 16 байт") + if has_bss: + print(f" BSS присутствует → индекс .shstrtab = 21") + else: + print(f" BSS отсутствует → индекс .shstrtab = 21") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/тесты/cleaner.py b/тесты/cleaner.py index 8fc8b5f..105755c 100644 --- a/тесты/cleaner.py +++ b/тесты/cleaner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os -extensions = {'.аст', '.csv', '.токены', '.проход1', '.elf'} +extensions = {'.аст', '.csv', '.токены', '.проход1', '.elf', '.log'} for file in os.listdir('.'): if os.path.isfile(file) and os.path.splitext(file)[1] in extensions: diff --git a/тесты/сравнитель/compare_elf.sh b/тесты/сравнитель/compare_elf.sh new file mode 100755 index 0000000..62dd8ba --- /dev/null +++ b/тесты/сравнитель/compare_elf.sh @@ -0,0 +1,149 @@ +#!/bin/bash + +# Сравнение ELF-файлов побайтно +# Сохраняет отчёт в файл +# Нужен для проверки совместимости нового компилятора с 8й версией + +if [ $# -ne 2 ]; then + echo "Использование: $0 <файл1.elf> <файл2.elf>" + exit 1 +fi + +FILE1="$1" +FILE2="$2" +OUTPUT="compare_$(basename "$1")_$(basename "$2").txt" + +echo "Сравнение: $FILE1 vs $FILE2" +echo "Отчёт: $OUTPUT" + +# Заголовок отчёта +{ + echo "========================================" + echo "ПОБАЙТНОЕ СРАВНЕНИЕ ELF-ФАЙЛОВ" + echo "========================================" + echo "Файл 1: $FILE1" + echo "Файл 2: $FILE2" + echo "Время: $(date)" + echo "========================================" + echo "" + + # Размеры файлов + SIZE1=$(stat -c %s "$FILE1") + SIZE2=$(stat -c %s "$FILE2") + echo "Размер файла 1: $SIZE1 байт" + echo "Размер файла 2: $SIZE2 байт" + echo "" + + if [ "$SIZE1" -ne "$SIZE2" ]; then + echo "ВНИМАНИЕ: Размеры файлов отличаются!" + echo "" + fi + + # Побайтное сравнение + echo "ПОБАЙТНОЕ СРАВНЕНИЕ:" + echo "--------------" + + # Используем cmp для побайтного сравнения + cmp -l "$FILE1" "$FILE2" 2>/dev/null | while read offset byte1 byte2; do + echo "Смещение 0x$(printf "%08x" $((offset-1))): байт1=0x$byte1, байт2=0x$byte2" + done + + DIFF_COUNT=$(cmp -l "$FILE1" "$FILE2" 2>/dev/null | wc -l) + + if [ "$DIFF_COUNT" -eq 0 ]; then + echo "Файлы идентичны!" + else + echo "" + echo "Всего различий: $DIFF_COUNT" + fi + + echo "" + echo "========================================" + echo "СРАВНЕНИЕ ELF-ЗАГОЛОВКОВ" + echo "========================================" + + # Сравнение ELF-заголовков + echo "" + echo "--- readelf -h ---" + echo "=== $FILE1 ===" >> /tmp/elf1_header.txt + readelf -h "$FILE1" 2>/dev/null >> /tmp/elf1_header.txt + echo "=== $FILE2 ===" >> /tmp/elf2_header.txt + readelf -h "$FILE2" 2>/dev/null >> /tmp/elf2_header.txt + diff /tmp/elf1_header.txt /tmp/elf2_header.txt 2>/dev/null || echo "ELF-заголовки отличаются" + rm -f /tmp/elf1_header.txt /tmp/elf2_header.txt + + echo "" + echo "--- readelf -l (программные заголовки) ---" + echo "=== $FILE1 ===" >> /tmp/elf1_ph.txt + readelf -l "$FILE1" 2>/dev/null >> /tmp/elf1_ph.txt + echo "=== $FILE2 ===" >> /tmp/elf2_ph.txt + readelf -l "$FILE2" 2>/dev/null >> /tmp/elf2_ph.txt + diff /tmp/elf1_ph.txt /tmp/elf2_ph.txt 2>/dev/null || echo "Программные заголовки отличаются" + rm -f /tmp/elf1_ph.txt /tmp/elf2_ph.txt + + echo "" + echo "--- readelf -S (секции) ---" + echo "=== $FILE1 ===" >> /tmp/elf1_sec.txt + readelf -S "$FILE1" 2>/dev/null >> /tmp/elf1_sec.txt + echo "=== $FILE2 ===" >> /tmp/elf2_sec.txt + readelf -S "$FILE2" 2>/dev/null >> /tmp/elf2_sec.txt + diff /tmp/elf1_sec.txt /tmp/elf2_sec.txt 2>/dev/null || echo "Секции отличаются" + rm -f /tmp/elf1_sec.txt /tmp/elf2_sec.txt + + echo "" + echo "========================================" + echo "HEX-ДАМП ПЕРВЫХ 256 БАЙТ" + echo "========================================" + echo "" + echo "=== $FILE1 ===" + hexdump -C "$FILE1" -n 256 + echo "" + echo "=== $FILE2 ===" + hexdump -C "$FILE2" -n 256 + + echo "" + echo "========================================" + echo "HEX-ДАМП ОБЛАСТИ ПРОГРАММНЫХ ЗАГОЛОВКОВ (0x40-0xe8)" + echo "========================================" + echo "" + echo "=== $FILE1 ===" + hexdump -C "$FILE1" -s 0x40 -n 168 + echo "" + echo "=== $FILE2 ===" + hexdump -C "$FILE2" -s 0x40 -n 168 + + echo "" + echo "========================================" + echo "HEX-ДАМП ОБЛАСТИ ТАБЛИЦЫ СЕКЦИЙ (0x2040-0x2140)" + echo "========================================" + echo "" + echo "=== $FILE1 ===" + hexdump -C "$FILE1" -s 0x2040 -n 256 2>/dev/null || echo "Нет данных по адресу 0x2040" + echo "" + echo "=== $FILE2 ===" + hexdump -C "$FILE2" -s 0x2040 -n 256 2>/dev/null || echo "Нет данных по адресу 0x2040" + + echo "" + echo "========================================" + echo "СРАВНЕНИЕ ВЫПОЛНЕНИЯ" + echo "========================================" + + echo "=== Выполнение $FILE1 ===" + ./"$FILE1" + echo "Код возврата: $?" + + echo "=== Выполнение $FILE2 ===" + ./"$FILE2" + echo "Код возврата: $?" + +} > "$OUTPUT" + +echo "Отчёт сохранён в: $OUTPUT" + +# Показать количество различий +DIFF_COUNT=$(cmp -l "$FILE1" "$FILE2" 2>/dev/null | wc -l) +if [ "$DIFF_COUNT" -eq 0 ]; then + echo "✅ Файлы идентичны!" +else + echo "❌ Найдено $DIFF_COUNT различий" +fi \ No newline at end of file diff --git a/тесты/тест_бнд2.квс b/тесты/тест_бнд2.квс index d80f2a2..0d1df2b 100644 --- a/тесты/тест_бнд2.квс +++ b/тесты/тест_бнд2.квс @@ -1,3 +1,5 @@ +; НЕ РАБОТАЕТ С НОВЫМ СБОРЩИКОМ + .текст .глобал _start