This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ELF section in segment


This all started when I noticed a bug when using objcopy on SPU
executables.  SPU executables always have a PT_NOTE header, but the
note isn't one that is normally loaded like eg. .note.ABI-tag.  The
note section doesn't occupy memory and usually gets stored late in
the object file.  A typical executable might look like the following,
according to readelf.

  [ 1] .init           PROGBITS  00000000 000080 000024 00  AX  0   0  4
[snip]
  [19] .note.spu_name  PROGBITS  00000000 011630 000024 00      0   0 16
[snip]

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000080 0x00000000 0x00000000 0x03570 0x03620 RWE 0x10
  NOTE           0x011630 0x00000000 0x00000000 0x00024 0x00024 R   0x10

 Section to Segment mapping:
  Segment Sections...
   00     .init .text .fini .rodata .ctors .dtors .jcr .data .bss
   01     .init .note.spu_name

Notice how readelf thinks .init belongs in the PT_NOTE header, which
is silly.  .init is nowhere near this segment in the file.  The
problem here is that ELF_IS_SECTION_IN_SEGMENT only checks vmas for
allocated sections.  .init's vma of 0 and size of 24 happens to fit
inside the PT_NOTE virtual address range.

After an objcopy we get:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000080 0x00000000 0x00000000 0x03570 0x03620 RWE 0x10
  NOTE           0x000080 0x00000000 0x00000000 0x115d4 0x00024 R   0x10

Oops.  This is the same underlying ELF_IS_SECTION_IN_SEGMENT problem
again.  Fixing the bug is relatively easy, just check that all
sections besides SHT_NOBITS ones have a file offset that fits in
the segment file offset range.  However, HJ added tests at the end of
assign_file_positions_for_load_sections that make use of
ELF_IS_SECTION_IN_SEGMENT before we have set the section header file
offset.  So set sh_offset before HJ's tests.

Running the testsuite now gave me a failure in ld SIZEOF.
Interestingly, this was HJ's section in segment test triggering, and
looking at the executable with readelf shows why:

[snip]
  [ 1] .text           PROGBITS  00000000 001000 000010 00  AX  0   0  4
  [ 2] .data           PROGBITS  00000010 001010 000010 00  WA  0   0  1
[snip]
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x00000000 0x00000000 0x00010 0x00020 RWE 0x1000

Notice how .data is PROGBITS at file offset 1010, size 10, but the
single program header only covers file offsets 1000 to 100f.  The
problem here is that ld looks at bfd section flags when mapping
sections to segments, and sees that this particular .data section is
not marked SEC_LOAD (ie. it's like a bss section).  Quite reasonably,
ld thus need not allocate file space for .data.  ld can include it in
the segment p_memsz.  Fixed by twiddling PROGBITS to NOBITS.

Finally, getting back to that SPU PT_NOTE, I believe we should be
setting p_memsz zero for a segment that isn't loaded to memory.


include/elf/
	* internal.h (ELF_IS_SECTION_IN_SEGMENT): Check both file offset
	and vma for appropriate sections.
bfd/
	* elf.c (assign_file_positions_for_load_sections): Set sh_offset
	here.  Set sh_type to SHT_NOBITS if we won't be allocating
	file space.  Don't bump p_memsz for non-alloc sections.  Adjust
	section-in-segment check.
	(assign_file_positions_for_non_load_sections): Don't set sh_offset
	here for sections that have already been handled above.

Index: include/elf/internal.h
===================================================================
RCS file: /cvs/src/src/include/elf/internal.h,v
retrieving revision 1.16
diff -u -p -r1.16 internal.h
--- include/elf/internal.h	19 Dec 2006 08:49:38 -0000	1.16
+++ include/elf/internal.h	2 May 2007 10:08:31 -0000
@@ -268,23 +268,23 @@ struct elf_segment_map
 /* Decide if the given sec_hdr is in the given segment.  PT_TLS segment
    contains only SHF_TLS sections.  Only PT_LOAD and PT_TLS segments
    can contain SHF_TLS sections.  */
-#define ELF_IS_SECTION_IN_SEGMENT(sec_hdr, segment)		\
-  (((((sec_hdr->sh_flags & SHF_TLS) != 0)			\
-     && (segment->p_type == PT_TLS				\
-	 || segment->p_type == PT_LOAD))			\
-    || ((sec_hdr->sh_flags & SHF_TLS) == 0			\
-	&& segment->p_type != PT_TLS))				\
-   /* Compare allocated sec_hdrs by VMA, unallocated sec_hdrs	\
-      by file offset.  */					\
-   && (sec_hdr->sh_flags & SHF_ALLOC				\
-       ? (sec_hdr->sh_addr >= segment->p_vaddr			\
-	  && (sec_hdr->sh_addr					\
-	      + ELF_SECTION_SIZE(sec_hdr, segment)		\
-	      <= segment->p_vaddr + segment->p_memsz))		\
-       : ((bfd_vma) sec_hdr->sh_offset >= segment->p_offset	\
-	  && (sec_hdr->sh_offset				\
-	      + ELF_SECTION_SIZE(sec_hdr, segment)		\
-	      <= segment->p_offset + segment->p_filesz))))
+#define ELF_IS_SECTION_IN_SEGMENT(sec_hdr, segment)			\
+  (((((sec_hdr->sh_flags & SHF_TLS) != 0)				\
+     && (segment->p_type == PT_TLS					\
+	 || segment->p_type == PT_LOAD))				\
+    || ((sec_hdr->sh_flags & SHF_TLS) == 0				\
+	&& segment->p_type != PT_TLS))					\
+   /* Any section besides one of type SHT_NOBITS must have a file	\
+      offset within the segment.  */					\
+   && (sec_hdr->sh_type == SHT_NOBITS					\
+       || ((bfd_vma) sec_hdr->sh_offset >= segment->p_offset		\
+	   && (sec_hdr->sh_offset + ELF_SECTION_SIZE(sec_hdr, segment)	\
+	       <= segment->p_offset + segment->p_filesz)))		\
+   /* SHF_ALLOC sections must have VMAs within the segment.  */		\
+   && ((sec_hdr->sh_flags & SHF_ALLOC) == 0				\
+       || (sec_hdr->sh_addr >= segment->p_vaddr				\
+	   && (sec_hdr->sh_addr + ELF_SECTION_SIZE(sec_hdr, segment)	\
+	       <= segment->p_vaddr + segment->p_memsz))))
 
 /* Decide if the given sec_hdr is in the given segment in file.  */
 #define ELF_IS_SECTION_IN_SEGMENT_FILE(sec_hdr, segment)	\
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.384
diff -u -p -r1.384 elf.c
--- bfd/elf.c	26 Apr 2007 14:46:56 -0000	1.384
+++ bfd/elf.c	2 May 2007 12:40:44 -0000
@@ -4511,6 +4511,7 @@ assign_file_positions_for_load_sections 
 	  asection *sec;
 	  flagword flags;
 	  bfd_size_type align;
+	  Elf_Internal_Shdr *this_hdr;
 
 	  sec = *secpp;
 	  flags = sec->flags;
@@ -4543,13 +4544,14 @@ assign_file_positions_for_load_sections 
 		}
 	    }
 
