• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 24282034487

11 Apr 2026 12:00PM UTC coverage: 92.339% (-0.003%) from 92.342%
24282034487

push

github

nickg
Move JIT trampoline into a separate file

24 of 24 new or added lines in 2 files covered. (100.0%)

82 existing lines in 3 files now uncovered.

76444 of 82786 relevant lines covered (92.34%)

600209.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.63
/src/jit/jit-code.c
1
//
2
//  Copyright (C) 2022-2024  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "cpustate.h"
20
#include "debug.h"
21
#include "hash.h"
22
#include "ident.h"
23
#include "jit/jit-priv.h"
24
#include "option.h"
25
#include "printf.h"
26
#include "thread.h"
27

28
#include <assert.h>
29
#include <math.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <stdio.h>
33
#include <unistd.h>
34
#include <inttypes.h>
35
#include <signal.h>
36

37
#if defined __MINGW32__
38
#include <winnt.h>
39
#elif defined __APPLE__
40
#include <mach-o/loader.h>
41
#include <mach-o/reloc.h>
42
#include <mach-o/nlist.h>
43
#include <mach-o/stab.h>
44
#include <mach-o/arm64/reloc.h>
45
#include <mach-o/x86_64/reloc.h>
46
#else
47
#include <elf.h>
48
#endif
49

50
#ifdef HAVE_CAPSTONE
51
#include <capstone.h>
52
#endif
53

54
#ifndef R_AARCH64_MOVW_UABS_G0_NC
55
#define R_AARCH64_MOVW_UABS_G0_NC 264
56
#endif
57

58
#ifndef R_AARCH64_MOVW_UABS_G1_NC
59
#define R_AARCH64_MOVW_UABS_G1_NC 266
60
#endif
61

62
#ifndef R_AARCH64_MOVW_UABS_G2_NC
63
#define R_AARCH64_MOVW_UABS_G2_NC 268
64
#endif
65

66
#ifndef R_AARCH64_MOVW_UABS_G3
67
#define R_AARCH64_MOVW_UABS_G3 269
68
#endif
69

70
#ifndef SHT_X86_64_UNWIND
71
#define SHT_X86_64_UNWIND 0x70000001
72
#endif
73

74
#ifndef IMAGE_REL_ARM64_BRANCH26
75
#define IMAGE_REL_ARM64_BRANCH26 0x03
76
#endif
77

78
#ifndef IMAGE_REL_ARM64_ADDR32NB
79
#define IMAGE_REL_ARM64_ADDR32NB 0x02
80
#endif
81

82
#ifndef IMAGE_REL_ARM64_PAGEBASE_REL21
83
#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x04
84
#endif
85

86
#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12A
87
#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x06
88
#endif
89

90
#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12L
91
#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x07
92
#endif
93

94
#define CODE_PAGE_ALIGN   4096
95
#define CODE_PAGE_SIZE    0x400000
96
#define THREAD_CACHE_SIZE 0x10000
97
#define CODE_BLOB_ALIGN   256
98
#define MIN_BLOB_SIZE     0x4000
99

100
#define __IMM64(x) __IMM32(x), __IMM32((x) >> 32)
101
#define __IMM32(x) __IMM16(x), __IMM16((x) >> 16)
102
#define __IMM16(x) (x) & 0xff, ((x) >> 8) & 0xff
103

104
STATIC_ASSERT(MIN_BLOB_SIZE <= THREAD_CACHE_SIZE);
105
STATIC_ASSERT(MIN_BLOB_SIZE % CODE_BLOB_ALIGN == 0);
106
STATIC_ASSERT(CODE_PAGE_SIZE % THREAD_CACHE_SIZE == 0);
107

108
typedef struct _code_page code_page_t;
109

110
typedef struct {
111
   uintptr_t  addr;
112
   char      *text;
113
} code_comment_t;
114

115
typedef struct {
116
   unsigned        count;
117
   unsigned        max;
118
   code_comment_t *comments;
119
} code_debug_t;
120

121
typedef struct _code_span {
122
   code_cache_t *owner;
123
   code_span_t  *next;
124
   ident_t       name;
125
   uint8_t      *base;
126
   void         *entry;
127
   size_t        size;
128
#ifdef DEBUG
129
   code_debug_t  debug;
130
#endif
131
} code_span_t;
132

133
typedef struct _patch_list {
134
   patch_list_t    *next;
135
   uint8_t         *wptr;
136
   jit_label_t      label;
137
   code_patch_fn_t  fn;
138
} patch_list_t;
139

140
typedef struct _code_page {
141
   code_cache_t *owner;
142
   code_page_t  *next;
143
   uint8_t      *mem;
144
} code_page_t;
145

146
typedef struct _code_cache {
147
   nvc_lock_t   lock;
148
   code_page_t *pages;
149
   code_span_t *spans;
150
   code_span_t *freelist[MAX_THREADS];
151
   code_span_t *globalfree;
152
   shash_t     *symbols;
153
   FILE        *perfmap;
154
#ifdef HAVE_CAPSTONE
155
   csh          capstone;
156
#endif
157
#ifdef DEBUG
158
   size_t       used;
159
#endif
160
} code_cache_t;
161

162
static void code_disassemble(code_span_t *span, uintptr_t mark,
163
                             struct cpu_state *cpu);
164

UNCOV
165
static void code_cache_unwinder(uintptr_t addr, debug_frame_t *frame,
×
166
                                void *context)
167
{
UNCOV
168
   code_cache_t *code = context;
×
169

UNCOV
170
   const uint8_t *pc = (uint8_t *)addr;
×
171
   for (code_span_t *span = code->spans; span; span = span->next) {
×
172
      if (pc >= span->base && pc < span->base + span->size) {
×
173
         frame->kind = FRAME_VHDL;
×
174
         frame->disp = pc - span->base;
×
175
         frame->symbol = istr(span->name);
×
176
      }
177
   }
UNCOV
178
}
×
179

UNCOV
180
static void code_fault_handler(int sig, void *addr, struct cpu_state *cpu,
×
181
                               void *context)
182
{
UNCOV
183
   code_page_t *page = context;
×
184

UNCOV
185
   const uint8_t *pc = (uint8_t *)cpu->pc;
×
186
   if (pc < page->mem || pc > page->mem + CODE_PAGE_SIZE)
×
187
      return;
188

UNCOV
189
   uintptr_t mark = cpu->pc;
×
190
#ifndef __MINGW32__
UNCOV
191
   if (sig == SIGTRAP)
×
192
      mark--;   // Point to faulting instruction
×
193
#endif
194

UNCOV
195
   for (code_span_t *span = page->owner->spans; span; span = span->next) {
×
196
      if (pc >= span->base && pc < span->base + span->size && span->name)
×
197
         code_disassemble(span, mark, cpu);
×
198
   }
199
}
200

201
#ifdef DEBUG
202
static bool code_cache_contains(code_cache_t *code, uint8_t *base, size_t size)
16,256✔
203
{
204
   assert_lock_held(&code->lock);
16,256✔
205

206
   for (code_page_t *p = code->pages; p; p = p->next) {
16,256✔
207
      if (base >= p->mem && base + size <= p->mem + CODE_PAGE_SIZE)
16,256✔
208
         return true;
209
   }
210

211
   return false;
212
}
213
#endif
214

215
static code_span_t *code_span_new(code_cache_t *code, ident_t name,
16,256✔
216
                                  uint8_t *base, size_t size)
217
{
218
   SCOPED_LOCK(code->lock);
16,256✔
219

220
   assert(code_cache_contains(code, base, size));
16,256✔
221

222
   code_span_t *span = xcalloc(sizeof(code_span_t));
16,256✔
223
   span->name  = name;
16,256✔
224
   span->next  = code->spans;
16,256✔
225
   span->base  = base;
16,256✔
226
   span->entry = base;
16,256✔
227
   span->size  = size;
16,256✔
228
   span->owner = code;
16,256✔
229

230
   code->spans = span;
16,256✔
231
   return span;
16,256✔
232
}
233

