Use segments for section layout
authorArtem Pyanykh <artempyanykh@gmail.com>
Mon, 11 Feb 2019 11:05:26 +0000 (14:05 +0300)
committerMarge Bot <ben+marge-bot@smart-cactus.org>
Wed, 20 Mar 2019 23:52:39 +0000 (19:52 -0400)
rts/Linker.c
rts/LinkerInternals.h
rts/RtsUtils.c
rts/RtsUtils.h
rts/linker/MachO.c
rts/linker/MachO.h

index ba78efe..3d56989 100644 (file)
@@ -1195,6 +1195,7 @@ void freeObjectCode (ObjectCode *oc)
     }
 
     freeProddableBlocks(oc);
+    freeSegments(oc);
 
     /* Free symbol_extras.  On x86_64 Windows, symbol_extras are allocated
      * alongside the image, so we don't need to free. */
@@ -1279,6 +1280,8 @@ mkOc( pathchar *path, char *image, int imageSize,
    oc->symbols           = NULL;
    oc->n_sections        = 0;
    oc->sections          = NULL;
+   oc->n_segments        = 0;
+   oc->segments          = NULL;
    oc->proddables        = NULL;
    oc->stable_ptrs       = NULL;
 #if defined(NEED_SYMBOL_EXTRAS)
@@ -1840,3 +1843,50 @@ addSection (Section *s, SectionKind kind, SectionAlloc alloc,
                        start, (void*)((StgWord)start + size),
                        size, kind ));
 }
+
+/* -----------------------------------------------------------------------------
+ * Segment management
+ */
+void
+initSegment (Segment *s, void *start, size_t size, SegmentProt prot, int n_sections)
+{
+    s->start = start;
+    s->size = size;
+    s->prot = prot;
+    s->sections_idx = (int *)stgCallocBytes(n_sections, sizeof(int),
+                                               "initSegment(segment)");
+    s->n_sections = n_sections;
+}
+
+void freeSegments (ObjectCode *oc)
+{
+    if (oc->segments != NULL) {
+        IF_DEBUG(linker, debugBelch("freeSegments: freeing %d segments\n", oc->n_segments));
+
+        for (int i = 0; i < oc->n_segments; i++) {
+            Segment *s = &oc->segments[i];
+
+            IF_DEBUG(linker, debugBelch("freeSegments: freeing segment %d at %p size %zu\n",
+                                        i, s->start, s->size));
+
+            stgFree(s->sections_idx);
+            s->sections_idx = NULL;
+
+            if (0 == s->size) {
+                IF_DEBUG(linker, debugBelch("freeSegment: skipping segment of 0 size\n"));
+                continue;
+            } else {
+#if RTS_LINKER_USE_MMAP
+                CHECKM(0 == munmap(s->start, s->size), "freeSegments: failed to unmap memory");
+#else
+                stgFree(s->start);
+#endif
+            }
+            s->start = NULL;
+        }
+
+        stgFree(oc->segments);
+        oc->segments = NULL;
+    }
+}
+
index 81a09fe..cd36fd5 100644 (file)
@@ -62,6 +62,11 @@ typedef
         }
    SectionAlloc;
 
+typedef enum {
+    SEGMENT_PROT_RX,
+    SEGMENT_PROT_RW
+} SegmentProt;
+
 /*
  * Note [No typedefs for customizable types]
  * Some pointer-to-struct types are defined opaquely
@@ -103,6 +108,18 @@ typedef
    }
    ProddableBlock;
 
+typedef struct _Segment {
+    void *start;                /* page aligned start address of a segment */
+    size_t size;                /* page rounded size of a segment */
+    SegmentProt prot;           /* mem protection to set after all symbols were
+                                 * resolved */
+
+    int *sections_idx;       /* an array of section indexes assigned to this segment */
+    int n_sections;
+} Segment;
+
+/* todo (AP): add freeSegments */
+
 /*
  * We must keep track of the StablePtrs that are created for foreign
  * exports by constructor functions when the module is loaded, so that
@@ -179,9 +196,14 @@ typedef struct _ObjectCode {
 
     /* The section-kind entries for this object module.  Linked
        list. */
+    /* fixme (AP): doesn't look like a linked list. On MachO it's an array, and
+     * generally Section struct doesn't have pointers to next. */
     int n_sections;
     Section* sections;
 
+    int n_segments;
+    Segment *segments;
+
     /* Allow a chain of these things */
     struct _ObjectCode * next;
 
