aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2000-07-31 18:45:26 +0000
committerNick Clifton <nickc@redhat.com>2000-07-31 18:45:26 +0000
commit5c440b1e3b3b9ec61ffb1cc149216603f1e2d550 (patch)
tree5d16659c9ec736a39e74d425414efdf8c2110db3
parent34e9d9bbe19dddb89155f7e22271d50ff2b0d597 (diff)
downloadfsf-binutils-gdb-5c440b1e3b3b9ec61ffb1cc149216603f1e2d550.zip
fsf-binutils-gdb-5c440b1e3b3b9ec61ffb1cc149216603f1e2d550.tar.gz
fsf-binutils-gdb-5c440b1e3b3b9ec61ffb1cc149216603f1e2d550.tar.bz2
Improve section to segment mapping code.
-rw-r--r--bfd/ChangeLog22
-rw-r--r--bfd/bfd-in2.h3
-rw-r--r--bfd/elf.c234
-rw-r--r--bfd/section.c17
4 files changed, 206 insertions, 70 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 9b471b8..fb20f01 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -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.
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index ed4113b..c82df23 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -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
diff --git a/bfd/elf.c b/bfd/elf.c
index 0da1c95..297c29d 100644
--- 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;
}
}
diff --git a/bfd/section.c b/bfd/section.c
index 9006d19..b9a39b6 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -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, \