diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2016-11-02 15:01:15 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2016-11-02 15:42:00 -0400 |
commit | 3f05126253a7d68b37a388c26b586d94e871614e (patch) | |
tree | 16ac13364193992be02b12ddf0068d2343ad91b6 /rts/linker/SymbolExtras.c | |
parent | 43c8c1c4a21c3b0a281433394163ba46a124a925 (diff) | |
download | haskell-3f05126253a7d68b37a388c26b586d94e871614e.tar.gz |
linker: Split symbol extras logic into new source file
Test Plan: Validate
Reviewers: erikd, austin, simonmar
Reviewed By: simonmar
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2646
Diffstat (limited to 'rts/linker/SymbolExtras.c')
-rw-r--r-- | rts/linker/SymbolExtras.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/rts/linker/SymbolExtras.c b/rts/linker/SymbolExtras.c new file mode 100644 index 0000000000..c98510488d --- /dev/null +++ b/rts/linker/SymbolExtras.c @@ -0,0 +1,209 @@ +/* -------------------------------------------------------------------------- + * Symbol Extras. + * This is about allocating a small chunk of memory for every symbol in the + * object file. We make sure that the SymboLExtras are always "in range" of + * limited-range PC-relative instructions on various platforms by allocating + * them right next to the object code itself. + * + * This implementation is shared by the MachO and ELF implementations. Windows' + * PEi386 has its own implementation of symbol extras. + */ + +#include "LinkerInternals.h" + +#if NEED_SYMBOL_EXTRAS +#if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS) + +#include "RtsUtils.h" +#include "sm/OSMem.h" +#include "linker/SymbolExtras.h" +#include "linker/M32Alloc.h" + +#include <string.h> +#if RTS_LINKER_USE_MMAP +#include <sys/mman.h> +#endif /* RTS_LINKER_USE_MMAP */ + +/* + ocAllocateSymbolExtras + + Allocate additional space at the end of the object file image to make room + for jump islands (powerpc, x86_64, arm) and GOT entries (x86_64). + + PowerPC relative branch instructions have a 24 bit displacement field. + As PPC code is always 4-byte-aligned, this yields a +-32MB range. + If a particular imported symbol is outside this range, we have to redirect + the jump to a short piece of new code that just loads the 32bit absolute + address and jumps there. + On x86_64, PC-relative jumps and PC-relative accesses to the GOT are limited + to 32 bits (+-2GB). + + This function just allocates space for one SymbolExtra for every + undefined symbol in the object file. The code for the jump islands is + filled in by makeSymbolExtra below. +*/ + +int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first ) +{ + size_t n; + + if (RTS_LINKER_USE_MMAP && USE_CONTIGUOUS_MMAP) { + n = roundUpToPage(oc->fileSize); + + /* Keep image and symbol_extras contiguous */ + void *new = mmapForLinker(n + (sizeof(SymbolExtra) * count), + MAP_ANONYMOUS, -1, 0); + if (new) { + memcpy(new, oc->image, oc->fileSize); + if (oc->imageMapped) { + munmap(oc->image, n); + } + oc->image = new; + oc->imageMapped = rtsTrue; + oc->fileSize = n + (sizeof(SymbolExtra) * count); + oc->symbol_extras = (SymbolExtra *) (oc->image + n); + } + else { + oc->symbol_extras = NULL; + return 0; + } + } + else if( count > 0 ) { + if (RTS_LINKER_USE_MMAP) { + n = roundUpToPage(oc->fileSize); + + oc->symbol_extras = m32_alloc(sizeof(SymbolExtra) * count, 8); + if (oc->symbol_extras == NULL) return 0; + } + else { + // round up to the nearest 4 + int aligned = (oc->fileSize + 3) & ~3; + int misalignment = oc->misalignment; + + oc->image -= misalignment; + oc->image = stgReallocBytes( oc->image, + misalignment + + aligned + sizeof (SymbolExtra) * count, + "ocAllocateSymbolExtras" ); + oc->image += misalignment; + + oc->symbol_extras = (SymbolExtra *) (oc->image + aligned); + } + } + + if (oc->symbol_extras != NULL) { + memset( oc->symbol_extras, 0, sizeof (SymbolExtra) * count ); + } + + oc->first_symbol_extra = first; + oc->n_symbol_extras = count; + + return 1; +} + + +#ifndef arm_HOST_ARCH +SymbolExtra* makeSymbolExtra( ObjectCode* oc, + unsigned long symbolNumber, + unsigned long target ) +{ + SymbolExtra *extra; + + ASSERT( symbolNumber >= oc->first_symbol_extra + && symbolNumber - oc->first_symbol_extra < oc->n_symbol_extras); + + extra = &oc->symbol_extras[symbolNumber - oc->first_symbol_extra]; + +#ifdef powerpc_HOST_ARCH + // lis r12, hi16(target) + extra->jumpIsland.lis_r12 = 0x3d80; + extra->jumpIsland.hi_addr = target >> 16; + + // ori r12, r12, lo16(target) + extra->jumpIsland.ori_r12_r12 = 0x618c; + extra->jumpIsland.lo_addr = target & 0xffff; + + // mtctr r12 + extra->jumpIsland.mtctr_r12 = 0x7d8903a6; + + // bctr + extra->jumpIsland.bctr = 0x4e800420; +#endif /* powerpc_HOST_ARCH */ +#ifdef x86_64_HOST_ARCH + // jmp *-14(%rip) + static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF }; + extra->addr = target; + memcpy(extra->jumpIsland, jmp, 6); +#endif /* x86_64_HOST_ARCH */ + + return extra; +} +#endif + +#ifdef arm_HOST_ARCH +SymbolExtra* makeArmSymbolExtra( ObjectCode* oc, + unsigned long symbolNumber, + unsigned long target, + int fromThumb, + int toThumb ) +{ + SymbolExtra *extra; + + ASSERT( symbolNumber >= oc->first_symbol_extra + && symbolNumber - oc->first_symbol_extra < oc->n_symbol_extras); + + extra = &oc->symbol_extras[symbolNumber - oc->first_symbol_extra]; + + // Make sure instruction mode bit is set properly + if (toThumb) + target |= 1; + else + target &= ~1; + + if (!fromThumb) { + // In ARM encoding: + // movw r12, #0 + // movt r12, #0 + // bx r12 + uint32_t code[] = { 0xe300c000, 0xe340c000, 0xe12fff1c }; + + // Patch lower half-word into movw + code[0] |= ((target>>12) & 0xf) << 16; + code[0] |= target & 0xfff; + // Patch upper half-word into movt + target >>= 16; + code[1] |= ((target>>12) & 0xf) << 16; + code[1] |= target & 0xfff; + + memcpy(extra->jumpIsland, code, 12); + + } else { + // In Thumb encoding: + // movw r12, #0 + // movt r12, #0 + // bx r12 + uint16_t code[] = { 0xf240, 0x0c00, + 0xf2c0, 0x0c00, + 0x4760 }; + + // Patch lower half-word into movw + code[0] |= (target>>12) & 0xf; + code[0] |= ((target>>11) & 0x1) << 10; + code[1] |= ((target>>8) & 0x7) << 12; + code[1] |= target & 0xff; + // Patch upper half-word into movt + target >>= 16; + code[2] |= (target>>12) & 0xf; + code[2] |= ((target>>11) & 0x1) << 10; + code[3] |= ((target>>8) & 0x7) << 12; + code[3] |= target & 0xff; + + memcpy(extra->jumpIsland, code, 10); + } + + return extra; +} +#endif // arm_HOST_ARCH + +#endif +#endif // NEED_SYMBOL_EXTRAS |