@@ -312,6 +334,9 @@ ObjectCode* mkOc( pathchar *path, char *image, int imageSize,
                   int misalignment
                   );
 
+void initSegment(Segment *s, void *start, size_t size, SegmentProt prot, int n_sections);
+void freeSegments(ObjectCode *oc);
+
 /* MAP_ANONYMOUS is MAP_ANON on some systems,
    e.g. OS X (before Sierra), OpenBSD etc */
 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
index d5fa168..b099fa2 100644 (file)
@@ -97,13 +97,13 @@ stgReallocBytes (void *p, size_t n, char *msg)
 }
 
 void *
-stgCallocBytes (size_t n, size_t m, char *msg)
+stgCallocBytes (size_t count, size_t size, char *msg)
 {
     void *space;
 
-    if ((space = calloc(n, m)) == NULL) {
+    if ((space = calloc(count, size)) == NULL) {
       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
-      rtsConfig.mallocFailHook((W_) n*m, msg);
+      rtsConfig.mallocFailHook((W_) count*size, msg);
       stg_exit(EXIT_INTERNAL_ERROR);
     }
     return space;
index 49712c0..5c986fe 100644 (file)
@@ -22,7 +22,7 @@ void *stgMallocBytes(size_t n, char *msg)
 
 void *stgReallocBytes(void *p, size_t n, char *msg);
 
-void *stgCallocBytes(size_t n, size_t m, char *msg)
+void *stgCallocBytes(size_t count, size_t size, char *msg)
      GNUC3_ATTRIBUTE(__malloc__);
 
 char *stgStrndup(const char *s, size_t n);
index 7ec3cab..772ae21 100644 (file)
@@ -1021,18 +1021,163 @@ relocateSection(ObjectCode* oc, int curSection)
  *   addressability for relative or absolute access.
  */
 
-size_t ocGetRequiredSpace_MachO(ObjectCode* oc)
+SectionKind
+getSectionKind_MachO(MachOSection *section)
 {
-    size_t bytesNeeded = 0;
-    size_t sectionSize;
+    SectionKind kind;
+
+    /* todo: Use section flags instead */
+    if (0==strcmp(section->sectname,"__text")) {
+        kind = SECTIONKIND_CODE_OR_RODATA;
+    } else if (0==strcmp(section->sectname,"__const") ||
+               0==strcmp(section->sectname,"__data") ||
+               0==strcmp(section->sectname,"__bss") ||
+               0==strcmp(section->sectname,"__common") ||
+               0==strcmp(section->sectname,"__mod_init_func")) {
+        kind = SECTIONKIND_RWDATA;
+    } else {
+        kind = SECTIONKIND_OTHER;
+    }
 
-    for(int i = 0; i < oc->n_sections; i++)
+    return kind;
+}
+
+/* Calculate the # of active segments and their sizes based on section
+ * sizes and alignments. This is done in 2 passes over sections:
+ * 1. Calculate how many sections is going to be in each segment and
+ * the total segment size.
+ * 2. Fill in segment's sections_idx arrays.
+ *
+ * gbZerofillSegment is there because of this comment in mach-o/loader.h:
+ * The gigabyte zero fill sections, those with the section type
+ * S_GB_ZEROFILL, can only be in a segment with sections of this
+ * type. These segments are then placed after all other segments.
+ */
+int
+ocBuildSegments_MachO(ObjectCode *oc)
+{
+    int n_rxSections = 0;
+    size_t size_rxSegment = 0;
+    Segment *rxSegment = NULL;
+
+    int n_rwSections = 0;
+    size_t size_rwSegment = 0;
+    Segment *rwSegment = NULL;
+
+    int n_gbZerofills = 0;
+    size_t size_gbZerofillSegment = 0;
+    Segment *gbZerofillSegment = NULL;
+
+    int n_activeSegments = 0;
+    int curSegment = 0;
+    size_t size_compound;
+
+    Segment *segments = NULL;
+    void *mem = NULL, *curMem = NULL;
+
+    for (int i = 0; i < oc->n_sections; i++) {
+        MachOSection *macho = &oc->info->macho_sections[i];
+        size_t alignment = 1 << macho->align;
+
+        if (S_GB_ZEROFILL == (macho->flags & SECTION_TYPE)) {
+            size_gbZerofillSegment = roundUpToAlign(size_gbZerofillSegment, alignment);
+            size_gbZerofillSegment += macho->size;
+            n_gbZerofills++;
+        } else if (getSectionKind_MachO(macho) == SECTIONKIND_CODE_OR_RODATA) {
+            size_rxSegment = roundUpToAlign(size_rxSegment, alignment);
+            size_rxSegment += macho->size;
+            n_rxSections++;
+        } else {
+            size_rwSegment = roundUpToAlign(size_rwSegment, alignment);
+            size_rwSegment += macho->size;
+            n_rwSections++;
+        }
+    }
+
+    size_compound = roundUpToPage(size_rxSegment) +
+        roundUpToPage(size_rwSegment) +
+        roundUpToPage(size_gbZerofillSegment);
+
+    if (n_rxSections > 0) {
+        n_activeSegments++;
+    }
+    if (n_rwSections > 0) {
+        n_activeSegments++;
+    }
+    if (n_gbZerofills >0) {
+        n_activeSegments++;
+    }
+
+    mem = mmapForLinker(size_compound, MAP_ANON, -1, 0);
+    if (NULL == mem) return 0;
+
+    IF_DEBUG(linker, debugBelch("ocBuildSegments: allocating %d segments\n", n_activeSegments));
+    segments = (Segment*)stgCallocBytes(n_activeSegments, sizeof(Segment),
+                                        "ocBuildSegments_MachO(segments)");
+    curMem = mem;
+
+    /* Allocate space for RX segment */
+    if (n_rxSections > 0) {
+        rxSegment = &segments[curSegment];
+        initSegment(rxSegment,
+                    curMem,
+                    roundUpToPage(size_rxSegment),
+                    SEGMENT_PROT_RX,
+                    n_rxSections);
+        IF_DEBUG(linker, debugBelch("ocBuildSegments_MachO: init segment %d (RX) at %p size %zu\n",
+                                    curSegment, rxSegment->start, rxSegment->size));
+        curMem = (char *)curMem + rxSegment->size;
+        curSegment++;
+    }
+
+    /* Allocate space for RW segment */
+    if (n_rwSections > 0) {
+        rwSegment = &segments[curSegment];
+        initSegment(rwSegment,
+                    curMem,
+                    roundUpToPage(size_rwSegment),
+                    SEGMENT_PROT_RW,
+                    n_rwSections);
+        IF_DEBUG(linker, debugBelch("ocBuildSegments_MachO: init segment %d (RW) at %p size %zu\n",
+                                    curSegment, rwSegment->start, rwSegment->size));
+        curMem = (char *)curMem + rwSegment->size;
+        curSegment++;
+    }
+
+    /* Allocate space for GB_ZEROFILL segment */
+    if (n_gbZerofills > 0) {
+        gbZerofillSegment = &segments[curSegment];
+        initSegment(gbZerofillSegment,
+                    curMem,
+                    roundUpToPage(size_gbZerofillSegment),
+                    SEGMENT_PROT_RW,
+                    n_gbZerofills);
+        IF_DEBUG(linker, debugBelch("ocBuildSegments_MachO: init segment %d (GB_ZEROFILL) at %p size %zu\n",
+                                    curSegment, gbZerofillSegment->start, gbZerofillSegment->size));
+        curMem = (char *)curMem + gbZerofillSegment->size;
+        curSegment++;
+    }
+
+    /* Second pass over sections to fill in sections_idx arrays */
+    for (int i = 0, rx = 0, rw = 0, gb = 0;
+         i < oc->n_sections;
+         i++)
     {
-        sectionSize = oc->info->macho_sections[i].size;
-        bytesNeeded += roundUpToPage(sectionSize);
+        MachOSection *macho = &oc->info->macho_sections[i];
+
+        if (S_GB_ZEROFILL == (macho->flags & SECTION_TYPE)) {
+            gbZerofillSegment->sections_idx[gb++] = i;
+        } else if (getSectionKind_MachO(macho) == SECTIONKIND_CODE_OR_RODATA) {
+            rxSegment->sections_idx[rx++] = i;
+        } else {
+            rwSegment->sections_idx[rw++] = i;
+        }
     }
 
-    return bytesNeeded;
+    oc->segments = segments;
+    oc->n_segments = n_activeSegments;
+
+    return 1;
 }
 
 int
@@ -1048,27 +1193,16 @@ ocGetNames_MachO(ObjectCode* oc)
 
     Section *secArray;
     secArray = (Section*)stgCallocBytes(
-         sizeof(Section),
-         oc->info->segCmd->nsects,
-         "ocGetNames_MachO(sections)");
+        oc->info->segCmd->nsects,
+        sizeof(Section),
+        "ocGetNames_MachO(sections)");
 
     oc->sections = secArray;
 
     IF_DEBUG(linker, debugBelch("ocGetNames_MachO: will load %d sections\n",
                                 oc->n_sections));
 
-#if defined(darwin_HOST_OS)
-    size_t requiredSize = ocGetRequiredSpace_MachO(oc);
-    IF_DEBUG(linker, debugBelch("ocGetNames_MachO: required mmaped space is %zu\n",
-                                requiredSize));
-
-    void* memStart = mmapForLinker(requiredSize, MAP_ANONYMOUS, -1, 0);
-    if (memStart == NULL) {
-        barf("ocGetNames_MachO: failed to mmap");
-    }
-    void* memCur = memStart;
-#endif // darwin_HOST_OS
-
+#if defined (ios_HOST_OS)
     for(int i=0; i < oc->n_sections; i++)
     {
         MachOSection * section = &oc->info->macho_sections[i];
@@ -1080,23 +1214,9 @@ ocGetNames_MachO(ObjectCode* oc)
             continue;
         }
 
-        // XXX, use SECTION_TYPE attributes, instead of relying on the name?
-
-        SectionKind kind = SECTIONKIND_OTHER;
-
-        if (0==strcmp(section->sectname,"__text")) {
-            kind = SECTIONKIND_CODE_OR_RODATA;
-        }
-        else if (0==strcmp(section->sectname,"__const") ||
-                 0==strcmp(section->sectname,"__data") ||
-                 0==strcmp(section->sectname,"__bss") ||
-                 0==strcmp(section->sectname,"__common") ||
-                 0==strcmp(section->sectname,"__mod_init_func")) {
-            kind = SECTIONKIND_RWDATA;
-        }
+        SectionKind kind = getSectionKind_MachO(section);
 
         switch(section->flags & SECTION_TYPE) {
-#if defined(ios_HOST_OS)
             case S_ZEROFILL:
             case S_GB_ZEROFILL: {
                 // See Note [mmap r+w+x]
@@ -1105,7 +1225,8 @@ ocGetNames_MachO(ObjectCode* oc)
                                   MAP_ANON | MAP_PRIVATE,
                                   -1, 0);
                 if( mem == MAP_FAILED ) {
-                    barf("failed to mmap allocate memory for zerofill section %d of size %d. errno = %d", i, section->size, errno);
+                    barf("failed to mmap allocate memory for zerofill section %d of size %d. errno = %d",
+                         i, section->size, errno);
                 }
                 addSection(&secArray[i], kind, SECTION_MMAP, mem, section->size,
                            0, mem, roundUpToPage(section->size));
@@ -1177,72 +1298,76 @@ ocGetNames_MachO(ObjectCode* oc)
                   = (MachORelocationInfo*)(oc->image + section->reloff);
                 break;
             }
+        }
+    }
+#else /* !ios_HOST_OS */
+    IF_DEBUG(linker, debugBelch("ocGetNames_MachO: building segments\n"));
 
