Improve section to segment mapping code.
authorNick Clifton <nickc@redhat.com>
Mon, 31 Jul 2000 18:45:26 +0000 (18:45 +0000)
committerNick Clifton <nickc@redhat.com>
Mon, 31 Jul 2000 18:45:26 +0000 (18:45 +0000)
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf.c
bfd/section.c

index 9b471b8..fb20f01 100644 (file)
@@ -1,3 +1,25 @@
+2000-07-31  Nick Clifton  <nickc@cygnus.com>
+
+       * section.c (struct sec): Add new boolean field 'segment_mark'.
+       (STD_SECTION): Initialise new field to zero.
+
+       * bfd-in2.h: Regenerate.
+
+       * elf.c (copy_private_bfd_data): Reorganise section to segment 
+       mapping to cope with moved sections requiring new segments,
+       and overlapping segments.
+       (SEGMENT_END): New macro: Return the end address of a segment.
+       (IS_CONTAINED_BY_VMA): New macro: Determine if a segment
+       contains a section by comparing their VMA addresses.
+       (IS_CONTAINED_BY_LMA): New macro: Determine if a segment
+       contains a section by comparing their LMA addresses.
+       (INCLUDE_SECTION_IN_SEGMENT): New macro: Determine if a
+       section should be included in a segment.
+       (SEGMENT_AFTER_SEGMENT): New macro: Determine if one segment
+       follows another in memory.
+       (SEGMENT_OVERLAPS_SEGMENT): New macro: Determine if two
+       segments overlap.
+
 200007-22  Jason Eckhardt  <jle@cygnus.com>
 
        * cpu-i860.c: Added comments.
index ed4113b..c82df23 100644 (file)
@@ -1105,6 +1105,9 @@ typedef struct sec
   /* A mark flag used by some linker backends for garbage collection.  */
   unsigned int gc_mark : 1;
 
+  /* Used by the ELF code to mark sections which have been allocated to segments.  */
+  unsigned int segment_mark : 1;
+
   /* End of internal packed boolean fields.  */
 
   /*  The virtual memory address of the section - where it will be
index 0da1c95..297c29d 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -981,7 +981,7 @@ _bfd_elf_link_hash_copy_indirect (dir, ind)
 }
 
 void
-_bfd_elf_link_hash_hide_symbol(info, h)
+_bfd_elf_link_hash_hide_symbol (info, h)
      struct bfd_link_info *info ATTRIBUTE_UNUSED;
      struct elf_link_hash_entry *h;
 {
@@ -3619,6 +3619,10 @@ copy_private_bfd_data (ibfd, obfd)
   unsigned int i;
   unsigned int num_segments;
   boolean phdr_included = false;
+  asection *s;
+  bfd_vma maxpagesize;
+  struct elf_segment_map * phdr_adjust_seg = NULL;
+  unsigned int phdr_adjust_num = 0;
 
   if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
       || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
@@ -3633,41 +3637,137 @@ copy_private_bfd_data (ibfd, obfd)
   pm = &mfirst;
 
   num_segments = elf_elfheader (ibfd)->e_phnum;
-
-#define IS_CONTAINED_BY(addr, len, bottom, phdr)                       \
-         ((addr) >= (bottom)                                           \
-          && (   ((addr) + (len)) <= ((bottom) + (phdr)->p_memsz)      \
-              || ((addr) + (len)) <= ((bottom) + (phdr)->p_filesz)))
+  maxpagesize = get_elf_backend_data (obfd)->maxpagesize;
+
+  /* Returns the end address of the segment + 1.  */
+#define SEGMENT_END(segment, start)                    \
+  (start + (segment->p_memsz > segment->p_filesz       \
+   ? segment->p_memsz : segment->p_filesz))
+
+  /* Returns true if the given section is contained within
+     the given segment.  VMA addresses are compared.  */
+#define IS_CONTAINED_BY_VMA(section, segment)          \
+  (section->vma >= segment->p_vaddr                    \
+   && (section->vma + section->_raw_size)              \
+   <= (SEGMENT_END (segment, segment->p_vaddr)))
+    
+  /* Returns true if the given section is contained within
+     the given segment.  LMA addresses are compared.  */
+#define IS_CONTAINED_BY_LMA(section, segment, base)    \
+    (section->lma >= base                              \
+     && (section->lma + section->_raw_size)            \
+     <= SEGMENT_END (segment, base))
 
   /* Special case: corefile "NOTE" section containing regs, prpsinfo etc. */
