💾 Archived View for danielc.dev › tiny-embedded-module-system-spec.gmi captured on 2023-04-27 at 07:30:17. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

Tiny Embedded Module System Spec

Feb 4 2022

The Idea: Create a memory efficient alternative to ELF that

is less powerful, but good enough for a plugin/module system.

---

The Solution: TEMS (Tiny Embedded Module System)

Modules are loaded via file on SD card/storage device.

Module file is compiled machine code, with the first bytes being

the `_start` function.

`_start` recieves an address to this function:

struct ModuleInfo {
	// Version of the module API - default is 0
	uint32_t version;

	// Length of symbols
	uint32_t length;

	// Variable length struct
	struct Symbol {
		char *name;
		uintptr_t addr;
	}sym[];
};

`_start` parses this structure. It should only

load in addresses for symbols that have been compiled

into the module. Excess symbols are ignored. Error is reported

when symbols aren't found.

`_start` should have the following header:

int _start(struct ModuleInfo *info, int action);

If `action` is `0x0`:

Load symbols, run a "startup" code if needed.

Return `0x0` on success. Return `0x1` on symbol error.

Return `0x3` on startup error.

If `action` is `0x1`:

Run the main module code. Returns `0x0` on success.

Returns `0x1` on error.

How this is done isn't crucial, here are some of the top of my head:

- The module file has a local copy of `struct ModuleInfo`.

- It includes the required functions, and `addr` stores an address to

an assembly variable.

- `version` is checked between both structs, to ensure compatibility.

Basic trampoline:

Here, `ModuleInfo.sym[x].addr` would refer to the address of `sprintf_addr`.

.global sprintf
.global sprintf_addr
sprintf:
	adr r9, sprintf_addr
	ldr r9, [r9]
	bx r9
sprintf_addr: .long 0

Here is a generic lazy way:

if (!strcmp(info.funcs[0].type, "sprintf") {
	sprintf_addr = info.funcs[0].addr;
} ...

Generating the structures and code could be done in the following ways:

- Write a linker script

- Write a python script

- Write a C "script"

(Writing a Python script is the most readable choice.)

With this spec, writing cross platform modules could be

possible, assuming the device provides POSIX functions.