From 4421fb34b3a70db1323833337c94ac4364824124 Mon Sep 17 00:00:00 2001 From: Moritz Angermann Date: Sat, 13 Feb 2021 10:55:58 +0800 Subject: [macho] improved linker with proper plt support This is a pre-requisite for making aarch64-darwin work. --- rts/ghc.mk | 11 ++- rts/linker/MachO.c | 177 ++++++++++++++++++++--------------------- rts/linker/MachOTypes.h | 5 ++ rts/linker/macho/plt.c | 93 ++++++++++++++++++++++ rts/linker/macho/plt.h | 35 ++++++++ rts/linker/macho/plt_aarch64.c | 62 +++++++++++++++ rts/linker/macho/plt_aarch64.h | 23 ++++++ rts/rts.cabal.in | 2 + 8 files changed, 311 insertions(+), 97 deletions(-) create mode 100644 rts/linker/macho/plt.c create mode 100644 rts/linker/macho/plt.h create mode 100644 rts/linker/macho/plt_aarch64.c create mode 100644 rts/linker/macho/plt_aarch64.h (limited to 'rts') diff --git a/rts/ghc.mk b/rts/ghc.mk index 010146ce8a..16fd42f133 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 56e31c486b..b17279d514 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 */ /* 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 already 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); @@ -271,12 +269,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; } @@ -344,7 +342,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 } @@ -424,67 +422,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 apart 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 @@ -614,9 +551,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"); } } @@ -1227,9 +1164,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" @@ -1242,28 +1185,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 @@ -1447,6 +1418,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; } @@ -1505,8 +1496,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. } @@ -1515,10 +1508,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 +#include +#include +#include +#include + +#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 + +#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 + +#if defined(aarch64_HOST_ARCH) + +#if defined(OBJFORMAT_MACHO) + +#include +#include +#include +#include +#include + +/* 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 + +#if defined(OBJFORMAT_MACHO) + +#if defined(x86_64_HOST_ARCH) +# include +#endif + +#if defined(aarch64_HOST_ARCH) +# include +#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 3b42544116..a2e22acae1 100644 --- a/rts/rts.cabal.in +++ b/rts/rts.cabal.in @@ -498,6 +498,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 -- cgit v1.2.1