/* -----------------------------------------------------------------------------
- * $Id: Linker.c,v 1.127 2003/08/29 16:00:26 simonmar Exp $
+ * $Id: Linker.c,v 1.139 2003/11/02 06:55:24 dons Exp $
*
* (c) The GHC Team, 2000-2003
*
# include <mach-o/loader.h>
# include <mach-o/nlist.h>
# include <mach-o/reloc.h>
+# include <mach-o/dyld.h>
#endif
/* Hash table mapping symbol names to Symbol */
static int ocGetNames_PEi386 ( ObjectCode* oc );
static int ocResolve_PEi386 ( ObjectCode* oc );
#elif defined(OBJFORMAT_MACHO)
+static int ocAllocateJumpIslands_MachO ( ObjectCode* oc );
static int ocVerifyImage_MachO ( ObjectCode* oc );
static int ocGetNames_MachO ( ObjectCode* oc );
static int ocResolve_MachO ( ObjectCode* oc );
SymX(divExactIntegerzh_fast) \
SymX(divModIntegerzh_fast) \
SymX(forkzh_fast) \
- SymX(forkProcesszh_fast) \
+ SymX(forkProcess) \
+ SymX(forkOS_createThread) \
SymX(freeHaskellFunctionPtr) \
SymX(freeStablePtr) \
SymX(gcdIntegerzh_fast) \
SymX(gcdIntegerIntzh_fast) \
SymX(gcdIntzh_fast) \
+ SymX(genSymZh) \
SymX(getProgArgv) \
SymX(getStablePtr) \
SymX(int2Integerzh_fast) \
SymX(integer2Intzh_fast) \
SymX(integer2Wordzh_fast) \
+ SymX(isCurrentThreadBoundzh_fast) \
SymX(isDoubleDenormalized) \
SymX(isDoubleInfinite) \
SymX(isDoubleNaN) \
SymX(rts_eval) \
SymX(rts_evalIO) \
SymX(rts_evalLazyIO) \
+ SymX(rts_evalStableIO) \
SymX(rts_eval_) \
SymX(rts_getBool) \
SymX(rts_getChar) \
SymX(rts_mkWord64) \
SymX(rts_mkWord8) \
SymX(rts_unlock) \
+ SymX(rtsSupportsBoundThreads) \
SymX(run_queue_hd) \
+ SymX(__hscore_get_saved_termios) \
+ SymX(__hscore_set_saved_termios) \
SymX(setProgArgv) \
SymX(startupHaskell) \
SymX(shutdownHaskell) \
initLinker();
-#if !defined(openbsd_TARGET_OS)
hdl= dlopen(dll_name, RTLD_NOW | RTLD_GLOBAL);
-#else
- hdl= dlopen(dll_name, RTLD_LAZY);
-#endif
+
if (hdl == NULL) {
/* dlopen failed; return a ptr to the error msg. */
errmsg = dlerror();
val = lookupStrHashTable(symhash, lbl);
if (val == NULL) {
-# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
+# if defined(OBJFORMAT_ELF)
return dlsym(dl_prog_handle, lbl);
+# elif defined(OBJFORMAT_MACHO)
+ if(NSIsSymbolNameDefined(lbl)) {
+ NSSymbol symbol = NSLookupAndBindSymbol(lbl);
+ return NSAddressOfSymbol(symbol);
+ } else {
+ return NULL;
+ }
# elif defined(OBJFORMAT_PEi386)
OpenedDLL* o_dll;
void* sym;
#endif /* USE_MMAP */
+# if defined(OBJFORMAT_MACHO)
+ r = ocAllocateJumpIslands_MachO ( oc );
+ if (!r) { return r; }
+#endif
+
/* verify the in-memory image */
# if defined(OBJFORMAT_ELF)
r = ocVerifyImage_ELF ( oc );
#if defined(OBJFORMAT_MACHO)
/*
- Initial support for MachO linking on Darwin/MacOS X on PowerPC chips
+ Support for MachO linking on Darwin/MacOS X on PowerPC chips
by Wolfgang Thaller (wolfgang.thaller@gmx.net)
I hereby formally apologize for the hackish nature of this code.
Things that need to be done:
- *) get common symbols and .bss sections to work properly.
- Haskell modules seem to work, but C modules can cause problems
*) implement ocVerifyImage_MachO
- *) add more sanity checks. The current code just has to segfault if there's a
- broken .o file.
+ *) add still more sanity checks.
*/
+
+/*
+ ocAllocateJumpIslands_MachO
+
+ Allocate additional space at the end of the object file image to make room
+ for jump islands.
+
+ 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.
+ This function just allocates space for one 16 byte jump island for every
+ undefined symbol in the object file. The code for the islands is filled in by
+ makeJumpIsland below.
+*/
+
+static const int islandSize = 16;
+
+static int ocAllocateJumpIslands_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;
+
+ for(i=0;i<header->ncmds;i++)
+ {
+ if(lc->cmd == LC_DYSYMTAB)
+ {
+ struct dysymtab_command *dsymLC = (struct dysymtab_command*) lc;
+ unsigned long nundefsym = dsymLC->nundefsym;
+ oc->island_start_symbol = dsymLC->iundefsym;
+ oc->n_islands = nundefsym;
+
+ if(nundefsym > 0)
+ {
+#ifdef USE_MMAP
+ #error ocAllocateJumpIslands_MachO doesnt want USE_MMAP to be defined
+#else
+ oc->image = stgReallocBytes(
+ image, oc->fileSize + islandSize * nundefsym,
+ "ocAllocateJumpIslands_MachO");
+#endif
+ oc->jump_islands = oc->image + oc->fileSize;
+ memset(oc->jump_islands, 0, islandSize * nundefsym);
+ }
+
+ break; // there can be only one LC_DSYMTAB
+ }
+ lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize );
+ }
+ return 1;
+}
+
static int ocVerifyImage_MachO(ObjectCode* oc)
{
// FIXME: do some verifying here
return 0;
}
ASSERT(addr);
+ checkProddableBlock(oc,((void**)(image + sect->offset)) + i);
((void**)(image + sect->offset))[i] = addr;
}
return 1;
}
-static int relocateSection(char *image,
+static void* makeJumpIsland(
+ ObjectCode* oc,
+ unsigned long symbolNumber,
+ void* target)
+{
+ if(symbolNumber < oc->island_start_symbol ||
+ symbolNumber - oc->island_start_symbol > oc->n_islands)
+ return NULL;
+ symbolNumber -= oc->island_start_symbol;
+
+ void *island = (void*) ((char*)oc->jump_islands + islandSize * symbolNumber);
+ unsigned long *p = (unsigned long*) island;
+
+ // lis r12, hi16(target)
+ *p++ = 0x3d800000 | ( ((unsigned long) target) >> 16 );
+ // ori r12, r12, lo16(target)
+ *p++ = 0x618c0000 | ( ((unsigned long) target) & 0xFFFF );
+ // mtctr r12
+ *p++ = 0x7d8903a6;
+ // bctr
+ *p++ = 0x4e800420;
+
+ return (void*) island;
+}
+
+static char* relocateAddress(
+ ObjectCode* oc,
+ int nSections,
+ struct section* sections,
+ unsigned long address)
+{
+ int i;
+ for(i = 0; i < nSections; i++)
+ {
+ if(sections[i].addr <= address
+ && address < sections[i].addr + sections[i].size)
+ {
+ return oc->image + sections[i].offset + address - sections[i].addr;
+ }
+ }
+ barf("Invalid Mach-O file:"
+ "Address out of bounds while relocating object file");
+ return NULL;
+}
+
+static int relocateSection(
+ ObjectCode* oc,
+ char *image,
struct symtab_command *symLC, struct nlist *nlist,
- struct section* sections, struct section *sect)
+ int nSections, struct section* sections, struct section *sect)
{
struct relocation_info *relocs;
int i,n;
if(!scat->r_pcrel)
{
- if(scat->r_length == 2 && scat->r_type == GENERIC_RELOC_VANILLA)
+ if(scat->r_length == 2)
{
- unsigned long* word = (unsigned long*) (image + sect->offset + scat->r_address);
+ unsigned long word = 0;
+ unsigned long* wordPtr = (unsigned long*) (image + sect->offset + scat->r_address);
+ checkProddableBlock(oc,wordPtr);
- *word = scat->r_value + sect->offset + ((long) image);
+ // Step 1: Figure out what the relocated value should be
+ if(scat->r_type == GENERIC_RELOC_VANILLA)
+ {
+ word = scat->r_value + sect->offset + ((long) image);
+ }
+ 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)
+ {
+ struct scattered_relocation_info *pair =
+ (struct scattered_relocation_info*) &relocs[i+1];
+
+ if(!pair->r_scattered || pair->r_type != PPC_RELOC_PAIR)
+ barf("Invalid Mach-O file: "
+ "PPC_RELOC_*_SECTDIFF not followed by PPC_RELOC_PAIR");
+
+ word = (unsigned long)
+ (relocateAddress(oc, nSections, sections, scat->r_value)
+ - relocateAddress(oc, nSections, sections, pair->r_value));
+ i++;
+ }
+ else
+ continue; // ignore the others
+
+ if(scat->r_type == GENERIC_RELOC_VANILLA
+ || scat->r_type == PPC_RELOC_SECTDIFF)
+ {
+ *wordPtr = word;
+ }
+ else if(scat->r_type == PPC_RELOC_LO16_SECTDIFF)
+ {
+ ((unsigned short*) wordPtr)[1] = word & 0xFFFF;
+ }
+ else if(scat->r_type == PPC_RELOC_HI16_SECTDIFF)
+ {
+ ((unsigned short*) wordPtr)[1] = (word >> 16) & 0xFFFF;
+ }
+ else if(scat->r_type == PPC_RELOC_HA16_SECTDIFF)
+ {
+ ((unsigned short*) wordPtr)[1] = ((word >> 16) & 0xFFFF)
+ + ((word & (1<<15)) ? 1 : 0);
+ }
}
}
if(reloc->r_length == 2)
{
unsigned long word = 0;
-
+ unsigned long jumpIsland = 0;
+ long offsetToJumpIsland;
+
unsigned long* wordPtr = (unsigned long*) (image + sect->offset + reloc->r_address);
+ checkProddableBlock(oc,wordPtr);
if(reloc->r_type == GENERIC_RELOC_VANILLA)
{
}
if(reloc->r_pcrel)
+ {
+ jumpIsland = (long) makeJumpIsland(oc,reloc->r_symbolnum,(void*)word);
word -= ((long)image) + sect->offset + reloc->r_address;
+ if(jumpIsland != 0)
+ {
+ offsetToJumpIsland = jumpIsland
+ - (((long)image) + sect->offset + reloc->r_address);
+ }
+ }
}
if(reloc->r_type == GENERIC_RELOC_VANILLA)
}
else if(reloc->r_type == PPC_RELOC_BR24)
{
+ if((long)word > (long)0x01FFFFFF || (long)word < (long)0xFFE00000)
+ {
+ // The branch offset is too large.
+ // Therefore, we try to use a jump island.
+ if(jumpIsland == 0)
+ barf("unconditional relative branch out of range: "
+ "no jump island available");
+
+ word = offsetToJumpIsland;
+ if((long)word > (long)0x01FFFFFF || (long)word < (long)0xFFE00000)
+ barf("unconditional relative branch out of range: "
+ "jump island out of range");
+ }
*wordPtr = (*wordPtr & 0xFC000003) | (word & 0x03FFFFFC);
continue;
}
struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header));
unsigned i,curSymbol;
struct segment_command *segLC = NULL;
- struct section *sections, *la_ptrs = NULL, *nl_ptrs = NULL;
+ struct section *sections;
struct symtab_command *symLC = NULL;
struct dysymtab_command *dsymLC = NULL;
struct nlist *nlist;
for(i=0;i<segLC->nsects;i++)
{
- if(!strcmp(sections[i].sectname,"__la_symbol_ptr"))
- la_ptrs = §ions[i];
- else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr"))
- nl_ptrs = §ions[i];
-
- // for now, only add __text and __const to the sections table
- else if(!strcmp(sections[i].sectname,"__text"))
+ if(sections[i].size == 0)
+ continue;
+
+ if((sections[i].flags & SECTION_TYPE) == S_ZEROFILL)
+ {
+ char * zeroFillArea = stgCallocBytes(1,sections[i].size,
+ "ocGetNames_MachO(common symbols)");
+ sections[i].offset = zeroFillArea - image;
+ }
+
+ if(!strcmp(sections[i].sectname,"__text"))
addSection(oc, SECTIONKIND_CODE_OR_RODATA,
(void*) (image + sections[i].offset),
(void*) (image + sections[i].offset + sections[i].size));
addSection(oc, SECTIONKIND_RWDATA,
(void*) (image + sections[i].offset),
(void*) (image + sections[i].offset + sections[i].size));
+ else if(!strcmp(sections[i].sectname,"__bss")
+ || !strcmp(sections[i].sectname,"__common"))
+ addSection(oc, SECTIONKIND_RWDATA,
+ (void*) (image + sections[i].offset),
+ (void*) (image + sections[i].offset + sections[i].size));
+
+ addProddableBlock(oc, (void*) (image + sections[i].offset),
+ sections[i].size);
}
// count external symbols defined here
for(i=0;i<segLC->nsects;i++)
{
- if(!relocateSection(image,symLC,nlist,sections,§ions[i]))
+ if(!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,§ions[i]))
return 0;
}
/* Free the local symbol table; we won't need it again. */
freeHashTable(oc->lochash, NULL);
oc->lochash = NULL;
+
+ /*
+ Flush the data & instruction caches.
+ Because the PPC has split data/instruction caches, we have to
+ do that whenever we modify code at runtime.
+ */
+ {
+ int n = (oc->fileSize + islandSize * oc->n_islands) / 4;
+ unsigned long *p = (unsigned long*)oc->image;
+ while(n--)
+ {
+ __asm__ volatile ("dcbf 0,%0\n\tsync\n\ticbi 0,%0"
+ : : "r" (p));
+ p++;
+ }
+ __asm__ volatile ("sync\n\tisync");
+ }
return 1;
}