234
static void code_page_new(code_cache_t *code)
5,425✔
235
{
236
   assert_lock_held(&code->lock);
5,425✔
237

238
   code_page_t *page = xcalloc(sizeof(code_page_t));
5,425✔
239
   page->owner = code;
5,425✔
240
   page->next  = code->pages;
5,425✔
241
   page->mem   = map_jit_pages(CODE_PAGE_ALIGN, CODE_PAGE_SIZE);
5,425✔
242

243
   add_fault_handler(code_fault_handler, page);
5,425✔
244
   debug_add_unwinder(page->mem, CODE_PAGE_SIZE, code_cache_unwinder, code);
5,425✔
245

246
   code->pages = page;
5,425✔
247

248
   code_span_t *span = xcalloc(sizeof(code_span_t));
5,425✔
249
   span->next  = code->spans;
5,425✔
250
   span->base  = page->mem;
5,425✔
251
   span->size  = CODE_PAGE_SIZE;
5,425✔
252
   span->owner = code;
5,425✔
253

254
   code->globalfree = code->spans = span;
5,425✔
255
}
5,425✔
256

257
code_cache_t *code_cache_new(void)
5,418✔
258
{
259
   code_cache_t *code = xcalloc(sizeof(code_cache_t));
5,418✔
260

261
   {
262
      SCOPED_LOCK(code->lock);
10,836✔
263
      code_page_new(code);
5,418✔
264
   }
265

266
#ifdef HAVE_CAPSTONE
267
#if defined ARCH_X86_64
268
   if (cs_open(CS_ARCH_X86, CS_MODE_64, &(code->capstone)) != CS_ERR_OK)
269
      fatal_trace("failed to init capstone for x86_64");
270
#elif defined ARCH_ARM64
271
   if (cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &(code->capstone)) != CS_ERR_OK)
272
      fatal_trace("failed to init capstone for Arm64");
273
#else
274
#error Cannot configure capstone for this architecture
275
#endif
276

277
   if (cs_option(code->capstone, CS_OPT_DETAIL, 1) != CS_ERR_OK)
278
      fatal_trace("failed to set capstone detailed mode");
279
#endif
280

281
   shash_t *s = shash_new(32);
5,418✔
282

283
   extern void __nvc_putpriv(jit_handle_t, void *);
5,418✔
284
   extern void __nvc_sched_waveform(jit_anchor_t *, jit_scalar_t *, tlab_t *);
5,418✔
285
   extern void __nvc_sched_process(jit_anchor_t *, jit_scalar_t *, tlab_t *);
5,418✔
286
   extern void __nvc_test_event(jit_anchor_t *, jit_scalar_t *, tlab_t *);
5,418✔
287
   extern void __nvc_last_event(jit_anchor_t *, jit_scalar_t *, tlab_t *);
5,418✔
288

289
   shash_put(s, "__nvc_sched_waveform", &__nvc_sched_waveform);
5,418✔
290
   shash_put(s, "__nvc_sched_process", &__nvc_sched_process);
5,418✔
291
   shash_put(s, "__nvc_test_event", &__nvc_test_event);
5,418✔
292
   shash_put(s, "__nvc_last_event", &__nvc_last_event);
5,418✔
293
   shash_put(s, "__nvc_mspace_alloc", &__nvc_mspace_alloc);
5,418✔
294
   shash_put(s, "__nvc_putpriv", &__nvc_putpriv);
5,418✔
295
   shash_put(s, "__nvc_do_exit", &__nvc_do_exit);
5,418✔
296
   shash_put(s, "__nvc_pack", &__nvc_pack);
5,418✔
297
   shash_put(s, "__nvc_unpack", &__nvc_unpack);
5,418✔
298
   shash_put(s, "__nvc_vec4op", &__nvc_vec4op);
5,418✔
299
   shash_put(s, "memmove", &memmove);
5,418✔
300
   shash_put(s, "memcpy", &memcpy);
5,418✔
301
   shash_put(s, "memset", &memset);
5,418✔
302
   shash_put(s, "pow", &pow);
5,418✔
303
   shash_put(s, "ldexp", &ldexp);
5,418✔
304
   shash_put(s, "exp2", &exp2);
5,418✔
305

306
#if defined __APPLE__ && defined ARCH_ARM64
307
   shash_put(s, "bzero", &bzero);
308
#elif defined __APPLE__ && defined ARCH_X86_64
309
   shash_put(s, "__bzero", &bzero);
310
#elif defined __MINGW32__ && defined ARCH_X86_64
311
   extern void ___chkstk_ms(void);
312
   shash_put(s, "___chkstk_ms", &___chkstk_ms);
313
#endif
314

315
   store_release(&code->symbols, s);
5,418✔
316

317
   return code;
5,418✔
318
}
319

320
void code_cache_free(code_cache_t *code)
5,409✔
321
{
322
   for (code_page_t *it = code->pages, *tmp; it; it = tmp) {
10,825✔
323
      debug_remove_unwinder(it->mem);
5,416✔
324
      remove_fault_handler(code_fault_handler, it);
5,416✔
325

326
      nvc_munmap(it->mem, CODE_PAGE_SIZE);
5,416✔
327

328
      tmp = it->next;
5,416✔
329
      free(it);
5,416✔
330
   }
331

332
   for (code_span_t *it = code->spans, *tmp; it; it = tmp) {
27,073✔
333
      tmp = it->next;
21,664✔
334
      DEBUG_ONLY(free(it->debug.comments));
21,664✔
335
      free(it);
21,664✔
336
   }
337

338
#ifdef HAVE_CAPSTONE
339
   cs_close(&(code->capstone));
340
#endif
341

342
#ifdef DEBUG
343
   if (code->used > 0)
5,409✔
344
      debugf("JIT code footprint: %zu bytes", code->used);
1,490✔
345
#endif
346

347
   shash_free(code->symbols);
5,409✔
348
   free(code);
5,409✔
349
}
5,409✔
350

351
#ifdef HAVE_CAPSTONE
352
static int code_print_spaces(int col, int tab)
353
{
354
   for (; col < tab; col++)
355
      fputc(' ', stdout);
356
   return col;
357
}
358
#endif
359

360
#if defined DEBUG && HAVE_CAPSTONE
361
static int code_comment_compare(const void *a, const void *b)
362
{
363
   const code_comment_t *ca = a;
364
   const code_comment_t *cb = b;
365

366
   if (ca->addr < cb->addr)
367
      return -1;
368
   else if (ca->addr > cb->addr)
369
      return 1;
370
   else
371
      return 0;
372
}
373
#endif
374

UNCOV
375
static void code_disassemble(code_span_t *span, uintptr_t mark,
×
376
                             struct cpu_state *cpu)
