Ben Gamari pushed to branch wip/T25989 at Glasgow Haskell Compiler / GHC
Commits:
-
f4996065
by Ben Gamari at 2025-05-17T14:28:55-04:00
-
88dc662f
by Ben Gamari at 2025-05-19T12:30:34-04:00
8 changed files:
- rts/Linker.c
- rts/LinkerInternals.h
- rts/linker/Elf.c
- rts/linker/MachO.c
- rts/linker/PEi386.c
- + rts/linker/ProddableBlocks.c
- + rts/linker/ProddableBlocks.h
- rts/rts.cabal
Changes:
| ... | ... | @@ -1194,7 +1194,7 @@ void freeObjectCode (ObjectCode *oc) |
| 1194 | 1194 | stgFree(oc->sections);
|
| 1195 | 1195 | }
|
| 1196 | 1196 | |
| 1197 | - freeProddableBlocks(oc);
|
|
| 1197 | + freeProddableBlocks(&oc->proddables);
|
|
| 1198 | 1198 | freeSegments(oc);
|
| 1199 | 1199 | |
| 1200 | 1200 | /* Free symbol_extras. On x86_64 Windows, symbol_extras are allocated
|
| ... | ... | @@ -1279,7 +1279,7 @@ mkOc( ObjectType type, pathchar *path, char *image, int imageSize, |
| 1279 | 1279 | oc->sections = NULL;
|
| 1280 | 1280 | oc->n_segments = 0;
|
| 1281 | 1281 | oc->segments = NULL;
|
| 1282 | - oc->proddables = NULL;
|
|
| 1282 | + initProddableBlockSet(&oc->proddables);
|
|
| 1283 | 1283 | oc->foreign_exports = NULL;
|
| 1284 | 1284 | #if defined(NEED_SYMBOL_EXTRAS)
|
| 1285 | 1285 | oc->symbol_extras = NULL;
|
| ... | ... | @@ -1834,50 +1834,6 @@ OStatus getObjectLoadStatus (pathchar *path) |
| 1834 | 1834 | return r;
|
| 1835 | 1835 | }
|
| 1836 | 1836 | |
| 1837 | -/* -----------------------------------------------------------------------------
|
|
| 1838 | - * Sanity checking. For each ObjectCode, maintain a list of address ranges
|
|
| 1839 | - * which may be prodded during relocation, and abort if we try and write
|
|
| 1840 | - * outside any of these.
|
|
| 1841 | - */
|
|
| 1842 | -void
|
|
| 1843 | -addProddableBlock ( ObjectCode* oc, void* start, int size )
|
|
| 1844 | -{
|
|
| 1845 | - ProddableBlock* pb
|
|
| 1846 | - = stgMallocBytes(sizeof(ProddableBlock), "addProddableBlock");
|
|
| 1847 | - |
|
| 1848 | - IF_DEBUG(linker, debugBelch("addProddableBlock: %p %p %d\n", oc, start, size));
|
|
| 1849 | - ASSERT(size > 0);
|
|
| 1850 | - pb->start = start;
|
|
| 1851 | - pb->size = size;
|
|
| 1852 | - pb->next = oc->proddables;
|
|
| 1853 | - oc->proddables = pb;
|
|
| 1854 | -}
|
|
| 1855 | - |
|
| 1856 | -void
|
|
| 1857 | -checkProddableBlock (ObjectCode *oc, void *addr, size_t size )
|
|
| 1858 | -{
|
|
| 1859 | - ProddableBlock* pb;
|
|
| 1860 | - |
|
| 1861 | - for (pb = oc->proddables; pb != NULL; pb = pb->next) {
|
|
| 1862 | - char* s = (char*)(pb->start);
|
|
| 1863 | - char* e = s + pb->size;
|
|
| 1864 | - char* a = (char*)addr;
|
|
| 1865 | - if (a >= s && (a+size) <= e) return;
|
|
| 1866 | - }
|
|
| 1867 | - barf("checkProddableBlock: invalid fixup in runtime linker: %p", addr);
|
|
| 1868 | -}
|
|
| 1869 | - |
|
| 1870 | -void freeProddableBlocks (ObjectCode *oc)
|
|
| 1871 | -{
|
|
| 1872 | - ProddableBlock *pb, *next;
|
|
| 1873 | - |
|
| 1874 | - for (pb = oc->proddables; pb != NULL; pb = next) {
|
|
| 1875 | - next = pb->next;
|
|
| 1876 | - stgFree(pb);
|
|
| 1877 | - }
|
|
| 1878 | - oc->proddables = NULL;
|
|
| 1879 | -}
|
|
| 1880 | - |
|
| 1881 | 1837 | /* -----------------------------------------------------------------------------
|
| 1882 | 1838 | * Section management.
|
| 1883 | 1839 | */
|
| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | #include "RtsSymbols.h"
|
| 13 | 13 | #include "Hash.h"
|
| 14 | 14 | #include "linker/M32Alloc.h"
|
| 15 | +#include "linker/ProddableBlocks.h"
|
|
| 15 | 16 | |
| 16 | 17 | #if RTS_LINKER_USE_MMAP
|
| 17 | 18 | #include <sys/mman.h>
|
| ... | ... | @@ -175,14 +176,6 @@ struct _Section { |
| 175 | 176 | struct SectionFormatInfo* info;
|
| 176 | 177 | };
|
| 177 | 178 | |
| 178 | -typedef
|
|
| 179 | - struct _ProddableBlock {
|
|
| 180 | - void* start;
|
|
| 181 | - int size;
|
|
| 182 | - struct _ProddableBlock* next;
|
|
| 183 | - }
|
|
| 184 | - ProddableBlock;
|
|
| 185 | - |
|
| 186 | 179 | typedef struct _Segment {
|
| 187 | 180 | void *start; /* page aligned start address of a segment */
|
| 188 | 181 | size_t size; /* page rounded size of a segment */
|
| ... | ... | @@ -328,7 +321,7 @@ struct _ObjectCode { |
| 328 | 321 | /* SANITY CHECK ONLY: a list of the only memory regions which may
|
| 329 | 322 | safely be prodded during relocation. Any attempt to prod
|
| 330 | 323 | outside one of these is an error in the linker. */
|
| 331 | - ProddableBlock* proddables;
|
|
| 324 | + ProddableBlockSet proddables;
|
|
| 332 | 325 | |
| 333 | 326 | #if defined(NEED_SYMBOL_EXTRAS)
|
| 334 | 327 | SymbolExtra *symbol_extras;
|
| ... | ... | @@ -434,10 +427,6 @@ void exitLinker( void ); |
| 434 | 427 | void freeObjectCode (ObjectCode *oc);
|
| 435 | 428 | SymbolAddr* loadSymbol(SymbolName *lbl, RtsSymbolInfo *pinfo);
|
| 436 | 429 | |
| 437 | -void addProddableBlock ( ObjectCode* oc, void* start, int size );
|
|
| 438 | -void checkProddableBlock (ObjectCode *oc, void *addr, size_t size );
|
|
| 439 | -void freeProddableBlocks (ObjectCode *oc);
|
|
| 440 | - |
|
| 441 | 430 | void addSection (Section *s, SectionKind kind, SectionAlloc alloc,
|
| 442 | 431 | void* start, StgWord size, StgWord mapped_offset,
|
| 443 | 432 | void* mapped_start, StgWord mapped_size);
|
| ... | ... | @@ -924,7 +924,7 @@ ocGetNames_ELF ( ObjectCode* oc ) |
| 924 | 924 | oc->sections[i].info->stubs = NULL;
|
| 925 | 925 | #endif
|
| 926 | 926 | |
| 927 | - addProddableBlock(oc, start, size);
|
|
| 927 | + addProddableBlock(&oc->proddables, start, size);
|
|
| 928 | 928 | } else {
|
| 929 | 929 | addSection(&oc->sections[i], kind, alloc, oc->image+offset, size,
|
| 930 | 930 | 0, 0, 0);
|
| ... | ... | @@ -1272,7 +1272,7 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC, |
| 1272 | 1272 | debugBelch("Reloc: P = %p S = %p A = %p type=%d\n",
|
| 1273 | 1273 | (void*)P, (void*)S, (void*)A, reloc_type ));
|
| 1274 | 1274 | #if defined(DEBUG)
|
| 1275 | - checkProddableBlock ( oc, pP, sizeof(Elf_Word) );
|
|
| 1275 | + checkProddableBlock ( &oc->proddables, pP, sizeof(Elf_Word) );
|
|
| 1276 | 1276 | #else
|
| 1277 | 1277 | (void) pP; /* suppress unused varialbe warning in non-debug build */
|
| 1278 | 1278 | #endif
|
| ... | ... | @@ -1684,7 +1684,7 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, |
| 1684 | 1684 | #if defined(DEBUG)
|
| 1685 | 1685 | IF_DEBUG(linker_verbose,debugBelch("Reloc: P = %p S = %p A = %p\n",
|
| 1686 | 1686 | (void*)P, (void*)S, (void*)A ));
|
| 1687 | - checkProddableBlock(oc, (void*)P, sizeof(Elf_Word));
|
|
| 1687 | + checkProddableBlock(&oc->proddables, (void*)P, sizeof(Elf_Word));
|
|
| 1688 | 1688 | #endif
|
| 1689 | 1689 | |
| 1690 | 1690 | #if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
|
| ... | ... | @@ -253,7 +253,7 @@ resolveImports( |
| 253 | 253 | return 0;
|
| 254 | 254 | }
|
| 255 | 255 | |
| 256 | - checkProddableBlock(oc,
|
|
| 256 | + checkProddableBlock(&oc->proddables,
|
|
| 257 | 257 | ((void**)(oc->image + sect->offset)) + i,
|
| 258 | 258 | sizeof(void *));
|
| 259 | 259 | ((void**)(oc->image + sect->offset))[i] = addr;
|
| ... | ... | @@ -287,7 +287,7 @@ decodeAddend(ObjectCode * oc, Section * section, MachORelocationInfo * ri) { |
| 287 | 287 | /* the instruction. It is 32bit wide */
|
| 288 | 288 | uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
|
| 289 | 289 | |
| 290 | - checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
|
|
| 290 | + checkProddableBlock(&oc->proddables, (void*)p, 1 << ri->r_length);
|
|
| 291 | 291 | |
| 292 | 292 | switch(ri->r_type) {
|
| 293 | 293 | case ARM64_RELOC_UNSIGNED: {
|
| ... | ... | @@ -364,7 +364,7 @@ encodeAddend(ObjectCode * oc, Section * section, |
| 364 | 364 | MachORelocationInfo * ri, int64_t addend) {
|
| 365 | 365 | uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
|
| 366 | 366 | |
| 367 | - checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
|
|
| 367 | + checkProddableBlock(&oc->proddables, (void*)p, 1 << ri->r_length);
|
|
| 368 | 368 | |
| 369 | 369 | switch (ri->r_type) {
|
| 370 | 370 | case ARM64_RELOC_UNSIGNED: {
|
| ... | ... | @@ -788,7 +788,7 @@ relocateSection(ObjectCode* oc, int curSection) |
| 788 | 788 | default:
|
| 789 | 789 | barf("Unknown size.");
|
| 790 | 790 | }
|
| 791 | - checkProddableBlock(oc,thingPtr,relocLenBytes);
|
|
| 791 | + checkProddableBlock(&oc->proddables,thingPtr,relocLenBytes);
|
|
| 792 | 792 | |
| 793 | 793 | /*
|
| 794 | 794 | * With SIGNED_N the relocation is not at the end of the
|
| ... | ... | @@ -1034,9 +1034,9 @@ relocateSection(ObjectCode* oc, int curSection) |
| 1034 | 1034 | */
|
| 1035 | 1035 | if (0 == reloc->r_extern) {
|
| 1036 | 1036 | if (reloc->r_pcrel) {
|
| 1037 | - checkProddableBlock(oc, (void *)((char *)thing + baseValue), 1);
|
|
| 1037 | + checkProddableBlock(&oc->proddables, (void *)((char *)thing + baseValue), 1);
|
|
| 1038 | 1038 | } else {
|
| 1039 | - checkProddableBlock(oc, (void *)thing, 1);
|
|
| 1039 | + checkProddableBlock(&oc->proddables, (void *)thing, 1);
|
|
| 1040 | 1040 | }
|
| 1041 | 1041 | }
|
| 1042 | 1042 | |
| ... | ... | @@ -1343,7 +1343,7 @@ ocGetNames_MachO(ObjectCode* oc) |
| 1343 | 1343 | secArray[sec_idx].info->stub_size = 0;
|
| 1344 | 1344 | secArray[sec_idx].info->stubs = NULL;
|
| 1345 | 1345 | #endif
|
| 1346 | - addProddableBlock(oc, start, section->size);
|
|
| 1346 | + addProddableBlock(&oc->proddables, start, section->size);
|
|
| 1347 | 1347 | }
|
| 1348 | 1348 | |
| 1349 | 1349 | curMem = (char*) secMem + section->size;
|
| ... | ... | @@ -1658,7 +1658,7 @@ ocGetNames_PEi386 ( ObjectCode* oc ) |
| 1658 | 1658 | }
|
| 1659 | 1659 | |
| 1660 | 1660 | addSection(section, kind, SECTION_NOMEM, start, sz, 0, 0, 0);
|
| 1661 | - addProddableBlock(oc, oc->sections[i].start, sz);
|
|
| 1661 | + addProddableBlock(&oc->proddables, oc->sections[i].start, sz);
|
|
| 1662 | 1662 | }
|
| 1663 | 1663 | |
| 1664 | 1664 | /* Copy exported symbols into the ObjectCode. */
|
| ... | ... | @@ -1690,7 +1690,7 @@ ocGetNames_PEi386 ( ObjectCode* oc ) |
| 1690 | 1690 | SECTIONKIND_RWDATA, SECTION_MALLOC,
|
| 1691 | 1691 | bss, globalBssSize, 0, 0, 0);
|
| 1692 | 1692 | IF_DEBUG(linker_verbose, debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize));
|
| 1693 | - addProddableBlock(oc, bss, globalBssSize);
|
|
| 1693 | + addProddableBlock(&oc->proddables, bss, globalBssSize);
|
|
| 1694 | 1694 | } else {
|
| 1695 | 1695 | addSection(&oc->sections[oc->n_sections-1],
|
| 1696 | 1696 | SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0);
|
| ... | ... | @@ -2067,13 +2067,13 @@ ocResolve_PEi386 ( ObjectCode* oc ) |
| 2067 | 2067 | IF_DEBUG(linker_verbose, debugBelch("S=%zx\n", S));
|
| 2068 | 2068 | |
| 2069 | 2069 | /* All supported relocations write at least 4 bytes */
|
| 2070 | - checkProddableBlock(oc, pP, 4);
|
|
| 2070 | + checkProddableBlock(&oc->proddables, pP, 4);
|
|
| 2071 | 2071 | switch (reloc->Type) {
|
| 2072 | 2072 | #if defined(x86_64_HOST_ARCH)
|
| 2073 | 2073 | case 1: /* R_X86_64_64 (ELF constant 1) - IMAGE_REL_AMD64_ADDR64 (PE constant 1) */
|
| 2074 | 2074 | {
|
| 2075 | 2075 | uint64_t A;
|
| 2076 | - checkProddableBlock(oc, pP, 8);
|
|
| 2076 | + checkProddableBlock(&oc->proddables, pP, 8);
|
|
| 2077 | 2077 | A = *(uint64_t*)pP;
|
| 2078 | 2078 | *(uint64_t *)pP = S + A;
|
| 2079 | 2079 | break;
|
| ... | ... | @@ -2114,7 +2114,7 @@ ocResolve_PEi386 ( ObjectCode* oc ) |
| 2114 | 2114 | {
|
| 2115 | 2115 | /* mingw will emit this for a pc-rel 64 relocation */
|
| 2116 | 2116 | uint64_t A;
|
| 2117 | - checkProddableBlock(oc, pP, 8);
|
|
| 2117 | + checkProddableBlock(&oc->proddables, pP, 8);
|
|
| 2118 | 2118 | A = *(uint64_t*)pP;
|
| 2119 | 2119 | *(uint64_t *)pP = S + A - (intptr_t)pP;
|
| 2120 | 2120 | break;
|
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 2025
|
|
| 4 | + *
|
|
| 5 | + * RTS Object Linker
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | + |
|
| 10 | +/*
|
|
| 11 | + * Sanity checking. For each ObjectCode, maintain a list of address ranges
|
|
| 12 | + * which may be prodded during relocation, and abort if we try and write
|
|
| 13 | + * outside any of these.
|
|
| 14 | + */
|
|
| 15 | + |
|
| 16 | +#include "Rts.h"
|
|
| 17 | +#include "RtsUtils.h"
|
|
| 18 | +#include "linker/ProddableBlocks.h"
|
|
| 19 | + |
|
| 20 | +#include <stdlib.h>
|
|
| 21 | +#include <string.h>
|
|
| 22 | + |
|
| 23 | +typedef struct _ProddableBlock {
|
|
| 24 | + uintptr_t start; // inclusive
|
|
| 25 | + uintptr_t end; // inclusive
|
|
| 26 | +} ProddableBlock;
|
|
| 27 | + |
|
| 28 | +void
|
|
| 29 | +initProddableBlockSet ( ProddableBlockSet* set )
|
|
| 30 | +{
|
|
| 31 | + set->data = NULL;
|
|
| 32 | + set->capacity = 0;
|
|
| 33 | + set->size = 0;
|
|
| 34 | +}
|
|
| 35 | + |
|
| 36 | +void freeProddableBlocks (ProddableBlockSet *set)
|
|
| 37 | +{
|
|
| 38 | + stgFree(set->data);
|
|
| 39 | + set->data = NULL;
|
|
| 40 | + set->size = 0;
|
|
| 41 | + set->capacity = 0;
|
|
| 42 | +}
|
|
| 43 | + |
|
| 44 | +// Binary search for the first interval with start >= value. Returns index or
|
|
| 45 | +// size if none.
|
|
| 46 | +static size_t
|
|
| 47 | +findLower(const ProddableBlockSet *set, uintptr_t value)
|
|
| 48 | +{
|
|
| 49 | + size_t l = 0;
|
|
| 50 | + size_t r = set->size;
|
|
| 51 | + while (l < r) {
|
|
| 52 | + size_t mid = l + (r - l) / 2;
|
|
| 53 | + if (set->data[mid].start < value) {
|
|
| 54 | + l = mid + 1;
|
|
| 55 | + } else {
|
|
| 56 | + r = mid;
|
|
| 57 | + }
|
|
| 58 | + }
|
|
| 59 | + return l;
|
|
| 60 | +}
|
|
| 61 | + |
|
| 62 | +// Check whether a given value is a member of the set.
|
|
| 63 | +bool
|
|
| 64 | +containsSpan ( const ProddableBlockSet *set, uintptr_t start, uintptr_t end )
|
|
| 65 | +{
|
|
| 66 | + size_t i = findLower(set, start+1);
|
|
| 67 | + return i > 0
|
|
| 68 | + && set->data[i-1].start <= start
|
|
| 69 | + && end <= set->data[i-1].end;
|
|
| 70 | +}
|
|
| 71 | + |
|
| 72 | +void
|
|
| 73 | +checkProddableBlock (const ProddableBlockSet *set, void *addr, size_t size )
|
|
| 74 | +{
|
|
| 75 | + if (! containsSpan(set, (uintptr_t) addr, (uintptr_t) addr+size)) {
|
|
| 76 | + barf("checkProddableBlock: invalid fixup in runtime linker: %p", addr);
|
|
| 77 | + }
|
|
| 78 | +}
|
|
| 79 | + |
|
| 80 | +// Ensure capacity for at least new_capacity intervals
|
|
| 81 | +static void
|
|
| 82 | +ensureCapacity(ProddableBlockSet *set, size_t new_capacity) {
|
|
| 83 | + if (new_capacity > set->capacity) {
|
|
| 84 | + size_t cap = set->capacity ? set->capacity * 2 : 4;
|
|
| 85 | + if (cap < new_capacity) {
|
|
| 86 | + cap = new_capacity;
|
|
| 87 | + }
|
|
| 88 | + ProddableBlock *tmp = stgReallocBytes(set->data, cap * sizeof(ProddableBlock), "addProddableBlock");
|
|
| 89 | + set->data = tmp;
|
|
| 90 | + set->capacity = cap;
|
|
| 91 | + }
|
|
| 92 | +}
|
|
| 93 | + |
|
| 94 | +void
|
|
| 95 | +addProddableBlock ( ProddableBlockSet* set, void* start_ptr, size_t size )
|
|
| 96 | +{
|
|
| 97 | + const uintptr_t start = (uintptr_t) start_ptr;
|
|
| 98 | + const uintptr_t end = (uintptr_t) start + size;
|
|
| 99 | + size_t i = findLower(set, start);
|
|
| 100 | + |
|
| 101 | + // check previous interval if it is overlapping or adjacent
|
|
| 102 | + if (i > 0 && start <= set->data[i-1].end + 1) {
|
|
| 103 | + // merge with left interval
|
|
| 104 | + i--;
|
|
| 105 | + if (end > set->data[i].end) {
|
|
| 106 | + set->data[i].end = end;
|
|
| 107 | + }
|
|
| 108 | + } else {
|
|
| 109 | + // insert new interval
|
|
| 110 | + ensureCapacity(set, set->size + 1);
|
|
| 111 | + memmove(&set->data[i+1], &set->data[i], sizeof(ProddableBlock) * (set->size - i));
|
|
| 112 | + set->data[i].start = start;
|
|
| 113 | + set->data[i].end = end;
|
|
| 114 | + set->size++;
|
|
| 115 | + }
|
|
| 116 | + |
|
| 117 | + // coalesce overlaps on right
|
|
| 118 | + size_t j = i;
|
|
| 119 | + while (j < set->size && set->data[j].start <= set->data[i].end + 1) {
|
|
| 120 | + set->data[i].end = set->data[j].end;
|
|
| 121 | + j++;
|
|
| 122 | + }
|
|
| 123 | + |
|
| 124 | + if (j != i) {
|
|
| 125 | + memmove(&set->data[i+1], &set->data[j], sizeof(ProddableBlock) * (set->size - j));
|
|
| 126 | + set->size -= j - i - 1;
|
|
| 127 | + }
|
|
| 128 | +}
|
|
| 129 | + |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 2025
|
|
| 4 | + *
|
|
| 5 | + * RTS Object Linker
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | +#pragma once
|
|
| 10 | + |
|
| 11 | +#include <stdbool.h>
|
|
| 12 | +#include <stddef.h>
|
|
| 13 | +#include <stdint.h>
|
|
| 14 | + |
|
| 15 | +// An interval set on uintptr_t.
|
|
| 16 | +struct _ProddableBlock;
|
|
| 17 | + |
|
| 18 | +typedef struct {
|
|
| 19 | + size_t size;
|
|
| 20 | + size_t capacity;
|
|
| 21 | + // sorted list of disjoint (start,end) pairs
|
|
| 22 | + struct _ProddableBlock *data;
|
|
| 23 | +} ProddableBlockSet;
|
|
| 24 | + |
|
| 25 | +void initProddableBlockSet ( ProddableBlockSet* set );
|
|
| 26 | + |
|
| 27 | +// Insert an interval.
|
|
| 28 | +void addProddableBlock ( ProddableBlockSet* set, void* start, size_t size );
|
|
| 29 | + |
|
| 30 | +// Check that an address belongs to the set.
|
|
| 31 | +void checkProddableBlock (const ProddableBlockSet *set, void *addr, size_t size );
|
|
| 32 | + |
|
| 33 | + |
|
| 34 | +// Free a set.
|
|
| 35 | +void freeProddableBlocks (ProddableBlockSet *set);
|
|
| 36 | + |
|
| 37 | +// For testing.
|
|
| 38 | +bool containsSpan ( const ProddableBlockSet *set, uintptr_t start, uintptr_t end ); |
| ... | ... | @@ -491,6 +491,7 @@ library |
| 491 | 491 | linker/MachO.c
|
| 492 | 492 | linker/macho/plt.c
|
| 493 | 493 | linker/macho/plt_aarch64.c
|
| 494 | + linker/ProddableBlocks.c
|
|
| 494 | 495 | linker/PEi386.c
|
| 495 | 496 | linker/SymbolExtras.c
|
| 496 | 497 | linker/elf_got.c
|