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

nickg / nvc / 12967565412

25 Jan 2025 06:48PM UTC coverage: 92.184% (+0.02%) from 92.165%
12967565412

push

github

nickg
Only compact objects when writing to disk

112 of 119 new or added lines in 13 files covered. (94.12%)

606 existing lines in 10 files now uncovered.

64153 of 69592 relevant lines covered (92.18%)

520365.25 hits per line

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

69.66
/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 "thread.h"
26

27
#include <assert.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <stdio.h>
31
#include <unistd.h>
32
#include <inttypes.h>
33

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

47
#ifdef HAVE_CAPSTONE
48
#include <capstone.h>
49
#endif
50

51
#ifndef R_AARCH64_MOVW_UABS_G0_NC
52
#define R_AARCH64_MOVW_UABS_G0_NC 264
53
#endif
54

55
#ifndef R_AARCH64_MOVW_UABS_G1_NC
56
#define R_AARCH64_MOVW_UABS_G1_NC 266
57
#endif
58

59
#ifndef R_AARCH64_MOVW_UABS_G2_NC
60
#define R_AARCH64_MOVW_UABS_G2_NC 268
61
#endif
62

63
#ifndef R_AARCH64_MOVW_UABS_G3
64
#define R_AARCH64_MOVW_UABS_G3 269
65
#endif
66

67
#ifndef SHT_X86_64_UNWIND
68
#define SHT_X86_64_UNWIND 0x70000001
69
#endif
70

71
#ifndef IMAGE_REL_ARM64_BRANCH26
72
#define IMAGE_REL_ARM64_BRANCH26 0x03
73
#endif
74

75
#ifndef IMAGE_REL_ARM64_ADDR32NB
76
#define IMAGE_REL_ARM64_ADDR32NB 0x02
77
#endif
78

79
#ifndef IMAGE_REL_ARM64_PAGEBASE_REL21
80
#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x04
81
#endif
82

83
#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12A
84
#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x06
85
#endif
86

87
#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12L
88
#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x07
89
#endif
90

91
#define CODE_PAGE_ALIGN   4096
92
#define CODE_PAGE_SIZE    0x400000
93
#define THREAD_CACHE_SIZE 0x10000
94
#define CODE_BLOB_ALIGN   256
95
#define MIN_BLOB_SIZE     0x4000
96

97
#define __IMM64(x) __IMM32(x), __IMM32((x) >> 32)
98
#define __IMM32(x) __IMM16(x), __IMM16((x) >> 16)
99
#define __IMM16(x) (x) & 0xff, ((x) >> 8) & 0xff
100

101
STATIC_ASSERT(MIN_BLOB_SIZE <= THREAD_CACHE_SIZE);
102
STATIC_ASSERT(MIN_BLOB_SIZE % CODE_BLOB_ALIGN == 0);
103
STATIC_ASSERT(CODE_PAGE_SIZE % THREAD_CACHE_SIZE == 0);
104

105
typedef struct _code_page code_page_t;
106

107
typedef struct {
108
   uintptr_t  addr;
109
   char      *text;
110
} code_comment_t;
111

112
typedef struct {
113
   unsigned        count;
114
   unsigned        max;
115
   code_comment_t *comments;
116
} code_debug_t;
117

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

130
typedef struct _patch_list {
131
   patch_list_t    *next;
132
   uint8_t         *wptr;
133
   jit_label_t      label;
134
   code_patch_fn_t  fn;
135
} patch_list_t;
136

137
typedef struct _code_page {
138
   code_cache_t *owner;
139
   code_page_t  *next;
140
   uint8_t      *mem;
141
} code_page_t;
142

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

158
static void code_disassemble(code_span_t *span, uintptr_t mark,
159
                             struct cpu_state *cpu);
160

161
static void code_cache_unwinder(uintptr_t addr, debug_frame_t *frame,
×
162
                                void *context)
