summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
Diffstat (limited to 'rts')
-rw-r--r--rts/linker/MachO.c748
1 files changed, 721 insertions, 27 deletions
diff --git a/rts/linker/MachO.c b/rts/linker/MachO.c
index b0c4b1e50c..9fc3c5b8e9 100644
--- a/rts/linker/MachO.c
+++ b/rts/linker/MachO.c
@@ -2,6 +2,14 @@
#if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
+#if defined(ios_HOST_OS)
+#if !RTS_LINKER_USE_MMAP
+#error "ios must use mmap and mprotect!"
+#endif
+/* for roundUpToPage */
+#include "sm/OSMem.h"
+#endif
+
#include "RtsUtils.h"
#include "GetEnv.h"
#include "LinkerInternals.h"
@@ -29,6 +37,10 @@
# include <mach-o/x86_64/reloc.h>
#endif
+#if defined(aarch64_HOST_ARCH)
+# include <mach-o/arm64/reloc.h>
+#endif
+
/*
Support for MachO linking on Darwin/MacOS X
by Wolfgang Thaller (wolfgang.thaller@gmx.net)
@@ -38,6 +50,47 @@
*) implement ocVerifyImage_MachO
*) add still more sanity checks.
*/
+#if defined(aarch64_HOST_ARCH)
+/* aarch64 linker by moritz angermann <moritz@lichtzwerge.de> */
+
+/* often times we need to extend some value of certain number of bits
+ * int an int64_t for e.g. relative offsets.
+ */
+int64_t sign_extend(uint64_t val, uint8_t bits);
+/* Helper functions to check some instruction properties */
+bool is_vector_op(uint32_t *p);
+bool is_load_store(uint32_t *p);
+
+/* aarch64 relocations may contain an addend alreay in the position
+ * where we want to write the address offset to. Thus decoding as well
+ * as encoding is needed.
+ */
+bool fits_bits(size_t bits, int64_t value);
+int64_t decode_addend(ObjectCode * oc, Section * section,
+ MachORelocationInfo * ri);
+void encode_addend(ObjectCode * oc, Section * section,
+ MachORelocationInfo * ri, int64_t addend);
+
+/* finding and making stubs. We don't need to care about the symbol they
+ * represent. As long as two stubs point to the same address, they are identical
+ */
+bool find_stub(Section * section, void ** addr);
+bool make_stub(Section * section, void ** addr);
+void free_stubs(Section * section);
+
+/* Global Offset Table logic */
+bool is_got_load(MachORelocationInfo * ri);
+bool need_got_slot(MachONList * symbol);
+bool make_got(ObjectCode * oc);
+void free_got(ObjectCode * oc);
+#endif /* aarch64_HOST_ARCH */
+
+#if defined(ios_HOST_OS)
+/* on iOS we need to ensure we only have r+w or r+x pages hence we need to mmap
+ * pages r+w and r+x mprotect them later on.
+ */
+bool ocMprotect_MachO( ObjectCode *oc );
+#endif /* ios_HOST_OS */
/*
* Initialize some common data in the object code so we don't have to
@@ -108,6 +161,12 @@ ocDeinit_MachO(ObjectCode * oc) {
if(oc->info->n_macho_symbols > 0) {
stgFree(oc->info->macho_symbols);
}
+#if defined(aarch64_HOST_ARCH)
+ free_got(oc);
+ for(int i = 0; i < oc->n_sections; i++) {
+ free_stubs(&oc->sections[i]);
+ }
+#endif
stgFree(oc->info);
}
@@ -158,7 +217,7 @@ ocAllocateSymbolExtras_MachO(ObjectCode* oc)
return ocAllocateSymbolExtras(oc,0,0);
}
-#elif defined(x86_64_HOST_ARCH)
+#elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
int
ocAllocateSymbolExtras_MachO(ObjectCode* oc)
@@ -282,12 +341,426 @@ resolveImports(
return 1;
}
+#if defined(aarch64_HOST_ARCH)
+/* aarch64 linker by moritz angermann <moritz@lichtzwerge.de> */
+
+int64_t
+sign_extend(uint64_t val, uint8_t bits) {
+ return (int64_t)(val << (64-bits)) >> (64-bits);
+}
+
+bool
+is_vector_op(uint32_t *p) {
+ return (*p & 0x04800000) == 0x04800000;
+}
+
+bool
+is_load_store(uint32_t *p) {
+ return (*p & 0x3B000000) == 0x39000000;
+}
+
+int64_t
+decode_addend(ObjectCode * oc, Section * section, MachORelocationInfo * ri) {
+
+ /* the instruction. It is 32bit wide */
+ uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
+
+ checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
+
+ switch(ri->r_type) {
+ case ARM64_RELOC_UNSIGNED:
+ case ARM64_RELOC_SUBTRACTOR: {
+ switch (ri->r_length) {
+ case 0: return sign_extend(*(uint8_t*)p, 8 * (1 << ri->r_length));
+ case 1: return sign_extend(*(uint16_t*)p, 8 * (1 << ri->r_length));
+ case 2: return sign_extend(*(uint32_t*)p, 8 * (1 << ri->r_length));
+ case 3: return sign_extend(*(uint64_t*)p, 8 * (1 << ri->r_length));
+ default:
+ barf("Unsupported r_length (%d) for SUBTRACTOR relocation",
+ ri->r_length);
+ }
+ }
+ case ARM64_RELOC_BRANCH26:
+ /* take the lower 26 bits and shift them by 2. The last two are
+ * implicilty 0 (as the instructions must be aligned!) and sign
+ * extend to 64 bits.
+ */
+ return sign_extend( (*p & 0x03FFFFFF) << 2, 28 );
+ case ARM64_RELOC_PAGE21:
+ case ARM64_RELOC_GOT_LOAD_PAGE21:
+ /* take the instruction bits masked with 0x6 (0110), and push them
+ * down. into the last two bits, and mask in the
+ *
+ * the 21 bits are encoded as follows in the instruction
+ *
+ * -**- ---* **** **** **** **** ***-- ----
+ * ^^
+ * ''-- these are the low two bits.
+ */
+ return sign_extend( (*p & 0x60000000) >> 29
+ | ((*p & 0x01FFFFE0) >> 3) << 12, 33);
+ case ARM64_RELOC_PAGEOFF12:
+ case ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
+ /* the 12 bits for the page offset are encoded from bit 11 onwards
+ *
+ * ---- ---- --** **** **** **-- ---- ----
+ */
+ int64_t a = (*p & 0x003FFC00) >> 10;
+ int shift = 0;
+ if (is_load_store(p)) {
+ shift = (*p >> 30) & 0x3;
+ if(0 == shift && is_vector_op(p)) {
+ shift = 4;
+ }
+ }
+ return a << shift;
+ }
+ }
+ barf("unsupported relocation type: %d\n", ri->r_type);
+}
+
+inline bool
+fits_bits(size_t bits, int64_t value) {
+ if(bits == 64) return true;
+ if(bits > 64) barf("fits_bits with %d bits and an 64bit integer!", bits);
+ return 0 == (value >> bits) // All bits off: 0
+ || -1 == (value >> bits); // All bits on: -1
+}
+
+void
+encode_addend(ObjectCode * oc, Section * section,
+ MachORelocationInfo * ri, int64_t addend) {
+ uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
+
+ checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
+
+ switch (ri->r_type) {
+ case ARM64_RELOC_UNSIGNED:
+ case ARM64_RELOC_SUBTRACTOR: {
+ if(!fits_bits(8 << ri->r_length, addend))
+ barf("Relocation out of range for UNSIGNED/SUBTRACTOR");
+ switch (ri->r_length) {
+ case 0: *(uint8_t*)p = (uint8_t)addend; break;
+ case 1: *(uint16_t*)p = (uint16_t)addend; break;
+ case 2: *(uint32_t*)p = (uint32_t)addend; break;
+ case 3: *(uint64_t*)p = (uint64_t)addend; break;
+ default:
+ barf("Unsupported r_length (%d) for SUBTRACTOR relocation",
+ ri->r_length);
+ }
+ return;
+ }
+ case ARM64_RELOC_BRANCH26: {
+ /* We can only store 26 bits in the instruction, due to alignment we
+ * do not need the last two bits of the value. If the value >> 2
+ * still exceeds 26bits, we won't be able to reach it.
+ */
+ if(!fits_bits(26, addend >> 2))
+ barf("Relocation target for BRACH26 out of range.");
+ *p = (*p & 0xFC000000) | ((uint32_t)(addend >> 2) & 0x03FFFFFF);
+ return;
+ }
+ case ARM64_RELOC_PAGE21:
+ case ARM64_RELOC_GOT_LOAD_PAGE21: {
+ /* We store 21bits, in bits 6 to 24, and bits 30 and 31.
+ * The encoded value describes a multiple of 4k pages, and together
+ * with the PAGEOFF12 relocation allows to address a relative range
+ * of +-4GB.
+ */
+ if(!fits_bits(21, addend >> 12))
+ barf("Relocation target for PAGE21 out of range.");
+ *p = (*p & 0x9F00001F) | (uint32_t)((addend << 17) & 0x60000000)
+ | (uint32_t)((addend >> 9) & 0x00FFFFE0);
+ return;
+ }
+ case ARM64_RELOC_PAGEOFF12:
+ case ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
+ /* Store an offset into a page (4k). Depending on the instruction
+ * the bits are stored at slightly different positions.
+ */
+ if(!fits_bits(12, addend))
+ barf("Relocation target for PAGEOFF12 out or range.");
+
+ int shift = 0;
+ if(is_load_store(p)) {
+ shift = (*p >> 30) & 0x3;
+ if(0 == shift && is_vector_op(p)) {
+ shift = 4;
+ }
+ }
+ *p = (*p & 0xFFC003FF)
+ | ((uint32_t)(addend >> shift << 10) & 0x003FFC00);
+ return;
+ }
+ }
+ barf("unsupported relocation type: %d\n", ri->r_type);
+}
+
+bool
+is_got_load(struct relocation_info * ri) {
+ return ri->r_type == ARM64_RELOC_GOT_LOAD_PAGE21
+ || ri->r_type == ARM64_RELOC_GOT_LOAD_PAGEOFF12;
+}
+
+/* This is very similar to makeSymbolExtra
+ * However, as we load sections into different
+ * pages, that may be further appart than
+ * branching allows, we'll use some extra
+ * space at the end of each section allocated
+ * for stubs.
+ */
+bool
+find_stub(Section * section, void ** addr) {
+
+ for(Stub * s = section->info->stubs; s != NULL; s = s->next) {
+ if(s->target == *addr) {
+ *addr = s->addr;
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
+
+bool
+make_stub(Section * section, void ** addr) {
+
+ Stub * s = stgCallocBytes(1, sizeof(Stub), "make_stub(Stub)");
+ s->target = *addr;
+ s->addr = (uint8_t*)section->info->stub_offset
+ + ((8+8)*section->info->nstubs) + 8;
+ s->next = NULL;
+
+ /* target address */
+ *(uint64_t*)((uint8_t*)s->addr - 8) = (uint64_t)s->target;
+ /* ldr x16, - (8 bytes) */
+ *(uint32_t*)(s->addr) = (uint32_t)0x58ffffd0;
+ /* br x16 */
+ *(uint32_t*)((uint8_t*)s->addr + 4) = (uint32_t)0xd61f0200;
+
+ if(section->info->nstubs == 0) {
+ /* no stubs yet, let's just create this one */
+ section->info->stubs = s;
+ } else {
+ Stub * tail = section->info->stubs;
+ while(tail->next != NULL) tail = tail->next;
+ tail->next = s;
+ }
+ section->info->nstubs += 1;
+ *addr = s->addr;
+ return EXIT_SUCCESS;
+}
+void
+free_stubs(Section * section) {
+ if(section->info->nstubs == 0)
+ return;
+ Stub * last = section->info->stubs;
+ while(last->next != NULL) {
+ Stub * t = last;
+ last = last->next;
+ stgFree(t);
+ }
+ section->info->stubs = NULL;
+ section->info->nstubs = 0;
+}
+
+/*
+ * Check if we need a global offset table slot for a
+ * given symbol
+ */
+bool
+need_got_slot(MachONList * symbol) {
+ return (symbol->n_type & N_EXT) /* is an external symbol */
+ && (N_UNDF == (symbol->n_type & N_TYPE) /* and is undefined */
+ || NO_SECT != symbol->n_sect); /* or is defined in a
+ * different section */
+}
+
+bool
+make_got(ObjectCode * oc) {
+ size_t got_slots = 0;
+
+ for(size_t i=0; i < oc->info->n_macho_symbols; i++)
+ if(need_got_slot(oc->info->macho_symbols[i].nlist))
+ got_slots += 1;
+
+ if(got_slots > 0) {
+ oc->info->got_size = got_slots * sizeof(void*);
+ oc->info->got_start = mmap(NULL, oc->info->got_size,
+ PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if( oc->info->got_start == MAP_FAILED ) {
+ barf("MAP_FAILED. errno=%d", errno );
+ return EXIT_FAILURE;
+ }
+ /* update got_addr */
+ size_t slot = 0;
+ for(size_t i=0; i < oc->info->n_macho_symbols; i++)
+ if(need_got_slot(oc->info->macho_symbols[i].nlist))
+ oc->info->macho_symbols[i].got_addr
+ = ((uint8_t*)oc->info->got_start)
+ + (slot++ * sizeof(void *));
+ }
+ return EXIT_SUCCESS;
+}
+
+void
+free_got(ObjectCode * oc) {
+ munmap(oc->info->got_start, oc->info->got_size);
+ oc->info->got_start = NULL;
+ oc->info->got_size = 0;
+}
+
+static int
+relocateSection_aarch64(ObjectCode * oc, Section * section)
+{
+ if(section->size == 0)
+ return 1;
+ /* at this point, we have:
+ *
+ * - loaded the sections (potentially into non-continuous memory),
+ * (in ocGetNames_MachO)
+ * - registered exported sybmols
+ * (in ocGetNames_MachO)
+ * - and fixed the nlist[i].n_value for common storage symbols (N_UNDF,
+ * N_EXT and n_value != 0) so that they point into the common storage.
+ * (in ocGetNames_MachO)
+ * - All oc->symbols however should now point at the right place.
+ */
+
+ /* we need to care about the explicity addend */
+ int64_t explicit_addend = 0;
+ size_t nreloc = section->info->macho_section->nreloc;
+
+ for(size_t i = 0; i < nreloc; i++) {
+ MachORelocationInfo * ri = &section->info->relocation_info[i];
+ switch (ri->r_type) {
+ case ARM64_RELOC_UNSIGNED: {
+ MachOSymbol* symbol = &oc->info->macho_symbols[ri->r_symbolnum];
+ int64_t addend = decode_addend(oc, section, ri);
+ uint64_t value = 0;
+ if(symbol->nlist->n_type & N_EXT) {
+ /* external symbols should be able to be
+ * looked up via the lookupSymbol_ function.
+ * Either through the global symbol hashmap
+ * or asking the system, if not found
+ * in the symbol hashmap
+ */
+ value = (uint64_t)lookupSymbol_((char*)symbol->name);
+ if(!value)
+ barf("Could not lookup symbol: %s!", symbol->name);
+ } else {
+ value = (uint64_t)symbol->addr; // address of the symbol.
+ }
+ encode_addend(oc, section, ri, value + addend);
+ break;
+ }
+ case ARM64_RELOC_SUBTRACTOR:
+ {
+ MachOSymbol* symbol = &oc->info->macho_symbols[ri->r_symbolnum];
+ // subtractor and unsigned are called in tandem:
+ // first pc <- pc - symbol address (SUBTRACTOR)
+ // second pc <- pc + symbol address (UNSIGNED)
+ // to achieve pc <- pc + target - base.
+ //
+ // the current implementation uses absolute addresses,
+ // which is simpler than trying to do this section
+ // relative, but could more easily lead to overflow.
+ //
+ if(!(i+1 < nreloc)
+ || !(section->info->relocation_info[i+1].r_type
+ == ARM64_RELOC_UNSIGNED))
+ barf("SUBTRACTOR relocation *must* be followed by UNSIGNED relocation.");
+
+ int64_t addend = decode_addend(oc, section, ri);
+ int64_t value = (uint64_t)symbol->addr;
+ encode_addend(oc, section, ri, addend - value);
+ break;
+ }
+ case ARM64_RELOC_BRANCH26: {
+ MachOSymbol* symbol = &oc->info->macho_symbols[ri->r_symbolnum];
+
+ // pre-existing addend
+ int64_t addend = decode_addend(oc, section, ri);
+ // address of the branch (b/bl) instruction.
+ uint64_t pc = (uint64_t)section->start + ri->r_address;
+ uint64_t value = 0;
+ if(symbol->nlist->n_type & N_EXT) {
+ value = (uint64_t)lookupSymbol_((char*)symbol->name);
+ if(!value)
+ barf("Could not lookup symbol: %s!", symbol->name);
+ } else {
+ value = (uint64_t)symbol->addr; // address of the symbol.
+ }
+ if((value - pc + addend) >> (2 + 26)) {
+ /* we need a stub */
+ /* check if we already have that stub */
+ if(find_stub(section, (void**)&value)) {
+ /* did not find it. Crete a new stub. */
+ if(make_stub(section, (void**)&value)) {
+ barf("could not find or make stub");
+ }
+ }
+ }
+ encode_addend(oc, section, ri, value - pc + addend);
+ break;
+ }
+ case ARM64_RELOC_PAGE21:
+ case ARM64_RELOC_GOT_LOAD_PAGE21: {
+ MachOSymbol* symbol = &oc->info->macho_symbols[ri->r_symbolnum];
+ int64_t addend = decode_addend(oc, section, ri);
+ if(!(explicit_addend == 0 || addend == 0))
+ barf("explicit_addend and addend can't be set at the same time.");
+ uint64_t pc = (uint64_t)section->start + ri->r_address;
+ uint64_t value = (uint64_t)(is_got_load(ri) ? symbol->got_addr : symbol->addr);
+ encode_addend(oc, section, ri, ((value + addend + explicit_addend) & (-4096)) - (pc & (-4096)));
+
+ // reset, just in case.
+ explicit_addend = 0;
+ break;
+ }
+ case ARM64_RELOC_PAGEOFF12:
+ case ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
+ MachOSymbol* symbol = &oc->info->macho_symbols[ri->r_symbolnum];
+ int64_t addend = decode_addend(oc, section, ri);
+ if(!(explicit_addend == 0 || addend == 0))
+ barf("explicit_addend and addend can't be set at the same time.");
+ uint64_t value = (uint64_t)(is_got_load(ri) ? symbol->got_addr : symbol->addr);
+ encode_addend(oc, section, ri, 0xFFF & (value + addend + explicit_addend));
+
+ // reset, just in case.
+ explicit_addend = 0;
+ break;
+ }
+ case ARM64_RELOC_ADDEND: {
+ explicit_addend = sign_extend(ri->r_symbolnum, 24);
+ if(!(i+1 < nreloc)
+ || !(section->info->relocation_info[i+1].r_type == ARM64_RELOC_PAGE21
+ || section->info->relocation_info[i+1].r_type == ARM64_RELOC_PAGEOFF12))
+ barf("ADDEND relocation *must* be followed by PAGE or PAGEOFF relocation");
+ break;
+ }
+ default: {
+ barf("Relocation of type: %d not (yet) supported!\n", ri->r_type);
+ }
+ }
+ }
+ return 1;
+}
+
+#else /* non aarch64_HOST_ARCH branch -- aarch64 doesn't use relocateAddress */
+
+/*
+ * Try to find the final loaded address for some addres.
+ * Look through all sections, locating the section that
+ * contains the address and compute the absolue address.
+ */
static unsigned long
relocateAddress(
- ObjectCode* oc,
- int nSections,
- MachOSection* sections,
- unsigned long address)
+ ObjectCode* oc,
+ int nSections,
+ MachOSection* sections,
+ unsigned long address)
{
int i;
IF_DEBUG(linker, debugBelch("relocateAddress: start\n"));
@@ -298,7 +771,7 @@ relocateAddress(
&& address < sections[i].addr + sections[i].size)
{
return (unsigned long)oc->image
- + sections[i].offset + address - sections[i].addr;
+ + sections[i].offset + address - sections[i].addr;
}
}
barf("Invalid Mach-O file:"
@@ -306,6 +779,9 @@ relocateAddress(
return 0;
}
+#endif /* aarch64_HOST_ARCH */
+
+#ifndef aarch64_HOST_ARCH
static int
relocateSection(
ObjectCode* oc,
@@ -867,6 +1343,26 @@ relocateSection(
IF_DEBUG(linker, debugBelch("relocateSection: done\n"));
return 1;
}
+#endif /* aarch64_HOST_ARCH */
+
+/* Note [mmap r+w+x]
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * iOS does not permit to mmap r+w+x, hence wo only mmap r+w, and later change
+ * to r+x via mprotect. While this could would be nice to have for all hosts
+ * and not just for iOS, it entail that the rest of the linker code supports
+ * that, this includes:
+ *
+ * - mmap and mprotect need to be available.
+ * - text and data sections need to be mapped into different pages. Ideally
+ * the text and data sections would be aggregated, to prevent using a single
+ * page for every section, however tiny.
+ * - the relocation code for each object file format / architecture, needs to
+ * respect the (now) non-contiguousness of the sections.
+ * - with sections being mapped potentially far apart from each other, it must
+ * be made sure that the pages are reachable within the architectures
+ * addressability for relative or absolute access.
+ */
int
ocGetNames_MachO(ObjectCode* oc)
@@ -900,20 +1396,7 @@ ocGetNames_MachO(ObjectCode* oc)
continue;
}
- if((oc->info->macho_sections[i].flags & SECTION_TYPE) == S_ZEROFILL) {
- char * zeroFillArea;
- if (RTS_LINKER_USE_MMAP) {
- zeroFillArea = mmapForLinker(oc->info->macho_sections[i].size, MAP_ANONYMOUS,
- -1, 0);
- if (zeroFillArea == NULL) return 0;
- memset(zeroFillArea, 0, oc->info->macho_sections[i].size);
- }
- else {
- zeroFillArea = stgCallocBytes(1,oc->info->macho_sections[i].size,
- "ocGetNames_MachO(common symbols)");
- }
- oc->info->macho_sections[i].offset = zeroFillArea - oc->image;
- }
+ // XXX, use SECTION_TYPE attributes, instead of relying on the name?
SectionKind kind = SECTIONKIND_OTHER;
@@ -928,14 +1411,146 @@ ocGetNames_MachO(ObjectCode* oc)
kind = SECTIONKIND_RWDATA;
}
- addSection(&secArray[i], kind, SECTION_NOMEM,
- (void *)(oc->image + oc->info->macho_sections[i].offset),
- oc->info->macho_sections[i].size,
- 0, 0, 0);
+ switch(section->flags & SECTION_TYPE) {
+#if defined(ios_HOST_OS)
+ case S_ZEROFILL:
+ case S_GB_ZEROFILL: {
+ // See Note [mmap r+w+x]
+ void * mem = mmap(NULL, section->size,
+ PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if( mem == MAP_FAILED ) {
+ barf("failed to mmap allocate memory for zerofill section %d of size %d. errno = %d", i, section->size, errno);
+ }
+ addSection(&secArray[i], kind, SECTION_MMAP, mem, section->size,
+ 0, mem, roundUpToPage(section->size));
+ addProddableBlock(oc, mem, (int)section->size);
+
+ secArray[i].info->nstubs = 0;
+ secArray[i].info->stub_offset = NULL;
+ secArray[i].info->stub_size = 0;
+ secArray[i].info->stubs = NULL;
+
+ secArray[i].info->macho_section = section;
+ secArray[i].info->relocation_info
+ = (MachORelocationInfo*)(oc->image + section->reloff);
+ break;
+ }
+ default: {
+ // The secion should have a non-zero offset. As the offset is
+ // relativ to the image, and must be somewhere after the header.
+ if(section->offset == 0) barf("section with zero offset!");
+ /* on iOS, we must allocate the code in r+x sections and
+ * the data in r+w sections, as the system does not allow
+ * for r+w+x, we must allocate each section in a new page
+ * range.
+ *
+ * copy the sections's memory to some page-aligned place via
+ * mmap and memcpy. This will later allow us to selectively
+ * use mprotect on pages with data (r+w) and pages text (r+x).
+ * We initially start with r+w, so that we can modify the
+ * pages during relocations, prior to setting it r+x.
+ */
+
+ /* We also need space for stubs. As pages can be assigned
+ * randomly in the addressable space, we need to keep the
+ * stubs close to the section. The strategy we are going
+ * to use is to allocate them right after the section. And
+ * we are going to be generous and allocare a stub slot
+ * for each relocation to keep it simple.
+ */
+ size_t n_ext_sec_sym = section->nreloc; /* number of relocations
+ * for this section. Should
+ * be a good upper bound
+ */
+ size_t stub_space = /* eight bytes for the 64 bit address,
+ * and another eight bytes for the two
+ * instructions (ldr, br) for each relocation.
+ */ 16 * n_ext_sec_sym;
+ // See Note [mmap r+w+x]
+ void * mem = mmap(NULL, section->size+stub_space,
+ PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if( mem == MAP_FAILED ) {
+ barf("failed to mmap allocate memory to load section %d. errno = %d", i, errno );
+ }
+ memcpy( mem, oc->image + section->offset, section->size);
+
+ addSection(&secArray[i], kind, SECTION_MMAP,
+ mem, section->size,
+ 0, mem, roundUpToPage(section->size+stub_space));
+ addProddableBlock(oc, mem, (int)section->size);
+
+ secArray[i].info->nstubs = 0;
+ secArray[i].info->stub_offset = ((uint8_t*)mem) + section->size;
+ secArray[i].info->stub_size = stub_space;
+ secArray[i].info->stubs = NULL;
+
+ secArray[i].info->macho_section = section;
+ secArray[i].info->relocation_info
+ = (MachORelocationInfo*)(oc->image + section->reloff);
+ break;
+ }
+
+#else /* any other host */
+ case S_ZEROFILL:
+ case S_GB_ZEROFILL: {
+ char * zeroFillArea;
+ if (RTS_LINKER_USE_MMAP) {
+ zeroFillArea = mmapForLinker(section->size, MAP_ANONYMOUS,
+ -1, 0);
+ if (zeroFillArea == NULL) return 0;
+ memset(zeroFillArea, 0, section->size);
+ }
+ else {
+ zeroFillArea = stgCallocBytes(1,section->size,
+ "ocGetNames_MachO(common symbols)");
+ }
+ section->offset = zeroFillArea - oc->image;
+
+ addSection(&secArray[i], kind, SECTION_NOMEM,
+ (void *)(oc->image + section->offset),
+ section->size,
+ 0, 0, 0);
+
+ addProddableBlock(oc,
+ (void *) (oc->image + section->offset),
+ section->size);
+
+ secArray[i].info->nstubs = 0;
+ secArray[i].info->stub_offset = NULL;
+ secArray[i].info->stub_size = 0;
+ secArray[i].info->stubs = NULL;
+
+ secArray[i].info->macho_section = section;
+ secArray[i].info->relocation_info
+ = (MachORelocationInfo*)(oc->image + section->reloff);
+ }
+ default: {
+ // just set the pointer to the loaded image.
+ addSection(&secArray[i], kind, SECTION_NOMEM,
+ (void *)(oc->image + section->offset),
+ section->size,
+ 0, 0, 0);
+
+ addProddableBlock(oc,
+ (void *) (oc->image + section->offset),
+ section->size);
+
+ secArray[i].info->nstubs = 0;
+ secArray[i].info->stub_offset = NULL;
+ secArray[i].info->stub_size = 0;
+ secArray[i].info->stubs = NULL;
+
+ secArray[i].info->macho_section = section;
+ secArray[i].info->relocation_info
+ = (MachORelocationInfo*)(oc->image + section->reloff);
+ }
+#endif
+ }
- addProddableBlock(oc,
- (void *) (oc->image + oc->info->macho_sections[i].offset),
- oc->info->macho_sections[i].size);
}
/* now, as all sections have been loaded, we can resolve the absolute
* address of symbols defined in those sections.
@@ -1063,11 +1678,51 @@ ocGetNames_MachO(ObjectCode* oc)
}
}
}
+#if defined(aarch64_HOST_ARCH)
+ /* Setup the global offset table
+ * This is for symbols that are external, and not defined here.
+ * So that we can load their address indirectly.
+ *
+ * We will get GOT request for any symbol that is
+ * - EXT and UNDF
+ * - EXT and not in the same section.
+ *
+ * As sections are not necessarily continuous and can live
+ * anywhere in the addressable space. This obviously makes
+ * sense. However it took me a while to figure this out.
+ */
+ make_got(oc);
+ /* at this point, macho_symbols, should know the addresses for
+ * all symbols defined by this object code.
+ * - those that are defined in sections.
+ * - those that are undefined, but have a value (common storage).
+ */
+#endif
IF_DEBUG(linker, debugBelch("ocGetNames_MachO: done\n"));
return 1;
}
+#if defined(ios_HOST_OS)
+bool
+ocMprotect_MachO( ObjectCode *oc ) {
+ for(int i=0; i < oc->n_sections; i++) {
+ Section * section = &oc->sections[i];
+ if(section->size == 0) continue;
+ if( (section->info->macho_section->flags & SECTION_ATTRIBUTES_USR)
+ == S_ATTR_PURE_INSTRUCTIONS) {
+ if( 0 != mprotect(section->start,
+ section->size + section->info->stub_size,
+ PROT_READ | PROT_EXEC) ) {
+ barf("mprotect failed! errno = %d", errno);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+#endif
+
int
ocResolve_MachO(ObjectCode* oc)
{
@@ -1109,16 +1764,55 @@ ocResolve_MachO(ObjectCode* oc)
}
}
}
+#if defined(aarch64_HOST_ARCH)
+ /* fill the GOT table */
+ for(size_t i = 0; i < oc->info->n_macho_symbols; i++) {
+ MachOSymbol * symbol = &oc->info->macho_symbols[i];
+ if(need_got_slot(symbol->nlist)) {
+ if(N_UNDF == (symbol->nlist->n_type & N_TYPE)) {
+ /* an undefined symbol. So we need to ensure we
+ * have the address.
+ */
+ if(NULL == symbol->addr) {
+ symbol->addr = lookupSymbol_((char*)symbol->name);
+ if(NULL == symbol->addr)
+ barf("Failed to lookup symbol: %s", symbol->name);
+ } else {
+ // we already have the address.
+ }
+ } /* else it was defined in the same object,
+ * just a different section. We should have
+ * the address as well already
+ */
+ if(NULL == symbol->addr) {
+ barf("Something went wrong!");
+ }
+ if(NULL == symbol->got_addr) {
+ barf("Not good either!");
+ }
+ *(uint64_t*)symbol->got_addr = (uint64_t)symbol->addr;
+ }
+ }
+#endif
for(int i = 0; i < oc->n_sections; i++)
{
IF_DEBUG(linker, debugBelch("ocResolve_MachO: relocating section %d\n", i));
+#if defined aarch64_HOST_ARCH
+ if (!relocateSection_aarch64(oc, &oc->sections[i]))
+ return 0;
+#else
if (!relocateSection(oc,oc->image,oc->info->symCmd,oc->info->nlist,
oc->info->segCmd->nsects,oc->info->macho_sections,
&oc->info->macho_sections[i]))
return 0;
+#endif
}
+#if defined(ios_HOST_OS)
+ if(!ocMprotect_MachO ( oc ))
+ return 0;
+#endif
#if defined (powerpc_HOST_ARCH)
ocFlushInstructionCache( oc );