python module is working. no return values yet.

This commit is contained in:
Nicolas 2025-10-03 23:09:25 +02:00
parent 672a67837f
commit 6625da1a47
15 changed files with 211 additions and 94 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ test.copapy
token.txt
/src/copapy/obj/*.o
runmem2
src/*.so
bin/*

View File

@ -9,5 +9,5 @@ gcc -c src/copapy/stencils.c -O2 -o src/copapy/obj/stencils_x86_64_O2.o
gcc -c src/copapy/stencils.c -O3 -o src/copapy/obj/stencils_x86_64_O3.o
mkdir bin -p
gcc -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wstrict-overflow -Werror -g src/coparun/coparun.c src/coparun/runmem.c -o bin/coparun
gcc -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wstrict-overflow -Werror -g src/coparun/runmem.c src/coparun/coparun.c -o bin/coparun
#x86_64-w64-mingw32-gcc -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wstrict-overflow -Werror src/runner/runmem2.c -Wall -O3 -o bin/runmem2.exe

View File

@ -30,9 +30,6 @@ where = ["src"]
[tool.setuptools.package-data]
copapy = ["*.o"]
[tool.setuptools.ext_modules]
coparun = { sources = ["src/coparun/coparun_module.c"] }
[project.optional-dependencies]
dev = [
"flake8",

13
setup.py Normal file
View File

@ -0,0 +1,13 @@
from setuptools import setup, Extension
ext = Extension(
"coparun_module",
sources=[
"src/coparun/coparun_module.c",
"src/coparun/runmem.c"
]
)
setup(
ext_modules=[ext],
)

View File

@ -149,11 +149,11 @@ def stable_toposort(edges: Iterable[tuple[Node, Node]]) -> list[Node]:
"""Perform a stable topological sort on a directed acyclic graph (DAG).
Arguments:
edges: Iterable of (u, v) pairs meaning u -> v
Returns:
List of nodes in topologically sorted order.
"""
# Track adjacency and indegrees
adj: defaultdict[Node, list[Node]] = defaultdict(list)
indeg: defaultdict[Node, int] = defaultdict(int)
@ -163,9 +163,11 @@ def stable_toposort(edges: Iterable[tuple[Node, Node]]) -> list[Node]:
pos = 0
for u, v in edges:
if u not in order:
order[u] = pos; pos += 1
order[u] = pos
pos += 1
if v not in order:
order[v] = pos; pos += 1
order[v] = pos
pos += 1
adj[u].append(v)
indeg[v] += 1
indeg.setdefault(u, 0)
@ -254,7 +256,7 @@ def add_read_ops(node_list: list[Node]) -> Generator[tuple[Net | None, Node], No
def add_write_ops(net_node_list: list[tuple[Net | None, Node]], const_nets: list[Net]) -> Generator[tuple[Net | None, Node], None, None]:
"""Add write operation for each new defined net if a read operation is later followed
Returns:
Yields tuples of a net and a node. The associated net is provided for read and write nodes.
Otherwise None is returned in the tuple.
@ -306,7 +308,6 @@ def compile_to_instruction_list(end_nodes: Iterable[Node] | Node) -> binw.data_w
# Get all nets associated with heap memory
variable_list = get_nets([[const_net_list]], extended_output_ops)
assert(len(set(variable_list)) == len(variable_list)), 'Duplicates!'
dw = binw.data_writer(sdb.byteorder)

View File

@ -7,7 +7,7 @@ Command = Enum('Command', [('ALLOCATE_DATA', 1), ('COPY_DATA', 2),
('ALLOCATE_CODE', 3), ('COPY_CODE', 4),
('PATCH_FUNC', 5), ('PATCH_OBJECT', 6),
('SET_ENTR_POINT', 64), ('READ_DATA', 65),
('END_PROG', 255)])
('END_PROG', 256), ('FREE_MEMORY', 257)])
class data_writer():

View File

@ -7,7 +7,7 @@ op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/'}
def get_function_start() -> str:
return """
int function_start(){
result_int(0); // dummy call instruction before marker gets striped
result_int(0); // dummy call instruction before marker gets striped
asm volatile (".long 0xF27ECAFE");
return 1;
}

View File

@ -224,7 +224,7 @@
}
int function_start(){
result_int(0); // dummy call instruction before marker gets striped
result_int(0); // dummy call instruction before marker gets striped
asm volatile (".long 0xF27ECAFE");
return 1;
}

View File

@ -0,0 +1,52 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "runmem.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <binary_file>\n", argv[0]);
return EXIT_FAILURE;
}
// Open the file
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
// Get file size
struct stat st;
if (fstat(fd, &st) < 0) {
perror("fstat");
close(fd);
return EXIT_FAILURE;
}
if (st.st_size == 0) {
fprintf(stderr, "Error: File is empty\n");
close(fd);
return EXIT_FAILURE;
}
//uint8_t *file_buff = get_data_memory((uint32_t)st.st_size);
uint8_t *file_buff = (uint8_t*)malloc((size_t)st.st_size);
// Read file into allocated memory
if (read(fd, file_buff, (long unsigned int)st.st_size) != st.st_size) {
perror("read");
close(fd);
return EXIT_FAILURE;
}
close(fd);
parse_commands(file_buff);
free_memory();
return EXIT_SUCCESS;
}

View File

@ -1,31 +1,46 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "runmem.h"
// A simple C function exposed to Python
static PyObject* my_function(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL; // Error if arguments aren't two integers
/*
* coparun(PyObject *self, PyObject *args)
* Accepts a Python `bytes` (or objects supporting the buffer protocol).
* We use the "y#" format in PyArg_ParseTuple which returns a pointer to
* the internal bytes buffer and its length (Py_ssize_t). For safety and
* performance we pass that pointer directly to parse_commands which expects
* a uint8_t* buffer. If parse_commands needs the length, consider
* extending its API to accept a length parameter.
*/
static PyObject* coparun(PyObject* self, PyObject* args) {
const char *buf;
Py_ssize_t buf_len;
int result;
if (!PyArg_ParseTuple(args, "y#", &buf, &buf_len)) {
return NULL; /* TypeError set by PyArg_ParseTuple */
}
return PyLong_FromLong(a + b); // Return sum as Python integer
/* If parse_commands may run for a long time, release the GIL. */
Py_BEGIN_ALLOW_THREADS
result = parse_commands((uint8_t*)buf);
Py_END_ALLOW_THREADS
return PyLong_FromLong(result);
}
// Method definitions
static PyMethodDef MyMethods[] = {
{"add", my_function, METH_VARARGS, "Adds two numbers"},
{NULL, NULL, 0, NULL} // Sentinel
{"coparun", coparun, METH_VARARGS, "Pass raw command data to coparun"},
{NULL, NULL, 0, NULL}
};
// Module definition
static struct PyModuleDef my_module = {
static struct PyModuleDef coparun_module = {
PyModuleDef_HEAD_INIT,
"my_module", // Module name
"coparun_module", // Module name
NULL, // Documentation
-1, // Size of per-interpreter state (-1 for global)
MyMethods
};
// Module initialization function
PyMODINIT_FUNC PyInit_my_module(void) {
return PyModule_Create(&my_module);
PyMODINIT_FUNC PyInit_coparun_module(void) {
return PyModule_Create(&coparun_module);
}

View File

@ -6,23 +6,14 @@
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
#include "runmem.h"
#define ALLOCATE_DATA 1
#define COPY_DATA 2
#define ALLOCATE_CODE 3
#define COPY_CODE 4
#define PATCH_FUNC 5
#define PATCH_OBJECT 6
#define SET_ENTR_POINT 64
#define READ_DATA 65
#define END_PROG 255
#define PATCH_RELATIVE_32 0
uint8_t *data_memory;
uint8_t *executable_memory;
uint32_t executable_memory_len;
int (*entr_point)();
/* Definitions for globals declared extern in runmem.h */
uint8_t *data_memory = NULL;
uint32_t data_memory_len = 0;
uint8_t *executable_memory = NULL;
uint32_t executable_memory_len = 0;
entry_point_t entr_point = NULL;
uint8_t *get_executable_memory(uint32_t num_bytes){
// Allocate executable memory
@ -62,7 +53,14 @@ int patch(uint8_t *patch_addr, uint32_t reloc_type, int32_t value){
return 0;
}
return 1;
}
}
void free_memory(){
if (executable_memory_len) munmap(executable_memory, executable_memory_len);
if (data_memory_len) munmap(data_memory, data_memory_len);
data_memory_len = 0;
executable_memory_len = 0;
}
int parse_commands(uint8_t *bytes){
int32_t value;
@ -78,9 +76,15 @@ int parse_commands(uint8_t *bytes){
command = *(uint32_t*)bytes;
bytes += 4;
switch(command) {
case FREE_MEMORY:
size = *(uint32_t*)bytes; bytes += 4;
free_memory();
break;
case ALLOCATE_DATA:
size = *(uint32_t*)bytes; bytes += 4;
data_memory = get_data_memory(size);
data_memory_len = size;
printf("ALLOCATE_DATA size=%i mem_addr=%p\n", size, (void*)data_memory);
break;
@ -163,47 +167,3 @@ int parse_commands(uint8_t *bytes){
}
return err_flag;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <binary_file>\n", argv[0]);
return EXIT_FAILURE;
}
// Open the file
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
// Get file size
struct stat st;
if (fstat(fd, &st) < 0) {
perror("fstat");
close(fd);
return EXIT_FAILURE;
}
if (st.st_size == 0) {
fprintf(stderr, "Error: File is empty\n");
close(fd);
return EXIT_FAILURE;
}
//uint8_t *file_buff = get_data_memory((uint32_t)st.st_size);
uint8_t *file_buff = (uint8_t*)malloc((size_t)st.st_size);
// Read file into allocated memory
if (read(fd, file_buff, (long unsigned int)st.st_size) != st.st_size) {
perror("read");
close(fd);
return EXIT_FAILURE;
}
close(fd);
parse_commands(file_buff);
munmap(executable_memory, executable_memory_len);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,39 @@
#ifndef RUNMEM_H
#define RUNMEM_H
#include <stdint.h>
/* Command opcodes used by the parser */
#define ALLOCATE_DATA 1
#define COPY_DATA 2
#define ALLOCATE_CODE 3
#define COPY_CODE 4
#define PATCH_FUNC 5
#define PATCH_OBJECT 6
#define SET_ENTR_POINT 64
#define READ_DATA 65
#define END_PROG 256
#define FREE_MEMORY 257
/* Relocation types */
#define PATCH_RELATIVE_32 0
/* Memory blobs accessible by other translation units */
extern uint8_t *data_memory;
extern uint32_t data_memory_len;
extern uint8_t *executable_memory;
extern uint32_t executable_memory_len;
/* Entry point type and variable */
typedef int (*entry_point_t)(void);
extern entry_point_t entr_point;
/* Command parser: takes a pointer to the command stream and returns
an error flag (0 on success according to current code) */
int parse_commands(uint8_t *bytes);
/* Free program and data memory */
void free_memory();
#endif /* RUNMEM_H */

View File

@ -1,6 +1,5 @@
from copapy import Write, const, Node
from copapy import Write, const
import copapy as rc
from typing import Iterable, Generator
def test_ast_generation():

View File

@ -2,7 +2,7 @@ from copapy import Write, const
import copapy
import subprocess
import struct
from copapy import binwrite as binw
from copapy import binwrite
def run_command(command: list[str], encoding: str = 'utf8') -> str:
@ -60,12 +60,12 @@ def test_compile():
copapy.read_variable(il, r1)
copapy.read_variable(il, r2)
il.write_com(binw.Command.READ_DATA)
il.write_com(binwrite.Command.READ_DATA)
il.write_int(0)
il.write_int(36)
# run program command
il.write_com(binw.Command.END_PROG)
il.write_com(binwrite.Command.END_PROG)
print('* Data to runner:')
il.print()

View File

@ -0,0 +1,39 @@
from coparun_module import coparun
from copapy import Write, const
import copapy
from copapy import binwrite
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)]
il = copapy.compile_to_instruction_list(out)
copapy.read_variable(il, r1)
copapy.read_variable(il, r2)
il.write_com(binwrite.Command.READ_DATA)
il.write_int(0)
il.write_int(36)
# run program command
il.write_com(binwrite.Command.END_PROG)
#print('* Data to runner:')
#il.print()
print('+ run coparun')
result = coparun(il.get_data())
assert result == 1
if __name__ == "__main__":
test_compile()