377
{
UNCOV
378
   SCOPED_LOCK(span->owner->lock);
×
379

UNCOV
380
   printf("--");
×
381

UNCOV
382
   const int namelen = ident_len(span->name);
×
383
   for (int i = 0; i < 72 - namelen; i++)
×
384
      fputc('-', stdout);
×
385

UNCOV
386
   printf(" %s ----\n", istr(span->name));
×
387

388
#ifdef HAVE_CAPSTONE
389
   cs_insn *insn = cs_malloc(span->owner->capstone);
390

391
#ifdef DEBUG
392
   qsort(span->debug.comments, span->debug.count, sizeof(code_comment_t),
393
         code_comment_compare);
394
   code_comment_t *comment = span->debug.comments;
395
#endif
396

397
   const uint8_t *const eptr = span->base + span->size;
398
   for (const uint8_t *ptr = span->base; ptr < eptr; ) {
399
      uint64_t address = (uint64_t)ptr;
400

401
#ifdef DEBUG
402
      for (; comment < span->debug.comments + span->debug.count
403
              && comment->addr <= address; comment++)
404
         printf("%30s;; %s\n", "", comment->text);
405
#endif
406

407
      int zeros = 0;
408
      for (const uint8_t *zp = ptr; zp < eptr && *zp == 0; zp++, zeros++);
409

410
      if (zeros > 8 || zeros == eptr - ptr) {
411
         printf("%30s;; skipping %d zero bytes\n", "", zeros);
412
         ptr += zeros;
413
         continue;
414
      }
415

416
      size_t size = eptr - ptr;
417
      int col = 0;
418
      if (cs_disasm_iter(span->owner->capstone, &ptr, &size, &address, insn)) {
419
         char hex1[33], *p = hex1;
420
         for (size_t k = 0; k < insn->size; k++)
421
            p += checked_sprintf(p, hex1 + sizeof(hex1) - p, "%02x",
422
                                 insn->bytes[k]);
423

424
         col = printf("%-12" PRIx64 " %-16.16s %s %s", insn->address,
425
                          hex1, insn->mnemonic, insn->op_str);
426

427
#ifdef ARCH_X86_64
428
         if (strcmp(insn->mnemonic, "movabs") == 0) {
429
            const cs_x86_op *src = &(insn->detail->x86.operands[1]);
430
            if (src->type == X86_OP_IMM) {
431
               const char *sym = debug_symbol_name((void *)src->imm);
432
               if (sym != NULL) {
433
                  col = code_print_spaces(col, 60);
434
                  col += printf(" ; %s", sym);
435
               }
436
            }
437
         }
438
#endif
439

440
         if (strlen(hex1) > 16)
441
            col = printf("\n%15s -%-16s", "", hex1 + 16) - 1;
442
      }
443
      else {
444
#ifdef ARCH_ARM64
445
         col = printf("%-12" PRIx64 " %-16.08x %s 0x%08x", (uint64_t)ptr,
446
                      *(uint32_t *)ptr, ".word", *(uint32_t *)ptr);
447
         ptr += 4;
448
#else
449
         col = printf("%-12" PRIx64 " %-16.02x %s 0x%02x", (uint64_t)ptr,
450
                      *ptr, ".byte", *ptr);
451
         ptr++;
452
#endif
453
      }
454

455
      if (mark != 0 && (ptr >= eptr || address > mark)) {
456
         col = code_print_spaces(col, 66);
457
         printf("<=============\n");
458
         if (cpu != NULL) {
459
#ifdef ARCH_X86_64
460
            const char *names[] = {
461
               "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
462
               "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"
463
            };
464
            for (int i = 0; i < ARRAY_LEN(names); i++)
465
               printf("\t%s\t%"PRIxPTR"\n", names[i], cpu->regs[i]);
466
#else
467
            for (int i = 0; i < 32; i++)
468
               printf("\tR%d\t%"PRIxPTR"\n", i, cpu->regs[i]);
469
#endif
470
         }
471
         mark = 0;
472
      }
473
      else
474
         printf("\n");
475
   }
476

477
   cs_free(insn, 1);
478
#else
UNCOV
479
   jit_hexdump(span->base, span->size, 16, (void *)mark, "");
×
480
#endif
481

UNCOV
482
   for (int i = 0; i < 80; i++)
×
483
      fputc('-', stdout);
×
484
   printf("\n");
×
485
   fflush(stdout);
×
486
}
×
487

UNCOV
488
static void code_write_perf_map(code_span_t *span)
×
489
{
UNCOV
490
   SCOPED_LOCK(span->owner->lock);
×
491

UNCOV
492
   if (span->owner->perfmap == NULL) {
×
493
      char *fname LOCAL = xasprintf("/tmp/perf-%d.map", getpid());
×
494
      if ((span->owner->perfmap = fopen(fname, "w")) == NULL) {
×
495
         warnf("cannot create %s: %s", fname, last_os_error());
×
496
         opt_set_int(OPT_PERF_MAP, 0);
×
497
         return;
×
498
      }
499
      else
UNCOV
500
         debugf("writing perf map to %s", fname);
×
501
   }
502

UNCOV
503
   fprintf(span->owner->perfmap, "%p 0x%zx %s\n", span->base, span->size,
×
504
           istr(span->name));
UNCOV
505
   fflush(span->owner->perfmap);
×
506
}
507

508
code_blob_t *code_blob_new(code_cache_t *code, ident_t name, size_t hint)
14,710✔
509
{
510
   code_span_t **freeptr = &(code->freelist[thread_id()]);
14,710✔
511

512
   code_span_t *free = relaxed_load(freeptr);
14,710✔
513
   if (free == NULL) {
14,710✔
514
      free = code_span_new(code, NULL, code->pages->mem, 0);
1,546✔
515
      relaxed_store(freeptr, free);
1,546✔
516
   }
517

518
   const size_t reqsz = hint ?: MIN_BLOB_SIZE;
14,710✔
519

520
   if (free->size < reqsz) {
14,710✔
521
      SCOPED_LOCK(code->lock);
1,622✔
522

523
#ifdef DEBUG
524
      if (free->size > 0)
1,622✔
525
         debugf("thread %d needs new code cache from global free list "
30✔
526
                "(requested %zu bytes, wasted %zu bytes)",
527
                thread_id(), reqsz, free->size);
528
#endif
529

530
      const size_t chunksz = MAX(reqsz, THREAD_CACHE_SIZE);
1,622✔
531
      const size_t alignedsz = ALIGN_UP(chunksz, CODE_BLOB_ALIGN);
1,622✔
532

533
      if (alignedsz > code->globalfree->size) {
1,622✔
534
         DEBUG_ONLY(debugf("requesting new %d byte code page", CODE_PAGE_SIZE));
7✔
535
         code_page_new(code);
7✔
536
         assert(code->globalfree->size == CODE_PAGE_SIZE);
7✔
537
      }
538

539
      const size_t take = MIN(code->globalfree->size, alignedsz);
1,622✔
540

541
      free->size = take;
1,622✔
542
      free->base = code->globalfree->base;
1,622✔
543

544
      code->globalfree->base += take;
1,622✔
545
      code->globalfree->size -= take;
1,622✔
546
   }
547

548
   assert(reqsz <= free->size);
14,710✔
549
   assert(((uintptr_t)free->base & (CODE_BLOB_ALIGN - 1)) == 0);
14,710✔
550

551
   code_span_t *span = code_span_new(code, name, free->base, free->size);
14,710✔
552

553
   free->base += span->size;
14,710✔
554
   free->size -= span->size;
14,710✔
555

556
   code_blob_t *blob = xcalloc(sizeof(code_blob_t));
14,710✔
557
   blob->span = span;
14,710✔
558
   blob->wptr = span->base;
14,710✔
559

560
   thread_wx_mode(WX_WRITE);
14,710✔
561

562
   return blob;
14,710✔
563
}
564

