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 infix_free(sec->name);
45 infix_free(sec->data);
46 infix_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 infix_free(sym->name);
54 infix_free(sym);
55 sym = next;
56 }
57
58 emit_relocation_t * rel = ctx->relocations;
59 while (rel) {
60 emit_relocation_t * next = rel->next;
63 infix_free(rel);
64 rel = next;
65 }
66
68}
69
70static char * _emit_strdup(const char * s) {
71 if (!s)
72 return NULL;
73
74 size_t len = strlen(s) + 1;
75 char * copy = infix_malloc(len);
76 if (copy)
77 memcpy(copy, s, len);
78 return copy;
79}
80
81static emit_section_t * _create_section(const char * name, emit_section_flags_t flags) {
82 emit_section_t * section = infix_calloc(1, sizeof(emit_section_t));
83 if (!section)
84 return NULL;
85
86 section->name = _emit_strdup(name);
87 section->flags = flags;
89 if (!section->data) {
90 infix_free(section->name);
91 infix_free(section);
92 return NULL;
93 }
95 section->size = 0;
96 section->next = NULL;
97
98 return section;
99}
100
102 if (!ctx || !name)
103 return NULL;
104
105 for (emit_section_t * sec = ctx->sections; sec != NULL; sec = sec->next)
106 if (strcmp(sec->name, name) == 0)
107 return sec;
108 return NULL;
109}
110
112 if (!ctx || !name)
113 return NULL;
114
115 for (emit_symbol_t * sym = ctx->symbols; sym != NULL; sym = sym->next)
116 if (strcmp(sym->name, name) == 0)
117 return sym;
118 return NULL;
119}
120
123 if (!out_ctx) {
126 }
127
128 emit_context_t * ctx = infix_calloc(1, sizeof(emit_context_t));
129 if (!ctx)
131
132 _emit_context_init(ctx, arch, format);
133
134 *out_ctx = ctx;
135 return INFIX_SUCCESS;
136}
137
142
145 if (!ctx || !name) {
148 }
149
150 emit_section_t * existing = _emit_lookup_section(ctx, name);
151 if (existing) {
154 }
155
156 emit_section_t * section = _create_section(name, flags);
157 if (!section)
159
160 section->next = ctx->sections;
161 ctx->sections = section;
162 ctx->section_count++;
163
164 return INFIX_SUCCESS;
165}
166
167INFIX_API infix_status emit_begin_section(emit_context_t * ctx, const char * section_name) {
169 if (!ctx || !section_name) {
172 }
173
174 emit_section_t * section = _emit_lookup_section(ctx, section_name);
175 if (!section) {
178 }
179
180 ctx->current_section = section;
182 return INFIX_SUCCESS;
183}
184
186 const char * name,
187 emit_visibility_t visibility,
188 bool is_function) {
190 if (!ctx || !name) {
193 }
194
195 (void)visibility;
196
197 emit_symbol_t * sym = _emit_lookup_symbol(ctx, name);
198 if (!sym) {
199 sym = infix_calloc(1, sizeof(emit_symbol_t));
200 if (!sym)
202
203 sym->name = _emit_strdup(name);
204 sym->next = ctx->symbols;
205 ctx->symbols = sym;
206 }
207
208 sym->is_defined = true;
209 sym->is_function = is_function;
210 sym->section = ctx->current_section;
211 sym->value = ctx->current_section ? ctx->current_section->size : 0;
212
213 return INFIX_SUCCESS;
214}
215
218 if (!ctx || !name) {
221 }
222
223 emit_symbol_t * sym = _emit_lookup_symbol(ctx, name);
224 if (!sym) {
225 sym = infix_calloc(1, sizeof(emit_symbol_t));
226 if (!sym)
228
229 sym->name = _emit_strdup(name);
230 sym->is_defined = true;
231 sym->is_function = false;
232 sym->section = ctx->current_section;
233 sym->value = ctx->current_section ? ctx->current_section->size : 0;
234
235 sym->next = ctx->symbols;
236 ctx->symbols = sym;
237 }
238 else {
239 sym->is_defined = true;
240 sym->value = ctx->current_section ? ctx->current_section->size : 0;
241 sym->section = ctx->current_section;
242 }
243
244 return INFIX_SUCCESS;
245}
246
248 return emit_define_symbol(ctx, name, EMIT_VISIBILITY_DEFAULT, false);
249}
250
252 if (!ctx->current_section)
254
255 if (needed <= ctx->current_section->capacity)
256 return INFIX_SUCCESS;
257
258 uint64_t new_capacity = ctx->current_section->capacity * EMIT_SECTION_GROWTH_FACTOR;
259 while (new_capacity < needed)
260 new_capacity *= EMIT_SECTION_GROWTH_FACTOR;
261
262 uint8_t * new_data = infix_realloc(ctx->current_section->data, new_capacity);
263 if (!new_data)
265
266 ctx->current_section->data = new_data;
267 ctx->current_section->capacity = new_capacity;
268 return INFIX_SUCCESS;
269}
270
271static infix_status emit_emit_bytes(emit_context_t * ctx, const void * data, size_t size) {
273 if (!ctx || !data) {
276 }
277
278 if (ctx->state != EMIT_STATE_SECTION_ACTIVE || !ctx->current_section) {
281 }
282
284 if (status != INFIX_SUCCESS)
285 return status;
286
287 memcpy(ctx->current_section->data + ctx->current_section->size, data, size);
288 ctx->current_section->size += size;
289
290 return INFIX_SUCCESS;
291}
292
294 return emit_emit_bytes(ctx, &byte, 1);
295}
296
298 uint8_t bytes[2] = {(uint8_t)(value & 0xFF), (uint8_t)((value >> 8) & 0xFF)};
299 return emit_emit_bytes(ctx, bytes, 2);
300}
301
303 uint8_t bytes[4] = {(uint8_t)(value & 0xFF),
304 (uint8_t)((value >> 8) & 0xFF),
305 (uint8_t)((value >> 16) & 0xFF),
306 (uint8_t)((value >> 24) & 0xFF)};
307 return emit_emit_bytes(ctx, bytes, 4);
308}
309
311 uint8_t bytes[8] = {(uint8_t)(value & 0xFF),
312 (uint8_t)((value >> 8) & 0xFF),
313 (uint8_t)((value >> 16) & 0xFF),
314 (uint8_t)((value >> 24) & 0xFF),
315 (uint8_t)((value >> 32) & 0xFF),
316 (uint8_t)((value >> 40) & 0xFF),
317 (uint8_t)((value >> 48) & 0xFF),
318 (uint8_t)((value >> 56) & 0xFF)};
319 return emit_emit_bytes(ctx, bytes, 8);
320}
321
324 if (!ctx || !ctx->current_section)
326
327 if (alignment == 0)
328 return INFIX_SUCCESS;
329
330 uint64_t current = ctx->current_section->size;
331 uint64_t aligned = (current + alignment - 1) & ~(alignment - 1);
332 uint64_t padding = aligned - current;
333
334 for (uint64_t i = 0; i < padding; i++) {
335 infix_status status = emit_emit_u8(ctx, 0x90);
336 if (status != INFIX_SUCCESS)
337 return status;
338 }
339
340 return INFIX_SUCCESS;
341}
342
344emit_add_relocation(emit_context_t * ctx, const char * name, uint64_t offset, uint8_t size, uint8_t inst_size) {
346 if (!ctx || !name) {
349 }
350
352 if (!rel)
354
355 rel->symbol_name = _emit_strdup(name);
357 rel->offset = offset;
358 rel->size = size;
359 rel->inst_size = inst_size;
360 rel->is_pc_relative = true;
361
362 rel->next = ctx->relocations;
363 ctx->relocations = rel;
364
365 return INFIX_SUCCESS;
366}
367
368static void write_raw_binary(emit_context_t * ctx, uint8_t * buffer, c23_maybe_unused uint64_t total_size) {
369 emit_section_t ** secs = infix_malloc(ctx->section_count * sizeof(emit_section_t *));
370 if (!secs)
371 return;
372
373 emit_section_t * sec = ctx->sections;
374 int count = 0;
375 while (sec) {
376 secs[count++] = sec;
377 sec = sec->next;
378 }
379
380 for (int i = count - 1; i >= 0; i--) {
381 uint64_t offset = 0;
382 for (int j = i + 1; j < count; j++)
383 offset += secs[j]->size;
384
385 memcpy(buffer + offset, secs[i]->data, secs[i]->size);
386 }
387
388 infix_free(secs);
389}
390
392 if (!ctx)
393 return INFIX_SUCCESS;
394
395 emit_section_t ** secs = infix_malloc(ctx->section_count * sizeof(emit_section_t *));
396 if (!secs)
398
399 emit_section_t * sec = ctx->sections;
400 int count = 0;
401 while (sec) {
402 secs[count++] = sec;
403 sec = sec->next;
404 }
405
406 uint64_t * section_offsets = infix_malloc((size_t)count * sizeof(uint64_t));
407 if (!section_offsets) {
408 infix_free(secs);
410 }
411 for (int i = 0; i < count; i++) {
412 section_offsets[i] = 0;
413 for (int j = i + 1; j < count; j++)
414 section_offsets[i] += secs[j]->size;
415 }
416
417 for (emit_relocation_t * rel = ctx->relocations; rel != NULL; rel = rel->next) {
418 emit_symbol_t * sym = _emit_lookup_symbol(ctx, rel->symbol_name);
419 if (!sym || !sym->is_defined)
420 continue;
421
422 emit_section_t * target_sec = sym->section;
423 if (!target_sec)
424 continue;
425
426 emit_section_t * reloc_sec = NULL;
427 if (rel->section_name)
428 reloc_sec = _emit_lookup_section(ctx, rel->section_name);
429 if (!reloc_sec)
430 reloc_sec = ctx->current_section;
431 if (!reloc_sec || reloc_sec->size == 0 || !reloc_sec->data)
432 continue;
433
434 uint64_t target_sec_offset = 0;
435 uint64_t reloc_sec_offset = 0;
436 for (int i = 0; i < count; i++) {
437 if (secs[i] == target_sec)
438 target_sec_offset = section_offsets[i];
439 if (secs[i] == reloc_sec)
440 reloc_sec_offset = section_offsets[i];
441 }
442
443 uint64_t target_addr = target_sec_offset + sym->value;
444 uint64_t reloc_addr = reloc_sec_offset + rel->offset;
445
446 int64_t displacement = (int64_t)target_addr - (int64_t)(reloc_addr + rel->size);
447
448 if (rel->size == 4) {
449 if (rel->is_pc_relative)
450 *(int32_t *)(reloc_sec->data + rel->offset) = (int32_t)displacement;
451 else
452 *(uint32_t *)(reloc_sec->data + rel->offset) = (uint32_t)target_addr;
453 }
454 else if (rel->size == 8) {
455 if (rel->is_pc_relative)
456 *(int64_t *)(reloc_sec->data + rel->offset) = displacement;
457 else
458 *(uint64_t *)(reloc_sec->data + rel->offset) = target_addr;
459 }
460 }
461
462 infix_free(section_offsets);
463 infix_free(secs);
464 return INFIX_SUCCESS;
465}
466
467void _emit_arch_nop(emit_context_t * ctx, uint8_t size) {
468 switch (ctx->arch) {
469 case EMIT_ARCH_X86_64:
470 for (uint8_t i = 0; i < size; i++) {
471 infix_status status = emit_emit_u8(ctx, 0x90);
472 (void)status;
473 }
474 break;
476 {
477 infix_status status = emit_emit_u32(ctx, 0xD503201F);
478 (void)status;
479 break;
480 }
481 default:
482 break;
483 }
484}
485
486infix_status _emit_arch_align(emit_context_t * ctx, uint64_t alignment) {
487 switch (ctx->arch) {
488 case EMIT_ARCH_X86_64:
489 for (uint64_t i = 0; i < alignment; i++) {
490 infix_status status = emit_emit_u8(ctx, 0x90);
491 if (status != INFIX_SUCCESS)
492 return status;
493 }
494 break;
496 for (uint64_t i = 0; i < alignment; i++) {
497 infix_status status = emit_emit_u32(ctx, 0xD503201F);
498 if (status != INFIX_SUCCESS)
499 return status;
500 }
501 break;
502 default:
503 break;
504 }
505 return INFIX_SUCCESS;
506}
507
508INFIX_API infix_status emit_get_binary(const emit_context_t * ctx, const uint8_t ** out_data, size_t * out_size) {
510 if (!ctx || !out_data || !out_size)
512
513 emit_context_t * mutable_ctx = (emit_context_t *)ctx;
515 if (status != INFIX_SUCCESS)
516 return status;
517
518 uint64_t total_size = 0;
519 for (emit_section_t * sec = ctx->sections; sec != NULL; sec = sec->next)
520 total_size += sec->size;
521
522 uint8_t * buffer = infix_malloc(total_size);
523 if (!buffer)
525
526 write_raw_binary(mutable_ctx, buffer, total_size);
527
528 *out_data = buffer;
529 *out_size = total_size;
530
531 return INFIX_SUCCESS;
532}
533
534INFIX_API infix_status emit_get_offset(const emit_context_t * ctx, uint64_t * out_offset) {
536 if (!ctx || !out_offset)
538
539 *out_offset = ctx->current_section ? ctx->current_section->size : 0;
540 return INFIX_SUCCESS;
541}
infix_status status
Definition 103_unions.c:61
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:508
infix_status _emit_arch_align(emit_context_t *ctx, uint64_t alignment)
Definition emit.c:486
#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:302
INFIX_API infix_status emit_emit_label(emit_context_t *ctx, const char *name)
Definition emit.c:216
INFIX_API infix_status emit_get_offset(const emit_context_t *ctx, uint64_t *out_offset)
Definition emit.c:534
static char * _emit_strdup(const char *s)
Definition emit.c:70
infix_status _emit_resolve_relocations(emit_context_t *ctx)
Definition emit.c:391
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:111
INFIX_API infix_status emit_align(emit_context_t *ctx, uint64_t alignment)
Definition emit.c:322
INFIX_API infix_status emit_define_symbol(emit_context_t *ctx, const char *name, emit_visibility_t visibility, bool is_function)
Definition emit.c:185
static infix_status _ensure_section_capacity(emit_context_t *ctx, uint64_t needed)
Definition emit.c:251
INFIX_API infix_status emit_create_label(emit_context_t *ctx, const char *name)
Definition emit.c:247
static infix_status emit_emit_bytes(emit_context_t *ctx, const void *data, size_t size)
Definition emit.c:271
INFIX_API void emit_destroy(emit_context_t *ctx)
Definition emit.c:138
void _emit_arch_nop(emit_context_t *ctx, uint8_t size)
Definition emit.c:467
static void write_raw_binary(emit_context_t *ctx, uint8_t *buffer, c23_maybe_unused uint64_t total_size)
Definition emit.c:368
INFIX_API infix_status emit_emit_u16(emit_context_t *ctx, uint16_t value)
Definition emit.c:297
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:344
INFIX_API infix_status emit_add_section(emit_context_t *ctx, const char *name, emit_section_flags_t flags)
Definition emit.c:143
static emit_section_t * _create_section(const char *name, emit_section_flags_t flags)
Definition emit.c:81
INFIX_API infix_status emit_create(emit_context_t **out_ctx, emit_architecture_t arch, emit_format_t format)
Definition emit.c:121
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:167
INFIX_API INFIX_NODISCARD infix_status emit_emit_u8(emit_context_t *ctx, uint8_t byte)
Definition emit.c:293
emit_section_t * _emit_lookup_section(emit_context_t *ctx, const char *name)
Definition emit.c:101
INFIX_API infix_status emit_emit_u64(emit_context_t *ctx, uint64_t value)
Definition emit.c:310
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:1429
@ INFIX_CATEGORY_PARSER
Definition infix.h:1406
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:486
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:488
@ INFIX_SUCCESS
Definition infix.h:487
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:489
#define infix_calloc(nelem, size)
A macro that can be defined to override infix's allocator at compile time.
Definition infix.h:427
#define infix_free(ptr)
A macro that can be defined to override infix's allocator at compile time.
Definition infix.h:435
#define infix_realloc(ptr, n)
A macro that can be defined to override infix's allocator at compile time.
Definition infix.h:431
#define infix_malloc(n)
A macro that can be defined to override infix's allocator at compile time.
Definition infix.h:423
#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