aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2016-11-23 11:10:39 +0000
committerNick Clifton <nickc@redhat.com>2016-11-23 11:10:39 +0000
commit1a9ccd70f9a75dc6b48d340059f28ef3550c107b (patch)
treea270b05984cd44808359c6f6773ce0dfa5d3dff7 /bfd
parent128e85e3ab36b8e30f6612fb50de3cbb4ede6824 (diff)
downloadgdb-1a9ccd70f9a75dc6b48d340059f28ef3550c107b.zip
gdb-1a9ccd70f9a75dc6b48d340059f28ef3550c107b.tar.gz
gdb-1a9ccd70f9a75dc6b48d340059f28ef3550c107b.tar.bz2
Fix the linker so that it will not silently generate ELF binaries with invalid program headers. Fix readelf to report such invalid binaries.
PR ld/20815 bfd * elf.c (elf_modify_segment_map): Allow empty LOAD segments if they contain the program headers. (_bfd_elf_map_sections_to_segments): If the linker created the PHDR segment then always attempt to include it in a LOAD segment. (assign_file_positions_for_non_load_sections): Allow LOAD segments to overlap PHDR segments. (phdr_sorter): New function. Sorts program headers. (assign_file_positions_except_relocs): Sort the program headers before writing them out. Issue an error if the PHDR segment is not covered by a LOAD segment, unless the backend allows it. * elf-bfd.h (struct elf_backend_data): Add elf_backend_allow_non_load_phdr. * elfxx-target.h (elf_backend_allow_non_load_phdr): Provide default definition that returns FALSE. (elfNN_bed): Initialise the elf_backend_allow_non_load_phdr field. * elf64-hppa.c (elf64_hppa_allow_non_load_phdr): New function. Returns TRUE. (elf_backend_allow_non_load_phdr): Define. * elf-m10300.c (_bfd_mn10300_elf_size_dynamic_sections): Do not place the interpreter string into the .interp section if the nointerp flag is set in the link info structure. * elf32-arc.c (elf_arc_size_dynamic_sections): Likewise. * elf32-score7.c (score_elf_final_link_relocate): Allow for the _gp symbol not being part of the output. binutils* readelf.c (process_program_headers): Check PT_LOAD and PT_PHDR segments for validity. ld * ld.texinfo: Note that PT_TLS can be used as a segment type. * testsuite/ld-discard/discard.ld: Add space for program headers. * testsuite/ld-elf/flags1.ld: Likewise. * testsuite/ld-elf/maxpage3.t: Likewise. * testsuite/ld-elf/noload-1.t: Likewise. * testsuite/ld-elf/orphan.ld: Likewise. * testsuite/ld-elf/overlay.t: Likewise. * testsuite/ld-elf/pr14052.t: Likewise. * testsuite/ld-elf/pr19539.t: Likewise. * testsuite/ld-elf/provide-hidden-1.ld: Likewise. * testsuite/ld-elf/provide-hidden-s.ld: Likewise. * testsuite/ld-elf/weak-dyn-1.ld: Likewise. * testsuite/ld-i386/pr19539.t: Likewise. * testsuite/ld-scripts/defined.t: Likewise. * testsuite/ld-scripts/defined6.t: Likewise. * testsuite/ld-scripts/dynamic-sections.t: Likewise. * testsuite/ld-scripts/empty-aligned.t: Likewise. * testsuite/ld-scripts/provide-2.t: Likewise. * testsuite/ld-scripts/provide-4.t: Likewise. * testsuite/ld-vax-elf/plt-local.ld: Likewise. * testsuite/ld-x86-64/pr19539.t: Likewise. * testsuite/ld-elf/ehdr_start-missing.d: Do not initialise the dynamic linker. * testsuite/ld-elf/ehdr_start-weak.d: Likewise. * testsuite/ld-elf/elf.exp (pr14170, pr17068): Likewise. * testsuite/ld-elf/loadaddr1.d: Update expected readelf output. * testsuite/ld-elf/noload-2.d: Likewise. * testsuite/ld-powerpc/vxworks2.sd: Likewise. * testsuite/ld-scripts/phdrs3a.d: Likewise. * testsuite/ld-scripts/size-2.d: Likewise. * testsuite/ld-elf/group.ld: Add program headers. * testsuite/ld-elf/overlay.d: Skip for SPU. * testsuite/ld-elf/flags1.d: Skip for RX. * testsuite/ld-elf/pr19162.d: Skip for HPPA64. * testsuite/ld-elf/pr19539.d: Skip for ALPHA. * testsuite/ld-scripts/empty-orphan.t: Update program headers. * testsuite/ld-scripts/size-2.t: Likewise.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog29
-rw-r--r--bfd/elf-bfd.h5
-rw-r--r--bfd/elf-m10300.c2
-rw-r--r--bfd/elf.c125
-rw-r--r--bfd/elf32-arc.c2
-rw-r--r--bfd/elf32-score7.c9
-rw-r--r--bfd/elf64-hppa.c12
-rw-r--r--bfd/elfcode.h4
-rw-r--r--bfd/elfxx-target.h5
9 files changed, 179 insertions, 14 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 19a1026..346ff29 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,32 @@
+2016-11-23 Nick Clifton <nickc@redhat.com>
+
+ PR ld/20815
+ * elf.c (elf_modify_segment_map): Allow empty LOAD segments if
+ they contain the program headers.
+ (_bfd_elf_map_sections_to_segments): If the linker created the
+ PHDR segment then always attempt to include it in a LOAD segment.
+ (assign_file_positions_for_non_load_sections): Allow LOAD segments
+ to overlap PHDR segments.
+ (phdr_sorter): New function. Sorts program headers.
+ (assign_file_positions_except_relocs): Sort the program headers
+ before writing them out. Issue an error if the PHDR segment is
+ not covered by a LOAD segment, unless the backend allows it.
+ * elf-bfd.h (struct elf_backend_data): Add
+ elf_backend_allow_non_load_phdr.
+ * elfxx-target.h (elf_backend_allow_non_load_phdr): Provide
+ default definition that returns FALSE.
+ (elfNN_bed): Initialise the elf_backend_allow_non_load_phdr
+ field.
+ * elf64-hppa.c (elf64_hppa_allow_non_load_phdr): New function.
+ Returns TRUE.
+ (elf_backend_allow_non_load_phdr): Define.
+ * elf-m10300.c (_bfd_mn10300_elf_size_dynamic_sections): Do not
+ place the interpreter string into the .interp section if the
+ nointerp flag is set in the link info structure.
+ * elf32-arc.c (elf_arc_size_dynamic_sections): Likewise.
+ * elf32-score7.c (score_elf_final_link_relocate): Allow for the
+ _gp symbol not being part of the output.
+
2016-11-23 Alan Modra <amodra@gmail.com>
* elf-bfd.h (struct elf_backend_data): Add dtrel_excludes_plt.
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 7c78899..06bd800 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1077,6 +1077,11 @@ struct elf_backend_data
bfd_boolean (*elf_backend_modify_program_headers)
(bfd *, struct bfd_link_info *);
+ /* This function is called to see if the PHDR header should be
+ checked for validity. */
+ bfd_boolean (*elf_backend_allow_non_load_phdr)
+ (bfd *, const Elf_Internal_Phdr *, unsigned);
+
/* This function is called before section garbage collection to
mark entry symbol sections. */
void (*gc_keep)
diff --git a/bfd/elf-m10300.c b/bfd/elf-m10300.c
index 1ea5c27..4a8a297 100644
--- a/bfd/elf-m10300.c
+++ b/bfd/elf-m10300.c
@@ -5035,7 +5035,7 @@ _bfd_mn10300_elf_size_dynamic_sections (bfd * output_bfd,
if (elf_hash_table (info)->dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
- if (bfd_link_executable (info))
+ if (bfd_link_executable (info) && !info->nointerp)
{
s = bfd_get_linker_section (dynobj, ".interp");
BFD_ASSERT (s != NULL);
diff --git a/bfd/elf.c b/bfd/elf.c
index e7252c6..936255e 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -4254,7 +4254,7 @@ get_program_header_size (bfd *abfd, struct bfd_link_info *info)
PT_INTERP segment. In this case, assume we also need a
PT_PHDR segment, although that may not be true for all
targets. */
- segs += 2;
+ segs += 3;
}
if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
@@ -4438,7 +4438,10 @@ elf_modify_segment_map (bfd *abfd,
}
(*m)->count = new_count;
- if (remove_empty_load && (*m)->p_type == PT_LOAD && (*m)->count == 0)
+ if (remove_empty_load
+ && (*m)->p_type == PT_LOAD
+ && (*m)->count == 0
+ && !(*m)->includes_phdrs)
*m = (*m)->next;
else
m = &(*m)->next;
@@ -4488,6 +4491,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
asection *dynsec, *eh_frame_hdr;
bfd_size_type amt;
bfd_vma addr_mask, wrap_to = 0;
+ bfd_boolean linker_created_pt_phdr_segment = FALSE;
/* Select the allocated sections, and sort them. */
@@ -4540,7 +4544,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
m->p_flags = PF_R | PF_X;
m->p_flags_valid = 1;
m->includes_phdrs = 1;
-
+ linker_created_pt_phdr_segment = TRUE;
*pm = m;
pm = &m->next;
@@ -4591,7 +4595,19 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
|| ((sections[0]->lma & addr_mask) % maxpagesize
< phdr_size % maxpagesize)
|| (sections[0]->lma & addr_mask & -maxpagesize) < wrap_to)
- phdr_in_segment = FALSE;
+ {
+ /* PR 20815: The ELF standard says that a PT_PHDR segment, if
+ present, must be included as part of the memory image of the
+ program. Ie it must be part of a PT_LOAD segment as well.
+ If we have had to create our own PT_PHDR segment, but it is
+ not going to be covered by the first PT_LOAD segment, then
+ force the inclusion if we can... */
+ if ((abfd->flags & D_PAGED) != 0
+ && linker_created_pt_phdr_segment)
+ phdr_in_segment = TRUE;
+ else
+ phdr_in_segment = FALSE;
+ }
}
for (i = 0, hdrpp = sections; i < count; i++, hdrpp++)
@@ -5773,16 +5789,25 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
else if (m->count != 0)
{
unsigned int i;
+
if (p->p_type != PT_LOAD
&& (p->p_type != PT_NOTE
|| bfd_get_format (abfd) != bfd_core))
{
+ /* A user specified segment layout may include a PHDR
+ segment that overlaps with a LOAD segment... */
+ if (p->p_type == PT_PHDR)
+ {
+ m->count = 0;
+ continue;
+ }
+
if (m->includes_filehdr || m->includes_phdrs)
{
/* PR 17512: file: 2195325e. */
_bfd_error_handler
- (_("%B: warning: non-load segment includes file header and/or program header"),
- abfd);
+ (_("%B: error: non-load segment %d includes file header and/or program header"),
+ abfd, (int)(p - phdrs));
return FALSE;
}
@@ -5829,6 +5854,52 @@ find_section_in_list (unsigned int i, elf_section_list * list)
return list;
}
+/* Compare function used when sorting the program header table.
+ The ELF standard requires that a PT_PHDR segment, if present,
+ must appear before any PT_LOAD segments. It also requires
+ that all PT_LOAD segments are sorted into order of increasing
+ p_vaddr. */
+
+static signed int
+phdr_sorter (const void * a, const void * b)
+{
+ Elf_Internal_Phdr * ahdr = (Elf_Internal_Phdr *) a;
+ Elf_Internal_Phdr * bhdr = (Elf_Internal_Phdr *) b;
+
+ switch (ahdr->p_type)
+ {
+ case PT_LOAD:
+ switch (bhdr->p_type)
+ {
+ case PT_PHDR:
+ return 1;
+ case PT_LOAD:
+ if (ahdr->p_vaddr < bhdr->p_vaddr)
+ return -1;
+ if (ahdr->p_vaddr > bhdr->p_vaddr)
+ return 1;
+ return 0;
+ default:
+ return 0;
+ }
+ break;
+ case PT_PHDR:
+ switch (bhdr->p_type)
+ {
+ case PT_PHDR:
+ _bfd_error_handler (_("error: multiple PHDR segments detecetd"));
+ return 0;
+ case PT_LOAD:
+ return -1;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+}
+
/* Work out the file positions of all the sections. This is called by
_bfd_elf_compute_section_file_positions. All the section sizes and
VMAs must be known before this is called.
@@ -5892,6 +5963,7 @@ assign_file_positions_except_relocs (bfd *abfd,
}
else
{
+ Elf_Internal_Phdr * map;
unsigned int alloc;
/* Assign file positions for the loaded sections based on the
@@ -5930,9 +6002,46 @@ assign_file_positions_except_relocs (bfd *abfd,
/* Write out the program headers. */
alloc = elf_program_header_size (abfd) / bed->s->sizeof_phdr;
+
+ /* Sort the program headers into the ordering required by the ELF standard. */
+ if (alloc == 0)
+ return TRUE;
+
+ map = (Elf_Internal_Phdr *) xmalloc (alloc * sizeof (* tdata->phdr));
+ memcpy (map, tdata->phdr, alloc * sizeof (* tdata->phdr));
+ qsort (map, alloc, sizeof (* tdata->phdr), phdr_sorter);
+
+ /* PR ld/20815 - Check that the program header segment, if present, will
+ be loaded into memory. FIXME: The check below is not sufficient as
+ really all PT_LOAD segments should be checked before issuing an error
+ message. Plus the PHDR segment does not have to be the first segment
+ in the program header table. But this version of the check should
+ catch all real world use cases. */
+ if (alloc > 1
+ && map[0].p_type == PT_PHDR
+ && ! bed->elf_backend_allow_non_load_phdr (abfd, map, alloc)
+ && map[1].p_type == PT_LOAD
+ && (map[1].p_vaddr > map[0].p_vaddr
+ || (map[1].p_vaddr + map[1].p_memsz) < (map[0].p_vaddr + map[0].p_memsz)))
+ {
+ /* The fix for this error is usually to edit the linker script being
+ used and set up the program headers manually. Either that or
+ leave room for the headers at the start of the SECTIONS. */
+ _bfd_error_handler (_("\
+%B: error: PHDR segment not covered by LOAD segment"),
+ abfd);
+ free (map);
+ return FALSE;
+ }
+
if (bfd_seek (abfd, (bfd_signed_vma) bed->s->sizeof_ehdr, SEEK_SET) != 0
- || bed->s->write_out_phdrs (abfd, tdata->phdr, alloc) != 0)
- return FALSE;
+ || bed->s->write_out_phdrs (abfd, map, alloc) != 0)
+ {
+ free (map);
+ return FALSE;
+ }
+
+ free (map);
}
return TRUE;
diff --git a/bfd/elf32-arc.c b/bfd/elf32-arc.c
index 3d39c3a..9ef5368 100644
--- a/bfd/elf32-arc.c
+++ b/bfd/elf32-arc.c
@@ -2363,7 +2363,7 @@ elf_arc_size_dynamic_sections (bfd * output_bfd,
/* Set the contents of the .interp section to the
interpreter. */
- if (!bfd_link_pic (info))
+ if (!bfd_link_pic (info) && !info->nointerp)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
diff --git a/bfd/elf32-score7.c b/bfd/elf32-score7.c
index 79d6768..3813184 100644
--- a/bfd/elf32-score7.c
+++ b/bfd/elf32-score7.c
@@ -1852,9 +1852,12 @@ score_elf_final_link_relocate (reloc_howto_type *howto,
bh = bfd_link_hash_lookup (info->hash, "_gp", 0, 0, 1);
if (bh != NULL && bh->type == bfd_link_hash_defined)
- elf_gp (output_bfd) = (bh->u.def.value
- + bh->u.def.section->output_section->vma
- + bh->u.def.section->output_offset);
+ {
+ elf_gp (output_bfd) = (bh->u.def.value
+ + bh->u.def.section->output_offset);
+ if (bh->u.def.section->output_section)
+ elf_gp (output_bfd) += bh->u.def.section->output_section->vma;
+ }
else if (bfd_link_relocatable (info))
{
bfd_vma lo = -1;
diff --git a/bfd/elf64-hppa.c b/bfd/elf64-hppa.c
index a9e8d0c..0e19f0a 100644
--- a/bfd/elf64-hppa.c
+++ b/bfd/elf64-hppa.c
@@ -2666,6 +2666,14 @@ elf64_hppa_additional_program_headers (bfd *abfd,
return 0;
}
+static bfd_boolean
+elf64_hppa_allow_non_load_phdr (bfd *abfd ATTRIBUTE_UNUSED,
+ const Elf_Internal_Phdr *phdr ATTRIBUTE_UNUSED,
+ unsigned int count ATTRIBUTE_UNUSED)
+{
+ return TRUE;
+}
+
/* Allocate and initialize any program headers required by this
specific backend.
@@ -2692,6 +2700,7 @@ elf64_hppa_modify_segment_map (bfd *abfd,
for (m = elf_seg_map (abfd); m != NULL; m = m->next)
if (m->p_type == PT_PHDR)
break;
+
if (m == NULL)
{
m = ((struct elf_segment_map *)
@@ -4084,6 +4093,9 @@ const struct elf_size_info hppa64_elf_size_info =
#define elf_backend_modify_segment_map \
elf64_hppa_modify_segment_map
+#define elf_backend_allow_non_load_phdr \
+ elf64_hppa_allow_non_load_phdr
+
#define elf_backend_link_output_symbol_hook \
elf64_hppa_link_output_symbol_hook
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
index eb3a1ff..c7e0e46 100644
--- a/bfd/elfcode.h
+++ b/bfd/elfcode.h
@@ -756,7 +756,8 @@ elf_object_p (bfd *abfd)
So we are kind, and reset the string index value to 0
so that at least some processing can be done. */
i_ehdrp->e_shstrndx = SHN_UNDEF;
- _bfd_error_handler (_("warning: %s has a corrupt string table index - ignoring"), abfd->filename);
+ _bfd_error_handler (_("warning: %s has a corrupt string table index - ignoring"),
+ abfd->filename);
}
}
else if (i_ehdrp->e_shstrndx != SHN_UNDEF)
@@ -973,6 +974,7 @@ elf_write_out_phdrs (bfd *abfd,
while (count--)
{
Elf_External_Phdr extphdr;
+
elf_swap_phdr_out (abfd, phdr, &extphdr);
if (bfd_bwrite (&extphdr, sizeof (Elf_External_Phdr), abfd)
!= sizeof (Elf_External_Phdr))
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 5ff39dd..aff8621 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -479,6 +479,10 @@
#ifndef elf_backend_modify_program_headers
#define elf_backend_modify_program_headers 0
#endif
+#ifndef elf_backend_allow_non_load_phdr
+#define elf_backend_allow_non_load_phdr \
+ ((bfd_boolean (*) (bfd *, const Elf_Internal_Phdr *, unsigned)) bfd_false)
+#endif
#ifndef elf_backend_ecoff_debug_swap
#define elf_backend_ecoff_debug_swap 0
#endif
@@ -765,6 +769,7 @@ static struct elf_backend_data elfNN_bed =
elf_backend_additional_program_headers,
elf_backend_modify_segment_map,
elf_backend_modify_program_headers,
+ elf_backend_allow_non_load_phdr,
elf_backend_gc_keep,
elf_backend_gc_mark_dynamic_ref,
elf_backend_gc_mark_hook,