163
{
164
   code_cache_t *code = context;
×
165

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

176
static void code_fault_handler(int sig, void *addr, struct cpu_state *cpu,
×
177
                               void *context)
178
{
179
   code_page_t *page = context;
×
180

181
   const uint8_t *pc = (uint8_t *)cpu->pc;
×
182
   if (pc < page->mem || pc > page->mem + CODE_PAGE_SIZE)
×
183
      return;
184

185
   uintptr_t mark = cpu->pc;
×
186
#ifndef __MINGW32__
187
   if (sig == SIGTRAP)
×
188
      mark--;   // Point to faulting instruction
×
189
#endif
190

191
   for (code_span_t *span = page->owner->spans; span; span = span->next) {
×
192
      if (pc >= span->base && pc < span->base + span->size && span->name)
×
193
         code_disassemble(span, mark, cpu);
×
194
   }
195
}
196

197
#ifdef DEBUG
198
static bool code_cache_contains(code_cache_t *code, uint8_t *base, size_t size)
9,335✔
199
{
200
   assert_lock_held(&code->lock);
9,335✔
201

202
   for (code_page_t *p = code->pages; p; p = p->next) {
9,335✔
203
      if (base >= p->mem && base + size <= p->mem + CODE_PAGE_SIZE)
9,335✔
204
         return true;
205
   }
206

207
   return false;
208
}
209
#endif
210

211
static code_span_t *code_span_new(code_cache_t *code, ident_t name,
9,335✔
212
                                  uint8_t *base, size_t size)
213
{
214
   SCOPED_LOCK(code->lock);
9,335✔
215

216
   assert(code_cache_contains(code, base, size));
9,335✔
217

218
   code_span_t *span = xcalloc(sizeof(code_span_t));
9,335✔
219
   span->name  = name;
9,335✔
220
   span->next  = code->spans;
9,335✔
221
   span->base  = base;
9,335✔
222
   span->entry = base;
9,335✔
223
   span->size  = size;
9,335✔
224
   span->owner = code;
9,335✔
225

226
   code->spans = span;
9,335✔
227
   return span;
9,335✔
228
}
229

230
static void code_page_new(code_cache_t *code)
3,531✔
231
{
232
   assert_lock_held(&code->lock);
3,531✔
233

234
   code_page_t *page = xcalloc(sizeof(code_page_t));
3,531✔
235
   page->owner = code;
3,531✔
236
   page->next  = code->pages;
3,531✔
237
   page->mem   = map_jit_pages(CODE_PAGE_ALIGN, CODE_PAGE_SIZE);
3,531✔
238

239
   add_fault_handler(code_fault_handler, page);
3,531✔
240
   debug_add_unwinder(page->mem, CODE_PAGE_SIZE, code_cache_unwinder, code);
3,531✔
241

242
   code->pages = page;
3,531✔
243

244
   code_span_t *span = xcalloc(sizeof(code_span_t));
3,531✔
245
   span->next  = code->spans;
3,531✔
246
   span->base  = page->mem;
3,531✔
247
   span->size  = CODE_PAGE_SIZE;
3,531✔
248
   span->owner = code;
3,531✔
249

250
   code->globalfree = code->spans = span;
3,531✔
251
}
3,531✔
252

253
code_cache_t *code_cache_new(void)
3,525✔
254
{
255
   code_cache_t *code = xcalloc(sizeof(code_cache_t));
3,525✔
256

257
   {
258
      SCOPED_LOCK(code->lock);
7,050✔
259
      code_page_new(code);
3,525✔
260
   }
261

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

273
   if (cs_option(code->capstone, CS_OPT_DETAIL, 1) != CS_ERR_OK)
274
      fatal_trace("failed to set capstone detailed mode");
275
#endif
276

277
   return code;
3,525✔
278
}
279

280
void code_cache_free(code_cache_t *code)
3,521✔
281
{
282
   for (code_page_t *it = code->pages, *tmp; it; it = tmp) {
7,048✔
283
      debug_remove_unwinder(it->mem);
3,527✔
284
      remove_fault_handler(code_fault_handler, it);
3,527✔
285

286
      nvc_munmap(it->mem, CODE_PAGE_SIZE);
3,527✔
287

288
      tmp = it->next;
3,527✔
289
      free(it);
3,527✔
290
   }
291

292
   for (code_span_t *it = code->spans, *tmp; it; it = tmp) {
16,383✔
293
      tmp = it->next;
12,862✔
294
      free(it);
12,862✔
295
   }
296

297
#ifdef HAVE_CAPSTONE
298
   cs_close(&(code->capstone));
299
#endif
300

301
#ifdef DEBUG
302
   if (code->used > 0)
3,521✔
303
      debugf("JIT code footprint: %zu bytes", code->used);
1,245✔
304
#endif
305

306
   free(code);
3,521✔
307
}
3,521✔
308

309
#ifdef HAVE_CAPSTONE
310
static int code_print_spaces(int col, int tab)
311
{
312
   for (; col < tab; col++)
313
      fputc(' ', stdout);
314
   return col;
315
}
316
#endif
317

318
static void code_disassemble(code_span_t *span, uintptr_t mark,
×
319
                             struct cpu_state *cpu)
320
{
321
   SCOPED_LOCK(span->owner->lock);
×
322

323
   printf("--");
×
324

325
   const int namelen = ident_len(span->name);
×
326
   for (int i = 0; i < 72 - namelen; i++)
×
327
      fputc('-', stdout);
×
328

329
   printf(" %s ----\n", istr(span->name));
×
330

331
#ifdef HAVE_CAPSTONE
332
   cs_insn *insn = cs_malloc(span->owner->capstone);
333

334
#ifdef DEBUG
335
   code_comment_t *comment = span->debug.comments;
336
#endif
337

338
   const uint8_t *const eptr = span->base + span->size;
339
   for (const uint8_t *ptr = span->base; ptr < eptr; ) {
340
      uint64_t address = (uint64_t)ptr;
341

342
#ifdef DEBUG
343
      for (; comment < span->debug.comments + span->debug.count
344
              && comment->addr <= address; comment++)
345
         printf("%30s;; %s\n", "", comment->text);
346
#endif
347

348
      int zeros = 0;
349
      for (const uint8_t *zp = ptr; zp < eptr && *zp == 0; zp++, zeros++);
350

351
      if (zeros > 8 || zeros == eptr - ptr) {
352
         printf("%30s;; skipping %d zero bytes\n", "", zeros);
353
         ptr += zeros;
354
         continue;
355
      }
356

357
      size_t size = eptr - ptr;
358
      int col = 0;
359
      if (cs_disasm_iter(span->owner->capstone, &ptr, &size, &address, insn)) {
360
         char hex1[33], *p = hex1;
361
         for (size_t k = 0; k < insn->size; k++)
362
            p += checked_sprintf(p, hex1 + sizeof(hex1) - p, "%02x",
363
                                 insn->bytes[k]);
364

365
         col = printf("%-12" PRIx64 " %-16.16s %s %s", insn->address,
366
                          hex1, insn->mnemonic, insn->op_str);
367

368
#ifdef ARCH_X86_64
369
         if (strcmp(insn->mnemonic, "movabs") == 0) {
370
            const cs_x86_op *src = &(insn->detail->x86.operands[1]);
371
            if (src->type == X86_OP_IMM) {
372
               const char *sym = debug_symbol_name((void *)src->imm);
373
               if (sym != NULL) {
374
                  col = code_print_spaces(col, 60);
375
                  col += printf(" ; %s", sym);
376
               }
377
            }
378
         }
379
#endif
380

381
         if (strlen(hex1) > 16)
382
            col = printf("\n%15s -%-16s", "", hex1 + 16) - 1;
383
      }
384
      else {
385
#ifdef ARCH_ARM64
386
         col = printf("%-12" PRIx64 " %-16.08x %s 0x%08x", (uint64_t)ptr,
387
                      *(uint32_t *)ptr, ".word", *(uint32_t *)ptr);
388
         ptr += 4;
389
#else
390
         col = printf("%-12" PRIx64 " %-16.02x %s 0x%02x", (uint64_t)ptr,
391
                      *ptr, ".byte", *ptr);
392
         ptr++;
393
#endif
394
      }
395

396
      if (mark != 0 && (ptr >= eptr || address > mark)) {
397
         col = code_print_spaces(col, 66);
398
         printf("<=============\n");
399
         if (cpu != NULL) {
400
#ifdef ARCH_X86_64
401
            const char *names[] = {
402
               "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
403
               "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"
404
            };
405
            for (int i = 0; i < ARRAY_LEN(names); i++)
406
               printf("\t%s\t%"PRIxPTR"\n", names[i], cpu->regs[i]);
407
#else
408
            for (int i = 0; i < 32; i++)
409
               printf("\tR%d\t%"PRIxPTR"\n", i, cpu->regs[i]);
410
#endif
411
         }
412
         mark = 0;
413
      }
414
      else
415
         printf("\n");
416
   }
417

418
   cs_free(insn, 1);
419
#else
420
   jit_hexdump(span->base, span->size, 16, (void *)mark, "");
×
421
#endif
422

423
   for (int i = 0; i < 80; i++)
×
424
      fputc('-', stdout);
×
425
   printf("\n");
×
426
   fflush(stdout);
×
427
}
×
428

429
static void code_write_perf_map(code_span_t *span)
×
430
{
431
   SCOPED_LOCK(span->owner->lock);
×
432

433
   if (span->owner->perfmap == NULL) {
×
434
      char *fname LOCAL = xasprintf("/tmp/perf-%d.map", getpid());
×
435
      if ((span->owner->perfmap = fopen(fname, "w")) == NULL) {
×
436
         warnf("cannot create %s: %s", fname, last_os_error());
×
437
         opt_set_int(OPT_PERF_MAP, 0);
×
438
         return;
×
439
      }
440
      else
441
         debugf("writing perf map to %s", fname);
×
442
   }
443

444
   fprintf(span->owner->perfmap, "%p 0x%zx %s\n", span->base, span->size,
×
445
           istr(span->name));
446
   fflush(span->owner->perfmap);
×
447
}
448

449
code_blob_t *code_blob_new(code_cache_t *code, ident_t name, size_t hint)
8,087✔
450
{
451
   code_span_t **freeptr = &(code->freelist[thread_id()]);
8,087✔
452

453
   code_span_t *free = relaxed_load(freeptr);
8,087✔
454
   if (free == NULL) {
8,087✔
455
      free = code_span_new(code, NULL, code->pages->mem, 0);
1,248✔
456
      relaxed_store(freeptr, free);
1,248✔
457
   }
458

459
   const size_t reqsz = hint ?: MIN_BLOB_SIZE;
8,087✔
460

461
   if (free->size < reqsz) {
8,087✔
462
      SCOPED_LOCK(code->lock);
1,316✔
463

464
#ifdef DEBUG
465
      if (free->size > 0)
1,316✔
466
         debugf("thread %d needs new code cache from global free list "
23✔
467
                "(requested %zu bytes, wasted %zu bytes)",
468
                thread_id(), reqsz, free->size);
469
#endif
470

471
      const size_t chunksz = MAX(reqsz, THREAD_CACHE_SIZE);
1,316✔
472
      const size_t alignedsz = ALIGN_UP(chunksz, CODE_BLOB_ALIGN);
1,316✔
473

474
      if (alignedsz > code->globalfree->size) {
1,316✔
475
         DEBUG_ONLY(debugf("requesting new %d byte code page", CODE_PAGE_SIZE));
6✔
476
         code_page_new(code);
6✔
477
         assert(code->globalfree->size == CODE_PAGE_SIZE);
6✔
478
      }
479

480
      const size_t take = MIN(code->globalfree->size, alignedsz);
1,316✔
481

482
      free->size = take;
1,316✔
483
      free->base = code->globalfree->base;
1,316✔
484

485
      code->globalfree->base += take;
1,316✔
486
      code->globalfree->size -= take;
1,316✔
487
   }
488

489
   assert(reqsz <= free->size);
8,087✔
490
   assert(((uintptr_t)free->base & (CODE_BLOB_ALIGN - 1)) == 0);
8,087✔
491

492
   code_span_t *span = code_span_new(code, name, free->base, free->size);
8,087✔
493

494
   free->base += span->size;
8,087✔
495
   free->size -= span->size;
8,087✔
496

497
   code_blob_t *blob = xcalloc(sizeof(code_blob_t));
8,087✔
498
   blob->span = span;
8,087✔
499
   blob->wptr = span->base;
8,087✔
500

501
   thread_wx_mode(WX_WRITE);
8,087✔
502

503
   return blob;
8,087✔
504
}
505

506
void code_blob_finalise(code_blob_t *blob, jit_entry_fn_t *entry)
8,087✔
507
{
508
   code_span_t *span = blob->span;
8,087✔
509
   span->size = blob->wptr - span->base;
8,087✔
510

511
   code_span_t *freespan = relaxed_load(&(span->owner->freelist[thread_id()]));
8,087✔
512
   assert(freespan->size == 0);
8,087✔
513

514
   ihash_free(blob->labels);
8,087✔
515
   blob->labels = NULL;
8,087✔
516

517
   if (unlikely(blob->patches != NULL))
8,087✔
518
      fatal_trace("not all labels in %s were patched", istr(span->name));
519
   else if (unlikely(blob->overflow)) {
8,087✔
520
      // Return all the memory
521
      freespan->size = freespan->base - span->base;
1✔
522
      freespan->base = span->base;
1✔
523
      free(blob);
1✔
524
      return;
1✔
525
   }
526
   else if (span->size == 0)
8,086✔
527
      fatal_trace("code span %s is empty", istr(span->name));
528

529
   uint8_t *aligned = ALIGN_UP(blob->wptr, CODE_BLOB_ALIGN);
8,086✔
530
   freespan->size = freespan->base - aligned;
8,086✔
531
   freespan->base = aligned;
8,086✔
532

533
   if (opt_get_verbose(OPT_ASM_VERBOSE, istr(span->name))) {
8,086✔
534
      color_printf("\n$bold$$blue$");
×
535
      code_disassemble(span, 0, NULL);
×
536
      color_printf("$$\n");
×
537
   }
538

539
   __builtin___clear_cache((char *)span->base, (char *)blob->wptr);
8,086✔
540

541
   thread_wx_mode(WX_EXECUTE);
8,086✔
542

543
   store_release(entry, (jit_entry_fn_t)span->entry);
8,086✔
544

545
   DEBUG_ONLY(relaxed_add(&span->owner->used, span->size));
8,086✔
546
   free(blob);
8,086✔
547

548
   if (opt_get_int(OPT_PERF_MAP))
8,086✔
549
      code_write_perf_map(span);
×
550
}
551

552
__attribute__((cold, noinline))
553
static void code_blob_overflow(code_blob_t *blob)
1✔
554
{
555
   warnf("JIT code buffer for %s too small", istr(blob->span->name));
1✔
556
   for (patch_list_t *it = blob->patches, *tmp; it; it = tmp) {
1✔
557
      tmp = it->next;
×
558
      free(it);
×
559
   }
560
   blob->patches = NULL;
1✔
561
   blob->overflow = true;
1✔
562
}
1✔
563

564
void code_blob_emit(code_blob_t *blob, const uint8_t *bytes, size_t len)
17,050✔
565
{
566
   if (unlikely(blob->overflow))
17,050✔
567
      return;
568
   else if (unlikely(blob->wptr + len > blob->span->base + blob->span->size)) {
17,050✔
569
      code_blob_overflow(blob);
1✔
570
      return;
1✔
571
   }
572

573
   memcpy(blob->wptr, bytes, len);
17,049✔
574
   blob->wptr += len;
17,049✔
575
}
576

577
void code_blob_align(code_blob_t *blob, unsigned align)
8,376✔
578
{
579
#ifdef ARCH_X86_64
580
   const uint8_t pad[] = { 0x90 };
8,376✔
581
#else
582
   const uint8_t pad[] = { 0x00 };
583
#endif
584

585
   assert(is_power_of_2(align));
8,376✔
586
   assert(align % ARRAY_LEN(pad) == 0);
587

588
   while (((uintptr_t)blob->wptr & (align - 1)) && !blob->overflow)
11,834✔
589
      code_blob_emit(blob, pad, ARRAY_LEN(pad));
3,458✔
590
}
8,376✔
591

592
void code_blob_mark(code_blob_t *blob, jit_label_t label)
71✔
593
{
594
   if (unlikely(blob->overflow))
71✔
595
      return;
596
   else if (blob->labels == NULL)
71✔
597
      blob->labels = ihash_new(256);
66✔
598

599
   ihash_put(blob->labels, label, blob->wptr);
71✔
600

601
   for (patch_list_t **p = &(blob->patches); *p; ) {
88✔
602
      if ((*p)->label == label) {
17✔
603
         patch_list_t *next = (*p)->next;
7✔
604
         (*(*p)->fn)(blob, label, (*p)->wptr, blob->wptr);
7✔
605
         free(*p);
7✔
606
         *p = next;
7✔
607
      }
608
      else
609
         p = &((*p)->next);
10✔
610
   }
611
}
612

613
void code_blob_patch(code_blob_t *blob, jit_label_t label, code_patch_fn_t fn)
8✔
614
{
615
   void *ptr = NULL;
8✔
616
   if (unlikely(blob->overflow))
8✔
617
      return;
618
   else if (blob->labels != NULL && (ptr = ihash_get(blob->labels, label)))
8✔
619
      (*fn)(blob, label, blob->wptr, ptr);
1✔
620
   else {
621
      patch_list_t *new = xmalloc(sizeof(patch_list_t));
7✔
622
      new->next  = blob->patches;
7✔
623
      new->fn    = fn;
7✔
624
      new->label = label;
7✔
625
      new->wptr  = blob->wptr;
7✔
626

627
      blob->patches = new;
7✔
628
   }
629
}
630

631
#ifdef DEBUG
632
static void code_blob_print_value(text_buf_t *tb, jit_value_t value)
392✔
633
{
634
   switch (value.kind) {
392✔
635
   case JIT_VALUE_REG:
162✔
636
      tb_printf(tb, "R%d", value.reg);
162✔
637
      break;
162✔
638
   case JIT_VALUE_INT64:
203✔
639
      if (value.int64 < 4096)
203✔
640
         tb_printf(tb, "#%"PRIi64, value.int64);
199✔
641
      else
642
         tb_printf(tb, "#0x%"PRIx64, value.int64);
4✔
643
      break;
644
   case JIT_VALUE_DOUBLE:
1✔
645
      tb_printf(tb, "%%%g", value.dval);
1✔
646
      break;
1✔
647
   case JIT_ADDR_CPOOL:
×
648
      tb_printf(tb, "[CP+%"PRIi64"]", value.int64);
×
649
      break;
×
650
   case JIT_ADDR_REG:
19✔
651
      tb_printf(tb, "[R%d", value.reg);
19✔
652
      if (value.disp != 0)
19✔
653
         tb_printf(tb, "+%d", value.disp);
1✔
654
      tb_cat(tb, "]");
19✔
655
      break;
19✔
656
   case JIT_ADDR_ABS:
×
657
      tb_printf(tb, "[#%016"PRIx64"]", value.int64);
×
658
      break;
×
659
   case JIT_ADDR_COVER:
×
660
      tb_printf(tb, "@%"PRIi64, value.int64);
×
661
      break;
×
662
   case JIT_VALUE_LABEL:
5✔
663
      tb_printf(tb, "%d", value.label);
5✔
664
      break;
5✔
665
   case JIT_VALUE_HANDLE:
2✔
666
      tb_printf(tb, "<%d>", value.handle);
2✔
667
      break;
2✔
668
   case JIT_VALUE_EXIT:
×
669
      tb_printf(tb, "%s", jit_exit_name(value.exit));
×
670
      break;
×
671
   case JIT_VALUE_LOC:
×
672
      tb_printf(tb, "<%s:%d>", loc_file_str(&value.loc), value.loc.first_line);
×
673
      break;
×
674
   case JIT_VALUE_LOCUS:
×
NEW
675
      tb_printf(tb, "%p", value.locus);
×
676
      break;
×
677
   case JIT_VALUE_VPOS:
×
678
      tb_printf(tb, "%u:%u", value.vpos.block, value.vpos.op);
×
679
      break;
×
680
   default:
×
681
      tb_cat(tb, "???");
×
682
   }
683
}
392✔
684

685
static void code_blob_add_comment(code_blob_t *blob, char *text)
348✔
686
{
687
   code_debug_t *dbg = &(blob->span->debug);
348✔
688

689
   if (dbg->count == dbg->max) {
348✔
690
      dbg->max = MAX(128, dbg->max * 2);
66✔
691
      dbg->comments = xrealloc_array(dbg->comments, dbg->max,
66✔
692
                                     sizeof(code_comment_t));
693
   }
694

695
   dbg->comments[dbg->count].addr = (uintptr_t)blob->wptr;
348✔
696
   dbg->comments[dbg->count].text = text;
348✔
697
   dbg->count++;
348✔
698
}
348✔
699

700
void code_blob_print_ir(code_blob_t *blob, jit_ir_t *ir)
348✔
701
{
702
   LOCAL_TEXT_BUF tb = tb_new();
696✔
703
   tb_printf(tb, "%s%s", jit_op_name(ir->op), jit_cc_name(ir->cc));
348✔
704

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

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

710
   if (ir->result != JIT_REG_INVALID)
348✔
711
      tb_printf(tb, "R%d", ir->result);
203✔
712

713
   if (ir->arg1.kind != JIT_VALUE_INVALID) {
348✔
714
      if (ir->result != JIT_REG_INVALID)
263✔
715
         tb_cat(tb, ", ");
187✔
716
      code_blob_print_value(tb, ir->arg1);
263✔
717
   }
718

719
   if (ir->arg2.kind != JIT_VALUE_INVALID) {
348✔
720
      tb_cat(tb, ", ");
129✔
721
      code_blob_print_value(tb, ir->arg2);
129✔
722
   }
723

724
   code_blob_add_comment(blob, tb_claim(tb));
348✔
725
}
348✔
726

727
void code_blob_printf(code_blob_t *blob, const char *fmt, ...)
×
728
{
729
   code_debug_t *dbg = &(blob->span->debug);
×
730

731
   if (dbg->count == dbg->max) {
×
732
      dbg->max = MAX(128, dbg->max * 2);
×
733
      dbg->comments = xrealloc_array(dbg->comments, dbg->max,
×
734
                                     sizeof(code_comment_t));
735
   }
736

737
   va_list ap;
×
738
   va_start(ap, fmt);
×
739

740
   char *text = xvasprintf(fmt, ap);
×
741
   code_blob_add_comment(blob, text);
×
742

743
   va_end(ap);
×
744
}
×
745
#endif   // DEBUG
746

747
#ifdef ARCH_ARM64
748
static void *arm64_emit_trampoline(code_blob_t *blob, uintptr_t dest)
749
{
750
   const uint8_t veneer[] = {
751
      0x50, 0x00, 0x00, 0x58,   // LDR X16, [PC+8]
752
      0x00, 0x02, 0x1f, 0xd6,   // BR X16
753
      __IMM64(dest)
754
   };
755

756
   void *prev = memmem(blob->span->base, blob->span->size,
757
                       veneer, ARRAY_LEN(veneer));
758
   if (prev != NULL)
759
      return prev;
760
   else {
761
      void *addr = blob->wptr;
762
      code_blob_emit(blob, veneer, ARRAY_LEN(veneer));
763
      return addr;
764
   }
765
}
766

767
static void arm64_patch_page_offset21(code_blob_t *blob, uint32_t *patch,
768
                                      void *ptr)
769
{
770
   switch ((*patch >> 23) & 0x7f) {
771
   case 0b1111010:   // LDR (immediate, SIMD&FP)
772
   case 0b1110010:   // LDR (immediate)
773
      assert(*patch & (1 << 30));  // Quadword
774
      assert(((uintptr_t)ptr & 7) == 0);
775
      *patch |= (((uintptr_t)ptr & 0xfff) >> 3) << 10;
776
      break;
777
   case 0b0100010:   // ADD (immediate)
778
      *patch |= ((uintptr_t)ptr & 0xfff) << 10;
779
      break;
780
   default:
781
      blob->span->size = blob->wptr - blob->span->base;
782
      code_disassemble(blob->span, (uintptr_t)patch, NULL);
783
      fatal_trace("cannot patch instruction");
784
   }
785
}
786

787
static void arm64_patch_page_base_rel21(uint32_t *patch, void *ptr)
788
{
789
   const intptr_t dst_page = (intptr_t)ptr & ~UINT64_C(0xfff);
790
   const intptr_t src_page = (intptr_t)patch & ~UINT64_C(0xfff);
791
   const intptr_t upper21 = (dst_page - src_page) >> 12;
792
   *(uint32_t *)patch |= (upper21 & 3) << 29;
793
   *(uint32_t *)patch |= ((upper21 >> 2) & 0x7ffff) << 5;
794
}
795
#else
796
#define arm64_emit_trampoline(blob, dest) NULL
797
#endif
798

799
#if defined __MINGW32__
800
static void code_load_pe(code_blob_t *blob, const void *data, size_t size)
801
{
802
   const IMAGE_FILE_HEADER *imghdr = data;
803

804
   switch (imghdr->Machine) {
805
   case IMAGE_FILE_MACHINE_AMD64:
806
   case IMAGE_FILE_MACHINE_ARM64:
807
      break;
808
   default:
809
      fatal_trace("unknown target machine %x", imghdr->Machine);
810
   }
811

812
   const IMAGE_SYMBOL *symtab = data + imghdr->PointerToSymbolTable;
813
   const char *strtab = data + imghdr->PointerToSymbolTable
814
      + imghdr->NumberOfSymbols * sizeof(IMAGE_SYMBOL);
815

816
   const IMAGE_SECTION_HEADER *sections =
817
      data + IMAGE_SIZEOF_FILE_HEADER + imghdr->SizeOfOptionalHeader;
818

819
   void **load_addr LOCAL =
820
      xmalloc_array(imghdr->NumberOfSections, sizeof(void *));
821

822
   for (int i = 0; i < imghdr->NumberOfSections; i++) {
823
      if ((sections[i].Characteristics & IMAGE_SCN_CNT_CODE)
824
          || (sections[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)) {
825
         const int align = sections[i].Characteristics & IMAGE_SCN_ALIGN_MASK;
826
         code_blob_align(blob, 1 << ((align >> 20) - 1));
827
         load_addr[i] = blob->wptr;
828
         code_blob_emit(blob, data + sections[i].PointerToRawData,
829
                        sections[i].SizeOfRawData);
830
      }
831
      else if ((sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
832
               && sections[i].Misc.VirtualSize > 0)
833
         fatal_trace("non-empty BSS not supported");
834
   }
835

836
   if (blob->overflow)
837
      return;   // Relocations might point outside of code span
838

839
   for (int i = 0; i < imghdr->NumberOfSections; i++) {
840
      const IMAGE_RELOCATION *relocs = data + sections[i].PointerToRelocations;
841
      for (int j = 0; j < sections[i].NumberOfRelocations; j++) {
842
         const char *name = NULL;
843
         char tmp[9];
844

845
         assert(relocs[j].SymbolTableIndex < imghdr->NumberOfSymbols);
846
         const IMAGE_SYMBOL *sym = symtab + relocs[j].SymbolTableIndex;
847

848
         if (sym->N.Name.Short) {
849
            memcpy(tmp, sym->N.ShortName, 8);
850
            tmp[8] = '\0';
851
            name = tmp;
852
         }
853
         else
854
            name = strtab + sym->N.Name.Long;
855

856
         void *ptr = NULL;
857
         if (sym->SectionNumber > 0) {
858
            assert(sym->SectionNumber - 1 < imghdr->NumberOfSections);
859
            ptr = load_addr[sym->SectionNumber - 1] + sym->Value;
860
         }
861
#ifdef ARCH_X86_64
862
         else if (strcmp(name, "___chkstk_ms") == 0) {
863
            extern void ___chkstk_ms(void);
864
            ptr = &___chkstk_ms;
865
         }
866
#endif
867
         else
868
            ptr = ffi_find_symbol(NULL, name);
869

870
         if (ptr == NULL && icmp(blob->span->name, name))
871
            ptr = blob->span->base;
872

873
         if (ptr == NULL)
874
            fatal_trace("failed to resolve symbol %s", name);
875

876
         void *patch = load_addr[i] + relocs[j].VirtualAddress;
877
         assert((uint8_t *)patch >= blob->span->base);
878
         assert((uint8_t *)patch < blob->span->base + blob->span->size);
879

880
         switch (relocs[j].Type) {
881
#if defined ARCH_X86_64
882
         case IMAGE_REL_AMD64_ADDR64:
883
            *(uint64_t *)patch += (uint64_t)ptr;
884
            break;
885
         case IMAGE_REL_AMD64_ADDR32NB:
886
            *(uint32_t *)patch += (uint32_t)(ptr - (void *)blob->span->base);
887
            break;
888
#elif defined ARCH_ARM64
889
         case IMAGE_REL_ARM64_BRANCH26:
890
            {
891
               void *veneer = arm64_emit_trampoline(blob, (uintptr_t)ptr);
892
               const ptrdiff_t pcrel = (veneer - patch) >> 2;
893
               *(uint32_t *)patch &= ~0x3ffffff;
894
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
895
            }
896
            break;
897
         case IMAGE_REL_ARM64_ADDR32NB:
898
            *(uint32_t *)patch += (uint32_t)(ptr - (void *)blob->span->base);
899
            break;
900
         case IMAGE_REL_ARM64_PAGEBASE_REL21:
901
            arm64_patch_page_base_rel21(patch, ptr);
902
            break;
903
         case IMAGE_REL_ARM64_PAGEOFFSET_12A:
904
         case IMAGE_REL_ARM64_PAGEOFFSET_12L:
905
            arm64_patch_page_offset21(blob, patch, ptr);
906
            break;
907
#endif
908
         default:
909
            blob->span->size = blob->wptr - blob->span->base;
910
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
911
            fatal_trace("cannot handle relocation type %d for symbol %s",
912
                        relocs[j].Type, name);
913
         }
914
      }
915

916
      if (strncmp((const char *)sections[i].Name, ".pdata",
917
                  IMAGE_SIZEOF_SHORT_NAME) == 0) {
918
         assert(sections[i].SizeOfRawData % sizeof(RUNTIME_FUNCTION) == 0);
919
         const int count = sections[i].SizeOfRawData / sizeof(RUNTIME_FUNCTION);
920
         const DWORD64 base = (DWORD64)blob->span->base;
921

922
         // TODO: we should also call RtlDeleteFunctionTable at some point
923
         if (!RtlAddFunctionTable(load_addr[i], count, base))
924
            fatal_trace("RtlAddFunctionTable failed: %s", last_os_error());
925
      }
926
   }
927

928
   for (int i = 0; i < imghdr->NumberOfSymbols; i++) {
929
      const IMAGE_SYMBOL *sym = &(symtab[i]);
930

931
      if (sym->SectionNumber == 0 || sym->N.Name.Short)
932
         continue;
933
      else if ((sym->Type >> 4) != IMAGE_SYM_DTYPE_FUNCTION)
934
         continue;
935
      else if (icmp(blob->span->name, strtab + sym->N.Name.Long)) {
936
         blob->span->entry = load_addr[sym->SectionNumber - 1] + sym->Value;
937
         break;
938
      }
939
   }
940
}
941
#elif defined __APPLE__
942
static void code_load_macho(code_blob_t *blob, const void *data, size_t size)
943
{
944
   const void *rptr = data;
945

946
   const struct mach_header_64 *fhdr = rptr;
947
   rptr += sizeof(struct mach_header_64);
948

949
   if (fhdr->magic != MH_MAGIC_64)
950
      fatal_trace("bad Mach-O magic %x", fhdr->magic);
951

952
   const struct segment_command_64 *seg = NULL;
953
   const struct symtab_command *symtab = NULL;
954

955
   void **load_addr LOCAL = NULL;
956

957
   for (int i = 0; i < fhdr->ncmds; i++) {
958
      const struct load_command *load = rptr;
959
      switch (load->cmd) {
960
      case LC_SEGMENT_64:
961
         {
962
            seg = rptr;
963
            load_addr = xmalloc_array(seg->nsects, sizeof(void *));
964

965
            for (int j = 0; j < seg->nsects; j++) {
966
               const struct section_64 *sec =
967
                  (void *)seg + sizeof(struct segment_command_64)
968
                  + j * sizeof(struct section_64);
969
               code_blob_align(blob, 1 << sec->align);
970
               load_addr[j] = blob->wptr;
971
               code_blob_emit(blob, data + sec->offset, sec->size);
972
            }
973
         }
974
         break;
975
      case LC_SYMTAB:
976
         symtab = rptr;
977
         assert(symtab->cmdsize == sizeof(struct symtab_command));
978
         break;
979
      case LC_DATA_IN_CODE:
980
      case LC_LINKER_OPTIMIZATION_HINT:
981
      case LC_BUILD_VERSION:
982
      case LC_DYSYMTAB:
983
         break;
984
      default:
985
         warnf("unrecognised load command 0x%0x", load->cmd);
986
      }
987

988
      rptr += load->cmdsize;
989
   }
990
   assert(rptr == data + sizeof(struct mach_header_64) + fhdr->sizeofcmds);
991

992
   if (blob->overflow)
993
      return;   // Relocations might point outside of code span
994

995
   assert(seg != NULL);
996
   assert(symtab != NULL);
997

998
   for (int i = 0; i < seg->nsects; i++) {
999
      const struct section_64 *sec =
1000
         (void *)seg + sizeof(struct segment_command_64)
1001
         + i * sizeof(struct section_64);
1002

1003
      uint32_t addend = 0;
1004
      for (int j = 0; j < sec->nreloc; j++) {
1005
         const struct relocation_info *rel =
1006
            data + sec->reloff + j * sizeof(struct relocation_info);
1007
         const char *name = NULL;
1008
         void *ptr = NULL;
1009
         if (rel->r_extern) {
1010
            assert(rel->r_symbolnum < symtab->nsyms);
1011
            const struct nlist_64 *nl = data + symtab->symoff
1012
               + rel->r_symbolnum * sizeof(struct nlist_64);
1013
            name = data + symtab->stroff + nl->n_un.n_strx;
1014

1015
            if (nl->n_type & N_EXT) {
1016
               if (icmp(blob->span->name, name + 1))
1017
                  ptr = blob->span->base;
1018
               else if ((ptr = ffi_find_symbol(NULL, name + 1)) == NULL)
1019
                  fatal_trace("failed to resolve symbol %s", name + 1);
1020
            }
1021
            else if (nl->n_sect != NO_SECT)
1022
               ptr = blob->span->base + nl->n_value;
1023
         }
1024
         else
1025
            ptr = blob->span->base;
1026

1027
         ptr += addend;
1028
         addend = 0;
1029

1030
         void *patch = load_addr[i] + rel->r_address;
1031
         assert((uint8_t *)patch >= blob->span->base);
1032
         assert((uint8_t *)patch < blob->span->base + blob->span->size);
1033

1034
         switch (rel->r_type) {
1035
#ifdef ARCH_ARM64
1036
         case ARM64_RELOC_UNSIGNED:
1037
            assert(rel->r_length == 3);
1038
            *(void **)patch = ptr;
1039
            break;
1040
         case ARM64_RELOC_SUBTRACTOR:
1041
            break;   // What is this?
1042
         case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
1043
         case ARM64_RELOC_PAGEOFF12:
1044
            arm64_patch_page_offset21(blob, patch, ptr);
1045
            break;
1046
         case ARM64_RELOC_GOT_LOAD_PAGE21:
1047
         case ARM64_RELOC_PAGE21:
1048
            arm64_patch_page_base_rel21(patch, ptr);
1049
            break;
1050
         case ARM64_RELOC_BRANCH26:
1051
            {
1052
               void *veneer = arm64_emit_trampoline(blob, (uintptr_t)ptr);
1053
               const ptrdiff_t pcrel = (veneer - patch) >> 2;
1054
               *(uint32_t *)patch &= ~0x3ffffff;
1055
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
1056
            }
1057
            break;
1058
         case ARM64_RELOC_ADDEND:
1059
            addend = rel->r_symbolnum;
1060
            break;
1061
#elif defined ARCH_X86_64
1062
         case X86_64_RELOC_UNSIGNED:
1063
            *(uint64_t *)patch += (uint64_t)ptr;
1064
            break;
1065
         case X86_64_RELOC_BRANCH:
1066
            *(uint32_t *)patch += (uint32_t)(ptr - patch - 4);
1067
            break;
1068
#endif
1069
         default:
1070
            blob->span->size = blob->wptr - blob->span->base;
1071
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
1072
            fatal_trace("cannot handle relocation type %d for symbol %s",
1073
                        rel->r_type, name);
1074
         }
1075
      }
1076
   }
1077

1078
   for (int i = 0; i < symtab->nsyms; i++) {
1079
      const struct nlist_64 *sym =
1080
         data + symtab->symoff + i * sizeof(struct nlist_64);
1081

1082
      if (sym->n_sect == NO_SECT || (sym->n_type & N_TYPE) != N_SECT)
1083
         continue;
1084

1085
      const char *name = data + symtab->stroff + sym->n_un.n_strx;
1086
      if (name[0] == '_' && icmp(blob->span->name, name + 1)) {
1087
         blob->span->entry = load_addr[sym->n_sect - 1] + sym->n_value;
1088
         break;
1089
      }
1090
   }
1091
}
1092
#elif !defined __MINGW32__
1093
static void code_load_elf(code_blob_t *blob, const void *data, size_t size)
7,832✔
1094
{
1095
   const Elf64_Ehdr *ehdr = data;
7,832✔
1096

1097
   if (ehdr->e_ident[EI_MAG0] != ELFMAG0
7,832✔
1098
       || ehdr->e_ident[EI_MAG1] != ELFMAG1
1099
       || ehdr->e_ident[EI_MAG2] != ELFMAG2
1100
       || ehdr->e_ident[EI_MAG3] != ELFMAG3)
7,832✔
1101
      fatal_trace("bad ELF magic");
1102
   else if (ehdr->e_shentsize != sizeof(Elf64_Shdr))
7,832✔
1103
      fatal_trace("bad section header size %d != %zu", ehdr->e_shentsize,
1104
                  sizeof(Elf64_Shdr));
1105

1106
   const Elf64_Shdr *strtab_hdr =
7,832✔
1107
      data + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize;
7,832✔
1108
   const char *strtab = data + strtab_hdr->sh_offset;
7,832✔
1109

1110
   void **load_addr LOCAL = xcalloc_array(ehdr->e_shnum, sizeof(void *));
15,664✔
1111

1112
   for (int i = 0; i < ehdr->e_shnum; i++) {
70,998✔
1113
      const Elf64_Shdr *shdr = data + ehdr->e_shoff + i * ehdr->e_shentsize;
63,166✔
1114

1115
      switch (shdr->sh_type) {
63,166✔
1116
      case SHT_PROGBITS:
16,208✔
1117
         if (shdr->sh_flags & SHF_ALLOC) {
16,208✔
1118
            code_blob_align(blob, shdr->sh_addralign);
8,376✔
1119
            load_addr[i] = blob->wptr;
8,376✔
1120
            code_blob_emit(blob, data + shdr->sh_offset, shdr->sh_size);
8,376✔
1121
         }
1122
         break;
1123

1124
      case SHT_RELA:
1125
         // Handled in second pass
1126
         break;
1127

1128
      case SHT_NULL:
1129
      case SHT_STRTAB:
1130
      case SHT_X86_64_UNWIND:
1131
         break;
1132

1133
      case SHT_SYMTAB:
1134
         for (int i = 0; i < shdr->sh_size / shdr->sh_entsize; i++) {
31,872✔
1135
            const Elf64_Sym *sym =
31,872✔
1136
               data + shdr->sh_offset + i * shdr->sh_entsize;
31,872✔
1137

1138
            if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
31,872✔
1139
               continue;
24,040✔
1140
            else if (!icmp(blob->span->name, strtab + sym->st_name))
7,832✔
1141
               continue;
×
1142
            else if (load_addr[sym->st_shndx] == NULL)
7,832✔
1143
               fatal_trace("missing section %d for symbol %s", sym->st_shndx,
1144
                           strtab + sym->st_name);
×
1145
            else {
1146
               blob->span->entry = load_addr[sym->st_shndx] + sym->st_value;
7,832✔
1147
               break;
7,832✔
1148
            }
1149
         }
1150
         break;
1151

1152
      default:
×
1153
         warnf("ignoring ELF section %s with type %x", strtab + shdr->sh_name,
×
1154
               shdr->sh_type);
1155
      }
1156
   }
1157

1158
   if (blob->overflow)
7,832✔
1159
      return;   // Relocations might point outside of code span
×
1160

1161
   for (int i = 0; i < ehdr->e_shnum; i++) {
70,998✔
1162
      const Elf64_Shdr *shdr = data + ehdr->e_shoff + i * ehdr->e_shentsize;
63,166✔
1163
      if (shdr->sh_type != SHT_RELA)
63,166✔
1164
         continue;
47,536✔
1165

1166
      const Elf64_Shdr *mod =
15,630✔
1167
         data + ehdr->e_shoff + shdr->sh_info * ehdr->e_shentsize;
15,630✔
1168
      if (mod->sh_type != SHT_PROGBITS || !(mod->sh_flags & SHF_ALLOC))
15,630✔
1169
         continue;
7,832✔
1170
      else if (load_addr[shdr->sh_info] == NULL)
7,798✔
1171
         fatal_trace("section %s not loaded", strtab + mod->sh_name);
1172

1173
      const Elf64_Shdr *symtab =
7,798✔
1174
         data + ehdr->e_shoff + shdr->sh_link * ehdr->e_shentsize;
7,798✔
1175
      if (symtab->sh_type != SHT_SYMTAB)
7,798✔
1176
         fatal_trace("section %s is not a symbol table",
1177
                     strtab + symtab->sh_name);
×
1178

1179
      const Elf64_Rela *endp = data + shdr->sh_offset + shdr->sh_size;
7,798✔
1180
      for (const Elf64_Rela *r = data + shdr->sh_offset; r < endp; r++) {
45,214✔
1181
         const Elf64_Sym *sym = data + symtab->sh_offset
37,416✔
1182
            + ELF64_R_SYM(r->r_info) * symtab->sh_entsize;
37,416✔
1183

1184
         char *ptr = NULL;
37,416✔
1185
         switch (ELF64_ST_TYPE(sym->st_info)) {
37,416✔
1186
         case STT_NOTYPE:
27,616✔
1187
         case STT_FUNC:
1188
            ptr = ffi_find_symbol(NULL, strtab + sym->st_name);
27,616✔
1189
            break;
27,616✔
1190
         case STT_SECTION:
9,800✔
1191
            ptr = load_addr[sym->st_shndx];
9,800✔
1192
            break;
9,800✔
1193
         }
1194

1195
         if (ptr == NULL && icmp(blob->span->name, strtab + sym->st_name))
37,416✔
1196
            ptr = (char *)blob->span->base;
44✔
1197

1198
         if (ptr == NULL)
37,416✔
1199
            fatal_trace("cannot resolve symbol %s type %d",
1200
                        strtab + sym->st_name, ELF64_ST_TYPE(sym->st_info));
×
1201

1202
         ptr += r->r_addend;
37,416✔
1203

1204
         void *patch = load_addr[shdr->sh_info] + r->r_offset;
37,416✔
1205
         assert(r->r_offset < mod->sh_size);
37,416✔
1206

1207
         switch (ELF64_R_TYPE(r->r_info)) {
37,416✔
1208
         case R_X86_64_64:
37,416✔
1209
            *(uint64_t *)patch = (uint64_t)ptr;
37,416✔
1210
            break;
37,416✔
1211
         case R_AARCH64_CALL26:
×
1212
            {
1213
               void *veneer = arm64_emit_trampoline(blob, (uintptr_t)ptr);
×
1214
               const ptrdiff_t pcrel = (veneer - patch) >> 2;
×
1215
               *(uint32_t *)patch &= ~0x3ffffff;
×
1216
               *(uint32_t *)patch |= pcrel & 0x3ffffff;
×
1217
            }
1218
            break;
×
1219
         case R_AARCH64_PREL64:
×
1220
            *(uint64_t *)patch = ptr - (char *)patch;
×
1221
            break;
×
1222
         case R_AARCH64_MOVW_UABS_G0_NC:
×
1223
            *(uint32_t *)patch |= ((uintptr_t)ptr & 0xffff) << 5;
×
1224
            break;
×
1225
         case R_AARCH64_MOVW_UABS_G1_NC:
×
1226
            *(uint32_t *)patch |= (((uintptr_t)ptr >> 16) & 0xffff) << 5;
×
1227
            break;
×
1228
         case R_AARCH64_MOVW_UABS_G2_NC:
×
1229
            *(uint32_t *)patch |= (((uintptr_t)ptr >> 32) & 0xffff) << 5;
×
1230
            break;
×
1231
         case R_AARCH64_MOVW_UABS_G3:
×
1232
            *(uint32_t *)patch |= (((uintptr_t)ptr >> 48) & 0xffff) << 5;
×
1233
            break;
×
1234
         default:
×
1235
            blob->span->size = blob->wptr - blob->span->base;
×
1236
            code_disassemble(blob->span, (uintptr_t)patch, NULL);
×
1237
            fatal_trace("cannot handle relocation type %ld for symbol %s",
1238
                        ELF64_R_TYPE(r->r_info), strtab + sym->st_name);
×
1239
         }
1240
      }
1241
   }
1242
}
1243
#endif
1244

1245
void code_load_object(code_blob_t *blob, const void *data, size_t size)
7,832✔
1246
{
1247
#if defined __APPLE__
1248
   code_load_macho(blob, data, size);
1249
#elif defined __MINGW32__
1250
   code_load_pe(blob, data, size);
1251
#else
1252
   code_load_elf(blob, data, size);
7,832✔
1253
#endif
1254
}
7,832✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc