From 59f3b162deb3e12ad1321a5748c653238a37294e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 7 Oct 2025 22:56:04 +0200 Subject: [PATCH] test --- inspect.sh | 3 ++ src/copapy/__init__.py | 3 +- src/copapy/binwrite.py | 37 +++++++++++++++-- src/copapy/generate_stencils.py | 28 ++++++------- src/copapy/stencil_db.py | 9 +++-- src/coparun/coparun.c | 4 +- src/coparun/runmem.c | 15 ++++--- tests/test_ast_gen.py | 18 +++++++-- tests/test_compile.py | 10 +++-- tests/test_coparun_module.py | 2 +- tools/extract_code.py | 70 +++++++++++++++++++++++++++++++++ 11 files changed, 157 insertions(+), 42 deletions(-) create mode 100644 inspect.sh create mode 100644 tools/extract_code.py diff --git a/inspect.sh b/inspect.sh new file mode 100644 index 0000000..212a0ce --- /dev/null +++ b/inspect.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +objdump -D -b binary -m i386:x86-64 --adjust-vma=0x1000 bin/test_code.bin \ No newline at end of file diff --git a/src/copapy/__init__.py b/src/copapy/__init__.py index 457a01f..5b534fe 100644 --- a/src/copapy/__init__.py +++ b/src/copapy/__init__.py @@ -354,7 +354,6 @@ def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database for variable in variable_list: lengths = sdb.var_size['dummy_' + variable.dtype] - print('variable_mem_layout', variable.dtype, lengths) object_list.append((variable, offset, lengths)) offset += (lengths + 3) // 4 * 4 @@ -455,6 +454,8 @@ class Target(): dw = binw.data_writer(self.sdb.byteorder) dw.write_com(binw.Command.RUN_PROG) dw.write_int(0) + #for s in self._variables: + # add_read_command(dw, self._variables, s) dw.write_com(binw.Command.END_PROG) assert coparun(dw.get_data()) > 0 diff --git a/src/copapy/binwrite.py b/src/copapy/binwrite.py index 444aa85..ea0e3b2 100644 --- a/src/copapy/binwrite.py +++ b/src/copapy/binwrite.py @@ -2,24 +2,26 @@ from enum import Enum from typing import Literal import struct +ByteOrder = Literal['little', 'big'] Command = Enum('Command', [('ALLOCATE_DATA', 1), ('COPY_DATA', 2), ('ALLOCATE_CODE', 3), ('COPY_CODE', 4), ('PATCH_FUNC', 5), ('PATCH_OBJECT', 6), ('RUN_PROG', 64), ('READ_DATA', 65), ('END_PROG', 256), ('FREE_MEMORY', 257)]) +COMMAND_SIZE = 4 class data_writer(): - def __init__(self, byteorder: Literal['little', 'big']): + def __init__(self, byteorder: ByteOrder): self._data: list[tuple[str, bytes, int]] = list() - self.byteorder: Literal['little', 'big'] = byteorder + self.byteorder: ByteOrder = byteorder def write_int(self, value: int, num_bytes: int = 4, signed: bool = False) -> None: self._data.append((f"INT {value}", value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed), 0)) - def write_com(self, value: Enum, num_bytes: int = 4) -> None: - self._data.append((value.name, value.value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=False), 1)) + def write_com(self, value: Command) -> None: + self._data.append((value.name, value.value.to_bytes(length=COMMAND_SIZE, byteorder=self.byteorder, signed=False), 1)) def write_byte(self, value: int) -> None: self._data.append((f"BYTE {value}", bytes([value]), 0)) @@ -52,3 +54,30 @@ class data_writer(): def to_file(self, path: str) -> None: with open(path, 'wb') as f: f.write(self.get_data()) + +class data_reader(): + def __init__(self, data: bytes | bytearray, byteorder: ByteOrder): + self._data = data + self._index: int = 0 + self.byteorder: ByteOrder = byteorder + + def read_int(self, num_bytes: int = 4, signed: bool = False) -> int: + ret = int.from_bytes(self._data[self._index:self._index + num_bytes], byteorder=self.byteorder, signed=signed) + self._index += num_bytes + return ret + + def read_com(self) -> Command: + com_value = int.from_bytes(self._data[self._index:self._index + COMMAND_SIZE], byteorder=self.byteorder) + ret = Command(com_value) + self._index += COMMAND_SIZE + return ret + + def read_byte(self) -> int: + ret = self._data[self._index] + self._index += 1 + return ret + + def read_bytes(self, num_bytes: int) -> bytes: + ret = self._data[self._index:self._index + num_bytes] + self._index += num_bytes + return ret diff --git a/src/copapy/generate_stencils.py b/src/copapy/generate_stencils.py index d14ecaf..a25fb13 100644 --- a/src/copapy/generate_stencils.py +++ b/src/copapy/generate_stencils.py @@ -9,7 +9,7 @@ def get_function_start() -> str: return """ int function_start(){ result_int(0); // dummy call instruction before marker gets striped - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); return 1; } """ @@ -19,7 +19,7 @@ def get_function_end() -> str: return """ int function_end(){ result_int(0); - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); return 1; } """ @@ -28,9 +28,9 @@ def get_function_end() -> str: def get_op_code(op: str, type1: str, type2: str, type_out: str) -> str: return f""" void {op}_{type1}_{type2}({type1} arg1, {type2} arg2) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); result_{type_out}_{type2}(arg1 {op_signs[op]} arg2, arg2); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ @@ -38,9 +38,9 @@ def get_op_code(op: str, type1: str, type2: str, type_out: str) -> str: def get_op_code_float(op: str, type1: str, type2: str) -> str: return f""" void {op}_{type1}_{type2}({type1} arg1, {type2} arg2) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); result_float_{type2}((float)arg1 {op_signs[op]} arg2, arg2); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ @@ -48,9 +48,9 @@ def get_op_code_float(op: str, type1: str, type2: str) -> str: def get_op_code_int(op: str, type1: str, type2: str) -> str: return f""" void {op}_{type1}_{type2}({type1} arg1, {type2} arg2) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); result_int_{type2}((int)(arg1 {op_signs[op]} arg2), arg2); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ @@ -70,9 +70,9 @@ def get_result_stubs2(type1: str, type2: str) -> str: def get_read_reg0_code(type1: str, type2: str, type_out: str) -> str: return f""" void read_{type_out}_reg0_{type1}_{type2}({type1} arg1, {type2} arg2) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); result_{type_out}_{type2}(dummy_{type_out}, arg2); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ @@ -80,9 +80,9 @@ def get_read_reg0_code(type1: str, type2: str, type_out: str) -> str: def get_read_reg1_code(type1: str, type2: str, type_out: str) -> str: return f""" void read_{type_out}_reg1_{type1}_{type2}({type1} arg1, {type2} arg2) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); result_{type1}_{type_out}(arg1, dummy_{type_out}); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ @@ -90,10 +90,10 @@ def get_read_reg1_code(type1: str, type2: str, type_out: str) -> str: def get_write_code(type1: str) -> str: return f""" void write_{type1}({type1} arg1) {{ - asm volatile (".long 0xF17ECAFE"); + asm volatile (".long 0x0F1F4400E1"); dummy_{type1} = arg1; result_{type1}(arg1); - asm volatile (".long 0xF27ECAFE"); + asm volatile (".long 0x0F1F4400E2"); }} """ diff --git a/src/copapy/stencil_db.py b/src/copapy/stencil_db.py index 34a7965..c5a2424 100644 --- a/src/copapy/stencil_db.py +++ b/src/copapy/stencil_db.py @@ -5,11 +5,12 @@ from enum import Enum ByteOrder = Literal['little', 'big'] -START_MARKER = 0xF17ECAFE -END_MARKER = 0xF27ECAFE -MARKER_LENGTH = 4 +START_MARKER = 0x0F1F4400E1 # Nop on x86-64 +END_MARKER = 0x0F1F4400E2 # Nop on x86-64 +MARKER_LENGTH = 5 -LENGTH_CALL_INSTRUCTION = 5 # x86_64 + # on x86_64: call or jmp instruction when tail call optimized +LENGTH_CALL_INSTRUCTION = 5 RelocationType = Enum('RelocationType', [('RELOC_RELATIVE_32', 0)]) diff --git a/src/coparun/coparun.c b/src/coparun/coparun.c index 9dd1bae..15779b0 100644 --- a/src/coparun/coparun.c +++ b/src/coparun/coparun.c @@ -45,9 +45,9 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - parse_commands(file_buff); + int ret = parse_commands(file_buff); free_memory(); - return EXIT_SUCCESS; + return ret < 0; } diff --git a/src/coparun/runmem.c b/src/coparun/runmem.c index df809bc..8c56ac5 100644 --- a/src/coparun/runmem.c +++ b/src/coparun/runmem.c @@ -49,10 +49,10 @@ int parse_commands(uint8_t *bytes) { uint32_t reloc_type; uint32_t offs; uint32_t size; - int err_flag = 0; + int end_flag = 0; uint32_t rel_entr_point; - while(!err_flag) { + while(!end_flag) { command = *(uint32_t*)bytes; bytes += 4; switch(command) { @@ -61,7 +61,7 @@ int parse_commands(uint8_t *bytes) { data_memory = allocate_data_memory(size); data_memory_len = size; printf("ALLOCATE_DATA size=%i mem_addr=%p\n", size, (void*)data_memory); - if (!update_data_offs()) return EXIT_FAILURE; + if (!update_data_offs()) end_flag = -4; break; case COPY_DATA: @@ -77,7 +77,7 @@ int parse_commands(uint8_t *bytes) { executable_memory_len = size; printf("ALLOCATE_CODE size=%i mem_addr=%p\n", size, (void*)executable_memory); //printf("# d %i c %i off %i\n", data_memory, executable_memory, data_offs); - if (!update_data_offs()) return EXIT_FAILURE; + if (!update_data_offs()) end_flag = -4; break; case COPY_CODE: @@ -126,20 +126,19 @@ int parse_commands(uint8_t *bytes) { break; case FREE_MEMORY: - size = *(uint32_t*)bytes; bytes += 4; free_memory(); break; case END_PROG: printf("END_PROG\n"); - err_flag = 1; + end_flag = 1; break; default: printf("Unknown command\n"); - return EXIT_FAILURE; + end_flag = -1; break; } } - return EXIT_SUCCESS; + return end_flag; } diff --git a/tests/test_ast_gen.py b/tests/test_ast_gen.py index bc30bc3..f12af6d 100644 --- a/tests/test_ast_gen.py +++ b/tests/test_ast_gen.py @@ -13,11 +13,21 @@ def test_ast_generation(): #r1 = i1 + i3 #r2 = i3 * i2 + + #c1 = const(4) + #i1 = c1 * 2 + #r1 = i1 + 7 + #r2 = i1 + 9 + #out = [Write(r1), Write(r2)] + c1 = const(4) - i1 = c1 * 2 - r1 = i1 + 7 - r2 = i1 + 9 - out = [Write(r1), Write(r2)] + c2 = const(2) + #i1 = c1 * 2 + #r1 = i1 + 7 + (c2 + 7 * 9) + #r2 = i1 + 9 + #out = [Write(r1), Write(r2)] + r1 = c1 * 5 + 8 + c2 * 3 + out = [Write(r1)] print(out) print('-- get_edges:') diff --git a/tests/test_compile.py b/tests/test_compile.py index 7637ee9..51b9113 100644 --- a/tests/test_compile.py +++ b/tests/test_compile.py @@ -48,10 +48,12 @@ def test_compile(): c1 = const(4) c2 = const(2) - i1 = c1 * 2 - r1 = i1 + 7 + (c2 + 7 * 9) - r2 = i1 + 9 - out = [Write(r1), Write(r2)] + #i1 = c1 * 2 + #r1 = i1 + 7 + (c2 + 7 * 9) + #r2 = i1 + 9 + #out = [Write(r1), Write(r2)] + r1 = c1 * 5 + 8 + c2 * 3 + out = [Write(r1)] il, _ = copapy.compile_to_instruction_list(out, copapy.generic_sdb) diff --git a/tests/test_coparun_module.py b/tests/test_coparun_module.py index 9df01a1..84b5c8d 100644 --- a/tests/test_coparun_module.py +++ b/tests/test_coparun_module.py @@ -23,7 +23,7 @@ def test_compile(): tg.compile(ret) #time.sleep(5) print('* run and copy ...') - #tg.run() + tg.run() #print('* finished') ret_ref = function(4, 2) diff --git a/tools/extract_code.py b/tools/extract_code.py new file mode 100644 index 0000000..377f9f1 --- /dev/null +++ b/tools/extract_code.py @@ -0,0 +1,70 @@ +from copapy.binwrite import data_reader, Command, ByteOrder +from copapy.stencil_db import RelocationType + +input_file = "bin/test.copapy" + +output_file = "bin/test_code.bin" + +data_section_offset = 0x2000 + +byteorder: ByteOrder = 'little' + +with open(input_file, mode='rb') as f: + dr = data_reader(f.read(), byteorder) + +buffer_index: int = 0 +end_flag: int = 0 +program_data: bytearray = bytearray([]) + +while(end_flag == 0): + com = dr.read_com() + + if com == Command.ALLOCATE_DATA: + size = dr.read_int() + print(f"ALLOCATE_DATA size={size}") + elif com == Command.ALLOCATE_CODE: + size = dr.read_int() + program_data = bytearray(size) + print(f"ALLOCATE_CODE size={size}") + elif com == Command.COPY_DATA: + offs = dr.read_int() + size = dr.read_int() + datab = dr.read_bytes(size) + print(f"COPY_DATA offs={offs} size={size} data={' '.join(hex(d) for d in datab)}") + elif com == Command.COPY_CODE: + offs = dr.read_int() + size = dr.read_int() + datab = dr.read_bytes(size) + program_data[offs:offs + size] = datab + print(f"COPY_CODE offs={offs} size={size} data={' '.join(hex(d) for d in datab[:5])}...") + elif com == Command.PATCH_FUNC: + offs = dr.read_int() + reloc_type = dr.read_int() + value = dr.read_int(signed=True) + print(f"PATCH_FUNC patch_offs={offs} reloc_type={reloc_type} value={value}") + elif com == Command.PATCH_OBJECT: + offs = dr.read_int() + reloc_type = dr.read_int() + value = dr.read_int(signed=True) + assert reloc_type == RelocationType.RELOC_RELATIVE_32.value + program_data[offs:offs + 4] = (value + data_section_offset).to_bytes(4, byteorder, signed=True) + print(f"PATCH_OBJECT patch_offs={offs} reloc_type={reloc_type} value={value}") + elif com == Command.RUN_PROG: + rel_entr_point = dr.read_int() + print(f"RUN_PROG rel_entr_point={rel_entr_point}") + elif com == Command.READ_DATA: + offs = dr.read_int() + size = dr.read_int() + print(f"READ_DATA offs={offs} size={size}") + elif com == Command.FREE_MEMORY: + print("READ_DATA") + elif com == Command.END_PROG: + print("END_PROG") + end_flag = 1 + else: + assert False, f"Unknown command: {com}" + +with open(output_file, mode='wb') as f: + f.write(program_data) + +print('OK') \ No newline at end of file