+	  this_hdr = &elf_section_data (sec)->this_hdr;
 	  if (p->p_type == PT_NOTE && bfd_get_format (abfd) == bfd_core)
 	    {
 	      /* The section at i == 0 is the one that actually contains
 		 everything.  */
 	      if (i == 0)
 		{
-		  sec->filepos = off;
+		  this_hdr->sh_offset = sec->filepos = off;
 		  off += sec->size;
 		  p->p_filesz = sec->size;
 		  p->p_memsz = 0;
@@ -4568,7 +4570,7 @@ assign_file_positions_for_load_sections 
 	    {
 	      if (p->p_type == PT_LOAD)
 		{
-		  sec->filepos = off + voff;
+		  this_hdr->sh_offset = sec->filepos = off + voff;
 		  /* FIXME: The SEC_HAS_CONTENTS test here dates back to
 		     1997, and the exact reason for it isn't clear.  One
 		     plausible explanation is that it is to work around
@@ -4586,12 +4588,22 @@ assign_file_positions_for_load_sections 
 		  if ((flags & SEC_LOAD) != 0
 		      || (flags & SEC_HAS_CONTENTS) != 0)
 		    off += sec->size;
+		  else
+		    /* If we aren't making room for this section, then
+		       it must be SHT_NOBITS regardless of what we've
+		       set via struct bfd_elf_special_section.  */
+		    this_hdr->sh_type = SHT_NOBITS;
 		}
 
 	      if ((flags & SEC_LOAD) != 0)
 		{
 		  p->p_filesz += sec->size;
-		  p->p_memsz += sec->size;
+		  /* SEC_LOAD without SEC_ALLOC is a weird combination
+		     used by note sections to signify that a PT_NOTE
+		     segment should be created.  These take file space
+		     but are not actually loaded into memory.  */
+		  if ((flags & SEC_ALLOC) != 0)
+		    p->p_memsz += sec->size;
 		}
 
 	      /* .tbss is special.  It doesn't contribute to p_memsz of
@@ -4629,11 +4641,9 @@ assign_file_positions_for_load_sections 
 	    }
 	}
 
-      /* Check if all sections are in the segment.  Skip PT_GNU_RELRO
-	 and PT_NOTE segments since they will be processed by
-	 assign_file_positions_for_non_load_sections later.  */
-      if (p->p_type != PT_GNU_RELRO
-	  && p->p_type != PT_NOTE)
+      /* Check that all sections are in the segment.  */
+      if (p->p_type == PT_LOAD
+	  || (p->p_type == PT_NOTE && bfd_get_format (abfd) == bfd_core))
 	for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
 	  {
 	    Elf_Internal_Shdr *this_hdr;
@@ -4689,7 +4699,7 @@ assign_file_positions_for_non_load_secti
 	  && (hdr->bfd_section->filepos != 0
 	      || (hdr->sh_type == SHT_NOBITS
 		  && hdr->contents == NULL)))
-	hdr->sh_offset = hdr->bfd_section->filepos;
+	BFD_ASSERT (hdr->sh_offset == hdr->bfd_section->filepos);
       else if ((hdr->sh_flags & SHF_ALLOC) != 0)
 	{
 	  if (hdr->sh_size != 0)

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]