PR29495, rewrite_elf_program_header looping

This patch, in order of significance:
1) Replaces some macros with inline functions.
2) Those inline functions catch and avoid arithmetic overflows when
   comparing addresses.
3) When assigning sections to segments (IS_SECTION_IN_INPUT_SEGMENT)
   use bed->want_p_paddr_set_to_zero to decide whether lma vs p_paddr
   or vma vs p_vaddr should be tested.  When remapping, use the same
   test, and use is_note rather than the more restrictive
   IS_COREFILE_NOTE.

It's important that the later tests not be more restrictive.  If they
are it can lead to the situation triggered by the testcases, where a
section seemingly didn't fit and thus needed a new mapping.  It didn't
fit the new mapping either, and this repeated until memory exhausted.

	PR 29495
	* elf.c (SEGMENT_END, SECTION_SIZE, IS_CONTAINED_BY_VMA): Delete.
	(IS_CONTAINED_BY_LMA, IS_NOTE, IS_COREFILE_NOTE): Delete.
	(segment_size, segment_end, section_size): New inline function.
	(is_contained_by, is_note): Likewise.
	(rewrite_elf_program_header): Use new functions.
This commit is contained in:
Alan Modra 2022-08-16 17:02:24 +09:30
parent 390ddd6f68
commit 45d92439ae

140
bfd/elf.c
View file

