summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoritz Angermann <moritz.angermann@gmail.com>2021-02-13 10:55:58 +0800
committerMoritz Angermann <moritz.angermann@gmail.com>2021-02-16 12:36:08 +0800
commiteb7b1c2984f517bdc49c2c1985b466502f9acc6e (patch)
tree9c5c7b0b3a7287a429efe431d768f2c800571f71
parentec0f15837f08e6d71abf160a76f125bc8c77477b (diff)
downloadhaskell-wip/angerman/macho-linker-improvements.tar.gz
[macho] improved linker with proper plt supportwip/angerman/macho-linker-improvements
This is a pre-requisite for making aarch64-darwin work.
-rw-r--r--rts/ghc.mk11
-rw-r--r--rts/linker/MachO.c177
-rw-r--r--rts/linker/MachOTypes.h5
-rw-r--r--rts/linker/macho/plt.c93
-rw-r--r--rts/linker/macho/plt.h35
-rw-r--r--rts/linker/macho/plt_aarch64.c62
-rw-r--r--rts/linker/macho/plt_aarch64.h23
-rw-r--r--rts/rts.cabal.in2
8 files changed, 311 insertions, 97 deletions
diff --git a/rts/ghc.mk b/rts/ghc.mk
index 451b1912cb..ada9055ebd 100644
--- a/rts/ghc.mk
+++ b/rts/ghc.mk
@@ -37,7 +37,7 @@ $(eval $(call all-target,rts,$(ALL_RTS_LIBS)))
# -----------------------------------------------------------------------------
# Defining the sources
-ALL_DIRS = hooks sm eventlog linker
+ALL_DIRS = hooks sm eventlog linker linker/macho
ifeq "$(TargetOS_CPP)" "mingw32"
ALL_DIRS += win32
@@ -329,8 +329,8 @@ $(eval $(call distdir-opts,rts,dist,1))
# We like plenty of warnings.
WARNING_OPTS += -Wall
WARNING_OPTS += -Wextra
-WARNING_OPTS += -Wstrict-prototypes
-WARNING_OPTS += -Wmissing-prototypes
+WARNING_OPTS += -Wstrict-prototypes
+WARNING_OPTS += -Wmissing-prototypes
WARNING_OPTS += -Wmissing-declarations
WARNING_OPTS += -Winline
WARNING_OPTS += -Wpointer-arith
@@ -346,7 +346,7 @@ WARNING_OPTS += -Wno-aggregate-return
#WARNING_OPTS += -Wshadow
#WARNING_OPTS += -Wcast-qual
-# This one seems buggy on GCC 4.1.2, which is the only GCC version we
+# This one seems buggy on GCC 4.1.2, which is the only GCC version we
# have that can bootstrap the SPARC build. We end up with lots of supurious
# warnings of the form "cast increases required alignment of target type".
# Some legitimate warnings can be fixed by adding an intermediate cast to
@@ -383,7 +383,7 @@ rts_CC_OPTS += -DUSE_LIBFFI_FOR_ADJUSTORS
endif
# We *want* type-checking of hand-written cmm.
-rts_HC_OPTS += -dcmm-lint
+rts_HC_OPTS += -dcmm-lint
# -fno-strict-aliasing is required for the runtime, because we often
# use a variety of types to represent closure pointers (StgPtr,
@@ -658,4 +658,3 @@ install_libffi_headers :
$(eval $(call clean-target,rts,dist,rts/dist))
BINDIST_EXTRAS += rts/package.conf.in
-
diff --git a/rts/linker/MachO.c b/rts/linker/MachO.c
index 2d069a37c7..8928da6dee 100644
--- a/rts/linker/MachO.c
+++ b/rts/linker/MachO.c
@@ -42,6 +42,9 @@
*) add still more sanity checks.
*/
#if defined(aarch64_HOST_ARCH)
+# define NEED_PLT
+# include "macho/plt.h"
+
/* aarch64 linker by moritz angermann <moritz@lichtzwerge.de> */
/* often times we need to extend some value of certain number of bits
@@ -49,7 +52,7 @@
*/
int64_t signExtend(uint64_t val, uint8_t bits);
/* Helper functions to check some instruction properties */
-bool isVectorPp(uint32_t *p);
+bool isVectorOp(uint32_t *p);
bool isLoadStore(uint32_t *p);
/* aarch64 relocations may contain an addend alreay in the position
@@ -62,13 +65,6 @@ int64_t decodeAddend(ObjectCode * oc, Section * section,
void encodeAddend(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 findStub(Section * section, void ** addr);
-bool makeStub(Section * section, void ** addr);
-void freeStubs(Section * section);
-
/* Global Offset Table logic */
bool isGotLoad(MachORelocationInfo * ri);
bool needGotSlot(MachONList * symbol);
@@ -154,8 +150,10 @@ ocDeinit_MachO(ObjectCode * oc) {
}
#if defined(aarch64_HOST_ARCH)
freeGot(oc);
- for(int i = 0; i < oc->n_sections; i++) {
- freeStubs(&oc->sections[i]);
+ if(oc->sections != NULL) {
+ for(int i = 0; i < oc->n_sections; i++) {
+ freeStubs(&oc->sections[i]);
+ }
}
#endif
stgFree(oc->info);
@@ -272,12 +270,12 @@ signExtend(uint64_t val, uint8_t bits) {
return (int64_t)(val << (64-bits)) >> (64-bits);
}
-bool
+static bool
isVectorOp(uint32_t *p) {
return (*p & 0x04800000) == 0x04800000;
}
-bool
+static bool
isLoadStore(uint32_t *p) {
return (*p & 0x3B000000) == 0x39000000;
}
@@ -345,7 +343,7 @@ decodeAddend(ObjectCode * oc, Section * section, MachORelocationInfo * ri) {
inline bool
fitsBits(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);
+ if(bits > 64) barf("fits_bits with %zu bits and an 64bit integer!", bits);
return 0 == (value >> bits) // All bits off: 0
|| -1 == (value >> bits); // All bits on: -1
}
@@ -425,67 +423,6 @@ isGotLoad(struct relocation_info * ri) {
|| 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
-findStub(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
-makeStub(Section * section, void ** addr) {
-
- Stub * s = stgCallocBytes(1, sizeof(Stub), "makeStub(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
-freeStubs(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
@@ -618,9 +555,9 @@ relocateSectionAarch64(ObjectCode * oc, Section * section)
if((value - pc + addend) >> (2 + 26)) {
/* we need a stub */
/* check if we already have that stub */
- if(findStub(section, (void**)&value)) {
+ if(findStub(section, (void**)&value, 0)) {
/* did not find it. Crete a new stub. */
- if(makeStub(section, (void**)&value)) {
+ if(makeStub(section, (void**)&value, 0)) {
barf("could not find or make stub");
}
}
@@ -1223,9 +1160,15 @@ ocGetNames_MachO(ObjectCode* oc)
size_t alignment = 1 << section->align;
SectionKind kind = getSectionKind_MachO(section);
+ SectionAlloc alloc = SECTION_NOMEM;
+ void *start = NULL, *mapped_start = NULL;
+ StgWord mapped_size = 0, mapped_offset = 0;
+ StgWord size = section->size;
void *secMem = (void *)roundUpToAlign((size_t)curMem, alignment);
+ start = secMem;
+
IF_DEBUG(linker,
debugBelch("ocGetNames_MachO: loading section %d in segment %d "
"(#%d, %s %s)\n"
@@ -1238,28 +1181,56 @@ ocGetNames_MachO(ObjectCode* oc)
case S_GB_ZEROFILL:
IF_DEBUG(linker, debugBelch("ocGetNames_MachO: memset to 0 a ZEROFILL section\n"));
memset(secMem, 0, section->size);
+ addSection(&secArray[sec_idx], kind, alloc, start, size,
+ mapped_offset, mapped_start, mapped_size);
break;
default:
IF_DEBUG(linker,
debugBelch("ocGetNames_MachO: copying from %p to %p"
" a block of %" PRIu64 " bytes\n",
(void *) (oc->image + section->offset), secMem, section->size));
+#if defined(NEED_PLT)
+ unsigned nstubs = numberOfStubsForSection(oc, sec_idx);
+ unsigned stub_space = STUB_SIZE * nstubs;
- memcpy(secMem, oc->image + section->offset, section->size);
- }
+ void * mem = mmapForLinker(section->size+stub_space, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ if( mem == MAP_FAILED ) {
+ sysErrorBelch("failed to mmap allocated memory to load section %d. "
+ "errno = %d", sec_idx, errno);
+ }
+ /* copy only the image part over; we don't want to copy data
+ * into the stub part.
+ */
+ memcpy( mem, oc->image + section->offset, size );
+
+ alloc = SECTION_MMAP;
+ mapped_offset = 0;
+ mapped_size = roundUpToPage(size+stub_space);
+ start = mem;
+ mapped_start = mem;
+#else
+ memcpy(secMem, oc->image + section->offset, section->size);
+#endif
+ addSection(&secArray[sec_idx], kind, alloc, start, size,
+ mapped_offset, mapped_start, mapped_size);
/* SECTION_NOMEM since memory is already allocated in segments */
- addSection(&secArray[sec_idx], kind, SECTION_NOMEM,
- secMem, section->size,
- 0, 0, 0);
- addProddableBlock(oc, secMem, section->size);
- curMem = (char*) secMem + section->size;
+#if defined(NEED_PLT)
+ secArray[sec_idx].info->nstubs = 0;
+ secArray[sec_idx].info->stub_offset = (uint8_t*)mem + size;
+ secArray[sec_idx].info->stub_size = stub_space;
+ secArray[sec_idx].info->stubs = NULL;
+#else
+ secArray[sec_idx].info->nstubs = 0;
+ secArray[sec_idx].info->stub_offset = NULL;
+ secArray[sec_idx].info->stub_size = 0;
+ secArray[sec_idx].info->stubs = NULL;
+#endif
+ addProddableBlock(oc, start, section->size);
+ }
- secArray[sec_idx].info->nstubs = 0;
- secArray[sec_idx].info->stub_offset = NULL;
- secArray[sec_idx].info->stub_size = 0;
- secArray[sec_idx].info->stubs = NULL;
+ curMem = (char*) secMem + section->size;
secArray[sec_idx].info->macho_section = section;
secArray[sec_idx].info->relocation_info
@@ -1443,6 +1414,26 @@ ocMprotect_MachO( ObjectCode *oc )
mmapForLinkerMarkExecutable(segment->start, segment->size);
}
}
+
+ // Also mark mmaped, sections executable. Those are not part of the
+ // segments anymore and have been mapped separately.
+ for(int i=0; i < oc->n_sections; i++) {
+ Section *section = &oc->sections[i];
+ if(section->size == 0) continue;
+ if(section->alloc != SECTION_MMAP) continue;
+ // N.B. m32 handles protection of its allocations during
+ // flushing.
+ if(section->alloc == SECTION_M32) continue;
+ switch (section->kind) {
+ case SECTIONKIND_CODE_OR_RODATA: {
+ mmapForLinkerMarkExecutable(section->mapped_start, section->mapped_size);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
return true;
}
@@ -1498,8 +1489,10 @@ ocResolve_MachO(ObjectCode* oc)
*/
if(NULL == symbol->addr) {
symbol->addr = lookupDependentSymbol((char*)symbol->name, oc);
- if(NULL == symbol->addr)
- barf("Failed to lookup symbol: %s", symbol->name);
+ if(NULL == symbol->addr) {
+ errorBelch("Failed to lookup symbol: %s", symbol->name);
+ return 0;
+ }
} else {
// we already have the address.
}
@@ -1508,10 +1501,12 @@ ocResolve_MachO(ObjectCode* oc)
* the address as well already
*/
if(NULL == symbol->addr) {
- barf("Something went wrong!");
+ errorBelch("Symbol %s has no address!\n", (char*)symbol->name);
+ return 0;
}
if(NULL == symbol->got_addr) {
- barf("Not good either!");
+ errorBelch("Symbol %s has no Global Offset Table address!\n", (char*)symbol->name);
+ return 0;
}
*(uint64_t*)symbol->got_addr = (uint64_t)symbol->addr;
}
diff --git a/rts/linker/MachOTypes.h b/rts/linker/MachOTypes.h
index c5923b441f..4df1a728f4 100644
--- a/rts/linker/MachOTypes.h
+++ b/rts/linker/MachOTypes.h
@@ -103,6 +103,11 @@ typedef
struct _Stub {
void * addr;
void * target;
+ /* flags can hold architecture specific information they are used during
+ * lookup of stubs as well. Thus two stubs for the same target with
+ * different flags are considered unequal.
+ */
+ uint8_t flags;
struct _Stub * next;
}
Stub;
diff --git a/rts/linker/macho/plt.c b/rts/linker/macho/plt.c
new file mode 100644
index 0000000000..ac84cb1fee
--- /dev/null
+++ b/rts/linker/macho/plt.c
@@ -0,0 +1,93 @@
+#include "Rts.h"
+#include "plt.h"
+
+#if defined(aarch64_HOST_ARCH)
+
+#if defined(OBJFORMAT_MACHO)
+
+#include <mach/machine.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define _makeStub ADD_SUFFIX(makeStub)
+#define needStubForRel ADD_SUFFIX(needStubForRel)
+
+unsigned
+numberOfStubsForSection( ObjectCode *oc, unsigned sectionIndex) {
+ unsigned n = 0;
+
+ MachOSection *section = &oc->info->macho_sections[sectionIndex];
+ MachORelocationInfo *relocation_info = (MachORelocationInfo*)(oc->image + section->reloff);
+ if(section->size > 0)
+ for(size_t i = 0; i < section->nreloc; i++)
+ if(needStubForRel(&relocation_info[i]))
+ n += 1;
+
+ return n;
+}
+
+bool
+findStub(Section * section,
+ void* * addr,
+ uint8_t flags) {
+ for(Stub * s = section->info->stubs; s != NULL; s = s->next) {
+ if( s->target == *addr
+ && s->flags == flags) {
+ *addr = s->addr;
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
+
+bool
+makeStub(Section * section,
+ void* * addr,
+ uint8_t flags) {
+
+ Stub * s = stgCallocBytes(1, sizeof(Stub), "makeStub");
+ CHECK(s != NULL);
+ s->target = *addr;
+ s->flags = flags;
+ s->next = NULL;
+ s->addr = (uint8_t *)section->info->stub_offset + 8
+ + STUB_SIZE * section->info->nstubs;
+
+ if((*_makeStub)(s))
+ return EXIT_FAILURE;
+
+ if(section->info->stubs == NULL) {
+ CHECK(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
+freeStubs(Section * section) {
+ if(NULL == section || section->info->nstubs == 0)
+ return;
+ Stub * last = section->info->stubs;
+ while(last->next != NULL) {
+ Stub * t = last;
+ last = last->next;
+ free(t);
+ }
+ section->info->stubs = NULL;
+ section->info->nstubs = 0;
+}
+
+#endif // OBJECTFORMAT_MACHO
+#endif // aarch64_HOST_ARCH
diff --git a/rts/linker/macho/plt.h b/rts/linker/macho/plt.h
new file mode 100644
index 0000000000..ae1ff14390
--- /dev/null
+++ b/rts/linker/macho/plt.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <LinkerInternals.h>
+
+#include "plt_aarch64.h"
+
+#if defined(aarch64_HOST_ARCH)
+
+#if defined(OBJFORMAT_MACHO)
+
+#if defined(__x86_64__)
+#define __suffix__ X86_64
+#elif defined(__aarch64__) || defined(__arm64__)
+#define __suffix__ Aarch64
+#else
+#error "unknown architecture"
+#endif
+
+#define PASTE(x,y) x ## y
+#define EVAL(x,y) PASTE(x,y)
+#define ADD_SUFFIX(x) EVAL(PASTE(x,),__suffix__)
+
+unsigned numberOfStubsForSection( ObjectCode *oc, unsigned sectionIndex);
+
+#define STUB_SIZE ADD_SUFFIX(stubSize)
+
+bool findStub(Section * section, void* * addr, uint8_t flags);
+bool makeStub(Section * section, void* * addr, uint8_t flags);
+
+void freeStubs(Section * section);
+
+#endif // OBJECTFORMAT_MACHO
+
+#endif // aarch64_HOST_ARCH
+
diff --git a/rts/linker/macho/plt_aarch64.c b/rts/linker/macho/plt_aarch64.c
new file mode 100644
index 0000000000..5c72ac8d81
--- /dev/null
+++ b/rts/linker/macho/plt_aarch64.c
@@ -0,0 +1,62 @@
+#include "Rts.h"
+#include "plt_aarch64.h"
+
+#include <stdlib.h>
+
+#if defined(aarch64_HOST_ARCH)
+
+#if defined(OBJFORMAT_MACHO)
+
+#include <mach/machine.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+
+/* five 4 byte instructions */
+const size_t instSizeAarch64 = 4;
+const size_t stubSizeAarch64 = 5 * 4;
+
+bool needStubForRelAarch64(MachORelocationInfo * rel) {
+ switch(rel->r_type) {
+ case ARM64_RELOC_BRANCH26:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* see the elf_plt_aarch64.c for the discussion on this */
+bool
+makeStubAarch64(Stub * s) {
+ uint32_t mov__hw0_x16 = 0xd2800000 | 16;
+ uint32_t movk_hw0_x16 = mov__hw0_x16 | (1 << 29);
+
+ uint32_t mov__hw3_x16 = mov__hw0_x16 | (3 << 21);
+ uint32_t movk_hw2_x16 = movk_hw0_x16 | (2 << 21);
+ uint32_t movk_hw1_x16 = movk_hw0_x16 | (1 << 21);
+
+
+ uint32_t br_x16 = 0xd61f0000 | 16 << 5;
+
+ uint32_t *P = (uint32_t*)s->addr;
+
+ /* target address */
+ uint64_t addr = (uint64_t)s->target;
+ uint16_t addr_hw0 = (uint16_t)(addr >> 0);
+ uint16_t addr_hw1 = (uint16_t)(addr >> 16);
+ uint16_t addr_hw2 = (uint16_t)(addr >> 32);
+ uint16_t addr_hw3 = (uint16_t)(addr >> 48);
+
+ P[0] = mov__hw3_x16 | ((uint32_t)addr_hw3 << 5);
+ P[1] = movk_hw2_x16 | ((uint32_t)addr_hw2 << 5);
+ P[2] = movk_hw1_x16 | ((uint32_t)addr_hw1 << 5);
+ P[3] = movk_hw0_x16 | ((uint32_t)addr_hw0 << 5);
+ P[4] = br_x16;
+
+ return EXIT_SUCCESS;
+}
+
+#endif
+#endif
+
diff --git a/rts/linker/macho/plt_aarch64.h b/rts/linker/macho/plt_aarch64.h
new file mode 100644
index 0000000000..3a4091b938
--- /dev/null
+++ b/rts/linker/macho/plt_aarch64.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <LinkerInternals.h>
+
+#if defined(OBJFORMAT_MACHO)
+
+#if defined(x86_64_HOST_ARCH)
+# include <mach-o/x86_64/reloc.h>
+#endif
+
+#if defined(aarch64_HOST_ARCH)
+# include <mach-o/arm64/reloc.h>
+#endif
+
+#include "../MachOTypes.h"
+
+
+extern const size_t stubSizeAarch64;
+bool needStubForRelAarch64(MachORelocationInfo * rel);
+bool makeStubAarch64(Stub * s);
+
+#endif
+
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index 478e545589..3d4ee0f914 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -463,6 +463,8 @@ library
linker/LoadArchive.c
linker/M32Alloc.c
linker/MachO.c
+ linker/macho/plt.c
+ linker/macho/plt_aarch64.c
linker/PEi386.c
linker/SymbolExtras.c
linker/elf_got.c