-
-#define IS_COREFILE_NOTE(p, s)                                          \
-           (p->p_type == PT_NOTE                                       \
-            && bfd_get_format (ibfd) == bfd_core                       \
-            && s->vma == 0 && s->lma == 0                              \
-            && (bfd_vma) s->filepos >= p->p_offset                     \
-            && (bfd_vma) s->filepos + s->_raw_size                     \
+#define IS_COREFILE_NOTE(p, s)                          \
+           (p->p_type == PT_NOTE                       \
+            && bfd_get_format (ibfd) == bfd_core       \
+            && s->vma == 0 && s->lma == 0              \
+            && (bfd_vma) s->filepos >= p->p_offset     \
+            && (bfd_vma) s->filepos + s->_raw_size     \
             <= p->p_offset + p->p_filesz)
 
   /* The complicated case when p_vaddr is 0 is to handle the Solaris
      linker, which generates a PT_INTERP section with p_vaddr and
      p_memsz set to 0.  */
-
-#define IS_SOLARIS_PT_INTERP(p, s)                                     \
-           (p->p_vaddr == 0                                            \
-            && p->p_filesz > 0                                         \
-            && (s->flags & SEC_HAS_CONTENTS) != 0                      \
-            && s->_raw_size > 0                                        \
-            && (bfd_vma) s->filepos >= p->p_offset                     \
-            && ((bfd_vma) s->filepos + s->_raw_size                    \
+#define IS_SOLARIS_PT_INTERP(p, s)                     \
+           (p->p_vaddr == 0                            \
+            && p->p_filesz > 0                         \
+            && (s->flags & SEC_HAS_CONTENTS) != 0      \
+            && s->_raw_size > 0                        \
+            && (bfd_vma) s->filepos >= p->p_offset     \
+            && ((bfd_vma) s->filepos + s->_raw_size    \
                     <= p->p_offset + p->p_filesz))
 
+  /* Decide if the given section should be included in the given segment.
+     A section will be included if:
+       1. It is within the address space of the segment,
+       2. It is an allocated segment,
+       3. There is an output section associated with it,
+       4. The section has not already been allocated to a previous segment.  */
+#define INCLUDE_SECTION_IN_SEGMENT(section, segment)   \
+  ((((IS_CONTAINED_BY_VMA (section, segment)           \
+      || IS_SOLARIS_PT_INTERP (segment, section))      \
+     && (section->flags & SEC_ALLOC) != 0)             \
+    || IS_COREFILE_NOTE (segment, section))            \
+   && section->output_section != NULL                  \
+   && section->segment_mark == false)
+
+  /* Returns true iff seg1 starts after the end of seg2.  */
+#define SEGMENT_AFTER_SEGMENT(seg1, seg2)              \
+    (seg1->p_vaddr >= SEGMENT_END (seg2, seg2->p_vaddr))
+
+  /* Returns true iff seg1 and seg2 overlap.  */
+#define SEGMENT_OVERLAPS(seg1, seg2)                   \
+  (!(SEGMENT_AFTER_SEGMENT (seg1, seg2) && SEGMENT_AFTER_SEGMENT (seg2, seg1)))
+
+  
+  /* Initialise the segment mark field.  */
+  for (s = ibfd->sections; s != NULL; s = s->next)
+    s->segment_mark = false;
+
   /* Scan through the segments specified in the program header
-     of the input BFD.  */
+     of the input BFD.  For this first scan we look for overlaps.
+     These can be created by wierd parameters to objcopy.  */
+  for (i = 0, p = elf_tdata (ibfd)->phdr; i < num_segments; i ++, p ++)
+    {
+      unsigned int j;
+      Elf_Internal_Phdr * pp;
+
+      if (p->p_type == PT_NULL)
+       continue;
+      
+      /* Determine if this segment overlaps any previous segments.
+        This can happen when objcopy is used to adjust section LMAs.  */
+      for (j = 0, pp = elf_tdata (ibfd)->phdr; j < i; j ++, pp ++)
+       {
+         bfd_signed_vma extra_length;
+         
+         
+         if (pp->p_type == PT_NULL || ! SEGMENT_OVERLAPS (p, pp))
+           continue;
+         
+         /* Merge the two segments together.  */
+         if (pp->p_vaddr < p->p_vaddr)
+           {
+             /* Extend PP to include P and then delete P.  */
+             extra_length =
+               SEGMENT_END (p, p->p_vaddr) - SEGMENT_END (pp, pp->p_vaddr);
+             
+             if (extra_length > 0)
+               {
+                 pp->p_memsz  += extra_length;
+                 pp->p_filesz += extra_length;
+               }
+             
+             p->p_type = PT_NULL;
+             
+             /* Since we have deleted P we must restart the outer loop.  */
+             i = 0;
+             p = elf_tdata (ibfd)->phdr;
+             break;
+           }
+         else
+           {
+             /* Extend P to include PP and then delete PP.  */
+             extra_length =
+               SEGMENT_END (pp, pp->p_vaddr) - SEGMENT_END (p, p->p_vaddr);
+             
+             if (extra_length > 0)
+               {
+                 p->p_memsz  += extra_length;
+                 p->p_filesz += extra_length;
+               }
+             
+             pp->p_type = PT_NULL;
+           }
+       }
+    }
+  
+  /* The second scan attempts to assign sections to segments.  */
   for (i = 0, p = elf_tdata (ibfd)->phdr; i < num_segments; i++, p++)
     {
       unsigned int csecs;
-      asection *s;
       asection **sections;
       asection *os;
       unsigned int isec;
@@ -3675,22 +3775,14 @@ copy_private_bfd_data (ibfd, obfd)
       bfd_vma suggested_lma;
       unsigned int j;
 
-      /* For each section in the input BFD, decide if it should be
-        included in the current segment.  A section will be included
-        if it is within the address space of the segment, and it is
-        an allocated segment, and there is an output section
-        associated with it.  */
+      if (p->p_type == PT_NULL)
+       continue;
+      
+      /* Compute how many sections might be placed into this segment.  */
       csecs = 0;
       for (s = ibfd->sections; s != NULL; s = s->next)
-       if (s->output_section != NULL)
-         {
-           if ((IS_CONTAINED_BY (s->vma, s->_raw_size, p->p_vaddr, p)
-                || IS_SOLARIS_PT_INTERP (p, s))
-               && (s->flags & SEC_ALLOC) != 0)
-             ++csecs;
-           else if (IS_COREFILE_NOTE (p, s))
-             ++csecs;
-         }
+       if (INCLUDE_SECTION_IN_SEGMENT (s, p))
+         ++csecs;
 
       /* Allocate a segment map big enough to contain all of the
         sections we have selected.  */
@@ -3733,7 +3825,6 @@ copy_private_bfd_data (ibfd, obfd)
          /* Special segments, such as the PT_PHDR segment, may contain
             no sections, but ordinary, loadable segments should contain
             something.  */
-
          if (p->p_type == PT_LOAD)
              _bfd_error_handler
                (_("%s: warning: Empty loadable segment detected\n"),
@@ -3784,21 +3875,16 @@ copy_private_bfd_data (ibfd, obfd)
         case, where the sections have not been moved, this means that
         we have completely filled the segment, and there is nothing
         more to do.  */
-
       isec = 0;
       matching_lma = 0;
       suggested_lma = 0;
 
       for (j = 0, s = ibfd->sections; s != NULL; s = s->next)
        {
-         os = s->output_section;
-
-         if ((((IS_CONTAINED_BY (s->vma, s->_raw_size, p->p_vaddr, p)
-                || IS_SOLARIS_PT_INTERP (p, s))
-               && (s->flags & SEC_ALLOC) != 0)
-              || IS_COREFILE_NOTE (p, s))
-             && os != NULL)
+         if (INCLUDE_SECTION_IN_SEGMENT (s, p))
            {
+             os = s->output_section;
+
              sections[j++] = s;
 
              /* The Solaris native linker always sets p_paddr to 0.
@@ -3819,14 +3905,14 @@ copy_private_bfd_data (ibfd, obfd)
 
              /* Match up the physical address of the segment with the
                 LMA address of the output section.  */
-             if (IS_CONTAINED_BY (os->lma, os->_raw_size, m->p_paddr, p)
+             if (IS_CONTAINED_BY_LMA (os, p, m->p_paddr)
                  || IS_COREFILE_NOTE (p, s))
                {
                  if (matching_lma == 0)
                    matching_lma = os->lma;
 
                  /* We assume that if the section fits within the segment
-                    that it does not overlap any other section within that
+                    then it does not overlap any other section within that
                     segment.  */
                  m->sections[isec++] = os;
                }
@@ -3859,7 +3945,6 @@ copy_private_bfd_data (ibfd, obfd)
              /* At least one section fits inside the current segment.
                 Keep it, but modify its physical address to match the
                 LMA of the first section that fitted.  */
-
              m->p_paddr = matching_lma;
            }
          else
@@ -3867,17 +3952,26 @@ copy_private_bfd_data (ibfd, obfd)
              /* None of the sections fitted inside the current segment.
                 Change the current segment's physical address to match
                 the LMA of the first section.  */
-
              m->p_paddr = suggested_lma;
            }
 
-         /* Offset the segment physical address from the lma to allow
-            for space taken up by elf headers.  */
+         /* Offset the segment physical address from the lma
+            to allow for space taken up by elf headers.  */
          if (m->includes_filehdr)
            m->p_paddr -= iehdr->e_ehsize;
 
          if (m->includes_phdrs)
-           m->p_paddr -= iehdr->e_phnum * iehdr->e_phentsize;
+           {
+             m->p_paddr -= iehdr->e_phnum * iehdr->e_phentsize;
+
+             /* iehdr->e_phnum is just an estimate of the number
+                of program headers that we will need.  Make a note
+                here of the number we used and the segment we chose
+                to hold these headers, so that we can adjust the
+                offset when we know the correct value.  */
+             phdr_adjust_num = iehdr->e_phnum;
+             phdr_adjust_seg = m;
+           }
        }
 
       /* Step Three: Loop over the sections again, this time assigning
@@ -3903,7 +3997,9 @@ copy_private_bfd_data (ibfd, obfd)
 
              os = s->output_section;
 
-             if (IS_CONTAINED_BY (os->lma, os->_raw_size, m->p_paddr, p)
+             BFD_ASSERT (os != NULL);
+             
+             if (IS_CONTAINED_BY_LMA (os, p, m->p_paddr)
                  || IS_COREFILE_NOTE (p, s))
                {
                  if (m->count == 0)
@@ -3921,16 +4017,15 @@ copy_private_bfd_data (ibfd, obfd)
                  else
                    {
                      asection * prev_sec;
-                     bfd_vma maxpagesize;
 
                      prev_sec = m->sections[m->count - 1];
-                     maxpagesize = get_elf_backend_data (obfd)->maxpagesize;
 
                      /* If the gap between the end of the previous section
                         and the start of this section is more than maxpagesize
                         then we need to start a new segment.  */
-                     if (BFD_ALIGN (prev_sec->lma + prev_sec->_raw_size, maxpagesize)
+                     if ((BFD_ALIGN (prev_sec->lma + prev_sec->_raw_size, maxpagesize)
                          < BFD_ALIGN (os->lma, maxpagesize))
+                         || ((prev_sec->lma + prev_sec->_raw_size) > os->lma))
                        {
                          if (suggested_lma == 0)
                            suggested_lma = os->lma;
@@ -3942,6 +4037,7 @@ copy_private_bfd_data (ibfd, obfd)
                  m->sections[m->count++] = os;
                  ++isec;
                  sections[j] = NULL;
+                 s->segment_mark = true;
                }
              else if (suggested_lma == 0)
                suggested_lma = os->lma;
@@ -3958,7 +4054,6 @@ copy_private_bfd_data (ibfd, obfd)
              /* We still have not allocated all of the sections to
                 segments.  Create a new segment here, initialise it
                 and carry on looping.  */
-
              m = ((struct elf_segment_map *)
                   bfd_alloc (obfd,
                              (sizeof (struct elf_segment_map)
@@ -3969,7 +4064,6 @@ copy_private_bfd_data (ibfd, obfd)
              /* Initialise the fields of the segment map.  Set the physical
                 physical address to the LMA of the first section that has
                 not yet been assigned.  */
-
              m->next             = NULL;
              m->p_type           = p->p_type;
              m->p_flags          = p->p_flags;
@@ -4000,8 +4094,22 @@ copy_private_bfd_data (ibfd, obfd)
 
   elf_tdata (obfd)->segment_map = mfirst;
 
+  /* If we had to estimate the number of program headers that were
+     going to be needed, then check our estimate know and adjust
+     the offset if necessary.  */
+  if (phdr_adjust_seg != NULL)
+    {
+      unsigned int count;
+      
+      for (count = 0, m = mfirst; m != NULL; m = m->next)
+       count ++;
+
+      if (count > phdr_adjust_num)
+       phdr_adjust_seg->p_paddr -= (count - phdr_adjust_num) * iehdr->e_phentsize;
+    }
+  
 #if 0
-  /* Final Step: Sort the segments into ascending order of physical address. */
+  /* Final Step: Sort the segments into ascending order of physical address.  */
   if (mfirst != NULL)
     {
       struct elf_segment_map* prev;
@@ -4009,15 +4117,15 @@ copy_private_bfd_data (ibfd, obfd)
       prev = mfirst;
       for (m = mfirst->next; m != NULL; prev = m, m = m->next)
        {
-         /* Yes I know - its a bubble sort....*/
+         /* Yes I know - its a bubble sort....  */
          if (m->next != NULL && (m->next->p_paddr < m->p_paddr))
            {
-             /* swap m and m->next */
+             /* swap m and m->next */
              prev->next = m->next;
              m->next = m->next->next;
              prev->next->next = m;
 
-             /* restart loop. */
+             /* restart loop.  */
              m = mfirst;
            }
        }
index 9006d19..b9a39b6 100644 (file)
@@ -363,6 +363,9 @@ CODE_FRAGMENT
 .  {* A mark flag used by some linker backends for garbage collection.  *}
 .  unsigned int gc_mark : 1;
 .
+.  {* Used by the ELF code to mark sections which have been allocated to segments.  *}
+.  unsigned int segment_mark : 1;
+.
 .  {* End of internal packed boolean fields.  *}
 .
 .  {*  The virtual memory address of the section - where it will be
@@ -549,17 +552,17 @@ static const asymbol global_syms[] =
   GLOBAL_SYM_INIT (BFD_IND_SECTION_NAME, &bfd_ind_section)
 };
 
-#define STD_SECTION(SEC, FLAGS, SYM, NAME, IDX)        \
-  const asymbol * const SYM = (asymbol *) &global_syms[IDX]; \
-  const asection SEC = \
+#define STD_SECTION(SEC, FLAGS, SYM, NAME, IDX)                                \
+  const asymbol * const SYM = (asymbol *) &global_syms[IDX];           \
+  const asection SEC =                                                         \
     /* name, id,  index, next, flags, user_set_vma, reloc_done,      */        \
     { NAME,  IDX, 0,     NULL, FLAGS, 0,            0,                 \
                                                                        \
-    /* linker_mark, gc_mark, vma, lma, _cooked_size, _raw_size,      */        \
-       0,           0,       0,   0,   0,            0,                        \
+    /* linker_mark, gc_mark, segment_mark, vma, lma, _cooked_size,   */        \
+       0,           0,       0,            0,   0,   0,                \
                                                                        \
-    /* output_offset, output_section,      alignment_power,          */        \
-       0,             (struct sec *) &SEC, 0,                          \
+    /* _raw_size, output_offset, output_section,    alignment_power, */ \
+       0,         0,           (struct sec *) &SEC, 0,                 \
                                                                        \
     /* relocation, orelocation, reloc_count, filepos, rel_filepos,   */        \
        NULL,       NULL,        0,           0,       0,               \