565
void code_blob_finalise(code_blob_t *blob, jit_entry_fn_t *entry)
14,710✔
566
{
567
   code_span_t *span = blob->span;
14,710✔
568
   span->size = blob->wptr - span->base;
14,710✔
569

570
   code_span_t *freespan = relaxed_load(&(span->owner->freelist[thread_id()]));
14,710✔
571
   assert(freespan->size == 0);
14,710✔
572

573
   ihash_free(blob->labels);
14,710✔
574
   blob->labels = NULL;
14,710✔
575

576
   if (unlikely(blob->patches != NULL))
14,710✔
577
      fatal_trace("not all labels in %s were patched", istr(span->name));
578
   else if (unlikely(blob->overflow)) {
14,710✔
579
      // Return all the memory
580
      freespan->size = freespan->base - span->base;
1✔
581
      freespan->base = span->base;
1✔
582
      free(blob);
1✔
583
      return;
1✔
584
   }
585
   else if (span->size == 0)
14,709✔
586
      fatal_trace("code span %s is empty", istr(span->name));
587

588
   uint8_t *aligned = ALIGN_UP(blob->wptr, CODE_BLOB_ALIGN);
14,709✔
589
   freespan->size = freespan->base - aligned;
14,709✔
590
   freespan->base = aligned;
14,709✔
591

592
   if (opt_get_verbose(OPT_ASM_VERBOSE, istr(span->name))) {
14,709✔
UNCOV
593
      nvc_printf("\n$bold$$blue$");
×
594
      code_disassemble(span, 0, NULL);
×
595
      nvc_printf("$$\n");
×
596
   }
597

598
   __builtin___clear_cache((char *)span->base, (char *)blob->wptr);
14,709✔
599

600
   thread_wx_mode(WX_EXECUTE);
14,709✔
601

602
   store_release(entry, (jit_entry_fn_t)span->entry);
14,709✔
603

604
   DEBUG_ONLY(relaxed_add(&span->owner->used, span->size));
14,709✔
605
   free(blob);
14,709✔
606

607
   if (opt_get_int(OPT_PERF_MAP))
14,709✔
UNCOV
608
      code_write_perf_map(span);
×
609
}
610

611
__attribute__((cold, noinline))
612
static void code_blob_overflow(code_blob_t *blob)
1✔
613
{
614
   warnf("JIT code buffer for %s too small", istr(blob->span->name));
1✔
615
   for (patch_list_t *it = blob->patches, *tmp; it; it = tmp) {
1✔
UNCOV
616
      tmp = it->next;
×
617
      free(it);
×
618
   }
619
   blob->patches = NULL;
1✔
620
   blob->overflow = true;
1✔
621
}
1✔
622

623
void code_blob_emit(code_blob_t *blob, const uint8_t *bytes, size_t len)
25,724✔
624
{
625
   if (unlikely(blob->overflow))
25,724✔
626
      return;
627
   else if (unlikely(blob->wptr + len > blob->span->base + blob->span->size)) {
25,724✔
628
      code_blob_overflow(blob);
1✔
629
      return;
1✔
630
   }
631

632
   memcpy(blob->wptr, bytes, len);
25,723✔
633
   blob->wptr += len;
25,723✔
634
}
635

636
void code_blob_align(code_blob_t *blob, unsigned align)
15,300✔
637
{
638
#ifdef ARCH_X86_64
639
   const uint8_t pad[] = { 0x90 };
15,300✔
640
#else
641
   const uint8_t pad[] = { 0x00 };
642
#endif
643

644
   assert(is_power_of_2(align));
15,300✔
645
   assert(align % ARRAY_LEN(pad) == 0);
646

647
   while (((uintptr_t)blob->wptr & (align - 1)) && !blob->overflow)
20,508✔
648
      code_blob_emit(blob, pad, ARRAY_LEN(pad));
5,208✔
649
}
15,300✔
650

651
void code_blob_mark(code_blob_t *blob, jit_label_t label)
71✔
652
{
653
   if (unlikely(blob->overflow))
71✔
654
      return;
655
   else if (blob->labels == NULL)
71✔
656
      blob->labels = ihash_new(256);
66✔
657

658
   ihash_put(blob->labels, label, blob->wptr);
71✔
659

660
   for (patch_list_t **p = &(blob->patches); *p; ) {
88✔
661
      if ((*p)->label == label) {
17✔
662
         patch_list_t *next = (*p)->next;
7✔
663
         (*(*p)->fn)(blob, label, (*p)->wptr, blob->wptr);
7✔
664
         free(*p);
7✔
665
         *p = next;
7✔
666
      }
667
      else
668
         p = &((*p)->next);
10✔
669
   }
670
}
671

672
void code_blob_patch(code_blob_t *blob, jit_label_t label, code_patch_fn_t fn)
8✔
673
{
674
   void *ptr = NULL;
8✔
675
   if (unlikely(blob->overflow))
8✔
676
      return;
677
   else if (blob->labels != NULL && (ptr = ihash_get(blob->labels, label)))
8✔
678
      (*fn)(blob, label, blob->wptr, ptr);
1✔
679
   else {
680
      patch_list_t *new = xmalloc(sizeof(patch_list_t));
7✔
681
      new->next  = blob->patches;
7✔
682
      new->fn    = fn;
7✔
683
      new->label = label;
7✔
684
      new->wptr  = blob->wptr;
7✔
685

686
      blob->patches = new;
7✔
687
   }
688
}
689

690
#ifdef DEBUG
691
static void code_blob_print_value(text_buf_t *tb, jit_value_t value)
392✔
692
{
693
   switch (value.kind) {
392✔
694
   case JIT_VALUE_REG:
162✔
695
      tb_printf(tb, "R%d", value.reg);
162✔
696
      break;
162✔
697
   case JIT_VALUE_INT64:
203✔
698
      if (value.int64 < 4096)
203✔
699
         tb_printf(tb, "#%"PRIi64, value.int64);
199✔
700
      else
701
         tb_printf(tb, "#0x%"PRIx64, value.int64);
4✔
702
      break;
703
   case JIT_VALUE_DOUBLE:
1✔
704
      tb_printf(tb, "%%%g", value.dval);
1✔
705
      break;
1✔
UNCOV
706
   case JIT_ADDR_CPOOL:
×
707
      tb_printf(tb, "[CP+%"PRIi64"]", value.int64);
×
708
      break;
×
709
   case JIT_ADDR_REG:
19✔
710
      tb_printf(tb, "[R%d", value.reg);
19✔
711
      if (value.disp != 0)
19✔
712
         tb_printf(tb, "+%d", value.disp);
1✔
713
      tb_cat(tb, "]");
19✔
714
      break;
19✔
UNCOV
715
   case JIT_ADDR_ABS:
×
716
      tb_printf(tb, "[#%016"PRIx64"]", value.int64);
×
717
      break;
×
718
   case JIT_VALUE_LABEL:
5✔
719
      tb_printf(tb, "%d", value.label);
5✔
720
      break;
5✔
721
   case JIT_VALUE_HANDLE:
2✔
722
      tb_printf(tb, "<%d>", value.handle);
2✔
723
      break;
2✔
UNCOV
724
   case JIT_VALUE_EXIT:
×
725
      tb_printf(tb, "%s", jit_exit_name(value.exit));
×
726
      break;
×
727
   case JIT_VALUE_LOC:
×
728
      tb_printf(tb, "<%s:%d>", loc_file_str(&value.loc), value.loc.first_line);
×
729
      break;
×
730
   case JIT_VALUE_LOCUS:
×
731
      tb_printf(tb, "%p", value.locus);
×
732
      break;
×
733
   case JIT_VALUE_VPOS:
×
734
      tb_printf(tb, "%u:%u", value.vpos.block, value.vpos.op);
×
735
      break;
×
736
   default:
×
737
      tb_cat(tb, "???");
×
738
   }
739
}
392✔
740

741
static void code_blob_add_comment(code_blob_t *blob, uintptr_t addr, char *text)
79,512✔
742
{
743
   code_debug_t *dbg = &(blob->span->debug);
79,512✔
744

745
   if (dbg->count == dbg->max) {
79,512✔
746
      dbg->max = MAX(128, dbg->max * 2);
14,532✔
747
      dbg->comments = xrealloc_array(dbg->comments, dbg->max,
14,532✔
748
                                     sizeof(code_comment_t));
749
   }
750

751
   dbg->comments[dbg->count].addr = addr;
79,512✔
752
   dbg->comments[dbg->count].text = text;
79,512✔
753
   dbg->count++;
79,512✔
754
}
79,512✔
755