-#else /* any other host */
-            case S_ZEROFILL:
-            case S_GB_ZEROFILL: {
-                char * zeroFillArea;
-                if (RTS_LINKER_USE_MMAP) {
-                    zeroFillArea = mmapForLinker(section->size, MAP_ANONYMOUS,
-                                                 -1, 0);
-                    if (zeroFillArea == NULL) return 0;
-                    memset(zeroFillArea, 0, section->size);
-                }
-                else {
-                    zeroFillArea = stgCallocBytes(1,section->size,
-                                                  "ocGetNames_MachO(common symbols)");
-                }
-                section->offset = zeroFillArea - oc->image; // fixme: fails down the road
-                                                            // when zFA < oc->image since offset is unsigned
+    CHECKM(ocBuildSegments_MachO(oc), "ocGetNames_MachO: failed to build segments\n");
+
+    for (int seg_n = 0; seg_n < oc->n_segments; seg_n++) {
+        Segment *segment = &oc->segments[seg_n];
+        void *curMem = segment->start;
 
-                addSection(&secArray[i], kind, SECTION_NOMEM, // fixme: why NOMEM?
-                           (void *)(oc->image + section->offset),
-                           section->size,
-                           0, 0, 0);
+        IF_DEBUG(linker,
+                 debugBelch("ocGetNames_MachO: loading segment %d "
+                            "(address = %p, size = %zu) "
+                            "with %d sections\n",
+                            seg_n, segment->start, segment->size, segment->n_sections));
 
-                addProddableBlock(oc,
-                                  (void *) (oc->image + section->offset),
-                                  section->size);
+        for (int sec_n = 0; sec_n < segment->n_sections; sec_n++) {
+            int sec_idx = segment->sections_idx[sec_n];
+            MachOSection *section = &oc->info->macho_sections[sec_idx];
 
-                secArray[i].info->nstubs = 0;
-                secArray[i].info->stub_offset = NULL;
-                secArray[i].info->stub_size = 0;
-                secArray[i].info->stubs = NULL;
+            size_t alignment = 1 << section->align;
+            SectionKind kind = getSectionKind_MachO(section);
 
-                secArray[i].info->macho_section = section;
-                secArray[i].info->relocation_info
-                = (MachORelocationInfo*)(oc->image + section->reloff);
+            void *secMem = (void *)roundUpToAlign((size_t)curMem, alignment);
+
+            IF_DEBUG(linker,
+                     debugBelch("ocGetNames_MachO: loading section %d in segment %d "
+                                "(#%d, %s %s)\n"
+                                "                  skipped %zu bytes due to alignment of %zu\n",
+                                sec_n, seg_n, sec_idx, section->segname, section->sectname,
+                                (char *)secMem - (char *)curMem, alignment));
+
+            switch (section->flags & SECTION_TYPE) {
+            case S_ZEROFILL:
+            case S_GB_ZEROFILL:
+                IF_DEBUG(linker, debugBelch("ocGetNames_MachO: memset to 0 a ZEROFILL section\n"));
+                memset(secMem, 0, section->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));
+
+                memcpy(secMem, oc->image + section->offset, section->size);
             }
-            default: {
-                size_t sectionRoundedSize = roundUpToPage(section->size);
 
-                IF_DEBUG(linker, debugBelch("ocGetNames_MachO: copying from %p to %p a block of %" PRIu64 " bytes\n",
-                                            (void *) (oc->image + section->offset), memCur, section->size));
-                memcpy(memCur, oc->image + section->offset, section->size);
-                IF_DEBUG(linker, debugBelch("ocGetNames_MachO: copied %" PRIu64 " bytes\n",
-                                            section->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);
 
-                addSection(&secArray[i], kind, SECTION_MMAP,
-                           memCur, section->size,
-                           0, memCur, sectionRoundedSize);
-                addProddableBlock(oc, memCur, section->size);
+            curMem = (char*) secMem + section->size;
 
-                memCur = (char*) memCur + sectionRoundedSize;
+            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;
 
-                secArray[i].info->nstubs = 0;
-                secArray[i].info->stub_offset = NULL;
-                secArray[i].info->stub_size = 0;
-                secArray[i].info->stubs = NULL;
-
-                secArray[i].info->macho_section = section;
-                secArray[i].info->relocation_info
+            secArray[sec_idx].info->macho_section = section;
+            secArray[sec_idx].info->relocation_info
                 = (MachORelocationInfo*)(oc->image + section->reloff);
-            }
-#endif
+
         }
 
     }
+#endif
+
     /* now, as all sections have been loaded, we can resolve the absolute
      * address of symbols defined in those sections.
      */
index 20cb2d0..518c2ce 100644 (file)
@@ -9,11 +9,13 @@
 void   ocInit_MachO                 ( ObjectCode* oc );
 void   ocDeinit_MachO               ( ObjectCode* oc );
 int    ocVerifyImage_MachO          ( ObjectCode* oc );
-size_t ocGetRequiredSpace_MachO     ( ObjectCode* oc);
+int    ocBuildSegments_MachO        ( ObjectCode* oc );
 int    ocGetNames_MachO             ( ObjectCode* oc );
 int    ocResolve_MachO              ( ObjectCode* oc );
 int    ocRunInit_MachO              ( ObjectCode* oc );
 int    machoGetMisalignment         ( FILE * );
 int    ocAllocateExtras_MachO       ( ObjectCode* oc );
 
+SectionKind getSectionKind_MachO    ( MachOSection *macho );
+
 #include "EndPrivate.h"