|
libtealet 0.4.2
|
Public core API for libtealet. More...
#include <stddef.h>Go to the source code of this file.
Data Structures | |
| struct | tealet_alloc_t |
| struct | tealet_t |
| struct | tealet_config_t |
| struct | tealet_stats_t |
Macros | |
| #define | TEALET_VERSION_MAJOR 0 |
| #define | TEALET_VERSION_MINOR 4 |
| #define | TEALET_VERSION_PATCH 3 |
| #define | TEALET_VERSION "0.4.3" |
| #define | TEALET_VERSION_NUMBER ((TEALET_VERSION_MAJOR * 10000) + (TEALET_VERSION_MINOR * 100) + TEALET_VERSION_PATCH) |
| #define | TEALET_API |
| #define | TEALET_ALLOC_INIT_MALLOC { (tealet_malloc_t) & malloc, (tealet_free_t)&free, 0 } |
| #define | TEALET_ALLOC_MALLOC(alloc, size) (alloc)->malloc_p((size), (alloc)->context) |
| #define | TEALET_ALLOC_FREE(alloc, ptr) (alloc)->free_p((ptr), (alloc)->context) |
| #define | TEALET_ERR_MEM -1 /* memory allocation failed */ |
| #define | TEALET_ERR_DEFUNCT -2 /* the target tealet is corrupt */ |
| #define | TEALET_ERR_UNFORKABLE |
| #define | TEALET_ERR_INVAL -4 /* invalid argument */ |
| #define | TEALET_ERR_INTEGRITY -5 /* current tealet violated stack-integrity boundary */ |
| #define | TEALET_ERR_PANIC -6 /* switched to main due to panic reroute from tealet_exit() */ |
| #define | TEALET_CONFIG_VERSION_1 1 |
| #define | TEALET_CONFIG_CURRENT_VERSION TEALET_CONFIG_VERSION_1 |
| #define | TEALET_CONFIGF_STACK_INTEGRITY (1u << 0) |
| #define | TEALET_CONFIGF_STACK_GUARD (1u << 1) |
| #define | TEALET_CONFIGF_STACK_SNAPSHOT (1u << 2) |
| #define | TEALET_STACK_GUARD_MODE_NONE 0 |
| #define | TEALET_STACK_GUARD_MODE_READONLY 1 |
| #define | TEALET_STACK_GUARD_MODE_NOACCESS 2 |
| #define | TEALET_STACK_INTEGRITY_FAIL_ASSERT 0 |
| #define | TEALET_STACK_INTEGRITY_FAIL_ERROR 1 |
| #define | TEALET_STACK_INTEGRITY_FAIL_ABORT 2 |
| #define | TEALET_DEFAULT_MAX_STACK_SIZE ((size_t)(16u * 1024u * 1024u)) |
| #define | TEALET_CONFIG_INIT |
| #define | TEALET_EXIT_DEFAULT 0 /* Don't auto-delete */ |
| #define | TEALET_EXIT_DELETE 1 /* Auto-delete on exit */ |
| #define | TEALET_EXIT_DEFER 2 /* Defer exit to return statement */ |
| #define | TEALET_FORK_DEFAULT 0 |
| Fork the active tealet by duplicating its execution state. | |
| #define | TEALET_FORK_SWITCH 1 |
| #define | TEALET_ORIGIN_MAIN_LINEAGE (1u << 0) /* main tealet, or fork-descended from main */ |
| #define | TEALET_ORIGIN_FORK (1u << 1) /* tealet originated from tealet_fork() */ |
| #define | TEALET_STATUS_ACTIVE 0 |
| #define | TEALET_STATUS_EXITED 1 |
| #define | TEALET_STATUS_DEFUNCT -2 |
| #define | TEALET_MAIN(t) ((t)->main) |
| #define | TEALET_IS_MAIN(t) ((t) == TEALET_MAIN(t)) |
| #define | TEALET_CURRENT_IS_MAIN(t) (tealet_current(t) == TEALET_MAIN(t)) |
| #define | TEALET_IS_MAIN_LINEAGE(t) ((tealet_get_origin(t) & TEALET_ORIGIN_MAIN_LINEAGE) != 0) |
| #define | TEALET_IS_FORK(t) ((tealet_get_origin(t) & TEALET_ORIGIN_FORK) != 0) |
| #define | TEALET_RELATED(t1, t2) (TEALET_MAIN(t1) == TEALET_MAIN(t2)) |
| #define | TEALET_EXTRA(t, tp) ((tp *)((t)->extra)) |
Typedefs | |
| typedef void *(* | tealet_malloc_t) (size_t size, void *context) |
| typedef void(* | tealet_free_t) (void *ptr, void *context) |
| typedef struct tealet_alloc_t | tealet_alloc_t |
| typedef struct tealet_t | tealet_t |
| typedef tealet_t *(* | tealet_run_t) (tealet_t *current, void *arg) |
| typedef struct tealet_config_t | tealet_config_t |
| typedef struct tealet_stats_t | tealet_stats_t |
Functions | |
| TEALET_API tealet_t * | tealet_initialize (tealet_alloc_t *alloc, size_t extrasize) |
| Initialize libtealet and create the main tealet for the current thread. | |
| TEALET_API void | tealet_finalize (tealet_t *tealet) |
| Destroy a previously initialized main tealet. | |
| TEALET_API tealet_t * | tealet_create (tealet_t *tealet, tealet_run_t run, void *stack_far) |
| Create a new tealet without starting it. | |
| TEALET_API tealet_t * | tealet_new (tealet_t *tealet, tealet_run_t run, void **parg, void *stack_far) |
| Create and immediately start a new tealet. | |
| TEALET_API int | tealet_switch (tealet_t *target, void **parg) |
Suspend current tealet and resume target. | |
| TEALET_API int | tealet_exit (tealet_t *target, void *arg, int flags) |
Exit current tealet and transfer control to target. | |
| TEALET_API int | tealet_fork (tealet_t *current, tealet_t **pother, void **parg, int flags) |
| TEALET_API tealet_t * | tealet_duplicate (tealet_t *tealet) |
| Duplicate a suspended tealet and its saved stack state. | |
| TEALET_API void | tealet_delete (tealet_t *target) |
| Deallocate a non-main tealet. | |
| TEALET_API tealet_t * | tealet_current (tealet_t *tealet) |
| TEALET_API tealet_t * | tealet_previous (tealet_t *tealet) |
| TEALET_API void ** | tealet_main_userpointer (tealet_t *tealet) |
| TEALET_API unsigned int | tealet_get_origin (tealet_t *tealet) |
| Get tealet origin flags. | |
| TEALET_API int | tealet_status (tealet_t *tealet) |
| TEALET_API size_t | tealet_get_stacksize (tealet_t *tealet) |
| Get byte size of currently saved stack snapshot for a tealet. | |
| TEALET_API void * | tealet_get_far (tealet_t *tealet) |
| Get a tealet's far stack boundary marker. | |
| TEALET_API void | tealet_get_stats (tealet_t *t, tealet_stats_t *s) |
| TEALET_API void | tealet_reset_peak_stats (tealet_t *t) |
| TEALET_API int | tealet_set_far (tealet_t *tealet, void *far_boundary) |
| TEALET_API int | tealet_configure_get (tealet_t *tealet, tealet_config_t *config) |
| Get effective runtime configuration for a main tealet. | |
| TEALET_API int | tealet_configure_set (tealet_t *tealet, tealet_config_t *config) |
| Set runtime configuration for a main tealet. | |
| TEALET_API int | tealet_configure_check_stack (tealet_t *tealet, size_t stack_integrity_bytes) |
| Enable stack-integrity checking with practical defaults. | |
| TEALET_API void * | tealet_malloc (tealet_t *tealet, size_t s) |
| Allocate memory using the tealet-domain allocator. | |
| TEALET_API void | tealet_free (tealet_t *tealet, void *p) |
| Free memory using the tealet-domain allocator. | |
| TEALET_API ptrdiff_t | tealet_stack_diff (void *a, void *b) |
| Direction-aware stack pointer subtraction. | |
| TEALET_API void * | tealet_stack_further (void *a, void *b) |
| Return whichever of two addresses is farther in stack-growth direction. | |
| TEALET_API void * | tealet_new_probe (tealet_t *dummy1, tealet_run_t dummy2, void **dummy3, void *dummy4) |
| Probe helper returning the effective initial far boundary at call site depth. | |
| #define TEALET_ALLOC_INIT_MALLOC { (tealet_malloc_t) & malloc, (tealet_free_t)&free, 0 } |
use the following macro to initialize a tealet_alloc_t structure with stdlib malloc functions, for convenience, e.g.: tealet_alloc_t stdalloc = TEALET_MALLOC;
| #define TEALET_ALLOC_MALLOC | ( | alloc, | |
| size | |||
| ) | (alloc)->malloc_p((size), (alloc)->context) |
convenience macros to call an allocator
| #define TEALET_CONFIG_INIT |
| #define TEALET_ERR_MEM -1 /* memory allocation failed */ |
error codes. API functions that return int return a negative value to signal an error. Those that return tealet_t pointers return NULL to signal a memory error.
| #define TEALET_ERR_UNFORKABLE |
| #define TEALET_FORK_DEFAULT 0 |
| current | Currently active tealet to duplicate. |
| pother | Optional out-pointer to the opposite side (parent gets child, child gets parent). |
| parg | Optional in/out argument pointer passed to whichever side resumes later. |
| flags | Fork mode: TEALET_FORK_DEFAULT or TEALET_FORK_SWITCH. |
| 1 | Parent side. |
| 0 | Child side. |
| TEALET_ERR_UNFORKABLE | Current stack is unbounded (set far boundary first). |
| TEALET_ERR_MEM | Memory allocation failure. |
This function duplicates the currently executing tealet, including its entire execution stack. Both the parent and child tealets will resume execution from the same point (immediately after tealet_fork returns).
This function duplicates the currently executing tealet, including its entire execution stack. Both the parent and child tealets will resume execution from the same point (immediately after tealet_fork returns).
The function returns different values to distinguish parent from child:
If pchild is non-NULL, it will be filled with a pointer to the other tealet:
The parg parameter allows passing a pointer value to whichever side of the fork initially gets suspended (similar to tealet_new()). Can be NULL if no argument passing is desired. If non-NULL:
Prerequisites:
Flags:
Memory considerations:
stack_far) at fork time. For main-lineage forks, this means the configured main boundary propagates to the child clone.Exit behavior depends on what was forked:
Example: tealet_t *child = NULL; int result = tealet_fork(current, &child, NULL, TEALET_FORK_DEFAULT); if (result == 0) { // This is the child tealet // ... child-specific code ... } else if (result > 0) { // This is the parent tealet // ... parent-specific code ... tealet_switch(child, &arg); // Switch to child when ready } else { // Error occurred (result < 0) }
Returns: Parent: 1 Child: 0 Error: negative error code: TEALET_ERR_UNFORKABLE if current tealet has unbounded stack TEALET_ERR_MEM if memory allocation failed TEALET_ERR_DEFUNCT if current tealet is not active
| typedef struct tealet_config_t tealet_config_t |
Runtime configuration for stack integrity and related safety features.
ABI compatibility contract:
| typedef void *(* tealet_malloc_t) (size_t size, void *context) |
A structure to define the memory allocation api used. the functions have C89 semantics and take an additional "context" pointer that they can use as they please
The "run" function of a tealet. It is called with the current tealet and the argument provided to its invocation function, see tealet_create() and tealet_switch(). The return value of run() must be the next tealet in which to continue execution, which must be a different one, like for example the main tealet. When 'run(g)' returns, the tealet 'g' is freed.
The user-visible tealet structure. If an "extrasize" is provided when the main tealet was initialized, "extra" points to a private block of that size, otherwise it is initialized to NULL
| TEALET_API int tealet_configure_check_stack | ( | tealet_t * | tealet, |
| size_t | stack_integrity_bytes | ||
| ) |
| tealet | Any tealet in the domain. |
| stack_integrity_bytes | Requested watch window bytes; 0 picks sensible default. |
Convenience helper to enable stack checking with sensible defaults.
This enables stack integrity checks with both guard pages and snapshot verification, then applies the resulting configuration via tealet_configure_set().
If stack_integrity_bytes is 0, a default window of one OS memory page is used where available.
Note: this helper sets stack_guard_limit to a local stack marker inside this function. For best results, call it from a program top-level function whose frame should remain within the intended stack region for switched tealets.
This helper is intended as a one-way "turn checks on" API. You can still use tealet_configure_set() directly for custom tuning.
Return values: 0 on success <0 on error (e.g. TEALET_ERR_INVAL, TEALET_ERR_MEM)
| TEALET_API int tealet_configure_get | ( | tealet_t * | tealet, |
| tealet_config_t * | config | ||
| ) |
| tealet | Any tealet in the domain. |
| config | Size/versioned output buffer. |
Get or set runtime configuration on the main tealet.
These functions are intended for feature toggles such as optional stack integrity checks. The caller supplies a size/versioned config struct.
tealet_configure_set() canonicalizes the struct in-place to the effective configuration actually applied by this build/platform (unsupported features are cleared and dependent fields normalized).
Return values: 0 on success <0 on error (e.g. TEALET_ERR_INVAL)
| TEALET_API int tealet_configure_set | ( | tealet_t * | tealet, |
| tealet_config_t * | config | ||
| ) |
| tealet | Any tealet in the domain. |
| config | Size/versioned input/output configuration struct. |
Input is canonicalized in-place to effective applied settings.
| TEALET_API tealet_t * tealet_create | ( | tealet_t * | tealet, |
| tealet_run_t | run, | ||
| void * | stack_far | ||
| ) |
| tealet | Main/related tealet context used for allocation and ownership. |
| run | Entry function for the created tealet. |
| stack_far | Optional minimum far-boundary requirement for the initial stack snapshot. |
The new tealet enters execution when tealet_switch() first targets it. If stack_far is non-NULL, capture range is only extended (never shrunk) relative to the default internally selected boundary.
Return the current tealet, i.e. the one in which the caller of this function currently is. "tealet" can be any tealet derived from the main tealet.
| TEALET_API void tealet_delete | ( | tealet_t * | target | ) |
| target | Tealet to delete. |
Deallocate a tealet. Use this to delete a tealet that has exited with tealet_exit() with 'TEALET_EXIT_DEFAULT', or defunct tealets. Active tealet can also be deleted, such as stubs that are no longer in use, but take care because any local resources in such tealets won't be freed.
| tealet | Source tealet (must not be current/main). |
Duplicate starts with copied stack snapshot and copied internal flags. Extra payload area (extra) is copied when configured.
Duplicate a tealet. The active tealet is duplicated along with its stack contents. This can be used, for example, to create "stubs" that can be duplicated and re-used to run with different arguments. Use this with care, because initially the duplicate will share the same stack data as the original. This includes any local variables, even the "current" argument passed to the original "run" function which may be obsolete by the time the duplicate is run. Use the argument passing mechanism to provide the copy with fresh data. Any extra data is left uncopied, the application must do that.
| TEALET_API int tealet_exit | ( | tealet_t * | target, |
| void * | arg, | ||
| int | flags | ||
| ) |
| target | Requested target tealet. |
| arg | Optional argument to deliver to target. |
| flags | Exit behavior bits: TEALET_EXIT_DEFAULT, TEALET_EXIT_DELETE, TEALET_EXIT_DEFER. |
If the requested target is defunct, control is rerouted to main as panic fallback. return p; from run() is equivalent to tealet_exit(p, NULL, TEALET_EXIT_DELETE).
| TEALET_API void tealet_finalize | ( | tealet_t * | tealet | ) |
| tealet | Main tealet returned by tealet_initialize(). |
| TEALET_API void tealet_free | ( | tealet_t * | tealet, |
| void * | p | ||
| ) |
| tealet | Any tealet in the domain. |
| p | Pointer previously allocated by tealet_malloc() or equivalent domain allocator. |
| TEALET_API void * tealet_get_far | ( | tealet_t * | tealet | ) |
| tealet | Target tealet. |
Get a tealet's "far" position on the stack. This is an indicator of its creation position on the stack. The main tealet extends until the beginning of stack
| TEALET_API unsigned int tealet_get_origin | ( | tealet_t * | tealet | ) |
| tealet | Target tealet. |
Origin flags are immutable identity markers:
| TEALET_API size_t tealet_get_stacksize | ( | tealet_t * | tealet | ) |
| tealet | Target tealet. |
| TEALET_API tealet_t * tealet_initialize | ( | tealet_alloc_t * | alloc, |
| size_t | extrasize | ||
| ) |
| alloc | Allocator interface; use TEALET_ALLOC_INIT_MALLOC for stdlib malloc/free. |
| extrasize | Optional per-tealet extra bytes; exposed via tealet_t::extra / TEALET_EXTRA. |
The main tealet represents the ambient program execution context for this thread. Initialize/finalize pairs may be nested or used in multiple threads, but only tealets derived from the same main tealet are switch-compatible.
| TEALET_API void ** tealet_main_userpointer | ( | tealet_t * | tealet | ) |
Get the address of the tealet's main user pointer, a single void pointer associated with the main tealet. Use this to associate a (void*)value with the main tealet.
| TEALET_API void * tealet_malloc | ( | tealet_t * | tealet, |
| size_t | s | ||
| ) |
| tealet | Any tealet in the domain. |
| s | Byte count. |
access to the tealet's allocator. This can be useful to use e.g. when passing data between tealets but such data cannot reside on the stack (except during the initial call to tealet_new)
| TEALET_API tealet_t * tealet_new | ( | tealet_t * | tealet, |
| tealet_run_t | run, | ||
| void ** | parg, | ||
| void * | stack_far | ||
| ) |
| tealet | Main/related tealet context. |
| run | Entry function. |
| parg | In/out switch argument pointer (same semantics as tealet_switch()). |
| stack_far | Optional minimum far-boundary requirement for the initial stack snapshot. |
Semantically equivalent to tealet_create() followed by tealet_switch(), but performed as one operation.
| TEALET_API void * tealet_new_probe | ( | tealet_t * | dummy1, |
| tealet_run_t | dummy2, | ||
| void ** | dummy3, | ||
| void * | dummy4 | ||
| ) |
| dummy1 | Matches tealet_new() signature; ignored. |
| dummy2 | Matches tealet_new() signature; ignored. |
| dummy3 | Matches tealet_new() signature; ignored. |
| dummy4 | Optional requested boundary (as in tealet_new()). |
this is used to get the "far" address if a tealet were initialized here. The arguments must match tealet_new(); they are only dummies.
Return the previous tealet, i.e. the one that switched to us. "tealet" can be any tealet derived from the main tealet.
| TEALET_API int tealet_set_far | ( | tealet_t * | tealet, |
| void * | far_boundary | ||
| ) |
Set the far boundary of a tealet's stack.
This function sets the far boundary of a tealet's stack, which defines where the stack ends for stack-slicing operations.
For the main tealet: The main tealet normally has an unbounded stack extent (represented internally as STACKMAN_SP_FURTHEST). To enable operations like tealet_fork() that need to duplicate the stack, you must first set a far boundary.
IMPORTANT: The far_boundary pointer should typically come from a PARENT function of the function that will perform fork operations. This ensures that all local variables in the forking function (and any functions it calls) are included in the saved stack slice. If you pass a local variable from the same function that calls fork, that variable and others declared after it may not be properly saved.
Recommended pattern (far_boundary from parent function):
int main(void) { int far_marker; run_program(&far_marker); return 0; }
void run_program(void *far_marker) { tealet_t *main = tealet_initialize(&alloc, 0); tealet_set_far(main, far_marker);
int local_var = 0; tealet_fork(main, &child, 0); }
Alternative (far_boundary from same function, requires care):
void my_main() { int far_marker; tealet_t *main = tealet_initialize(&alloc, 0); tealet_set_far(main, &far_marker);
int local_var = 0; tealet_fork(main, &child, 0); }
By providing this address, you promise that no stack data beyond (further from) this point needs to be saved during fork/duplicate operations.
Note: Currently, this function can only be called on the main tealet. Calling it on a non-main tealet will return an error.
Returns: 0 on success -1 if called from a non-main tealet
| TEALET_API ptrdiff_t tealet_stack_diff | ( | void * | a, |
| void * | b | ||
| ) |
| a | First stack position. |
| b | Second stack position. |
b is deeper than a in stack-growth direction.subtract two stack positions, taking into account if the local stack grows up or down in memory. The result is positive if 'b' is 'deeper' on the stack than 'a'
| TEALET_API void * tealet_stack_further | ( | void * | a, |
| void * | b | ||
| ) |
| a | First stack position. |
| b | Second stack position. |
Return whichever stack position is farther from the active stack top. This is direction-aware: on descending stacks this is the larger address; on ascending stacks it is the smaller address.
| TEALET_API int tealet_switch | ( | tealet_t * | target, |
| void ** | parg | ||
| ) |
| target | Tealet to switch to; must share the same main tealet and thread. |
| parg | In/out argument pointer passed across switches; may be NULL. |
| 0 | Success. |
| TEALET_ERR_MEM | Save/restore failed due to memory pressure. |
| TEALET_ERR_DEFUNCT | Target tealet/stack is defunct. |
| TEALET_ERR_PANIC | Returned to main after panic reroute from tealet_exit(). |
| TEALET_ERR_INVAL | Invalid state/target. |
parg.