@ -6886,6 +6886,66 @@ _bfd_elf_symbol_from_bfd_symbol (bfd *abfd, asymbol **asym_ptr_ptr)
return idx; return idx;
} }
static inline bfd_vma
segment_size (Elf_Internal_Phdr *segment)
{
return (segment->p_memsz > segment->p_filesz
? segment->p_memsz : segment->p_filesz);
}
/* Returns the end address of the segment + 1. */
static inline bfd_vma
segment_end (Elf_Internal_Phdr *segment, bfd_vma start)
{
return start + segment_size (segment);
}
static inline bfd_size_type
section_size (asection *section, Elf_Internal_Phdr *segment)
{
if ((section->flags & SEC_HAS_CONTENTS) != 0
|| (section->flags & SEC_THREAD_LOCAL) == 0
|| segment->p_type == PT_TLS)
return section->size;
return 0;
}
/* Returns TRUE if the given section is contained within the given
segment. LMA addresses are compared against PADDR when
bed->want_p_paddr_set_to_zero is false, VMA against VADDR when true. */
static bool
is_contained_by (asection *section, Elf_Internal_Phdr *segment,
bfd_vma paddr, bfd_vma vaddr, unsigned int opb,
const struct elf_backend_data *bed)
{
bfd_vma seg_addr = !bed->want_p_paddr_set_to_zero ? paddr : vaddr;
bfd_vma addr = !bed->want_p_paddr_set_to_zero ? section->lma : section->vma;
bfd_vma octet;
if (_bfd_mul_overflow (addr, opb, &octet))
return false;
/* The third and fourth lines below are testing that the section end
address is within the segment. It's written this way to avoid
overflow. Add seg_addr + section_size to both sides of the
inequality to make it obvious. */
return (octet >= seg_addr
&& segment_size (segment) >= section_size (section, segment)
&& (octet - seg_addr
<= segment_size (segment) - section_size (section, segment)));
}
/* Handle PT_NOTE segment. */
static bool
is_note (Elf_Internal_Phdr *p, asection *s)
{
return (p->p_type == PT_NOTE
&& elf_section_type (s) == SHT_NOTE
&& (ufile_ptr) s->filepos >= p->p_offset
&& p->p_filesz >= s->size
&& ((ufile_ptr) s->filepos - p->p_offset
<= p->p_filesz - s->size));
}
/* Rewrite program header information. */ /* Rewrite program header information. */
static bool static bool
@ -6914,47 +6974,6 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
num_segments = elf_elfheader (ibfd)->e_phnum; num_segments = elf_elfheader (ibfd)->e_phnum;
/* 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))
#define SECTION_SIZE(section, segment) \
(((section->flags & (SEC_HAS_CONTENTS | SEC_THREAD_LOCAL)) \
!= SEC_THREAD_LOCAL || segment->p_type == PT_TLS) \
? section->size : 0)
/* Returns TRUE if the given section is contained within
the given segment. VMA addresses are compared. */
#define IS_CONTAINED_BY_VMA(section, segment, opb) \
(section->vma * (opb) >= segment->p_vaddr \
&& (section->vma * (opb) + SECTION_SIZE (section, segment) \
<= (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, opb) \
(section->lma * (opb) >= base \
&& (section->lma + SECTION_SIZE (section, segment) / (opb) >= section->lma) \
&& (section->lma * (opb) + SECTION_SIZE (section, segment) \
<= SEGMENT_END (segment, base)))
/* Handle PT_NOTE segment. */
#define IS_NOTE(p, s) \
(p->p_type == PT_NOTE \
&& elf_section_type (s) == SHT_NOTE \
&& (bfd_vma) s->filepos >= p->p_offset \
&& ((bfd_vma) s->filepos + s->size \
<= p->p_offset + p->p_filesz))
/* Special case: corefile "NOTE" section containing regs, prpsinfo
etc. */
#define IS_COREFILE_NOTE(p, s) \
(IS_NOTE (p, s) \
&& bfd_get_format (ibfd) == bfd_core \
&& s->vma == 0 \
&& s->lma == 0)
/* The complicated case when p_vaddr is 0 is to handle the Solaris /* The complicated case when p_vaddr is 0 is to handle the Solaris
linker, which generates a PT_INTERP section with p_vaddr and linker, which generates a PT_INTERP section with p_vaddr and
p_memsz set to 0. */ p_memsz set to 0. */
@ -6983,11 +7002,10 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
8. PT_DYNAMIC should not contain empty sections at the beginning 8. PT_DYNAMIC should not contain empty sections at the beginning
(with the possible exception of .dynamic). */ (with the possible exception of .dynamic). */
#define IS_SECTION_IN_INPUT_SEGMENT(section, segment, bed, opb) \ #define IS_SECTION_IN_INPUT_SEGMENT(section, segment, bed, opb) \
((((segment->p_paddr \ (((is_contained_by (section, segment, segment->p_paddr, \
? IS_CONTAINED_BY_LMA (section, segment, segment->p_paddr, opb) \ segment->p_vaddr, opb, bed) \
: IS_CONTAINED_BY_VMA (section, segment, opb)) \
&& (section->flags & SEC_ALLOC) != 0) \ && (section->flags & SEC_ALLOC) != 0) \
|| IS_NOTE (segment, section)) \ || is_note (segment, section)) \
&& segment->p_type != PT_GNU_STACK \ && segment->p_type != PT_GNU_STACK \
&& (segment->p_type != PT_TLS \ && (segment->p_type != PT_TLS \
|| (section->flags & SEC_THREAD_LOCAL)) \ || (section->flags & SEC_THREAD_LOCAL)) \
@ -6995,7 +7013,7 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
|| segment->p_type == PT_TLS \ || segment->p_type == PT_TLS \
|| (section->flags & SEC_THREAD_LOCAL) == 0) \ || (section->flags & SEC_THREAD_LOCAL) == 0) \
&& (segment->p_type != PT_DYNAMIC \ && (segment->p_type != PT_DYNAMIC \
|| SECTION_SIZE (section, segment) > 0 \ || section_size (section, segment) > 0 \
|| (segment->p_paddr \ || (segment->p_paddr \
? segment->p_paddr != section->lma * (opb) \ ? segment->p_paddr != section->lma * (opb) \
: segment->p_vaddr != section->vma * (opb)) \ : segment->p_vaddr != section->vma * (opb)) \
@ -7010,7 +7028,7 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
/* Returns TRUE iff seg1 starts after the end of seg2. */ /* Returns TRUE iff seg1 starts after the end of seg2. */
#define SEGMENT_AFTER_SEGMENT(seg1, seg2, field) \ #define SEGMENT_AFTER_SEGMENT(seg1, seg2, field) \
(seg1->field >= SEGMENT_END (seg2, seg2->field)) (seg1->field >= segment_end (seg2, seg2->field))
/* Returns TRUE iff seg1 and seg2 overlap. Segments overlap iff both /* Returns TRUE iff seg1 and seg2 overlap. Segments overlap iff both
their VMA address ranges and their LMA address ranges overlap. their VMA address ranges and their LMA address ranges overlap.
@ -7090,8 +7108,8 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
{ {
/* Extend SEGMENT2 to include SEGMENT and then delete /* Extend SEGMENT2 to include SEGMENT and then delete
SEGMENT. */ SEGMENT. */
extra_length = (SEGMENT_END (segment, segment->p_vaddr) extra_length = (segment_end (segment, segment->p_vaddr)
- SEGMENT_END (segment2, segment2->p_vaddr)); - segment_end (segment2, segment2->p_vaddr));
if (extra_length > 0) if (extra_length > 0)
{ {
@ -7110,8 +7128,8 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
{ {
/* Extend SEGMENT to include SEGMENT2 and then delete /* Extend SEGMENT to include SEGMENT2 and then delete
SEGMENT2. */ SEGMENT2. */
extra_length = (SEGMENT_END (segment2, segment2->p_vaddr) extra_length = (segment_end (segment2, segment2->p_vaddr)
- SEGMENT_END (segment, segment->p_vaddr)); - segment_end (segment, segment->p_vaddr));
if (extra_length > 0) if (extra_length > 0)
{ {
@ -7311,11 +7329,9 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
/* Match up the physical address of the segment with the /* Match up the physical address of the segment with the
LMA address of the output section. */ LMA address of the output section. */
if (IS_CONTAINED_BY_LMA (output_section, segment, map->p_paddr, if (is_contained_by (output_section, segment, map->p_paddr,
opb) map->p_paddr + map->p_vaddr_offset, opb, bed)
|| IS_COREFILE_NOTE (segment, section) || is_note (segment, section))
|| (bed->want_p_paddr_set_to_zero
&& IS_CONTAINED_BY_VMA (output_section, segment, opb)))
{ {
if (matching_lma == NULL if (matching_lma == NULL
|| output_section->lma < matching_lma->lma) || output_section->lma < matching_lma->lma)
@ -7431,9 +7447,9 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
BFD_ASSERT (output_section != NULL); BFD_ASSERT (output_section != NULL);
if (IS_CONTAINED_BY_LMA (output_section, segment, map->p_paddr, if (is_contained_by (output_section, segment, map->p_paddr,
opb) map->p_paddr + map->p_vaddr_offset, opb, bed)
|| IS_COREFILE_NOTE (segment, section)) || is_note (segment, section))
{ {
if (map->count == 0) if (map->count == 0)
{ {
@ -7556,12 +7572,6 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
} }
} }
#undef SEGMENT_END
#undef SECTION_SIZE
#undef IS_CONTAINED_BY_VMA
#undef IS_CONTAINED_BY_LMA
#undef IS_NOTE
#undef IS_COREFILE_NOTE
#undef IS_SOLARIS_PT_INTERP #undef IS_SOLARIS_PT_INTERP
#undef IS_SECTION_IN_INPUT_SEGMENT #undef IS_SECTION_IN_INPUT_SEGMENT
#undef INCLUDE_SECTION_IN_SEGMENT #undef INCLUDE_SECTION_IN_SEGMENT