756
void code_blob_print_ir(code_blob_t *blob, jit_ir_t *ir)
348✔
757
{
758
   LOCAL_TEXT_BUF tb = tb_new();
696✔
759
   tb_printf(tb, "%s%s", jit_op_name(ir->op), jit_cc_name(ir->cc));
348✔
760

761
   if (ir->size != JIT_SZ_UNSPEC)
348✔
762
      tb_printf(tb, ".%d", 1 << (3 + ir->size));
36✔
763

764
   tb_printf(tb, "%*.s", (int)MAX(0, 10 - tb_len(tb)), "");
348✔
765

766
   if (ir->result != JIT_REG_INVALID)
348✔
767
      tb_printf(tb, "R%d", ir->result);
203✔
768

769
   if (ir->arg1.kind != JIT_VALUE_INVALID) {
348✔
770
      if (ir->result != JIT_REG_INVALID)
263✔
771
         tb_cat(tb, ", ");
187✔
772
      code_blob_print_value(tb, ir->arg1);
263✔
773
   }
774

775
   if (ir->arg2.kind != JIT_VALUE_INVALID) {
348✔
776
      tb_cat(tb, ", ");
129✔
777
      code_blob_print_value(tb, ir->arg2);
129✔
778
   }
779

780
   code_blob_add_comment(blob, (uintptr_t)blob->wptr, tb_claim(tb));
348✔
781
}
348✔
782

783
void code_blob_printf(code_blob_t *blob, const char *fmt, ...)
15,300✔
784
{
785
   va_list ap;
15,300✔
786
   va_start(ap, fmt);
15,300✔
787

788
   char *text = xvasprintf(fmt, ap);
15,300✔
789
   code_blob_add_comment(blob, (uintptr_t)blob->wptr, text);
15,300✔
790

791
   va_end(ap);
15,300✔
792
}
15,300✔
793

794
__attribute__((format(printf, 3, 4)))
795
static void debug_reloc(code_blob_t *blob, void *patch, const char *fmt, ...)
63,864✔
796
{
797
   va_list ap;
63,864✔
798
   va_start(ap, fmt);
63,864✔
799

800
   char *text = xvasprintf(fmt, ap);
63,864✔
801
   code_blob_add_comment(blob, (uintptr_t)patch, text);
63,864✔
802

803
   va_end(ap);
63,864✔
804
}
63,864✔
805
#else
806
#define debug_reloc(...)
807
#endif   // DEBUG
808

809
#ifdef ARCH_ARM64
810
static void arm64_patch_page_offset21(code_blob_t *blob, uint32_t *patch,
811
                                      void *ptr)
812
{
813
   switch ((*patch >> 23) & 0x7f) {
814
   case 0b1111010:   // LDR (immediate, SIMD&FP)
815
   case 0b1110010:   // LDR (immediate)
816
      assert(*patch & (1 << 30));  // Quadword
817
      assert(((uintptr_t)ptr & 7) == 0);
818
      *patch |= (((uintptr_t)ptr & 0xfff) >> 3) << 10;
819
      break;
820
   case 0b0100010:   // ADD (immediate)
821
      *patch |= ((uintptr_t)ptr & 0xfff) << 10;
822
      break;
823
   default:
824
      blob->span->size = blob->wptr - blob->span->base;
825
      code_disassemble(blob->span, (uintptr_t)patch, NULL);
826
      fatal_trace("cannot patch instruction");
827
   }
828
}
829

830
static void arm64_patch_page_base_rel21(uint32_t *patch, void *ptr)
831
{
832
   const intptr_t dst_page = (intptr_t)ptr & ~UINT64_C(0xfff);
833
   const intptr_t src_page = (intptr_t)patch & ~UINT64_C(0xfff);
834
   const intptr_t upper21 = (dst_page - src_page) >> 12;
835
   assert(upper21 >= -(1 << 20) && upper21 < (1 << 20));
836
   *patch &= ~((0x3 << 29) | (0x7ffff << 5));
837
   *patch |= (upper21 & 3) << 29;
838
   *patch |= ((upper21 >> 2) & 0x7ffff) << 5;
839
}
840
#endif
841

UNCOV
842
static void *code_emit_trampoline(code_blob_t *blob, void *dest)
×
843
{
844
#if defined ARCH_X86_64
UNCOV
845
   const uint8_t veneer[] = {
×
846
      0x48, 0xb8, __IMM64((uintptr_t)dest),  // MOVABS RAX, dest
×
847
      0xff, 0xe0                             // CALL RAX
848
   };
849
#elif defined ARCH_ARM64
850
   const uint8_t veneer[] = {
851
      0x50, 0x00, 0x00, 0x58,   // LDR X16, [PC+8]
852
      0x00, 0x02, 0x1f, 0xd6,   // BR X16
853
      __IMM64((uintptr_t)dest)
854
   };
855
#else
856
   should_not_reach_here();
857
#endif
858

UNCOV
859
   void *prev = memmem(blob->veneers, blob->wptr - blob->veneers,
×
860
                       veneer, ARRAY_LEN(veneer));
UNCOV
861
   if (prev != NULL)
×
862
      return prev;
863
   else {
UNCOV
864
      DEBUG_ONLY(code_blob_printf(blob, "Trampoline for %p", dest));
×
865

UNCOV
866
      void *addr = blob->wptr;
×
867
      code_blob_emit(blob, veneer, ARRAY_LEN(veneer));
×
868
      return addr;
×
869
   }
870
}
871

872
#if !defined __MINGW32__ && !defined __APPLE__
UNCOV
873
static void *code_emit_got(code_blob_t *blob, void *dest)
×
874
{
UNCOV
875
   const uint8_t data[] = { __IMM64((uintptr_t)dest) };
×
876

UNCOV
877
   void *prev = memmem(blob->veneers, blob->veneers - blob->wptr,
×
878
                       data, ARRAY_LEN(data));
UNCOV
879
   if (prev != NULL)
×
880
      return prev;
881
   else {
UNCOV
882
      DEBUG_ONLY(code_blob_printf(blob, "GOT entry for %p", dest));
×
883

UNCOV
884
      void *addr = blob->wptr;
×
885
      code_blob_emit(blob, data, ARRAY_LEN(data));
×
886
      return addr;
×
887
   }
888
}
889
#endif
890

