aboutsummaryrefslogtreecommitdiff
path: root/include/ccdjit.h
blob: f8a212bec9384e28563fe6e7fed5b2ac061dc7aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#ifndef CCDJIT_PUBLIC_H
#define CCDJIT_PUBLIC_H

/*
 * CCDJIT public embedding API
 *
 * This header is the stable boundary for hosts that want to compile and run C
 * code at runtime.  The implementation lives in ccdjit.c, but the intended use
 * should be clear from this file alone.
 *
 * Basic use:
 *
 *      ccdjit_context *ctx = ccdjit_context_new(NULL);
 *      ccdjit_module *module = NULL;
 *
 *      if (ccdjit_compile_string(ctx, source, "plugin.c", &module) == 0) {
 *              int result = 0;
 *              ccdjit_module_call_main(module, 0, NULL, &result);
 *              ccdjit_module_free(module);
 *      }
 *      ccdjit_context_free(ctx);
 *
 * Return convention:
 *      Functions returning int use 0 for success and -1 for failure unless
 *      documented otherwise.  On failure, call ccdjit_context_last_error().
 *
 * Lifetime:
 *      A context owns options, include paths, registered symbols, and the last
 *      diagnostic.  A module owns generated code or a loaded CCDJIT binary.
 *      Modules keep their context state alive, so ccdjit_context_free() may be
 *      called before ccdjit_module_free(); the context is released when the
 *      last module using it is freed.
 *      Function pointers returned by ccdjit_module_symbol() are invalid after
 *      ccdjit_module_free().
 *
 * Threads:
 *      Independent contexts may compile and run from different threads.  Do
 *      not mutate or use the same context or module concurrently from multiple
 *      threads unless the host provides its own synchronization.
 *
 * Safety:
 *      Normal calls run in the host process.  Set sandbox_execution to
 *      CCDJIT_SANDBOX_PROCESS to run ccdjit_module_call_main() in a child
 *      process with optional resource, syscall, and filesystem limits.
 */

#include <stddef.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ccdjit_context ccdjit_context;
typedef struct ccdjit_module ccdjit_module;

typedef struct {
	/* Host compiler or linker driver.  NULL uses $CC, then cc. */
	const char *driver;

	/* Extra linker arguments such as -L, -l, or -Wl options. */
	const char *const *link_args;
	size_t link_arg_count;

	/* Print the host linker command to stderr when nonzero. */
	int verbose;
} ccdjit_link_options;

/*
 * Host symbol callback.  Return NULL when the name is unknown.
 *
 * The returned pointer is used as an external function or object address by
 * generated code.  Keep any pointed-to object alive while modules using it may
 * run.
 */
typedef void *(*ccdjit_symbol_resolver)(const char *name, void *userdata);

typedef enum {
	/* Run generated code directly in the host process. */
	CCDJIT_SANDBOX_OFF = 0,

	/* Run ccdjit_module_call_main() in a forked child process. */
	CCDJIT_SANDBOX_PROCESS = 1,
} ccdjit_sandbox_mode;

typedef enum {
	/*
	 * Minimal child profile: enough to write the result pipe, write
	 * captured output, and exit.  Unexpected syscalls are trapped.
	 */
	CCDJIT_SANDBOX_SYSCALL_STRICT = 0,

	/* STRICT plus read/close for simple stdio-style code. */
	CCDJIT_SANDBOX_SYSCALL_STDIO = 1,

	/* STDIO plus mmap/munmap/mprotect/brk/clock_gettime. */
	CCDJIT_SANDBOX_SYSCALL_RUNTIME = 2,

	/* Disable the seccomp filter.  Resource limits still apply. */
	CCDJIT_SANDBOX_SYSCALL_PERMISSIVE = 3,
} ccdjit_sandbox_syscall_policy;

typedef enum {
	/* Runtime file syscalls are denied by the syscall policy. */
	CCDJIT_SANDBOX_FS_NONE = 0,

	/* Permit read-only access under explicitly added roots. */
	CCDJIT_SANDBOX_FS_READ_ONLY_ROOTS = 1,

	/* Permit writes only for roots added with writable != 0. */
	CCDJIT_SANDBOX_FS_WRITABLE_ROOTS = 2,
} ccdjit_sandbox_filesystem_policy;

