This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] program header instability
- From: Nathan Sidwell <nathan at codesourcery dot com>
- To: binutils at sources dot redhat dot com
- Date: Tue, 19 Dec 2006 15:46:12 +0000
- Subject: [PATCH] program header instability
This patch fixes a problem with instability in calculating program headers. The
original testcase came from a vxworks build, but the problem can manifest on any
elf target. The problem occurs when a section boundary happens to lie on a page
boundary, and the previous and next sections differ in the WRITABLE attribute.
This bit of code in elf.c:
else if (! writable
&& (hdr->flags & SEC_READONLY) == 0
&& (((last_hdr->lma + last_size - 1)
& ~(maxpagesize - 1))
!= (hdr->lma & ~(maxpagesize - 1))))
{
/* We don't want to put a writable section in a read only
segment, unless they are on the same page in memory
anyhow. We already know that the last section does not
bring us past the current section on the page, so the
only case in which the new section is not on the same
page as the previous section is when the previous section
ends precisely on a page boundary. */
new_segment = TRUE;
places those two sections in different segments, necessitating an additional
program header. Now, when the new program header is included, the section
boundary is no longer page aligned, and we decide we only need 1 program header
for the two sections. So the section boundary shifts down and we're back where
we started.
The linker bails out with
./ld-new: looping in map_segments
That loop fails because, even though bfd caches the program_header size during
the linking layout phase, we keep changing our minds as to whether the first
segment contains the pheaders or not, depending on whether we think we need N or
N+1 of them.
if ((abfd->flags & D_PAGED) == 0
|| sections[0]->lma < phdr_size
|| sections[0]->lma % maxpagesize < phdr_size % maxpagesize)
phdr_in_segment = FALSE;
This patch changes elf.c to initialize the program_header_size to zero (rather
than the 'unknown' value of -1), and then always recalculate it. furthermore we
never reduce the size of the program headers, to avoid the above instability.
Tested in i686-pc-linux-gnu, ok?
nathan
--
Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery
nathan@codesourcery.com :: http://www.planetfall.pwp.blueyonder.co.uk
2006-12-19 Nathan Sidwell <nathan@codesourcery.com>
* elf.c (bfd_elf_mkobject): Default program_header_size to zero.
(_bfd_elf_map_sections_to_segments): Adjust default
program_header_size value. Don't shrink the program header size.
(assign_file_positions_for_load_sections): Adjust for default
program_header_size value. Assert we've got enough room.
(_bfd_elf_sizeof_headers): Always recalculate the header size.
Never shrink it.
* ld/testsuite/ld-elf/header.d: New.
* ld/testsuite/ld-elf/header.t: New.
* ld/testsuite/ld-elf/header.s: New.
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.369
diff -c -3 -p -r1.369 elf.c
*** bfd/elf.c 19 Dec 2006 08:49:38 -0000 1.369
--- bfd/elf.c 19 Dec 2006 15:32:08 -0000
*************** bfd_elf_mkobject (bfd *abfd)
*** 231,237 ****
return FALSE;
}
! elf_tdata (abfd)->program_header_size = (bfd_size_type) -1;
return TRUE;
}
--- 231,237 ----
return FALSE;
}
! elf_tdata (abfd)->program_header_size = 0;
return TRUE;
}
*************** _bfd_elf_map_sections_to_segments (bfd *
*** 3894,3900 ****
{
bfd_size_type phdr_size = elf_tdata (abfd)->program_header_size;
! if (phdr_size == (bfd_size_type) -1)
phdr_size = get_program_header_size (abfd, info);
if ((abfd->flags & D_PAGED) == 0
|| sections[0]->lma < phdr_size
--- 3894,3900 ----
{
bfd_size_type phdr_size = elf_tdata (abfd)->program_header_size;
! if (!phdr_size)
phdr_size = get_program_header_size (abfd, info);
if ((abfd->flags & D_PAGED) == 0
|| sections[0]->lma < phdr_size
*************** _bfd_elf_map_sections_to_segments (bfd *
*** 4145,4151 ****
for (count = 0, m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
++count;
! elf_tdata (abfd)->program_header_size = count * bed->s->sizeof_phdr;
return TRUE;
--- 4145,4152 ----
for (count = 0, m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
++count;
! if (elf_tdata (abfd)->program_header_size < count * bed->s->sizeof_phdr)
! elf_tdata (abfd)->program_header_size = count * bed->s->sizeof_phdr;
return TRUE;
*************** assign_file_positions_for_load_sections
*** 4271,4281 ****
elf_elfheader (abfd)->e_phentsize = bed->s->sizeof_phdr;
elf_elfheader (abfd)->e_phnum = alloc;
! if (elf_tdata (abfd)->program_header_size == (bfd_size_type) -1)
elf_tdata (abfd)->program_header_size = alloc * bed->s->sizeof_phdr;
else
BFD_ASSERT (elf_tdata (abfd)->program_header_size
! == alloc * bed->s->sizeof_phdr);
if (alloc == 0)
{
--- 4272,4282 ----
elf_elfheader (abfd)->e_phentsize = bed->s->sizeof_phdr;
elf_elfheader (abfd)->e_phnum = alloc;
! if (!elf_tdata (abfd)->program_header_size)
elf_tdata (abfd)->program_header_size = alloc * bed->s->sizeof_phdr;
else
BFD_ASSERT (elf_tdata (abfd)->program_header_size
! >= alloc * bed->s->sizeof_phdr);
if (alloc == 0)
{
*************** _bfd_elf_sizeof_headers (bfd *abfd, stru
*** 7193,7213 ****
if (!info->relocatable)
{
! bfd_size_type phdr_size = elf_tdata (abfd)->program_header_size;
! if (phdr_size == (bfd_size_type) -1)
! {
! struct elf_segment_map *m;
! phdr_size = 0;
! for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
! phdr_size += bed->s->sizeof_phdr;
! if (phdr_size == 0)
! phdr_size = get_program_header_size (abfd, info);
! }
!
! elf_tdata (abfd)->program_header_size = phdr_size;
ret += phdr_size;
}
--- 7194,7216 ----
if (!info->relocatable)
{
! bfd_size_type phdr_size = 0;
! struct elf_segment_map *m;
! for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
! phdr_size += bed->s->sizeof_phdr;
! if (phdr_size == 0)
! phdr_size = get_program_header_size (abfd, info);
! /* Avoid shrinking program header size. Shrinking can lead to
! instability when a section's placement becomes aligned on a
! page boundary with N segments, requiring N+1 segments. And
! with N+1 segments it no longer is aligned. */
! if (elf_tdata (abfd)->program_header_size > phdr_size)
! phdr_size = elf_tdata (abfd)->program_header_size;
! else
! elf_tdata (abfd)->program_header_size = phdr_size;
ret += phdr_size;
}
Index: ld/testsuite/ld-elf/header.d
===================================================================
RCS file: ld/testsuite/ld-elf/header.d
diff -N ld/testsuite/ld-elf/header.d
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-elf/header.d 19 Dec 2006 15:32:08 -0000
***************
*** 0 ****
--- 1,12 ----
+ # ld: -T header.t -z max-page-size=0x10000
+ # objdump: -hpw
+
+ #...
+ Program Header:
+ LOAD off 0x0*0000000 vaddr 0x0*0010000 paddr 0x0*0010000 align 2..16
+ filesz 0x0*0010024 memsz 0x0*0010024 flags rwx
+
+ Sections:
+ Idx Name Size VMA LMA File off Algn Flags
+ 0 .text 0*000ffac 0*0010074 0*0010074 0*0000074 2... CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 1 .data 0*0000004 0*0020020 0*0020020 0*0010020 2... CONTENTS, ALLOC, LOAD, DATA
Index: ld/testsuite/ld-elf/header.s
===================================================================
RCS file: ld/testsuite/ld-elf/header.s
diff -N ld/testsuite/ld-elf/header.s
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-elf/header.s 19 Dec 2006 15:32:08 -0000
***************
*** 0 ****
--- 1,8 ----
+ .text
+ .globl main
+ main:
+ .rept 0x4000 - 0x15
+ .long 0xfedcba98
+ .endr
+ .data
+ .long 0x76543210
Index: ld/testsuite/ld-elf/header.t
===================================================================
RCS file: ld/testsuite/ld-elf/header.t
diff -N ld/testsuite/ld-elf/header.t
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-elf/header.t 19 Dec 2006 15:32:08 -0000
***************
*** 0 ****
--- 1,8 ----
+ ENTRY(main)
+
+ SECTIONS
+ {
+ . = 0x10000 + SIZEOF_HEADERS;
+ .text : { *(.text) }
+ .data : { *(.data) }
+ }