mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-25 04:49:54 +08:00
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.
This commit is contained in:
125
bfd/elf.c
125
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;
|
||||
|
Reference in New Issue
Block a user