typedef struct {
	/* Allow #include to read files from include paths. */
	int allow_filesystem_includes;

	/* Allow the preprocessor to discover and read host system includes. */
	int allow_system_includes;

	/* Allow executable mappings for JIT code and loaded CCDJIT binaries. */
	int allow_executable_memory;

	/* ccdjit_sandbox_mode.  Default is CCDJIT_SANDBOX_OFF. */
	int sandbox_execution;

	/* ccdjit_sandbox_syscall_policy.  Default is STRICT. */
	int sandbox_syscalls;

	/* ccdjit_sandbox_filesystem_policy.  Default is FS_NONE. */
	int sandbox_filesystem;

	/*
	 * In sandbox mode, default dlsym lookup is disabled unless this is set.
	 * Prefer explicit ccdjit_context_register_symbol() capabilities.
	 */
	int sandbox_allow_implicit_symbols;

	/* Wall-clock timeout for one sandboxed main call.  Zero means no limit.
	 */
	unsigned int sandbox_timeout_ms;

	/* RLIMIT_CPU for sandboxed calls, rounded up to seconds by the OS. */
	unsigned int sandbox_cpu_time_ms;

	/* RLIMIT_AS for sandboxed calls.  Zero means keep the host default. */
	size_t sandbox_address_space_bytes;

	/* RLIMIT_STACK for sandboxed calls.  Zero means keep the host default.
	 */
	size_t sandbox_stack_bytes;

	/* RLIMIT_FSIZE for sandboxed calls.  Zero means keep the host default.
	 */
	size_t sandbox_file_size_bytes;

	/* Captured stdout/stderr cap for sandboxed calls.  Zero means 64 KiB.
	 */
	size_t sandbox_output_limit_bytes;

	/*
	 * Preprocessor output byte cap.  Zero uses the default currently 128
	 * MiB.
	 */
	size_t preprocessor_output_limit_bytes;

	/* Macro expansion recursion cap.  Zero uses the default. */
	unsigned int preprocessor_macro_depth_limit;

	/* Nested include cap.  Zero uses the default. */
	unsigned int preprocessor_include_depth_limit;
} ccdjit_options;

typedef struct {
	/* Compiler phase such as LEXER, PARSER, JIT, API, or SANDBOX. */
	const char *phase;

	/* Source file, binary path, symbol name, or "<input>". */
	const char *filename;

	/* Human-readable error text owned by the context. */
	const char *message;

	/* Optional source excerpt for lexer/parser/type diagnostics. */
	const char *source_line;
	const char *caret_line;

	/* One-based line and column when available, otherwise zero. */
	int line;
	int column;

	/* Raw child wait status or errno-style value for sandbox/API failures.
	 */
	int status;

	/* Signal that terminated the sandbox child, or zero. */
	int signal_number;
} ccdjit_diagnostic;

/* Create a compiler context.  Passing NULL uses permissive CLI-like defaults.
 */
ccdjit_context *ccdjit_context_new(const ccdjit_options *options);

/* Free the context and all context-owned diagnostics/options/lists. */
void ccdjit_context_free(ccdjit_context *ctx);

/* Add a preprocessor include search path, like -I. */
int ccdjit_context_add_include_path(ccdjit_context *ctx, const char *path);

/* Restrict filesystem includes to this root when include roots are used. */
int ccdjit_context_add_include_root(ccdjit_context *ctx, const char *path);

/* Add a preprocessor definition, like -DNAME or -DNAME=value. */
int ccdjit_context_add_define(ccdjit_context *ctx, const char *define);

/* Grant generated code an explicit external symbol capability. */
int ccdjit_context_register_symbol(ccdjit_context *ctx, const char *name,
    void *addr);

/* Add an explicit shared library path for external symbol lookup. */
int ccdjit_context_add_library(ccdjit_context *ctx, const char *path);

/* Install a fallback external symbol resolver. */
void ccdjit_context_set_symbol_resolver(ccdjit_context *ctx,
    ccdjit_symbol_resolver resolver, void *userdata);

/* Update sandbox mode, syscall policy, filesystem policy, and symbol policy. */
int ccdjit_context_set_sandbox_options(ccdjit_context *ctx, int mode,
    int syscall_policy, int filesystem_policy, int allow_implicit_symbols);