891
#if defined __MINGW32__
892
static void code_load_pe(code_blob_t *blob, const void *data, size_t size)
893
{
894
   const IMAGE_FILE_HEADER *imghdr = data;
895

896
   switch (imghdr->Machine) {
897
   case IMAGE_FILE_MACHINE_AMD64:
898
   case IMAGE_FILE_MACHINE_ARM64:
899
      break;
900
   default:
901
      fatal_trace("unknown target machine %x", imghdr->Machine);
902
   }
903

904
   const IMAGE_SYMBOL *symtab = data + imghdr->PointerToSymbolTable;
905
   const char *strtab = data + imghdr->PointerToSymbolTable
906
      + imghdr->NumberOfSymbols * sizeof(IMAGE_SYMBOL);
907

908
   const IMAGE_SECTION_HEADER *sections =
909
      data + IMAGE_SIZEOF_FILE_HEADER + imghdr->SizeOfOptionalHeader;
910

911
   void **load_addr LOCAL =
912
      xmalloc_array(imghdr->NumberOfSections, sizeof(void *));
913

914
   for (int i = 0; i < imghdr->NumberOfSections; i++) {
915
      if ((sections[i].Characteristics & IMAGE_SCN_CNT_CODE)
916
          || (sections[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)) {
917
         const int align = sections[i].Characteristics & IMAGE_SCN_ALIGN_MASK;
918
         code_blob_align(blob, 1 << ((align >> 20) - 1));
919
         load_addr[i] = blob->wptr;
920
         code_blob_emit(blob, data + sections[i].PointerToRawData,
921
                        sections[i].SizeOfRawData);
922
      }
923
      else if ((sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
924
               && sections[i].Misc.VirtualSize > 0)
925
         fatal_trace("non-empty BSS not supported");
926
   }
927

928
   if (blob->overflow)
929
      return;   // Relocations might point outside of code span
930

931
   blob->veneers = blob->wptr;
932

933
   shash_t *external = load_acquire(&blob->span->owner->symbols);
934

935
   for (int i = 0; i < imghdr->NumberOfSections; i++) {
936
      const IMAGE_RELOCATION *relocs = data + sections[i].PointerToRelocations;
937
      for (int j = 0; j < sections[i].NumberOfRelocations; j++) {
938
         const char *name = NULL;
939
         char tmp[9];
940

941
         assert(relocs[j].SymbolTableIndex < imghdr->NumberOfSymbols);
942
         const IMAGE_SYMBOL *sym = symtab + relocs[j].SymbolTableIndex;
943

944
         if (sym->N.Name.Short) {
945
            memcpy(tmp, sym->N.ShortName, 8);
946
            tmp[8] = '\0';
947
            name = tmp;
948
         }
949
         else
950
            name = strtab + sym->N.Name.Long;
951

952
         void *ptr = NULL;
953
         if (sym->SectionNumber > 0) {
954
            assert(sym->SectionNumber - 1 < imghdr->NumberOfSections);
955
            ptr = load_addr[sym->SectionNumber - 1] + sym->Value;
956
         }
957
         else
958
            ptr = shash_get(external, name);
959

960
         if (ptr == NULL && icmp(blob->span->name, name))
961
            ptr = blob->span->base;
962

963
         if (ptr == NULL)
964
            fatal_trace("failed to resolve symbol %s", name);
965

966
         void *patch = load_addr[i] + relocs[j].VirtualAddress;
967
         assert((uint8_t *)patch >= blob->span->base);
968
         assert((uint8_t *)patch < blob->span->base + blob->span->size);
969

970
         switch (relocs[j].Type) {
971
#if defined ARCH_X86_64
972
         case IMAGE_REL_AMD64_ADDR64:
973
            *(uint64_t *)patch += (uint64_t)ptr;
974
            break;
975
         case IMAGE_REL_AMD64_ADDR32NB:
976
            *(uint32_t *)patch += (uint32_t)(ptr - (void *)blob->span->base);
977
            break;
978
#elif defined ARCH_ARM64
979
         case IMAGE_REL_ARM64_BRANCH26:
980
            {
981
               void *veneer = code_emit_trampoline(blob, ptr);
982
               const ptrdiff_t pcrel = (veneer - patch) >> 2;
983
               *(uint32_t *)patch &= ~0x3ffffff;
984
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
985
            }
986
            break;
987
         case IMAGE_REL_ARM64_ADDR32NB:
988
            *(uint32_t *)patch += (uint32_t)(ptr - (void *)blob->span->base);
989
            break;
990
         case IMAGE_REL_ARM64_PAGEBASE_REL21:
991
            arm64_patch_page_base_rel21(patch, ptr);
992
            break;
993
         case IMAGE_REL_ARM64_PAGEOFFSET_12A:
994
         case IMAGE_REL_ARM64_PAGEOFFSET_12L:
995
            arm64_patch_page_offset21(blob, patch, ptr);
996
            break;
997
#endif
998
         default:
999
            blob->span->size = blob->wptr - blob->span->base;
1000
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
1001
            fatal_trace("cannot handle relocation type %d for symbol %s",
1002
                        relocs[j].Type, name);
1003
         }
1004
      }
1005

1006
      if (strncmp((const char *)sections[i].Name, ".pdata",
1007
                  IMAGE_SIZEOF_SHORT_NAME) == 0) {
1008
         assert(sections[i].SizeOfRawData % sizeof(RUNTIME_FUNCTION) == 0);
1009
         const int count = sections[i].SizeOfRawData / sizeof(RUNTIME_FUNCTION);
1010
         const DWORD64 base = (DWORD64)blob->span->base;
1011

1012
         // TODO: we should also call RtlDeleteFunctionTable at some point
1013
         if (!RtlAddFunctionTable(load_addr[i], count, base))
1014
            fatal_trace("RtlAddFunctionTable failed: %s", last_os_error());
1015
      }
1016
   }
1017

1018
   for (int i = 0; i < imghdr->NumberOfSymbols; i++) {
1019
      const IMAGE_SYMBOL *sym = &(symtab[i]);
1020

1021
      if (sym->SectionNumber == 0 || sym->N.Name.Short)
1022
         continue;
1023
      else if ((sym->Type >> 4) != IMAGE_SYM_DTYPE_FUNCTION)
1024
         continue;
1025
      else if (icmp(blob->span->name, strtab + sym->N.Name.Long)) {
1026
         blob->span->entry = load_addr[sym->SectionNumber - 1] + sym->Value;
1027
         break;
1028
      }
1029
   }
1030
}
1031
#elif defined __APPLE__
1032
static void code_load_macho(code_blob_t *blob, const void *data, size_t size)
1033
{
1034
   const void *rptr = data;
1035

1036
   const struct mach_header_64 *fhdr = rptr;
1037
   rptr += sizeof(struct mach_header_64);
1038

1039
   if (fhdr->magic != MH_MAGIC_64)
1040
      fatal_trace("bad Mach-O magic %x", fhdr->magic);
1041

1042
   const struct segment_command_64 *seg = NULL;
1043
   const struct symtab_command *symtab = NULL;
1044

1045
   void **load_addr LOCAL = NULL;
1046

1047
   for (int i = 0; i < fhdr->ncmds; i++) {
1048
      const struct load_command *load = rptr;
1049
      switch (load->cmd) {
1050
      case LC_SEGMENT_64:
1051
         {
1052
            seg = rptr;
1053
            load_addr = xmalloc_array(seg->nsects, sizeof(void *));
1054

1055
            for (int j = 0; j < seg->nsects; j++) {
1056
               const struct section_64 *sec =
1057
                  (void *)seg + sizeof(struct segment_command_64)
1058
                  + j * sizeof(struct section_64);
1059
               code_blob_align(blob, 1 << sec->align);
1060
               load_addr[j] = blob->wptr;
1061
               DEBUG_ONLY(code_blob_printf(blob, "%s", sec->sectname));
1062
               code_blob_emit(blob, data + sec->offset, sec->size);
1063
            }
1064
         }
1065
         break;
1066
      case LC_SYMTAB:
1067
         symtab = rptr;
1068
         assert(symtab->cmdsize == sizeof(struct symtab_command));
1069
         break;
1070
      case LC_DATA_IN_CODE:
1071
      case LC_LINKER_OPTIMIZATION_HINT:
1072
      case LC_BUILD_VERSION:
1073
      case LC_DYSYMTAB:
1074
         break;
1075
      default:
1076
         warnf("unrecognised load command 0x%0x", load->cmd);
1077
      }
1078

1079
      rptr += load->cmdsize;
1080
   }
1081
   assert(rptr == data + sizeof(struct mach_header_64) + fhdr->sizeofcmds);
1082

1083
   if (blob->overflow)
1084
      return;   // Relocations might point outside of code span
1085

1086
   blob->veneers = blob->wptr;
1087

1088
   assert(seg != NULL);
1089
   assert(symtab != NULL);
1090

1091
   shash_t *external = load_acquire(&blob->span->owner->symbols);
1092

1093
   for (int i = 0; i < seg->nsects; i++) {
1094
      const struct section_64 *sec =
1095
         (void *)seg + sizeof(struct segment_command_64)
1096
         + i * sizeof(struct section_64);
1097

1098
      uint32_t addend = 0;
1099
      for (int j = 0; j < sec->nreloc; j++) {
1100
         const struct relocation_info *rel =
1101
            data + sec->reloff + j * sizeof(struct relocation_info);
1102
         const char *name = NULL;
1103
         void *ptr = NULL;
1104
         if (rel->r_extern) {
1105
            assert(rel->r_symbolnum < symtab->nsyms);
1106
            const struct nlist_64 *nl = data + symtab->symoff
1107
               + rel->r_symbolnum * sizeof(struct nlist_64);
1108
            name = data + symtab->stroff + nl->n_un.n_strx;
1109

1110
            if (nl->n_type & N_EXT) {
1111
               if (icmp(blob->span->name, name + 1))
1112
                  ptr = blob->span->base;
1113
               else if ((ptr = shash_get(external, name + 1)) == NULL)
1114
                  fatal_trace("failed to resolve symbol %s", name + 1);
1115
            }
1116
            else if (nl->n_sect != NO_SECT)
1117
               ptr = blob->span->base + nl->n_value;
1118
         }
1119
         else
1120
            ptr = blob->span->base;
1121

1122
         ptr += addend;
1123
         addend = 0;
1124

1125
         void *patch = load_addr[i] + rel->r_address;
1126
         assert((uint8_t *)patch >= blob->span->base);
1127
         assert((uint8_t *)patch < blob->span->base + blob->span->size);
1128

1129
         switch (rel->r_type) {
1130
#ifdef ARCH_ARM64
1131
         case ARM64_RELOC_UNSIGNED:
1132
            assert(rel->r_length == 3);
1133
            *(void **)patch = ptr;
1134
            break;
1135
         case ARM64_RELOC_SUBTRACTOR:
1136
            break;   // What is this?
1137
         case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
1138
         case ARM64_RELOC_PAGEOFF12:
1139
            arm64_patch_page_offset21(blob, patch, ptr);
1140
            break;
1141
         case ARM64_RELOC_GOT_LOAD_PAGE21:
1142
         case ARM64_RELOC_PAGE21:
1143
            arm64_patch_page_base_rel21(patch, ptr);
1144
            break;
1145
         case ARM64_RELOC_BRANCH26:
1146
            {
1147
               void *veneer = code_emit_trampoline(blob, ptr);
1148
               const ptrdiff_t pcrel = (veneer - patch) >> 2;
1149
               debug_reloc(blob, patch, "ARM64_RELOC_BRANCH26 %s PC%+"PRIiPTR,
1150
                           name, pcrel);
1151
               *(uint32_t *)patch &= ~0x3ffffff;
1152
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
1153
            }
1154
            break;
1155
         case ARM64_RELOC_ADDEND:
1156
            addend = rel->r_symbolnum;
1157
            break;
1158
#elif defined ARCH_X86_64
1159
         case X86_64_RELOC_UNSIGNED:
1160
            *(uint64_t *)patch += (uint64_t)ptr;
1161
            break;
1162
         case X86_64_RELOC_BRANCH:
1163
            *(uint32_t *)patch += (uint32_t)(ptr - patch - 4);
1164
            break;
1165
#endif
1166
         default:
1167
            blob->span->size = blob->wptr - blob->span->base;
1168
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
1169
            fatal_trace("cannot handle relocation type %d for symbol %s",
1170
                        rel->r_type, name);
1171
         }
1172
      }
1173
   }
1174

1175
   for (int i = 0; i < symtab->nsyms; i++) {
1176
      const struct nlist_64 *sym =
1177
         data + symtab->symoff + i * sizeof(struct nlist_64);
1178

1179
      if (sym->n_sect == NO_SECT || (sym->n_type & N_TYPE) != N_SECT)
1180
         continue;
1181

1182
      const char *name = data + symtab->stroff + sym->n_un.n_strx;
1183
      if (name[0] == '_' && icmp(blob->span->name, name + 1)) {
1184
         blob->span->entry = load_addr[sym->n_sect - 1] + sym->n_value;
1185
         break;
1186
      }
1187
   }
1188
}
1189
#elif !defined __MINGW32__
1190
static void code_load_elf(code_blob_t *blob, const void *data, size_t size)
14,455✔
1191
{
1192
   const Elf64_Ehdr *ehdr = data;
14,455✔
1193

1194
   if (ehdr->e_ident[EI_MAG0] != ELFMAG0
14,455✔
1195
       || ehdr->e_ident[EI_MAG1] != ELFMAG1
1196
       || ehdr->e_ident[EI_MAG2] != ELFMAG2
1197
       || ehdr->e_ident[EI_MAG3] != ELFMAG3)
14,455✔
1198
      fatal_trace("bad ELF magic");
1199
   else if (ehdr->e_shentsize != sizeof(Elf64_Shdr))
14,455✔
1200
      fatal_trace("bad section header size %d != %zu", ehdr->e_shentsize,
1201
                  sizeof(Elf64_Shdr));
1202

1203
   const Elf64_Shdr *strtab_hdr =
14,455✔
1204
      data + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize;
14,455✔
1205
   const char *strtab = data + strtab_hdr->sh_offset;
14,455✔
1206

1207
   void **load_addr LOCAL = xcalloc_array(ehdr->e_shnum, sizeof(void *));
28,910✔
1208

1209
   for (int i = 0; i < ehdr->e_shnum; i++) {
129,855✔
1210
      const Elf64_Shdr *shdr = data + ehdr->e_shoff + i * ehdr->e_shentsize;
115,400✔
1211

1212
      switch (shdr->sh_type) {
115,400✔
1213
      case SHT_PROGBITS:
29,755✔
1214
         if (shdr->sh_flags & SHF_ALLOC) {
29,755✔
1215
            code_blob_align(blob, shdr->sh_addralign);
15,300✔
1216
            load_addr[i] = blob->wptr;
15,300✔
1217
            DEBUG_ONLY(code_blob_printf(blob, "%s", strtab + shdr->sh_name));
15,300✔
1218
            code_blob_emit(blob, data + shdr->sh_offset, shdr->sh_size);
15,300✔
1219
         }
1220
         break;
1221

1222
      case SHT_RELA:
1223
         // Handled in second pass
1224
         break;
1225

1226
      case SHT_NULL:
1227
      case SHT_STRTAB:
1228
      case SHT_X86_64_UNWIND:
1229
         break;
1230

1231
      case SHT_SYMTAB:
1232
         for (int i = 0; i < shdr->sh_size / shdr->sh_entsize; i++) {
58,665✔
1233
            const Elf64_Sym *sym =
58,665✔
1234
               data + shdr->sh_offset + i * shdr->sh_entsize;
58,665✔
1235

1236
            if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
58,665✔
1237
               continue;
44,210✔
1238
            else if (!icmp(blob->span->name, strtab + sym->st_name))
14,455✔
UNCOV
1239
               continue;
×
1240
            else if (load_addr[sym->st_shndx] == NULL)
14,455✔
1241
               fatal_trace("missing section %d for symbol %s", sym->st_shndx,
UNCOV
1242
                           strtab + sym->st_name);
×
1243
            else {
1244
               blob->span->entry = load_addr[sym->st_shndx] + sym->st_value;
14,455✔
1245
               break;
14,455✔
1246
            }
1247
         }
1248
         break;
1249

UNCOV
1250
      default:
×
1251
         warnf("ignoring ELF section %s with type %x", strtab + shdr->sh_name,
×
1252
               shdr->sh_type);
1253
      }
1254
   }
1255

1256
   if (blob->overflow)
14,455✔
UNCOV
1257
      return;   // Relocations might point outside of code span
×
1258

1259
   blob->veneers = blob->wptr;
14,455✔
1260

1261
   shash_t *external = load_acquire(&blob->span->owner->symbols);
14,455✔
1262

1263
   for (int i = 0; i < ehdr->e_shnum; i++) {
129,855✔
1264
      const Elf64_Shdr *shdr = data + ehdr->e_shoff + i * ehdr->e_shentsize;
115,400✔
1265
      if (shdr->sh_type != SHT_RELA)
115,400✔
1266
         continue;
87,575✔
1267

1268
      const Elf64_Shdr *mod =
27,825✔
1269
         data + ehdr->e_shoff + shdr->sh_info * ehdr->e_shentsize;
27,825✔
1270
      if (mod->sh_type != SHT_PROGBITS || !(mod->sh_flags & SHF_ALLOC))
27,825✔
1271
         continue;
14,455✔
1272
      else if (load_addr[shdr->sh_info] == NULL)
13,370✔
1273
         fatal_trace("section %s not loaded", strtab + mod->sh_name);
1274

1275
      const Elf64_Shdr *symtab =
13,370✔
1276
         data + ehdr->e_shoff + shdr->sh_link * ehdr->e_shentsize;
13,370✔
1277
      if (symtab->sh_type != SHT_SYMTAB)
13,370✔
1278
         fatal_trace("section %s is not a symbol table",
UNCOV
1279
                     strtab + symtab->sh_name);
×
1280

1281
      const Elf64_Rela *endp = data + shdr->sh_offset + shdr->sh_size;
13,370✔
1282
      for (const Elf64_Rela *r = data + shdr->sh_offset; r < endp; r++) {
77,234✔
1283
         const Elf64_Sym *sym = data + symtab->sh_offset
63,864✔
1284
            + ELF64_R_SYM(r->r_info) * symtab->sh_entsize;
63,864✔
1285

1286
         void *ptr = NULL;
63,864✔
1287
         switch (ELF64_ST_TYPE(sym->st_info)) {
63,864✔
1288
         case STT_NOTYPE:
45,059✔
1289
         case STT_FUNC:
1290
            if (sym->st_shndx == 0)
45,059✔
1291
               ptr = shash_get(external, strtab + sym->st_name);
44,729✔
1292
            else
1293
               ptr = load_addr[sym->st_shndx] + sym->st_value;
330✔
1294
            break;
1295
         case STT_SECTION:
18,805✔
1296
            ptr = load_addr[sym->st_shndx];
18,805✔
1297
            break;
18,805✔
UNCOV
1298
         default:
×
1299
            fatal_trace("cannot handle ELF symbol type %d",
1300
                        ELF64_ST_TYPE(sym->st_info));
1301
         }
1302

1303
         if (ptr == NULL)
63,864✔
1304
            fatal_trace("cannot resolve symbol %s type %d",
UNCOV
1305
                        strtab + sym->st_name, ELF64_ST_TYPE(sym->st_info));
×
1306

1307
         void *patch = load_addr[shdr->sh_info] + r->r_offset;
63,864✔
1308
         assert(r->r_offset < mod->sh_size);
63,864✔
1309

1310
         switch (ELF64_R_TYPE(r->r_info)) {
63,864✔
1311
         case R_X86_64_64:
63,864✔
1312
            debug_reloc(blob, patch, "R_X86_64_64 %s", strtab + sym->st_name);
63,864✔
1313
            *(uint64_t *)patch = (uint64_t)ptr + r->r_addend;
63,864✔
1314
            break;
63,864✔
UNCOV
1315
         case R_X86_64_PC32:
×
1316
            {
UNCOV
1317
               const ptrdiff_t pcrel = ptr + r->r_addend - patch;
×
1318
               debug_reloc(blob, patch, "R_X86_64_PC32 %s PC%+"PRIiPTR,
×
1319
                           strtab + sym->st_name, pcrel);
×
1320
               assert(pcrel >= INT32_MIN && pcrel <= INT32_MAX);
×
1321
               *(uint32_t *)patch = pcrel;
×
1322
            }
UNCOV
1323
            break;
×
1324
         case R_X86_64_GOTPCREL:
×
1325
            {
UNCOV
1326
               void *got = code_emit_got(blob, ptr);
×
1327
               const ptrdiff_t pcrel = got + r->r_addend - patch;
×
1328
               debug_reloc(blob, patch, "R_X86_64_GOTPCREL %s PC%+"PRIiPTR,
×
1329
                           strtab + sym->st_name, pcrel);
×
1330
               assert(pcrel >= INT32_MIN && pcrel <= INT32_MAX);
×
1331
               *(uint32_t *)patch = pcrel;
×
1332
            }
UNCOV
1333
            break;
×
1334
         case R_X86_64_PLT32:
×
1335
            {
UNCOV
1336
               void *veneer = code_emit_trampoline(blob, ptr);
×
1337
               const ptrdiff_t pcrel = veneer + r->r_addend - patch;
×
1338
               debug_reloc(blob, patch, "R_X86_64_PLT32 %s PC%+"PRIiPTR,
×
1339
                           strtab + sym->st_name, pcrel);
×
1340
               assert(pcrel >= INT32_MIN && pcrel <= INT32_MAX);
×
1341
               *(uint32_t *)patch = pcrel;
×
1342
            }
UNCOV
1343
            break;
×
1344
         case R_AARCH64_CALL26:
×
1345
            {
UNCOV
1346
               void *veneer = code_emit_trampoline(blob, ptr);
×
1347
               const ptrdiff_t pcrel = (veneer + r->r_addend - patch) >> 2;
×
1348
               *(uint32_t *)patch &= ~0x3ffffff;
×
1349
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
×
1350
            }
UNCOV
1351
            break;
×
1352
         case R_AARCH64_PREL64:
×
1353
            *(uint64_t *)patch = ptr + r->r_addend - patch;
×
1354
            break;
×
1355
         case R_AARCH64_MOVW_UABS_G0_NC:
×
1356
            *(uint32_t *)patch |=
×
1357
               (((uintptr_t)ptr + r->r_addend) & 0xffff) << 5;
×
1358
            break;
×
1359
         case R_AARCH64_MOVW_UABS_G1_NC:
×
1360
            *(uint32_t *)patch |=
×
1361
               ((((uintptr_t)ptr + r->r_addend) >> 16) & 0xffff) << 5;
×
1362
            break;
×
1363
         case R_AARCH64_MOVW_UABS_G2_NC:
×
1364
            *(uint32_t *)patch |=
×
1365
               ((((uintptr_t)ptr + r->r_addend) >> 32) & 0xffff) << 5;
×
1366
            break;
×
1367
         case R_AARCH64_MOVW_UABS_G3:
×
1368
            *(uint32_t *)patch |=
×
1369
               ((((uintptr_t)ptr + r->r_addend) >> 48) & 0xffff) << 5;
×
1370
            break;
×
1371
         default:
×
1372
            blob->span->size = blob->wptr - blob->span->base;
×
1373
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
×
1374
            fatal_trace("cannot handle relocation type %ld for symbol %s",
UNCOV
1375
                        ELF64_R_TYPE(r->r_info), strtab + sym->st_name);
×
1376
         }
1377
      }
1378
   }
1379
}
1380
#endif
1381

1382
void code_load_object(code_blob_t *blob, const void *data, size_t size)
14,455✔
1383
{
1384
#if defined __APPLE__
1385
   code_load_macho(blob, data, size);
1386
#elif defined __MINGW32__
1387
   code_load_pe(blob, data, size);
1388
#else
1389
   code_load_elf(blob, data, size);
14,455✔
1390
#endif
1391
}
14,455✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc