From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nick Clifton To: binutils@sourceware.cygnus.com Subject: Improvements to section to segment mapping Date: Fri, 28 Jul 2000 13:02:00 -0000 Message-id: <200007282002.NAA15045@elmo.cygnus.com> X-SW-Source: 2000-07/msg00475.html Hi Guys, I am considering checking in the patch below, but I wanted to give people a chance to look it over first in case they have any comments. The patch tidies up the piece of code in elf.c that handles the mapping of sections to segments. This came about because a customer had noticed that if they used objcopy to change the LMAs of some sections (but not all of them), the resulting segment map was broken. The patch does several things. It adds a new boolean flag to the asection structure, which is used to mark when a section has been allocated to a segment. It creates a couple of new macros to replace duplicated code, and it streamlines the code that uses them. It also adds code to cope with the case where overlapping segments have been created (again by objcopy) - it fixes the segments to remove the overlaps. It also allows new segments to be created, even beyond the number originally estimated in ihdr->e_phnum. It adjusts the appropriate fields if it had to generate extra segments. I have tested this code with an x86 Linux native build and an arm-elf cross compiler and everything worked OK. I am running a Solaris build and a Linux bootstrap at the moment, just in case there are any unforeseen problems. So unless there are any objections I will probably check this patch in next week. Cheers Nick 2000-07-28 Nick Clifton * 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. Index: section.c =================================================================== RCS file: /cvs/src//src/bfd/section.c,v retrieving revision 1.16 diff -p -r1.16 section.c *** section.c 2000/07/10 02:05:40 1.16 --- section.c 2000/07/28 18:56:49 *************** CODE_FRAGMENT *** 363,368 **** --- 363,371 ---- . {* 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 *************** static const asymbol global_syms[] = *** 549,565 **** 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 = \ /* 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, \ \ ! /* output_offset, output_section, alignment_power, */ \ ! 0, (struct sec *) &SEC, 0, \ \ /* relocation, orelocation, reloc_count, filepos, rel_filepos, */ \ NULL, NULL, 0, 0, 0, \ --- 552,568 ---- 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 = \ /* name, id, index, next, flags, user_set_vma, reloc_done, */ \ { NAME, IDX, 0, NULL, FLAGS, 0, 0, \ \ ! /* linker_mark, gc_mark, segment_mark, vma, lma, _cooked_size, */ \ ! 0, 0, 0, 0, 0, 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, \ Index: elf.c =================================================================== RCS file: /cvs/src//src/bfd/elf.c,v retrieving revision 1.44 diff -p -r1.44 elf.c *** elf.c 2000/07/20 16:21:06 1.44 --- elf.c 2000/07/28 18:56:50 *************** copy_private_bfd_data (ibfd, obfd) *** 3619,3624 **** --- 3619,3628 ---- 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) *************** copy_private_bfd_data (ibfd, obfd) *** 3633,3673 **** 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))) /* 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 \ <= 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 \ <= p->p_offset + p->p_filesz)) /* Scan through the segments specified in the program header ! of the input BFD. */ 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; --- 3637,3773 ---- pm = &mfirst; num_segments = elf_elfheader (ibfd)->e_phnum; + 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 \ <= 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 \ <= 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. 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 **sections; asection *os; unsigned int isec; *************** copy_private_bfd_data (ibfd, obfd) *** 3675,3696 **** 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. */ 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; ! } /* Allocate a segment map big enough to contain all of the sections we have selected. */ --- 3775,3788 ---- bfd_vma suggested_lma; unsigned int j; ! 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 (INCLUDE_SECTION_IN_SEGMENT (s, p)) ! ++csecs; /* Allocate a segment map big enough to contain all of the sections we have selected. */ *************** copy_private_bfd_data (ibfd, obfd) *** 3733,3739 **** /* 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"), --- 3825,3830 ---- *************** copy_private_bfd_data (ibfd, obfd) *** 3784,3804 **** 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) { sections[j++] = s; /* The Solaris native linker always sets p_paddr to 0. --- 3875,3890 ---- 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) { ! if (INCLUDE_SECTION_IN_SEGMENT (s, p)) { + os = s->output_section; + sections[j++] = s; /* The Solaris native linker always sets p_paddr to 0. *************** copy_private_bfd_data (ibfd, obfd) *** 3819,3832 **** /* 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) || 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 segment. */ m->sections[isec++] = os; } --- 3905,3918 ---- /* Match up the physical address of the segment with the LMA address of the output section. */ ! 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 ! then it does not overlap any other section within that segment. */ m->sections[isec++] = os; } *************** copy_private_bfd_data (ibfd, obfd) *** 3859,3865 **** /* 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 --- 3945,3950 ---- *************** copy_private_bfd_data (ibfd, obfd) *** 3867,3883 **** /* 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. */ if (m->includes_filehdr) m->p_paddr -= iehdr->e_ehsize; if (m->includes_phdrs) ! m->p_paddr -= iehdr->e_phnum * iehdr->e_phentsize; } /* Step Three: Loop over the sections again, this time assigning --- 3952,3977 ---- /* 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. */ if (m->includes_filehdr) m->p_paddr -= iehdr->e_ehsize; if (m->includes_phdrs) ! { ! 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 *************** copy_private_bfd_data (ibfd, obfd) *** 3903,3909 **** os = s->output_section; ! if (IS_CONTAINED_BY (os->lma, os->_raw_size, m->p_paddr, p) || IS_COREFILE_NOTE (p, s)) { if (m->count == 0) --- 3997,4005 ---- os = s->output_section; ! BFD_ASSERT (os != NULL); ! ! if (IS_CONTAINED_BY_LMA (os, p, m->p_paddr) || IS_COREFILE_NOTE (p, s)) { if (m->count == 0) *************** copy_private_bfd_data (ibfd, obfd) *** 3921,3936 **** 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) < BFD_ALIGN (os->lma, maxpagesize)) { if (suggested_lma == 0) suggested_lma = os->lma; --- 4017,4031 ---- else { asection * prev_sec; prev_sec = m->sections[m->count - 1]; /* 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) < BFD_ALIGN (os->lma, maxpagesize)) + || ((prev_sec->lma + prev_sec->_raw_size) > os->lma)) { if (suggested_lma == 0) suggested_lma = os->lma; *************** copy_private_bfd_data (ibfd, obfd) *** 3942,3947 **** --- 4037,4043 ---- m->sections[m->count++] = os; ++isec; sections[j] = NULL; + s->segment_mark = true; } else if (suggested_lma == 0) suggested_lma = os->lma; *************** copy_private_bfd_data (ibfd, obfd) *** 3958,3964 **** /* 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) --- 4054,4059 ---- *************** copy_private_bfd_data (ibfd, obfd) *** 3969,3975 **** /* 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; --- 4064,4069 ---- *************** copy_private_bfd_data (ibfd, obfd) *** 4000,4007 **** elf_tdata (obfd)->segment_map = mfirst; #if 0 ! /* Final Step: Sort the segments into ascending order of physical address. */ if (mfirst != NULL) { struct elf_segment_map* prev; --- 4094,4115 ---- 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. */ if (mfirst != NULL) { struct elf_segment_map* prev; *************** copy_private_bfd_data (ibfd, obfd) *** 4009,4023 **** prev = mfirst; for (m = mfirst->next; m != NULL; prev = m, m = m->next) { ! /* Yes I know - its a bubble sort....*/ if (m->next != NULL && (m->next->p_paddr < m->p_paddr)) { ! /* swap m and m->next */ prev->next = m->next; m->next = m->next->next; prev->next->next = m; ! /* restart loop. */ m = mfirst; } } --- 4117,4131 ---- prev = mfirst; for (m = mfirst->next; m != NULL; prev = m, m = m->next) { ! /* Yes I know - its a bubble sort.... */ if (m->next != NULL && (m->next->p_paddr < m->p_paddr)) { ! /* swap m and m->next. */ prev->next = m->next; m->next = m->next->next; prev->next->next = m; ! /* restart loop. */ m = mfirst; } }