237 lines
8 KiB
Python
237 lines
8 KiB
Python
|
|
#!/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 <output.csv>")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
generate_empty_program()
|
|||
|
|
save_to_csv(sys.argv[1])
|
|||
|
|
print("\nГотово!", file=sys.stderr)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|