/* Update all sandbox resource limits in one call. */
int ccdjit_context_set_sandbox_limits(ccdjit_context *ctx,
    unsigned int timeout_ms, unsigned int cpu_time_ms,
    size_t address_space_bytes, size_t stack_bytes, size_t file_size_bytes,
    size_t output_limit_bytes);

/* Update preprocessor resource limits.  Passing zero keeps the default. */
int ccdjit_context_set_preprocessor_limits(ccdjit_context *ctx,
    size_t output_limit_bytes, unsigned int macro_depth,
    unsigned int include_depth);

/*
 * Add a Landlock filesystem root for sandboxed execution.
 *
 * writable is only honored when sandbox_filesystem is
 * CCDJIT_SANDBOX_FS_WRITABLE_ROOTS.  On systems without Landlock, requesting a
 * root-based filesystem policy fails while entering the sandbox.
 */
int ccdjit_context_add_sandbox_filesystem_root(ccdjit_context *ctx,
    const char *path, int writable);

/* Return the last error for this context.  The pointer is context-owned. */
const ccdjit_diagnostic *ccdjit_context_last_error(ccdjit_context *ctx);

/* Return captured sandbox stdout/stderr from the last sandboxed main call. */
const char *ccdjit_context_last_stdout(ccdjit_context *ctx);
const char *ccdjit_context_last_stderr(ccdjit_context *ctx);

/* Compile a source file into a live JIT module. */
int ccdjit_compile_file(ccdjit_context *ctx, const char *path,
    ccdjit_module **out);

/* Compile a source string into a live JIT module. */
int ccdjit_compile_string(ccdjit_context *ctx, const char *source,
    const char *filename, ccdjit_module **out);

/*
 * Compile a source file into a live JIT module without requiring main().
 * Use ccdjit_module_symbol() to find and call exported config/plugin symbols.
 */
int ccdjit_compile_file_module(ccdjit_context *ctx, const char *path,
    ccdjit_module **out);

/*
 * Compile a source string into a live JIT module without requiring main().
 * Use ccdjit_module_symbol() to find and call exported config/plugin symbols.
 */
int ccdjit_compile_string_module(ccdjit_context *ctx, const char *source,
    const char *filename, ccdjit_module **out);

/* Convenience wrapper: compile a source string, call main, free the module. */
int ccdjit_eval_string(ccdjit_context *ctx, const char *source,
    const char *filename, int argc, char **argv, int *result_out);

/*
 * Find a function or object symbol in a module.
 *
 * Calling the returned function pointer runs in the host process even when the
 * context has sandbox_execution enabled.  Use ccdjit_module_call_main() for
 * sandboxed execution.
 */
void *ccdjit_module_symbol(ccdjit_module *module, const char *name);

/* Call module main(argc, argv).  Honors CCDJIT_SANDBOX_PROCESS. */
int ccdjit_module_call_main(ccdjit_module *module, int argc, char **argv,
    int *result_out);

/* Emit textual assembly-like bytes for inspection. */
int ccdjit_module_emit_assembly(ccdjit_module *module, FILE *out);

/* Emit a CCDJITB2 binary image. */
int ccdjit_module_emit_binary(ccdjit_module *module, FILE *out);

/* Emit an ELF64 relocatable object. */
int ccdjit_module_emit_object(ccdjit_module *module, FILE *out);

/*
 * Emit a linked host executable.
 *
 * This depends on the host compiler driver.  It is intended for trusted build
 * paths, not sandboxed execution.
 */
int ccdjit_module_emit_executable_file(ccdjit_module *module, const char *path,
    const ccdjit_link_options *options);

/* Emit a CCDJITB2 binary image directly to a path. */
int ccdjit_module_emit_binary_file(ccdjit_module *module, const char *path);

/* Load a CCDJITB2 binary image into executable memory. */
int ccdjit_module_load_binary(ccdjit_context *ctx, const char *path,
    ccdjit_module **out);

/* Free generated code, loaded binary storage, AST, and module-owned source. */
void ccdjit_module_free(ccdjit_module *module);

#ifdef __cplusplus
}
#endif

#endif