infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
emit.c
Go to the documentation of this file.
1
13#define INFIX_BUILDING
14#include "emit/emit.h"
15#include "common/compat_c23.h"
16#include "emit_internals.h"
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#define EMIT_DEFAULT_SECTION_CAPACITY 4096
22#define EMIT_SECTION_GROWTH_FACTOR 2
23
25 ctx->arch = arch;
26 ctx->format = format;
28 ctx->sections = NULL;
29 ctx->current_section = NULL;
30 ctx->symbols = NULL;
31 ctx->relocations = NULL;
32 ctx->binary_spec = NULL;
33 ctx->current_block_name = NULL;
34 ctx->section_count = 0;
35}
36
38 if (!ctx)
39 return;
40
41 emit_section_t * sec = ctx->sections;
42 while (sec) {
43 emit_section_t * next = sec->next;
44 free(sec->name);
45 free(sec->data);
46 free(sec);
47 sec = next;
48 }
49
50 emit_symbol_t * sym = ctx->symbols;
51 while (sym) {
52 emit_symbol_t * next = sym->next;
53 free(sym->name);
54 free(sym);
55 sym = next;
56 }
57
58 emit_relocation_t * rel = ctx->relocations;
59 while (rel) {
60 emit_relocation_t * next = rel->next;
61 free(rel->symbol_name);
62 free(rel->section_name);
63 free(rel);
64 rel = next;
65 }
66
68}
69
70static emit_section_t * _create_section(const char * name, emit_section_flags_t flags) {
71 emit_section_t * section = calloc(1, sizeof(emit_section_t));
72 if (!section)
73 return NULL;
74
75 section->name = strdup(name);
76 section->flags = flags;
77 section->data = malloc(EMIT_DEFAULT_SECTION_CAPACITY);
78 if (!section->data) {
79 free(section->name);
80 free(section);
81 return NULL;
82 }
84 section->size = 0;
85 section->next = NULL;
86
87 return section;
88}
89
91 if (!ctx || !name)
92 return NULL;
93
94 for (emit_section_t * sec = ctx->sections; sec != NULL; sec = sec->next)
95 if (strcmp(sec->name, name) == 0)
96 return sec;
97 return NULL;
98}
99
101 if (!ctx || !name)
102 return NULL;
103
104 for (emit_symbol_t * sym = ctx->symbols; sym != NULL; sym = sym->next)
105 if (strcmp(sym->name, name) == 0)
106 return sym;
107 return NULL;
108}
109
112 if (!out_ctx) {
115 }
116
117 emit_context_t * ctx = calloc(1, sizeof(emit_context_t));
118 if (!ctx)
120
121 _emit_context_init(ctx, arch, format);
122
123 *out_ctx = ctx;
124 return INFIX_SUCCESS;
125}
126
129 free(ctx);
130}
131
134 if (!ctx || !name) {
137 }
138
139 emit_section_t * existing = _emit_lookup_section(ctx, name);
140 if (existing) {
143 }
144
145 emit_section_t * section = _create_section(name, flags);
146 if (!section)
148
149 section->next = ctx->sections;
150 ctx->sections = section;
151 ctx->section_count++;
152
153 return INFIX_SUCCESS;
154}
155
156INFIX_API infix_status emit_begin_section(emit_context_t * ctx, const char * section_name) {
158 if (!ctx || !section_name) {
161 }
162
163 emit_section_t * section = _emit_lookup_section(ctx, section_name);
164 if (!section) {
167 }
168
169 ctx->current_section = section;
171 return INFIX_SUCCESS;
172}
173
175 const char * name,
176 emit_visibility_t visibility,
177 bool is_function) {
179 if (!ctx || !name) {
182 }
183
184 (void)visibility;
185
186 emit_symbol_t * sym = _emit_lookup_symbol(ctx, name);
187 if (!sym) {
188 sym = calloc(1, sizeof(emit_symbol_t));
189 if (!sym)
191
192 sym->name = strdup(name);
193 sym->next = ctx->symbols;
194 ctx->symbols = sym;
195 }
196
197 sym->is_defined = true;
198 sym->is_function = is_function;
199 sym->section = ctx->current_section;
200 sym->value = ctx->current_section ? ctx->current_section->size : 0;
201
202 return INFIX_SUCCESS;
203}
204
207 if (!ctx || !name) {
210 }
211
212 emit_symbol_t * sym = _emit_lookup_symbol(ctx, name);
213 if (!sym) {
214 sym = calloc(1, sizeof(emit_symbol_t));
215 if (!sym)
217
218 sym->name = strdup(name);
219 sym->is_defined = true;
220 sym->is_function = false;
221 sym->section = ctx->current_section;
222 sym->value = ctx->current_section ? ctx->current_section->size : 0;
223
224 sym->next = ctx->symbols;
225 ctx->symbols = sym;
226 }
227 else {
228 sym->is_defined = true;
229 sym->value = ctx->current_section ? ctx->current_section->size : 0;
230 sym->section = ctx->current_section;
231 }
232
233 return INFIX_SUCCESS;
234}
235
237 return emit_define_symbol(ctx, name, EMIT_VISIBILITY_DEFAULT, false);
238}
239
241 if (!ctx->current_section)
243
244 if (needed <= ctx->current_section->capacity)
245 return INFIX_SUCCESS;
246
247 uint64_t new_capacity = ctx->current_section->capacity * EMIT_SECTION_GROWTH_FACTOR;
248 while (new_capacity < needed)
249 new_capacity *= EMIT_SECTION_GROWTH_FACTOR;
250
251 uint8_t * new_data = realloc(ctx->current_section->data, new_capacity);
252 if (!new_data)
254
255 ctx->current_section->data = new_data;
256 ctx->current_section->capacity = new_capacity;
257 return INFIX_SUCCESS;
258}
259
260static infix_status emit_emit_bytes(emit_context_t * ctx, const void * data, size_t size) {
262 if (!ctx || !data) {
265 }
266
267 if (ctx->state != EMIT_STATE_SECTION_ACTIVE || !ctx->current_section) {
270 }
271
273 if (status != INFIX_SUCCESS)
274 return status;
275
276 memcpy(ctx->current_section->data + ctx->current_section->size, data, size);
277 ctx->current_section->size += size;
278
279 return INFIX_SUCCESS;
280}
281
283 return emit_emit_bytes(ctx, &byte, 1);
284}
285
287 uint8_t bytes[2] = {(uint8_t)(value & 0xFF), (uint8_t)((value >> 8) & 0xFF)};
288 return emit_emit_bytes(ctx, bytes, 2);
289}
290
292 uint8_t bytes[4] = {(uint8_t)(value & 0xFF),
293 (uint8_t)((value >> 8) & 0xFF),
294 (uint8_t)((value >> 16) & 0xFF),
295 (uint8_t)((value >> 24) & 0xFF)};
296 return emit_emit_bytes(ctx, bytes, 4);
297}
298
300 uint8_t bytes[8] = {(uint8_t)(value & 0xFF),
301 (uint8_t)((value >> 8) & 0xFF),
302 (uint8_t)((value >> 16) & 0xFF),
303 (uint8_t)((value >> 24) & 0xFF),
304 (uint8_t)((value >> 32) & 0xFF),
305 (uint8_t)((value >> 40) & 0xFF),
306 (uint8_t)((value >> 48) & 0xFF),
307 (uint8_t)((value >> 56) & 0xFF)};
308 return emit_emit_bytes(ctx, bytes, 8);
309}
310
313 if (!ctx || !ctx->current_section)
315
316 if (alignment == 0)
317 return INFIX_SUCCESS;
318
319 uint64_t current = ctx->current_section->size;
320 uint64_t aligned = (current + alignment - 1) & ~(alignment - 1);
321 uint64_t padding = aligned - current;
322
323 for (uint64_t i = 0; i < padding; i++) {
324 infix_status status = emit_emit_u8(ctx, 0x90);
325 if (status != INFIX_SUCCESS)
326 return status;
327 }
328
329 return INFIX_SUCCESS;
330}
331
333emit_add_relocation(emit_context_t * ctx, const char * name, uint64_t offset, uint8_t size, uint8_t inst_size) {
335 if (!ctx || !name) {
338 }
339
340 emit_relocation_t * rel = calloc(1, sizeof(emit_relocation_t));
341 if (!rel)
343
344 rel->symbol_name = strdup(name);
345 rel->section_name = ctx->current_section ? strdup(ctx->current_section->name) : NULL;
346 rel->offset = offset;
347 rel->size = size;
348 rel->inst_size = inst_size;
349 rel->is_pc_relative = true;
350
351 rel->next = ctx->relocations;
352 ctx->relocations = rel;
353
354 return INFIX_SUCCESS;
355}
356
357static void write_raw_binary(emit_context_t * ctx, uint8_t * buffer, c23_maybe_unused uint64_t total_size) {
358 emit_section_t ** secs = malloc(ctx->section_count * sizeof(emit_section_t *));
359 if (!secs)
360 return;
361
362 emit_section_t * sec = ctx->sections;
363 int count = 0;
364 while (sec) {
365 secs[count++] = sec;
366 sec = sec->next;
367 }
368
369 for (int i = count - 1; i >= 0; i--) {
370 uint64_t offset = 0;
371 for (int j = i + 1; j < count; j++)
372 offset += secs[j]->size;
373
374 memcpy(buffer + offset, secs[i]->data, secs[i]->size);
375 }
376
377 free(secs);
378}
379
381 if (!ctx)
382 return INFIX_SUCCESS;
383
384 emit_section_t ** secs = malloc(ctx->section_count * sizeof(emit_section_t *));
385 if (!secs)
387
388 emit_section_t * sec = ctx->sections;
389 int count = 0;
390 while (sec) {
391 secs[count++] = sec;
392 sec = sec->next;
393 }
394
395 uint64_t section_offsets[32] = {0};
396 for (int i = 0; i < count; i++) {
397 section_offsets[i] = 0;
398 for (int j = i + 1; j < count; j++)
399 section_offsets[i] += secs[j]->size;
400 }
401
402 for (emit_relocation_t * rel = ctx->relocations; rel != NULL; rel = rel->next) {
403 emit_symbol_t * sym = _emit_lookup_symbol(ctx, rel->symbol_name);
404 if (!sym || !sym->is_defined)
405 continue;
406
407 emit_section_t * target_sec = sym->section;
408 if (!target_sec)
409 continue;
410
411 emit_section_t * reloc_sec = NULL;
412 if (rel->section_name)
413 reloc_sec = _emit_lookup_section(ctx, rel->section_name);
414 if (!reloc_sec)
415 reloc_sec = ctx->current_section;
416 if (!reloc_sec || reloc_sec->size == 0 || !reloc_sec->data)
417 continue;
418
419 uint64_t target_sec_offset = 0;
420 uint64_t reloc_sec_offset = 0;
421 for (int i = 0; i < count; i++) {
422 if (secs[i] == target_sec)
423 target_sec_offset = section_offsets[i];
424 if (secs[i] == reloc_sec)
425 reloc_sec_offset = section_offsets[i];
426 }
427
428 uint64_t target_addr = target_sec_offset + sym->value;
429 uint64_t reloc_addr = reloc_sec_offset + rel->offset;
430
431 int64_t displacement = (int64_t)target_addr - (int64_t)(reloc_addr + rel->size);
432
433 if (rel->size == 4) {
434 if (rel->is_pc_relative)
435 *(int32_t *)(reloc_sec->data + rel->offset) = (int32_t)displacement;
436 else
437 *(uint32_t *)(reloc_sec->data + rel->offset) = (uint32_t)target_addr;
438 }
439 else if (rel->size == 8) {
440 if (rel->is_pc_relative)
441 *(int64_t *)(reloc_sec->data + rel->offset) = displacement;
442 else
443 *(uint64_t *)(reloc_sec->data + rel->offset) = target_addr;
444 }
445 }
446
447 free(secs);
448 return INFIX_SUCCESS;
449}
450
451void _emit_arch_nop(emit_context_t * ctx, uint8_t size) {
452 switch (ctx->arch) {
453 case EMIT_ARCH_X86_64:
454 for (uint8_t i = 0; i < size; i++) {
455 infix_status status = emit_emit_u8(ctx, 0x90);
456 (void)status;
457 }
458 break;
460 {
461 infix_status status = emit_emit_u32(ctx, 0xD503201F);
462 (void)status;
463 break;
464 }
465 default:
466 break;
467 }
468}
469
470infix_status _emit_arch_align(emit_context_t * ctx, uint64_t alignment) {
471 switch (ctx->arch) {
472 case EMIT_ARCH_X86_64:
473 for (uint64_t i = 0; i < alignment; i++) {
474 infix_status status = emit_emit_u8(ctx, 0x90);
475 if (status != INFIX_SUCCESS)
476 return status;
477 }
478 break;
480 for (uint64_t i = 0; i < alignment; i++) {
481 infix_status status = emit_emit_u32(ctx, 0xD503201F);
482 if (status != INFIX_SUCCESS)
483 return status;
484 }
485 break;
486 default:
487 break;
488 }
489 return INFIX_SUCCESS;
490}
491
492INFIX_API infix_status emit_get_binary(const emit_context_t * ctx, const uint8_t ** out_data, size_t * out_size) {
494 if (!ctx || !out_data || !out_size)
496
497 emit_context_t * mutable_ctx = (emit_context_t *)ctx;
499 if (status != INFIX_SUCCESS)
500 return status;
501
502 uint64_t total_size = 0;
503 for (emit_section_t * sec = ctx->sections; sec != NULL; sec = sec->next)
504 total_size += sec->size;
505
506 uint8_t * buffer = malloc(total_size);
507 if (!buffer)
509
510 write_raw_binary(mutable_ctx, buffer, total_size);
511
512 *out_data = buffer;
513 *out_size = total_size;
514
515 return INFIX_SUCCESS;
516}
517
518INFIX_API infix_status emit_get_offset(const emit_context_t * ctx, uint64_t * out_offset) {
520 if (!ctx || !out_offset)
522
523 *out_offset = ctx->current_section ? ctx->current_section->size : 0;
524 return INFIX_SUCCESS;
525}
infix_status status
Definition 103_unions.c:61
free(defs)
Provides forward compatibility macros for C23 features.
#define c23_maybe_unused
A compatibility macro for the C23 [[maybe_unused]] attribute.
Definition compat_c23.h:156
INFIX_API infix_status emit_get_binary(const emit_context_t *ctx, const uint8_t **out_data, size_t *out_size)
Definition emit.c:492
infix_status _emit_arch_align(emit_context_t *ctx, uint64_t alignment)
Definition emit.c:470
#define EMIT_SECTION_GROWTH_FACTOR
Definition emit.c:22
INFIX_API INFIX_NODISCARD infix_status emit_emit_u32(emit_context_t *ctx, uint32_t value)
Definition emit.c:291
INFIX_API infix_status emit_emit_label(emit_context_t *ctx, const char *name)
Definition emit.c:205
INFIX_API infix_status emit_get_offset(const emit_context_t *ctx, uint64_t *out_offset)
Definition emit.c:518
infix_status _emit_resolve_relocations(emit_context_t *ctx)
Definition emit.c:380
void _emit_context_free(emit_context_t *ctx)
Definition emit.c:37
emit_symbol_t * _emit_lookup_symbol(emit_context_t *ctx, const char *name)
Definition emit.c:100
INFIX_API infix_status emit_align(emit_context_t *ctx, uint64_t alignment)
Definition emit.c:311
INFIX_API infix_status emit_define_symbol(emit_context_t *ctx, const char *name, emit_visibility_t visibility, bool is_function)
Definition emit.c:174
static infix_status _ensure_section_capacity(emit_context_t *ctx, uint64_t needed)
Definition emit.c:240
INFIX_API infix_status emit_create_label(emit_context_t *ctx, const char *name)
Definition emit.c:236
static infix_status emit_emit_bytes(emit_context_t *ctx, const void *data, size_t size)
Definition emit.c:260
INFIX_API void emit_destroy(emit_context_t *ctx)
Definition emit.c:127
void _emit_arch_nop(emit_context_t *ctx, uint8_t size)
Definition emit.c:451
static void write_raw_binary(emit_context_t *ctx, uint8_t *buffer, c23_maybe_unused uint64_t total_size)
Definition emit.c:357
INFIX_API infix_status emit_emit_u16(emit_context_t *ctx, uint16_t value)
Definition emit.c:286
INFIX_API INFIX_NODISCARD infix_status emit_add_relocation(emit_context_t *ctx, const char *name, uint64_t offset, uint8_t size, uint8_t inst_size)
Definition emit.c:333
INFIX_API infix_status emit_add_section(emit_context_t *ctx, const char *name, emit_section_flags_t flags)
Definition emit.c:132
static emit_section_t * _create_section(const char *name, emit_section_flags_t flags)
Definition emit.c:70
INFIX_API infix_status emit_create(emit_context_t **out_ctx, emit_architecture_t arch, emit_format_t format)
Definition emit.c:110
void _emit_context_init(emit_context_t *ctx, emit_architecture_t arch, emit_format_t format)
Definition emit.c:24
#define EMIT_DEFAULT_SECTION_CAPACITY
Definition emit.c:21
INFIX_API infix_status emit_begin_section(emit_context_t *ctx, const char *section_name)
Definition emit.c:156
INFIX_API INFIX_NODISCARD infix_status emit_emit_u8(emit_context_t *ctx, uint8_t byte)
Definition emit.c:282
emit_section_t * _emit_lookup_section(emit_context_t *ctx, const char *name)
Definition emit.c:90
INFIX_API infix_status emit_emit_u64(emit_context_t *ctx, uint64_t value)
Definition emit.c:299
Public API for the emit JIT code generation system.
emit_section_flags_t
Definition emit.h:41
emit_architecture_t
Definition emit.h:30
@ EMIT_ARCH_X86_64
Definition emit.h:31
@ EMIT_ARCH_AARCH64
Definition emit.h:32
emit_format_t
Definition emit.h:35
@ EMIT_STATE_IDLE
Definition emit.h:55
@ EMIT_STATE_SECTION_ACTIVE
Definition emit.h:56
emit_visibility_t
Definition emit.h:48
@ EMIT_VISIBILITY_DEFAULT
Definition emit.h:49
Internal structures for the emit JIT code generation system.
@ INFIX_CODE_INVALID_KEYWORD
Definition infix.h:1378
@ INFIX_CATEGORY_PARSER
Definition infix.h:1355
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:435
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:437
@ INFIX_SUCCESS
Definition infix.h:436
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:438
#define INFIX_NODISCARD
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition infix.h:141
#define INFIX_API
Symbol visibility macro.
Definition infix.h:114
INFIX_INTERNAL void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:268
INFIX_INTERNAL void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position)
Sets the thread-local error state with detailed information.
Definition error.c:175
Definition emit_internals.h:50
emit_format_t format
Definition emit_internals.h:52
emit_relocation_t * relocations
Definition emit_internals.h:57
emit_symbol_t * symbols
Definition emit_internals.h:56
emit_section_t * sections
Definition emit_internals.h:54
emit_state_t state
Definition emit_internals.h:53
int section_count
Definition emit_internals.h:60
emit_architecture_t arch
Definition emit_internals.h:51
void * binary_spec
Definition emit_internals.h:58
emit_section_t * current_section
Definition emit_internals.h:55
char * current_block_name
Definition emit_internals.h:59
Definition emit_internals.h:40
uint8_t size
Definition emit_internals.h:44
uint8_t inst_size
Definition emit_internals.h:45
char * section_name
Definition emit_internals.h:42
uint64_t offset
Definition emit_internals.h:43
struct emit_relocation * next
Definition emit_internals.h:47
char * symbol_name
Definition emit_internals.h:41
bool is_pc_relative
Definition emit_internals.h:46
Definition emit_internals.h:22
uint64_t size
Definition emit_internals.h:26
char * name
Definition emit_internals.h:23
uint8_t * data
Definition emit_internals.h:25
uint64_t capacity
Definition emit_internals.h:27
struct emit_section * next
Definition emit_internals.h:28
emit_section_flags_t flags
Definition emit_internals.h:24
Definition emit_internals.h:31
bool is_function
Definition emit_internals.h:34
bool is_defined
Definition emit_internals.h:33
uint64_t value
Definition emit_internals.h:36
struct emit_symbol * next
Definition emit_internals.h:37
emit_section_t * section
Definition emit_internals.h:35
char * name
Definition emit_internals.h:32