kvs/kvs_builder.py

217 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Сборщик ELF из CSV
"""
import sys
import struct
import os
sys.path.insert(0, '.')
from kvs_data import PAGE_SIZE, align_up, SHT_NOBITS, SHF_WRITE, SHF_ALLOC
def read_csv(input_file, vaddr_text, vaddr_data, text_size, data_size):
"""Загружает байты из CSV используя точные виртуальные адреса"""
text_bytes = bytearray(text_size)
data_bytes = bytearray(data_size)
with open(input_file, 'r', encoding='utf-8') as f:
next(f)
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(';')
if len(parts) < 2:
continue
addr_str = parts[0]
byte_str = parts[1]
try:
addr = int(addr_str, 16)
byte_val = int(byte_str, 16)
except ValueError:
continue
if addr >= vaddr_data:
offset = addr - vaddr_data
if 0 <= offset < data_size:
data_bytes[offset] = byte_val
elif addr >= vaddr_text:
offset = addr - vaddr_text
if 0 <= offset < text_size:
text_bytes[offset] = byte_val
# Заменяем хвостовые нули на NOP'ы (0x90)
# Находим последний ненулевой байт
last_nonzero = -1
for i in range(len(text_bytes) - 1, -1, -1):
if text_bytes[i] != 0:
last_nonzero = i
break
if last_nonzero >= 0 and last_nonzero < len(text_bytes) - 1:
for i in range(last_nonzero + 1, len(text_bytes)):
text_bytes[i] = 0x90
return text_bytes, data_bytes
def read_pass1(input_file):
data = {}
labels = {}
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(':')
if parts[0] == "PARAM":
key = parts[1]
value = parts[2]
if value.isdigit():
value = int(value)
data[key] = value
elif parts[0] == "LABEL":
label = parts[1]
pos = int(parts[3])
labels[label] = pos
return data, labels
def create_elf(text_bytes, data_bytes, pass1_data, labels, output_file):
vaddr_text = pass1_data.get("vaddr_text", 0x401000)
text_size = pass1_data.get("text_size", len(text_bytes))
data_size = pass1_data.get("data_size", len(data_bytes))
bnd_size = pass1_data.get("bnd_size", 0)
# Вычисляем виртуальные адреса
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
vaddr_bnd = pass1_data.get("vaddr_bnd", align_up(vaddr_data + data_size, PAGE_SIZE))
offset_text = pass1_data.get("offset_text", 0x1000)
offset_data = align_up(offset_text + len(text_bytes), PAGE_SIZE)
offset_comment = pass1_data.get("offset_comment", align_up(offset_data + len(data_bytes), 1))
shstrtab_offset = pass1_data.get("shstrtab_offset", align_up(offset_comment + 32, 8))
shdr_offset = pass1_data.get("shdr_offset", align_up(shstrtab_offset + 32, 16))
entry_point_name = pass1_data.get("entry_point", "_start")
entry_addr = vaddr_text + labels.get(entry_point_name, 0)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
# shstrtab с учётом .bss
if bnd_size > 0:
shstrtab_content = b"\x00.text\x00.data\x00.bss\x00.comment\x00.shstrtab\x00"
else:
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5 + (1 if bnd_size > 0 else 0) # 5 базовых + .bss если есть
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident, 2, 0x3e, 1, entry_addr, elf_header_size, shdr_offset,
0, elf_header_size, ph_size, ph_num, shdr_size, shdr_num, 4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags, p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz, PAGE_SIZE
)
# Program headers
ph0 = phdr(1, 4, 0, 0x400000, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text_bytes), align_up(len(text_bytes), PAGE_SIZE))
if bnd_size > 0:
data_segment_filesz = len(data_bytes)
# memsz должно покрывать от vaddr_data до конца .bss
bss_end = vaddr_bnd + bnd_size
data_segment_memsz = align_up(bss_end - vaddr_data, PAGE_SIZE)
ph2 = phdr(1, 6, offset_data, vaddr_data, data_segment_filesz, data_segment_memsz)
else:
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data_bytes), align_up(len(data_bytes), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
# Данные секций
elf_data[offset_text : offset_text + len(text_bytes)] = text_bytes
elf_data[offset_data : offset_data + len(data_bytes)] = data_bytes
elf_data[offset_comment : offset_comment + len(comment_content)] = comment_content
elf_data[shstrtab_offset : shstrtab_offset + len(shstrtab_content)] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
# Индексы имён в shstrtab зависят от наличия .bss
if bnd_size > 0:
name_text = 1 # ".text"
name_data = 7 # ".data"
name_bss = 13 # ".bss"
name_comment = 18 # ".comment"
name_shstrtab = 27 # ".shstrtab"
else:
name_text = 1
name_data = 7
name_comment = 13
name_shstrtab = 22
# Section headers
sh0 = shdr(0, 0, 0, 0, 0, 0) # NULL
sh1 = shdr(name_text, 1, 6, vaddr_text, offset_text, len(text_bytes), 16) # .text
sh2 = shdr(name_data, 1, 3, vaddr_data, offset_data, len(data_bytes), 8) # .data
if bnd_size > 0:
sh3 = shdr(name_bss, SHT_NOBITS, SHF_WRITE | SHF_ALLOC, vaddr_bnd, 0, bnd_size, 16) # .bss
sh4 = shdr(name_comment, 1, 0, 0, offset_comment, len(comment_content), 1) # .comment
sh5 = shdr(name_shstrtab, 3, 0, 0, shstrtab_offset, len(shstrtab_content), 1) # .shstrtab
shdrs = sh0 + sh1 + sh2 + sh3 + sh4 + sh5
else:
sh3 = shdr(name_comment, 1, 0, 0, offset_comment, len(comment_content), 1)
sh4 = shdr(name_shstrtab, 3, 0, 0, shstrtab_offset, len(shstrtab_content), 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
with open(output_file, "wb") as f:
f.write(elf_data)
os.chmod(output_file, 0o755)
print(f"Сборщик: ELF-файл создан: {output_file}")
print(f" .text: {len(text_bytes)} байт, адрес: 0x{vaddr_text:x}")
print(f" .data: {len(data_bytes)} байт, адрес: 0x{vaddr_data:x}")
if bnd_size > 0:
print(f" .bss: {bnd_size} байт, адрес: 0x{vaddr_bnd:x}")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Использование: python kvs_builder.py <вход.csv> <вход.проход1> <выход.elf>")
sys.exit(1)
pass1_data, labels = read_pass1(sys.argv[2])
vaddr_text = pass1_data["vaddr_text"]
text_size = pass1_data["text_size"]
data_size = pass1_data["data_size"]
# Вычисляем vaddr_data
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
text_bytes, data_bytes = read_csv(sys.argv[1], vaddr_text, vaddr_data, text_size, data_size)
create_elf(text_bytes, data_bytes, pass1_data, labels, sys.argv[3])