[project @ 2003-12-17 17:18:27 by sof]
[ghc-hetmet.git] / ghc / rts / Linker.c
index 5e54215..85d5809 100644 (file)
@@ -1,5 +1,4 @@
 /* -----------------------------------------------------------------------------
- * $Id: Linker.c,v 1.134 2003/09/26 09:26:12 panne Exp $
  *
  * (c) The GHC Team, 2000-2003
  *
@@ -89,6 +88,7 @@ static int ocVerifyImage_PEi386 ( ObjectCode* oc );
 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 );
@@ -229,6 +229,7 @@ typedef struct _RtsSymbolVal {
 #define RTS_MINGW_ONLY_SYMBOLS                  \
       SymX(asyncReadzh_fast)                   \
       SymX(asyncWritezh_fast)                  \
+      SymX(asyncDoProczh_fast)                 \
       SymX(memset)                              \
       SymX(inet_ntoa)                           \
       SymX(inet_addr)                           \
@@ -369,7 +370,7 @@ typedef struct _RtsSymbolVal {
       SymX(divExactIntegerzh_fast)             \
       SymX(divModIntegerzh_fast)               \
       SymX(forkzh_fast)                                \
-      SymX(forkProcesszh_fast)                  \
+      SymX(forkProcess)                                \
       SymX(forkOS_createThread)                        \
       SymX(freeHaskellFunctionPtr)             \
       SymX(freeStablePtr)                      \
@@ -553,6 +554,13 @@ typedef struct _RtsSymbolVal {
 #define RTS_LONG_LONG_SYMS /* nothing */
 #endif
 
+#ifdef HAVE_TERMIOS_H
+#define RTS_TERMIOS_SYMS                        \
+      Sym(saved_termios)
+#else
+#define RTS_TERMIOS_SYMS /* nothing */
+#endif
+
 // 64-bit support functions in libgcc.a
 #if defined(__GNUC__) && SIZEOF_VOID_P <= 4
 #define RTS_LIBGCC_SYMBOLS                     \
@@ -594,6 +602,7 @@ RTS_LONG_LONG_SYMS
 RTS_POSIX_ONLY_SYMBOLS
 RTS_MINGW_ONLY_SYMBOLS
 RTS_CYGWIN_ONLY_SYMBOLS
+RTS_TERMIOS_SYMS
 RTS_LIBGCC_SYMBOLS
 #undef Sym
 #undef SymX
@@ -743,11 +752,8 @@ addDLL( char *dll_name )
 
    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();
@@ -1048,6 +1054,11 @@ loadObj( char *path )
 
 #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 );
@@ -1907,11 +1918,16 @@ ocResolve_PEi386 ( ObjectCode* oc )
         * real count can be found in the first reloc entry.
          *
         * See Section 4.1 (last para) of the PE spec (rev6.0).
+        *
+        * Nov2003 update: the GNU linker still doesn't correctly 
+        * handle the generation of relocatable object files with 
+        * overflown relocations. Hence the output to warn of potential 
+        * troubles.
         */
         COFF_reloc* rel = (COFF_reloc*)
                            myindex ( sizeof_COFF_reloc, reltab, 0 );
        noRelocs = rel->VirtualAddress;
-       fprintf(stderr, "Overflown relocs: %u\n", noRelocs);
+       fprintf(stderr, "WARNING: Overflown relocation field (# relocs found: %u)\n", noRelocs); fflush(stderr);
        j = 1;
       } else {
        noRelocs = sectab_i->NumberOfRelocations;
@@ -2994,18 +3010,70 @@ ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *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
@@ -3049,11 +3117,56 @@ static int resolveImports(
     return 1;
 }
 
+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;
@@ -3075,12 +3188,55 @@ static int relocateSection(
            
            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);
                    
-                   checkProddableBlock(oc,word);
-                   *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);
+                    }
                }
            }
            
@@ -3095,7 +3251,9 @@ static int relocateSection(
            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);
                
@@ -3146,7 +3304,15 @@ static int relocateSection(
                    }
                    
                    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)
@@ -3172,6 +3338,19 @@ static int relocateSection(
                }
                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;
                }
@@ -3190,7 +3369,7 @@ static int ocGetNames_MachO(ObjectCode* oc)
     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;
@@ -3214,13 +3393,17 @@ static int ocGetNames_MachO(ObjectCode* oc)
 
     for(i=0;i<segLC->nsects;i++)
     {
-       if(!strcmp(sections[i].sectname,"__la_symbol_ptr"))
-           la_ptrs = &sections[i];
-       else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr"))
-           nl_ptrs = &sections[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));
@@ -3232,10 +3415,14 @@ static int ocGetNames_MachO(ObjectCode* oc)
            addSection(oc, SECTIONKIND_RWDATA, 
                (void*) (image + sections[i].offset),
                (void*) (image + sections[i].offset + sections[i].size));
-               
-       if(sections[i].size > 0)    // size 0 segments do exist
-           addProddableBlock(oc, (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
@@ -3352,13 +3539,30 @@ static int ocResolve_MachO(ObjectCode* oc)
     
     for(i=0;i<segLC->nsects;i++)
     {
-       if(!relocateSection(oc,image,symLC,nlist,sections,&sections[i]))
+       if(!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,&sections[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;
 }