diff options
Diffstat (limited to 'rts/linker/MachO.c')
-rw-r--r-- | rts/linker/MachO.c | 1244 |
1 files changed, 1244 insertions, 0 deletions
diff --git a/rts/linker/MachO.c b/rts/linker/MachO.c new file mode 100644 index 0000000000..af53a1fe61 --- /dev/null +++ b/rts/linker/MachO.c @@ -0,0 +1,1244 @@ +#include "Rts.h" + +#ifdef darwin_HOST_OS + +#include "RtsUtils.h" +#include "GetEnv.h" +#include "LinkerInternals.h" +#include "linker/MachO.h" +#include "linker/CacheFlush.h" +#include "linker/SymbolExtras.h" + +#include <string.h> +#include <regex.h> +#include <mach/machine.h> +#include <mach-o/fat.h> +#include <mach-o/loader.h> +#include <mach-o/nlist.h> +#include <mach-o/reloc.h> + +#if defined(HAVE_SYS_MMAN_H) +# include <sys/mman.h> +#endif + +#if defined(powerpc_HOST_ARCH) +# include <mach-o/ppc/reloc.h> +#endif + +#if defined(x86_64_HOST_ARCH) +# include <mach-o/x86_64/reloc.h> +#endif + +/* + Support for MachO linking on Darwin/MacOS X + by Wolfgang Thaller (wolfgang.thaller@gmx.net) + + I hereby formally apologize for the hackish nature of this code. + Things that need to be done: + *) implement ocVerifyImage_MachO + *) add still more sanity checks. +*/ + +#if x86_64_HOST_ARCH || powerpc64_HOST_ARCH +#define mach_header mach_header_64 +#define segment_command segment_command_64 +#define section section_64 +#define nlist nlist_64 +#endif + +static int +resolveImports( + ObjectCode* oc, + char *image, + struct symtab_command *symLC, + struct section *sect, // ptr to lazy or non-lazy symbol pointer section + unsigned long *indirectSyms, + struct nlist *nlist); + +#if defined(powerpc_HOST_ARCH) +int +ocAllocateSymbolExtras_MachO(ObjectCode* oc) +{ + struct mach_header *header = (struct mach_header *) oc->image; + struct load_command *lc = (struct load_command *) (header + 1); + unsigned i; + + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: start\n")); + + for (i = 0; i < header->ncmds; i++) { + if (lc->cmd == LC_SYMTAB) { + + // Find out the first and last undefined external + // symbol, so we don't have to allocate too many + // jump islands/GOT entries. + + struct symtab_command *symLC = (struct symtab_command *) lc; + unsigned min = symLC->nsyms, max = 0; + struct nlist *nlist = + symLC ? (struct nlist*) ((char*) oc->image + symLC->symoff) + : NULL; + + for (i = 0; i < symLC->nsyms; i++) { + + if (nlist[i].n_type & N_STAB) { + ; + } else if (nlist[i].n_type & N_EXT) { + + if((nlist[i].n_type & N_TYPE) == N_UNDF + && (nlist[i].n_value == 0)) { + + if (i < min) { + min = i; + } + + if (i > max) { + max = i; + } + } + } + } + + if (max >= min) { + return ocAllocateSymbolExtras(oc, max - min + 1, min); + } + + break; + } + + lc = (struct load_command *) ( ((char *)lc) + lc->cmdsize ); + } + + return ocAllocateSymbolExtras(oc,0,0); +} + +#elif defined(x86_64_HOST_ARCH) + +int +ocAllocateSymbolExtras_MachO(ObjectCode* oc) +{ + struct mach_header *header = (struct mach_header *) oc->image; + struct load_command *lc = (struct load_command *) (header + 1); + unsigned i; + + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: start\n")); + + for (i = 0; i < header->ncmds; i++) { + if (lc->cmd == LC_SYMTAB) { + + // Just allocate one entry for every symbol + struct symtab_command *symLC = (struct symtab_command *) lc; + + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: allocate %d symbols\n", symLC->nsyms)); + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: done\n")); + return ocAllocateSymbolExtras(oc, symLC->nsyms, 0); + } + + lc = (struct load_command *) ( ((char *)lc) + lc->cmdsize ); + } + + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: allocated no symbols\n")); + IF_DEBUG(linker, debugBelch("ocAllocateSymbolExtras_MachO: done\n")); + return ocAllocateSymbolExtras(oc,0,0); +} + +#else +#error Unknown MachO architecture +#endif /* HOST_ARCH */ + +int +ocVerifyImage_MachO(ObjectCode * oc) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + + IF_DEBUG(linker, debugBelch("ocVerifyImage_MachO: start\n")); + +#if x86_64_HOST_ARCH || powerpc64_HOST_ARCH + if(header->magic != MH_MAGIC_64) { + errorBelch("Could not load image %s: bad magic!\n" + " Expected %08x (64bit), got %08x%s\n", + oc->fileName, MH_MAGIC_64, header->magic, + header->magic == MH_MAGIC ? " (32bit)." : "."); + return 0; + } +#else + if(header->magic != MH_MAGIC) { + errorBelch("Could not load image %s: bad magic!\n" + " Expected %08x (32bit), got %08x%s\n", + oc->fileName, MH_MAGIC, header->magic, + header->magic == MH_MAGIC_64 ? " (64bit)." : "."); + return 0; + } +#endif + + // FIXME: do some more verifying here + IF_DEBUG(linker, debugBelch("ocVerifyImage_MachO: done\n")); + return 1; +} + +static int +resolveImports( + ObjectCode* oc, + char *image, + struct symtab_command *symLC, + struct section *sect, // ptr to lazy or non-lazy symbol pointer section + unsigned long *indirectSyms, + struct nlist *nlist) +{ + unsigned i; + size_t itemSize = 4; + + IF_DEBUG(linker, debugBelch("resolveImports: start\n")); + +#if i386_HOST_ARCH + int isJumpTable = 0; + + if (strcmp(sect->sectname,"__jump_table") == 0) { + isJumpTable = 1; + itemSize = 5; + ASSERT(sect->reserved2 == itemSize); + } + +#endif + + for(i = 0; i * itemSize < sect->size; i++) + { + // according to otool, reserved1 contains the first index into the indirect symbol table + struct nlist *symbol = &nlist[indirectSyms[sect->reserved1+i]]; + SymbolName* nm = image + symLC->stroff + symbol->n_un.n_strx; + SymbolAddr* addr = NULL; + + IF_DEBUG(linker, debugBelch("resolveImports: resolving %s\n", nm)); + + if ((symbol->n_type & N_TYPE) == N_UNDF + && (symbol->n_type & N_EXT) && (symbol->n_value != 0)) { + addr = (SymbolAddr*) (symbol->n_value); + IF_DEBUG(linker, debugBelch("resolveImports: undefined external %s has value %p\n", nm, addr)); + } else { + addr = lookupSymbol_(nm); + IF_DEBUG(linker, debugBelch("resolveImports: looking up %s, %p\n", nm, addr)); + } + + if (addr == NULL) + { + errorBelch("\nlookupSymbol failed in resolveImports\n" + "%s: unknown symbol `%s'", oc->fileName, nm); + return 0; + } + ASSERT(addr); + +#if i386_HOST_ARCH + if (isJumpTable) { + checkProddableBlock(oc,image + sect->offset + i*itemSize, 5); + + *(image + sect->offset + i * itemSize) = 0xe9; // jmp opcode + *(unsigned*)(image + sect->offset + i*itemSize + 1) + = (SymbolAddr*)addr - (image + sect->offset + i*itemSize + 5); + } + else +#endif + { + checkProddableBlock(oc, + ((void**)(image + sect->offset)) + i, + sizeof(void *)); + ((void**)(image + sect->offset))[i] = addr; + } + } + + IF_DEBUG(linker, debugBelch("resolveImports: done\n")); + return 1; +} + +static unsigned long +relocateAddress( + ObjectCode* oc, + int nSections, + struct section* sections, + unsigned long address) +{ + int i; + IF_DEBUG(linker, debugBelch("relocateAddress: start\n")); + for (i = 0; i < nSections; i++) + { + IF_DEBUG(linker, debugBelch(" relocating address in section %d\n", i)); + if (sections[i].addr <= address + && address < sections[i].addr + sections[i].size) + { + return (unsigned long)oc->image + + sections[i].offset + address - sections[i].addr; + } + } + barf("Invalid Mach-O file:" + "Address out of bounds while relocating object file"); + return 0; +} + +static int +relocateSection( + ObjectCode* oc, + char *image, + struct symtab_command *symLC, struct nlist *nlist, + int nSections, struct section* sections, struct section *sect) +{ + struct relocation_info *relocs; + int i, n; + + IF_DEBUG(linker, debugBelch("relocateSection: start\n")); + + if(!strcmp(sect->sectname,"__la_symbol_ptr")) + return 1; + else if(!strcmp(sect->sectname,"__nl_symbol_ptr")) + return 1; + else if(!strcmp(sect->sectname,"__la_sym_ptr2")) + return 1; + else if(!strcmp(sect->sectname,"__la_sym_ptr3")) + return 1; + + n = sect->nreloc; + IF_DEBUG(linker, debugBelch("relocateSection: number of relocations: %d\n", n)); + + relocs = (struct relocation_info*) (image + sect->reloff); + + for(i = 0; i < n; i++) + { +#ifdef x86_64_HOST_ARCH + struct relocation_info *reloc = &relocs[i]; + + char *thingPtr = image + sect->offset + reloc->r_address; + uint64_t thing; + /* We shouldn't need to initialise this, but gcc on OS X 64 bit + complains that it may be used uninitialized if we don't */ + uint64_t value = 0; + uint64_t baseValue; + int type = reloc->r_type; + + IF_DEBUG(linker, debugBelch("relocateSection: relocation %d\n", i)); + IF_DEBUG(linker, debugBelch(" : type = %d\n", reloc->r_type)); + IF_DEBUG(linker, debugBelch(" : address = %d\n", reloc->r_address)); + IF_DEBUG(linker, debugBelch(" : symbolnum = %u\n", reloc->r_symbolnum)); + IF_DEBUG(linker, debugBelch(" : pcrel = %d\n", reloc->r_pcrel)); + IF_DEBUG(linker, debugBelch(" : length = %d\n", reloc->r_length)); + IF_DEBUG(linker, debugBelch(" : extern = %d\n", reloc->r_extern)); + IF_DEBUG(linker, debugBelch(" : type = %d\n", reloc->r_type)); + + switch(reloc->r_length) + { + case 0: + checkProddableBlock(oc,thingPtr,1); + thing = *(uint8_t*)thingPtr; + baseValue = (uint64_t)thingPtr + 1; + break; + case 1: + checkProddableBlock(oc,thingPtr,2); + thing = *(uint16_t*)thingPtr; + baseValue = (uint64_t)thingPtr + 2; + break; + case 2: + checkProddableBlock(oc,thingPtr,4); + thing = *(uint32_t*)thingPtr; + baseValue = (uint64_t)thingPtr + 4; + break; + case 3: + checkProddableBlock(oc,thingPtr,8); + thing = *(uint64_t*)thingPtr; + baseValue = (uint64_t)thingPtr + 8; + break; + default: + barf("Unknown size."); + } + + IF_DEBUG(linker, + debugBelch("relocateSection: length = %d, thing = %" PRId64 ", baseValue = %p\n", + reloc->r_length, thing, (char *)baseValue)); + + if (type == X86_64_RELOC_GOT + || type == X86_64_RELOC_GOT_LOAD) + { + struct nlist *symbol = &nlist[reloc->r_symbolnum]; + SymbolName* nm = image + symLC->stroff + symbol->n_un.n_strx; + SymbolAddr* addr = NULL; + + IF_DEBUG(linker, debugBelch("relocateSection: making jump island for %s, extern = %d, X86_64_RELOC_GOT\n", nm, reloc->r_extern)); + + ASSERT(reloc->r_extern); + if (reloc->r_extern == 0) { + errorBelch("\nrelocateSection: global offset table relocation for symbol with r_extern == 0\n"); + } + + if (symbol->n_type & N_EXT) { + // The external bit is set, meaning the symbol is exported, + // and therefore can be looked up in this object module's + // symtab, or it is undefined, meaning dlsym must be used + // to resolve it. + + addr = lookupSymbol_(nm); + IF_DEBUG(linker, debugBelch("relocateSection: looked up %s, " + "external X86_64_RELOC_GOT or X86_64_RELOC_GOT_LOAD\n", nm)); + IF_DEBUG(linker, debugBelch(" : addr = %p\n", addr)); + + if (addr == NULL) { + errorBelch("\nlookupSymbol failed in relocateSection (RELOC_GOT)\n" + "%s: unknown symbol `%s'", oc->fileName, nm); + return 0; + } + } else { + IF_DEBUG(linker, debugBelch("relocateSection: %s is not an exported symbol\n", nm)); + + // The symbol is not exported, or defined in another + // module, so it must be in the current object module, + // at the location given by the section index and + // symbol address (symbol->n_value) + + if ((symbol->n_type & N_TYPE) == N_SECT) { + addr = (void *)relocateAddress(oc, nSections, sections, symbol->n_value); + IF_DEBUG(linker, debugBelch("relocateSection: calculated relocation %p of " + "non-external X86_64_RELOC_GOT or X86_64_RELOC_GOT_LOAD\n", + (void *)symbol->n_value)); + IF_DEBUG(linker, debugBelch(" : addr = %p\n", addr)); + } else { + errorBelch("\nrelocateSection: %s is not exported," + " and should be defined in a section, but isn't!\n", nm); + } + } + + value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, (unsigned long)addr)->addr; + + type = X86_64_RELOC_SIGNED; + } + else if (reloc->r_extern) + { + struct nlist *symbol = &nlist[reloc->r_symbolnum]; + SymbolName* nm = image + symLC->stroff + symbol->n_un.n_strx; + SymbolAddr* addr = NULL; + + IF_DEBUG(linker, debugBelch("relocateSection: looking up external symbol %s\n", nm)); + IF_DEBUG(linker, debugBelch(" : type = %d\n", symbol->n_type)); + IF_DEBUG(linker, debugBelch(" : sect = %d\n", symbol->n_sect)); + IF_DEBUG(linker, debugBelch(" : desc = %d\n", symbol->n_desc)); + IF_DEBUG(linker, debugBelch(" : value = %p\n", (void *)symbol->n_value)); + + if ((symbol->n_type & N_TYPE) == N_SECT) { + value = relocateAddress(oc, nSections, sections, + symbol->n_value); + IF_DEBUG(linker, debugBelch("relocateSection, defined external symbol %s, relocated address %p\n", nm, (void *)value)); + } + else { + addr = lookupSymbol_(nm); + if (addr == NULL) + { + errorBelch("\nlookupSymbol failed in relocateSection (relocate external)\n" + "%s: unknown symbol `%s'", oc->fileName, nm); + return 0; + } + + value = (uint64_t) addr; + IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s, address %p\n", nm, (void *)value)); + } + } + else + { + // If the relocation is not through the global offset table + // or external, then set the value to the baseValue. This + // will leave displacements into the __const section + // unchanged (as they ought to be). + + value = baseValue; + } + + IF_DEBUG(linker, debugBelch("relocateSection: value = %p\n", (void *)value)); + + if (type == X86_64_RELOC_BRANCH) + { + if((int32_t)(value - baseValue) != (int64_t)(value - baseValue)) + { + ASSERT(reloc->r_extern); + value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, value) + -> jumpIsland; + } + ASSERT((int32_t)(value - baseValue) == (int64_t)(value - baseValue)); + type = X86_64_RELOC_SIGNED; + } + + switch(type) + { + case X86_64_RELOC_UNSIGNED: + ASSERT(!reloc->r_pcrel); + thing += value; + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + ASSERT(reloc->r_pcrel); + thing += value - baseValue; + break; + case X86_64_RELOC_SUBTRACTOR: + ASSERT(!reloc->r_pcrel); + thing -= value; + break; + default: + barf("unkown relocation"); + } + + switch(reloc->r_length) + { + case 0: + *(uint8_t*)thingPtr = thing; + break; + case 1: + *(uint16_t*)thingPtr = thing; + break; + case 2: + *(uint32_t*)thingPtr = thing; + break; + case 3: + *(uint64_t*)thingPtr = thing; + break; + } +#else /* x86_64_HOST_ARCH */ + if(relocs[i].r_address & R_SCATTERED) + { + struct scattered_relocation_info *scat = + (struct scattered_relocation_info*) &relocs[i]; + + if(!scat->r_pcrel) + { + if(scat->r_length == 2) + { + unsigned long word = 0; + unsigned long* wordPtr = (unsigned long*) (image + sect->offset + scat->r_address); + + /* In this check we assume that sizeof(unsigned long) = 2 * sizeof(unsigned short) + on powerpc_HOST_ARCH */ + checkProddableBlock(oc,wordPtr,sizeof(unsigned long)); + + // Note on relocation types: + // i386 uses the GENERIC_RELOC_* types, + // while ppc uses special PPC_RELOC_* types. + // *_RELOC_VANILLA and *_RELOC_PAIR have the same value + // in both cases, all others are different. + // Therefore, we use GENERIC_RELOC_VANILLA + // and GENERIC_RELOC_PAIR instead of the PPC variants, + // and use #ifdefs for the other types. + + // Step 1: Figure out what the relocated value should be + if (scat->r_type == GENERIC_RELOC_VANILLA) { + word = *wordPtr + + (unsigned long) relocateAddress(oc, + nSections, + sections, + scat->r_value) + - scat->r_value; + } +#ifdef powerpc_HOST_ARCH + else if(scat->r_type == PPC_RELOC_SECTDIFF + || scat->r_type == PPC_RELOC_LO16_SECTDIFF + || scat->r_type == PPC_RELOC_HI16_SECTDIFF + || scat->r_type == PPC_RELOC_HA16_SECTDIFF + || scat->r_type == PPC_RELOC_LOCAL_SECTDIFF) +#else /* powerpc_HOST_ARCH */ + else if(scat->r_type == GENERIC_RELOC_SECTDIFF + || scat->r_type == GENERIC_RELOC_LOCAL_SECTDIFF) +#endif /* powerpc_HOST_ARCH */ + { + struct scattered_relocation_info *pair = + (struct scattered_relocation_info*) &relocs[i+1]; + + if (!pair->r_scattered || pair->r_type != GENERIC_RELOC_PAIR) { + barf("Invalid Mach-O file: " + "RELOC_*_SECTDIFF not followed by RELOC_PAIR"); + } + + word = (unsigned long) + (relocateAddress(oc, nSections, sections, scat->r_value) + - relocateAddress(oc, nSections, sections, pair->r_value)); + i++; + } +#ifdef powerpc_HOST_ARCH + else if(scat->r_type == PPC_RELOC_HI16 + || scat->r_type == PPC_RELOC_LO16 + || scat->r_type == PPC_RELOC_HA16 + || scat->r_type == PPC_RELOC_LO14) + { // these are generated by label+offset things + struct relocation_info *pair = &relocs[i+1]; + + if ((pair->r_address & R_SCATTERED) || pair->r_type != PPC_RELOC_PAIR) { + barf("Invalid Mach-O file: " + "PPC_RELOC_* not followed by PPC_RELOC_PAIR"); + } + + if(scat->r_type == PPC_RELOC_LO16) + { + word = ((unsigned short*) wordPtr)[1]; + word |= ((unsigned long) relocs[i+1].r_address & 0xFFFF) << 16; + } + else if(scat->r_type == PPC_RELOC_LO14) + { + barf("Unsupported Relocation: PPC_RELOC_LO14"); + word = ((unsigned short*) wordPtr)[1] & 0xFFFC; + word |= ((unsigned long) relocs[i+1].r_address & 0xFFFF) << 16; + } + else if(scat->r_type == PPC_RELOC_HI16) + { + word = ((unsigned short*) wordPtr)[1] << 16; + word |= ((unsigned long) relocs[i+1].r_address & 0xFFFF); + } + else if(scat->r_type == PPC_RELOC_HA16) + { + word = ((unsigned short*) wordPtr)[1] << 16; + word += ((short)relocs[i+1].r_address & (short)0xFFFF); + } + + + word += (unsigned long) relocateAddress(oc, nSections, sections, scat->r_value) + - scat->r_value; + + i++; + } +#endif /* powerpc_HOST_ARCH */ + else { + barf ("Don't know how to handle this Mach-O " + "scattered relocation entry: " + "object file %s; entry type %ld; " + "address %#lx\n", + OC_INFORMATIVE_FILENAME(oc), + scat->r_type, + scat->r_address); + return 0; + } + +#ifdef powerpc_HOST_ARCH + if(scat->r_type == GENERIC_RELOC_VANILLA + || scat->r_type == PPC_RELOC_SECTDIFF) +#else /* powerpc_HOST_ARCH */ + if(scat->r_type == GENERIC_RELOC_VANILLA + || scat->r_type == GENERIC_RELOC_SECTDIFF + || scat->r_type == GENERIC_RELOC_LOCAL_SECTDIFF) +#endif /* powerpc_HOST_ARCH */ + { + *wordPtr = word; + } +#ifdef powerpc_HOST_ARCH + else if (scat->r_type == PPC_RELOC_LO16_SECTDIFF + || scat->r_type == PPC_RELOC_LO16) + { + ((unsigned short*) wordPtr)[1] = word & 0xFFFF; + } + else if (scat->r_type == PPC_RELOC_HI16_SECTDIFF + || scat->r_type == PPC_RELOC_HI16) + { + ((unsigned short*) wordPtr)[1] = (word >> 16) & 0xFFFF; + } + else if (scat->r_type == PPC_RELOC_HA16_SECTDIFF + || scat->r_type == PPC_RELOC_HA16) + { + ((unsigned short*) wordPtr)[1] = ((word >> 16) & 0xFFFF) + + ((word & (1<<15)) ? 1 : 0); + } +#endif /* powerpc_HOST_ARCH */ + } + else + { + barf("Can't handle Mach-O scattered relocation entry " + "with this r_length tag: " + "object file %s; entry type %ld; " + "r_length tag %ld; address %#lx\n", + OC_INFORMATIVE_FILENAME(oc), + scat->r_type, + scat->r_length, + scat->r_address); + return 0; + } + } + else /* scat->r_pcrel */ + { + barf("Don't know how to handle *PC-relative* Mach-O " + "scattered relocation entry: " + "object file %s; entry type %ld; address %#lx\n", + OC_INFORMATIVE_FILENAME(oc), + scat->r_type, + scat->r_address); + return 0; + } + + } + else /* !(relocs[i].r_address & R_SCATTERED) */ + { + struct relocation_info *reloc = &relocs[i]; + if (reloc->r_pcrel && !reloc->r_extern) { + IF_DEBUG(linker, debugBelch("relocateSection: pc relative but not external, skipping\n")); + continue; + } + + if (reloc->r_length == 2) { + unsigned long word = 0; +#ifdef powerpc_HOST_ARCH + unsigned long jumpIsland = 0; + long offsetToJumpIsland = 0xBADBAD42; // initialise to bad value + // to avoid warning and to catch + // bugs. +#endif /* powerpc_HOST_ARCH */ + + unsigned long* wordPtr = (unsigned long*) (image + sect->offset + reloc->r_address); + + /* In this check we assume that sizeof(unsigned long) = 2 * sizeof(unsigned short) + on powerpc_HOST_ARCH */ + checkProddableBlock(oc,wordPtr, sizeof(unsigned long)); + + if (reloc->r_type == GENERIC_RELOC_VANILLA) { + word = *wordPtr; + } +#ifdef powerpc_HOST_ARCH + else if (reloc->r_type == PPC_RELOC_LO16) { + word = ((unsigned short*) wordPtr)[1]; + word |= ((unsigned long) relocs[i+1].r_address & 0xFFFF) << 16; + } + else if (reloc->r_type == PPC_RELOC_HI16) { + word = ((unsigned short*) wordPtr)[1] << 16; + word |= ((unsigned long) relocs[i+1].r_address & 0xFFFF); + } + else if (reloc->r_type == PPC_RELOC_HA16) { + word = ((unsigned short*) wordPtr)[1] << 16; + word += ((short)relocs[i+1].r_address & (short)0xFFFF); + } + else if (reloc->r_type == PPC_RELOC_BR24) { + word = *wordPtr; + word = (word & 0x03FFFFFC) | ((word & 0x02000000) ? 0xFC000000 : 0); + } +#endif /* powerpc_HOST_ARCH */ + else { + barf("Can't handle this Mach-O relocation entry " + "(not scattered): " + "object file %s; entry type %ld; address %#lx\n", + OC_INFORMATIVE_FILENAME(oc), + reloc->r_type, + reloc->r_address); + return 0; + } + + if (!reloc->r_extern) { + long delta = sections[reloc->r_symbolnum-1].offset + - sections[reloc->r_symbolnum-1].addr + + ((long) image); + + word += delta; + } + else { + struct nlist *symbol = &nlist[reloc->r_symbolnum]; + char *nm = image + symLC->stroff + symbol->n_un.n_strx; + void *symbolAddress = lookupSymbol_(nm); + + if (!symbolAddress) { + errorBelch("\nunknown symbol `%s'", nm); + return 0; + } + + if (reloc->r_pcrel) { +#ifdef powerpc_HOST_ARCH + // In the .o file, this should be a relative jump to NULL + // and we'll change it to a relative jump to the symbol + ASSERT(word + reloc->r_address == 0); + jumpIsland = (unsigned long) + &makeSymbolExtra(oc, + reloc->r_symbolnum, + (unsigned long) symbolAddress) + -> jumpIsland; + if (jumpIsland != 0) { + offsetToJumpIsland = word + jumpIsland + - (((long)image) + sect->offset - sect->addr); + } +#endif /* powerpc_HOST_ARCH */ + word += (unsigned long) symbolAddress + - (((long)image) + sect->offset - sect->addr); + } + else { + word += (unsigned long) symbolAddress; + } + } + + if (reloc->r_type == GENERIC_RELOC_VANILLA) { + *wordPtr = word; + continue; + } +#ifdef powerpc_HOST_ARCH + else if(reloc->r_type == PPC_RELOC_LO16) + { + ((unsigned short*) wordPtr)[1] = word & 0xFFFF; + i++; + continue; + } + else if(reloc->r_type == PPC_RELOC_HI16) + { + ((unsigned short*) wordPtr)[1] = (word >> 16) & 0xFFFF; + i++; + continue; + } + else if(reloc->r_type == PPC_RELOC_HA16) + { + ((unsigned short*) wordPtr)[1] = ((word >> 16) & 0xFFFF) + + ((word & (1<<15)) ? 1 : 0); + i++; + continue; + } + else if(reloc->r_type == PPC_RELOC_BR24) + { + if ((word & 0x03) != 0) { + barf("%s: unconditional relative branch with a displacement " + "which isn't a multiple of 4 bytes: %#lx", + OC_INFORMATIVE_FILENAME(oc), + word); + } + + if((word & 0xFE000000) != 0xFE000000 && + (word & 0xFE000000) != 0x00000000) { + // The branch offset is too large. + // Therefore, we try to use a jump island. + if (jumpIsland == 0) { + barf("%s: unconditional relative branch out of range: " + "no jump island available: %#lx", + OC_INFORMATIVE_FILENAME(oc), + word); + } + + word = offsetToJumpIsland; + + if((word & 0xFE000000) != 0xFE000000 && + (word & 0xFE000000) != 0x00000000) { + barf("%s: unconditional relative branch out of range: " + "jump island out of range: %#lx", + OC_INFORMATIVE_FILENAME(oc), + word); + } + } + *wordPtr = (*wordPtr & 0xFC000003) | (word & 0x03FFFFFC); + continue; + } +#endif /* powerpc_HOST_ARCH */ + } + else + { + barf("Can't handle Mach-O relocation entry (not scattered) " + "with this r_length tag: " + "object file %s; entry type %ld; " + "r_length tag %ld; address %#lx\n", + OC_INFORMATIVE_FILENAME(oc), + reloc->r_type, + reloc->r_length, + reloc->r_address); + return 0; + } + } +#endif /* x86_64_HOST_ARCH */ + } + + IF_DEBUG(linker, debugBelch("relocateSection: done\n")); + return 1; +} + +int +ocGetNames_MachO(ObjectCode* oc) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header)); + unsigned i,curSymbol = 0; + struct segment_command *segLC = NULL; + struct section *sections; + struct symtab_command *symLC = NULL; + struct nlist *nlist; + unsigned long commonSize = 0; + SymbolAddr* commonStorage = NULL; + unsigned long commonCounter; + + IF_DEBUG(linker,debugBelch("ocGetNames_MachO: start\n")); + + for(i=0;i<header->ncmds;i++) + { + if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) { + segLC = (struct segment_command*) lc; + } + else if (lc->cmd == LC_SYMTAB) { + symLC = (struct symtab_command*) lc; + } + + lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); + } + + sections = (struct section*) (segLC+1); + nlist = symLC ? (struct nlist*) (image + symLC->symoff) + : NULL; + + if (!segLC) { + barf("ocGetNames_MachO: no segment load command"); + } + + Section *secArray; + secArray = (Section*)stgCallocBytes( + sizeof(Section), + segLC->nsects, + "ocGetNames_MachO(sections)"); + oc->sections = secArray; + oc->n_sections = segLC->nsects; + + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: will load %d sections\n", segLC->nsects)); + for(i=0;i<segLC->nsects;i++) + { + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: section %d\n", i)); + + if (sections[i].size == 0) { + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: found a zero length section, skipping\n")); + continue; + } + + if((sections[i].flags & SECTION_TYPE) == S_ZEROFILL) { + char * zeroFillArea; + if (RTS_LINKER_USE_MMAP) { + zeroFillArea = mmapForLinker(sections[i].size, MAP_ANONYMOUS, + -1, 0); + if (zeroFillArea == NULL) return 0; + memset(zeroFillArea, 0, sections[i].size); + } + else { + zeroFillArea = stgCallocBytes(1,sections[i].size, + "ocGetNames_MachO(common symbols)"); + } + sections[i].offset = zeroFillArea - image; + } + + SectionKind kind = SECTIONKIND_OTHER; + + if (0==strcmp(sections[i].sectname,"__text")) { + kind = SECTIONKIND_CODE_OR_RODATA; + } + else if (0==strcmp(sections[i].sectname,"__const") || + 0==strcmp(sections[i].sectname,"__data") || + 0==strcmp(sections[i].sectname,"__bss") || + 0==strcmp(sections[i].sectname,"__common") || + 0==strcmp(sections[i].sectname,"__mod_init_func")) { + kind = SECTIONKIND_RWDATA; + } + + addSection(&secArray[i], kind, SECTION_NOMEM, + (void *)(image + sections[i].offset), + sections[i].size, + 0, 0, 0); + + addProddableBlock(oc, + (void *) (image + sections[i].offset), + sections[i].size); + } + + // count external symbols defined here + oc->n_symbols = 0; + if (symLC) { + for (i = 0; i < symLC->nsyms; i++) { + if (nlist[i].n_type & N_STAB) { + ; + } + else if(nlist[i].n_type & N_EXT) + { + if((nlist[i].n_type & N_TYPE) == N_UNDF + && (nlist[i].n_value != 0)) + { + commonSize += nlist[i].n_value; + oc->n_symbols++; + } + else if((nlist[i].n_type & N_TYPE) == N_SECT) + oc->n_symbols++; + } + } + } + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: %d external symbols\n", oc->n_symbols)); + oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(SymbolName*), + "ocGetNames_MachO(oc->symbols)"); + + if(symLC) + { + for(i=0;i<symLC->nsyms;i++) + { + if(nlist[i].n_type & N_STAB) + ; + else if((nlist[i].n_type & N_TYPE) == N_SECT) + { + if(nlist[i].n_type & N_EXT) + { + SymbolName* nm = image + symLC->stroff + nlist[i].n_un.n_strx; + if ((nlist[i].n_desc & N_WEAK_DEF) && lookupSymbol_(nm)) { + // weak definition, and we already have a definition + IF_DEBUG(linker, debugBelch(" weak: %s\n", nm)); + } + else + { + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting %s\n", nm)); + SymbolAddr* addr = image + + sections[nlist[i].n_sect - 1].offset + - sections[nlist[i].n_sect - 1].addr + + nlist[i].n_value; + + ghciInsertSymbolTable( oc->fileName + , symhash + , nm + , addr + , HS_BOOL_FALSE + , oc); + + oc->symbols[curSymbol] = nm; + curSymbol++; + } + } + else + { + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: \t...not external, skipping\n")); + } + } + else + { + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: \t...not defined in this section, skipping\n")); + } + } + } + + commonStorage = stgCallocBytes(1,commonSize,"ocGetNames_MachO(common symbols)"); + commonCounter = (unsigned long)commonStorage; + + if (symLC) { + for (i = 0; i < symLC->nsyms; i++) { + if((nlist[i].n_type & N_TYPE) == N_UNDF + && (nlist[i].n_type & N_EXT) + && (nlist[i].n_value != 0)) { + + SymbolName* nm = image + symLC->stroff + nlist[i].n_un.n_strx; + unsigned long sz = nlist[i].n_value; + + nlist[i].n_value = commonCounter; + + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting common symbol: %s\n", nm)); + ghciInsertSymbolTable(oc->fileName, symhash, nm, + (void*)commonCounter, HS_BOOL_FALSE, oc); + oc->symbols[curSymbol] = nm; + curSymbol++; + + commonCounter += sz; + } + } + } + + IF_DEBUG(linker, debugBelch("ocGetNames_MachO: done\n")); + return 1; +} + +int +ocResolve_MachO(ObjectCode* oc) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header)); + unsigned i; + struct segment_command *segLC = NULL; + struct section *sections; + struct symtab_command *symLC = NULL; + struct dysymtab_command *dsymLC = NULL; + struct nlist *nlist; + + IF_DEBUG(linker, debugBelch("ocResolve_MachO: start\n")); + for (i = 0; i < header->ncmds; i++) + { + if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) { + segLC = (struct segment_command*) lc; + IF_DEBUG(linker, debugBelch("ocResolve_MachO: found a 32 or 64 bit segment load command\n")); + } + else if (lc->cmd == LC_SYMTAB) { + symLC = (struct symtab_command*) lc; + IF_DEBUG(linker, debugBelch("ocResolve_MachO: found a symbol table load command\n")); + } + else if (lc->cmd == LC_DYSYMTAB) { + dsymLC = (struct dysymtab_command*) lc; + IF_DEBUG(linker, debugBelch("ocResolve_MachO: found a dynamic symbol table load command\n")); + } + + lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); + } + + sections = (struct section*) (segLC+1); + nlist = symLC ? (struct nlist*) (image + symLC->symoff) + : NULL; + + if(dsymLC) + { + unsigned long *indirectSyms + = (unsigned long*) (image + dsymLC->indirectsymoff); + + IF_DEBUG(linker, debugBelch("ocResolve_MachO: resolving dsymLC\n")); + for (i = 0; i < segLC->nsects; i++) + { + if( !strcmp(sections[i].sectname,"__la_symbol_ptr") + || !strcmp(sections[i].sectname,"__la_sym_ptr2") + || !strcmp(sections[i].sectname,"__la_sym_ptr3")) + { + if(!resolveImports(oc,image,symLC,§ions[i],indirectSyms,nlist)) + return 0; + } + else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr") + || !strcmp(sections[i].sectname,"__pointers")) + { + if(!resolveImports(oc,image,symLC,§ions[i],indirectSyms,nlist)) + return 0; + } + else if(!strcmp(sections[i].sectname,"__jump_table")) + { + if(!resolveImports(oc,image,symLC,§ions[i],indirectSyms,nlist)) + return 0; + } + else + { + IF_DEBUG(linker, debugBelch("ocResolve_MachO: unknown section\n")); + } + } + } + + for(i=0;i<segLC->nsects;i++) + { + IF_DEBUG(linker, debugBelch("ocResolve_MachO: relocating section %d\n", i)); + + if (!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,§ions[i])) + return 0; + } + +#if defined (powerpc_HOST_ARCH) + ocFlushInstructionCache( oc ); +#endif + + return 1; +} + +int +ocRunInit_MachO ( ObjectCode *oc ) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header)); + struct segment_command *segLC = NULL; + struct section *sections; + uint32_t i; + + for (i = 0; i < header->ncmds; i++) { + if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) { + segLC = (struct segment_command*) lc; + } + lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); + } + if (!segLC) { + barf("ocRunInit_MachO: no segment load command"); + } + sections = (struct section*) (segLC+1); + + int argc, envc; + char **argv, **envv; + + getProgArgv(&argc, &argv); + getProgEnvv(&envc, &envv); + + for (i = 0; i < segLC->nsects; i++) { + // ToDo: replace this with a proper check for the S_MOD_INIT_FUNC_POINTERS + // flag. We should do this elsewhere in the Mach-O linker code + // too. Note that the system linker will *refuse* to honor + // sections which don't have this flag, so this could cause + // weird behavior divergence (albeit reproduceable). + if (0 == strcmp(sections[i].sectname,"__mod_init_func")) { + char *init_startC = image + sections[i].offset; + init_t *init = (init_t*)init_startC; + init_t *init_end = (init_t*)(init_startC + sections[i].size); + for (; init < init_end; init++) { + (*init)(argc, argv, envv); + } + } + } + + freeProgEnvv(envc, envv); + return 1; +} + +#ifdef powerpc_HOST_ARCH +/* + * The Mach-O object format uses leading underscores. But not everywhere. + * There is a small number of runtime support functions defined in + * libcc_dynamic.a whose name does not have a leading underscore. + * As a consequence, we can't get their address from C code. + * We have to use inline assembler just to take the address of a function. + * Yuck. + */ + +extern void* symbolsWithoutUnderscore[]; + +void +machoInitSymbolsWithoutUnderscore(void) +{ + void **p = symbolsWithoutUnderscore; + __asm__ volatile(".globl _symbolsWithoutUnderscore\n.data\n_symbolsWithoutUnderscore:"); + +#undef SymI_NeedsProto +#undef SymI_NeedsDataProto + +#define SymI_NeedsProto(x) \ + __asm__ volatile(".long " # x); + +#define SymI_NeedsDataProto(x) \ + SymI_NeedsProto(x) + + RTS_MACHO_NOUNDERLINE_SYMBOLS + + __asm__ volatile(".text"); + +#undef SymI_NeedsProto +#undef SymI_NeedsDataProto + +#define SymI_NeedsProto(x) \ + ghciInsertSymbolTable("(GHCi built-in symbols)", symhash, #x, *p++, HS_BOOL_FALSE, NULL); + +#define SymI_NeedsDataProto(x) \ + SymI_NeedsProto(x) + + RTS_MACHO_NOUNDERLINE_SYMBOLS + +#undef SymI_NeedsProto +#undef SymI_NeedsDataProto +} +#endif + +/* + * Figure out by how much to shift the entire Mach-O file in memory + * when loading so that its single segment ends up 16-byte-aligned + */ +int +machoGetMisalignment( FILE * f ) +{ + struct mach_header header; + int misalignment; + + { + int n = fread(&header, sizeof(header), 1, f); + if (n != 1) { + barf("machoGetMisalignment: can't read the Mach-O header"); + } + } + fseek(f, -sizeof(header), SEEK_CUR); + +#if x86_64_HOST_ARCH || powerpc64_HOST_ARCH + if(header.magic != MH_MAGIC_64) { + barf("Bad magic. Expected: %08x, got: %08x.", + MH_MAGIC_64, header.magic); + } +#else + if(header.magic != MH_MAGIC) { + barf("Bad magic. Expected: %08x, got: %08x.", + MH_MAGIC, header.magic); + } +#endif + + misalignment = (header.sizeofcmds + sizeof(header)) + & 0xF; + + return misalignment ? (16 - misalignment) : 0; +} + +#endif /* darwin_HOST_OS */ |