This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
elf.c assign_file_positions_for_segments
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: binutils at sources dot redhat dot com
- Date: Tue, 21 Sep 2004 20:59:02 +0930
- Subject: elf.c assign_file_positions_for_segments
This patch reworks quite a lot of assign_file_positions_for_segments
code. I won't commit this patch immediately as I intend to do some more
testing, and I'd like to give time for review. This is a fairly
critical piece of code..
The impetus for this rework was noticing the curious behaviour of
objcopy --only-keep-debug on the linux kernel. For instance, given a
ppc64 kernel with
Section Headers:
[Nr] Name Type Address Off Size
[ 0] NULL 0000000000000000 000000 000000
[ 1] .text PROGBITS c000000000000000 010000 37f000
[ 2] .exit.text PROGBITS c00000000037f000 38f000 001328
[ 3] __ex_table PROGBITS c000000000380328 390328 005d30
[snip]
[27] .bss NOBITS c000000000548000 558000 103930
[28] .comment PROGBITS 0000000000000000 558000 00405b
[29] .shstrtab STRTAB 0000000000000000 55c05b 000169
[30] .symtab SYMTAB 0000000000000000 55c9c8 0d5540
[31] .strtab STRTAB 0000000000000000 631f08 0a50be
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz
LOAD 0x010000 0xc000000000000000 0xc000000000000000 0x548000 0x64b930
objcopy --only-keep-debug would produce:
Section Headers:
[Nr] Name Type Address Off Size
[ 0] NULL 0000000000000000 000000 000000
[ 1] .text NOBITS c000000000000000 010000 37f000
[ 2] .exit.text NOBITS c00000000037f000 010000 001328
[ 3] __ex_table NOBITS c000000000380328 010000 005d30
[snip]
[27] .bss NOBITS c000000000548000 014452 103930
[28] .comment PROGBITS 0000000000000000 014452 00405b
[29] .shstrtab STRTAB 0000000000000000 0184ad 000169
[30] .symtab SYMTAB 0000000000000000 018e18 0d5540
[31] .strtab STRTAB 0000000000000000 0ee358 0a50be
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz
LOAD 0x010000 0xc000000000000000 0xc000000000000000 0x000000 0x64b930
Note the section offsets. We have space reserved in the file for NOBITS
sections, with one large chunk at the start so that p_offset and p_vaddr
are equal, modulo maxpagesize, and other chunks between sections due to
various bugs in assign_file_positions_for_segments. I think none of
this space is needed. It should be possible to assign p_offset to any
location in the file, since nothing will be loaded. Fixed version:
Section Headers:
[Nr] Name Type Address Off Size
[ 0] NULL 0000000000000000 000000 000000
[ 1] .text NOBITS c000000000000000 000078 37f000
[ 2] .exit.text NOBITS c00000000037f000 000078 001328
[ 3] __ex_table NOBITS c000000000380328 000078 005d30
[snip]
[27] .bss NOBITS c000000000548000 000078 103930
[28] .comment PROGBITS 0000000000000000 000078 00405b
[29] .shstrtab STRTAB 0000000000000000 0040d3 000169
[30] .symtab SYMTAB 0000000000000000 004a40 0d5540
[31] .strtab STRTAB 0000000000000000 0d9f80 0a50be
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz
LOAD 0x000000 0xc000000000000000 0xc000000000000000 0x000000 0x64b930
bfd/
* elf.c (IS_LOADED): Define.
(assign_file_positions_for_segments): Don't round up file offset of
PT_LOAD segments containing no SEC_LOAD sections, instead round down.
Hunks at 3906, 4044, and 4055 implement the above.
Delete code handling link script adjustment of lma. Do the adjust
in later code handling similar ajustments. Remove dead code error
check. Warn if section lma would require a negative offset
adjustment.
Hunk 4067. The old code handling link script adjustment meant that the
test for section start equal to segment start would always pass. Also,
the old code was buggy. It incorrectly tested sec->lma and adjusted
"off" for non-loaded sections. In moving this code, I'm not doing
exactly as before; The new code doesn't adjust for core PT_NOTE section
lma changes. I'm fairly certain this is OK since the linker doesn't
create core files. (We could be here on objcopy --change-section-lma
but that seems an unlikely thing to want to do on a PT_NOTE section,
particularly since the sections in a core file are faked by bfd and have
zero vmas and lmas.)
Test for loadable sections consistently using IS_LOADED.
Similarly, test for alloc-only sections other than .tbss consistently.
More from hunk 4067. I'm not certain whether the comment I moved above
IS_LOADED is true regarding NOLOAD, but it's certainly safe to check
SEC_HAS_CONTENTS everywhere we check SEC_LOAD. The extra .tbss checks
(ie. non-load, SEC_THREAD_LOCAL) ensure that .tbss alignment doesn't
affect a PT_LOAD segment.
Don't bother checking SEC_ALLOC in PT_LOAD segments. Remove FIXME.
Sections without SEC_ALLOC are removed earlier. The code after the
FIXME is needed, at least now..
Tidy PT_NOTE handling. Use %B and %A in error messages.
(assign_file_positions_except_relocs): Use %B in error message.
ld/testsuite
* ld-scripts/overlay-size.d: Don't check .mbss lma.
Some fallout from this patch: bss section lmas are changed in overlays.
As the test says:
# The .bss[123] LMAs are deliberately blanked out. We can't
# reliably map overlaid sections to segments.
--- cygnus.orig/bfd/elf.c 2004-09-20 15:58:11.000000000 +0930
+++ cygnus/bfd/elf.c 2004-09-21 19:56:20.269162159 +0930
@@ -3787,6 +3787,12 @@ vma_page_aligned_bias (bfd_vma vma, ufil
return ((vma - off) % maxpagesize);
}
+/* We check SEC_HAS_CONTENTS here because if NOLOAD is used in a linker
+ script we may have a section with SEC_LOAD clear but which is
+ supposed to have contents. */
+#define IS_LOADED(FLAGS) \
+ (((FLAGS) & SEC_LOAD) != 0 || ((FLAGS) & SEC_HAS_CONTENTS) != 0)
+
/* Assign file positions to the sections based on the mapping from
sections to segments. This function also sets up some fields in
the file header, and writes out the program headers. */
@@ -3866,8 +3872,8 @@ assign_file_positions_for_segments (bfd
if (alloc != 0 && count > alloc)
{
((*_bfd_error_handler)
- (_("%s: Not enough room for program headers (allocated %u, need %u)"),
- bfd_get_filename (abfd), alloc, count));
+ (_("%B: Not enough room for program headers (allocated %u, need %u)"),
+ abfd, alloc, count));
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
@@ -3906,32 +3912,61 @@ assign_file_positions_for_segments (bfd
qsort (m->sections, (size_t) m->count, sizeof (asection *),
elf_sort_sections);
+ /* In this loop, OFF tracks next available file offset in PT_LOAD
+ and PT_NOTE segments. OFF is used to place sections that need
+ file contents. VOFF (virtual offset) serves a similar purpose
+ for sections that have no file contents but need zero filled
+ memory allocation. An ELF segment (described by
+ Elf_Internal_Phdr) may contain a number of sections with
+ contents contributing to both p_filesz and p_memsz, followed
+ by a number of sections with no contents that just contribute
+ to p_memsz. */
+ voff = off;
p->p_type = m->p_type;
p->p_flags = m->p_flags;
if (p->p_type == PT_LOAD
- && m->count > 0
- && (m->sections[0]->flags & SEC_ALLOC) != 0)
+ && m->count > 0)
{
+ bfd_size_type align;
+ bfd_vma adjust;
+
if ((abfd->flags & D_PAGED) != 0)
- off += vma_page_aligned_bias (m->sections[0]->vma, off,
- bed->maxpagesize);
+ align = bed->maxpagesize;
else
{
- bfd_size_type align;
-
- align = 0;
+ unsigned int align_power = 0;
for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
{
- bfd_size_type secalign;
+ unsigned int secalign;
secalign = bfd_get_section_alignment (abfd, *secpp);
- if (secalign > align)
- align = secalign;
+ if (secalign > align_power)
+ align_power = secalign;
}
+ align = (bfd_size_type) 1 << align_power;
+ }
- off += vma_page_aligned_bias (m->sections[0]->vma, off,
- 1 << align);
+ adjust = vma_page_aligned_bias (m->sections[0]->vma, off, align);
+ if (adjust != 0)
+ {
+ if (IS_LOADED (m->sections[0]->flags)
+ || m->includes_filehdr
+ || m->includes_phdrs)
+ {
+ off += adjust;
+ voff = off;
+ }
+ else if ((m->sections[0]->flags & SEC_THREAD_LOCAL) == 0)
+ {
+ voff += adjust;
+ /* If the first section isn't loadable, the same holds
+ for any other sections. Since the segment won't
+ need file space, we can make p_offset overlap some
+ prior segment. */
+ if ((ufile_ptr) voff >= align)
+ voff -= align;
+ }
}
}
/* Make sure the .dynamic section is the first section in the
@@ -3941,8 +3976,8 @@ assign_file_positions_for_segments (bfd
&& strcmp (m->sections[0]->name, ".dynamic") != 0)
{
_bfd_error_handler
- (_("%s: The first section in the PT_DYNAMIC segment is not the .dynamic section"),
- bfd_get_filename (abfd));
+ (_("%B: The first section in the PT_DYNAMIC segment is not the .dynamic section"),
+ abfd);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
@@ -3985,8 +4020,8 @@ assign_file_positions_for_segments (bfd
if (p->p_vaddr < (bfd_vma) off)
{
(*_bfd_error_handler)
- (_("%s: Not enough room for program headers, try linking with -N"),
- bfd_get_filename (abfd));
+ (_("%B: Not enough room for program headers, try linking with -N"),
+ abfd);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
@@ -4044,7 +4079,7 @@ assign_file_positions_for_segments (bfd
|| (p->p_type == PT_NOTE && bfd_get_format (abfd) == bfd_core))
{
if (! m->includes_filehdr && ! m->includes_phdrs)
- p->p_offset = off;
+ p->p_offset = voff;
else
{
file_ptr adjust;
@@ -4055,8 +4090,6 @@ assign_file_positions_for_segments (bfd
}
}
- voff = off;
-
for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
{
asection *sec;
@@ -4067,117 +4100,86 @@ assign_file_positions_for_segments (bfd
flags = sec->flags;
align = 1 << bfd_get_section_alignment (abfd, sec);
- /* The section may have artificial alignment forced by a
- link script. Notice this case by the gap between the
- cumulative phdr lma and the section's lma. */
- if (p->p_paddr + p->p_memsz < sec->lma)
- {
- bfd_vma adjust = sec->lma - (p->p_paddr + p->p_memsz);
-
- p->p_memsz += adjust;
- if (p->p_type == PT_LOAD
- || (p->p_type == PT_NOTE
- && bfd_get_format (abfd) == bfd_core))
- {
- off += adjust;
- voff += adjust;
- }
- if ((flags & SEC_LOAD) != 0
- || (flags & SEC_THREAD_LOCAL) != 0)
- p->p_filesz += adjust;
- }
-
if (p->p_type == PT_LOAD)
{
bfd_signed_vma adjust;
- if ((flags & SEC_LOAD) != 0)
+ if (IS_LOADED (flags))
{
adjust = sec->lma - (p->p_paddr + p->p_memsz);
if (adjust < 0)
- adjust = 0;
+ {
+ (*_bfd_error_handler)
+ (_("%B: section %A lma 0x%lx overlaps previous sections"),
+ abfd, sec, (unsigned long) sec->lma);
+ adjust = 0;
+ }
+ off += adjust;
+ voff = off;
+ p->p_filesz += adjust;
+ p->p_memsz += adjust;
}
- else if ((flags & SEC_ALLOC) != 0)
+ else if ((flags & SEC_THREAD_LOCAL) == 0)
{
/* The section VMA must equal the file position
- modulo the page size. FIXME: I'm not sure if
- this adjustment is really necessary. We used to
- not have the SEC_LOAD case just above, and then
- this was necessary, but now I'm not sure. */
+ modulo the page size. */
if ((abfd->flags & D_PAGED) != 0)
adjust = vma_page_aligned_bias (sec->vma, voff,
bed->maxpagesize);
else
adjust = vma_page_aligned_bias (sec->vma, voff,
align);
- }
- else
- adjust = 0;
-
- if (adjust != 0)
- {
- if (i == 0)
- {
- (* _bfd_error_handler) (_("\
-Error: First section in segment (%s) starts at 0x%x whereas the segment starts at 0x%x"),
- bfd_section_name (abfd, sec),
- sec->lma,
- p->p_paddr);
- return FALSE;
- }
- p->p_memsz += adjust;
- off += adjust;
voff += adjust;
- if ((flags & SEC_LOAD) != 0)
- p->p_filesz += adjust;
+ p->p_memsz += adjust;
}
sec->filepos = off;
- /* We check SEC_HAS_CONTENTS here because if NOLOAD is
- used in a linker script we may have a section with
- SEC_LOAD clear but which is supposed to have
- contents. */
- if ((flags & SEC_LOAD) != 0
- || (flags & SEC_HAS_CONTENTS) != 0)
- off += sec->size;
-
- if ((flags & SEC_ALLOC) != 0
- && ((flags & SEC_LOAD) != 0
- || (flags & SEC_THREAD_LOCAL) == 0))
+ if (IS_LOADED (flags))
+ {
+ off += sec->size;
+ voff = off;
+ }
+ /* .tbss is special. It doesn't contribute to p_memsz of
+ normal segments. */
+ else if ((flags & SEC_THREAD_LOCAL) == 0)
voff += sec->size;
}
if (p->p_type == PT_NOTE && bfd_get_format (abfd) == bfd_core)
{
- /* The actual "note" segment has i == 0.
- This is the one that actually contains everything. */
+ /* The section at i == 0 is the one that actually contains
+ everything. */
if (i == 0)
{
sec->filepos = off;
p->p_filesz = sec->size;
off += sec->size;
voff = off;
+ p->p_memsz = 0;
+ p->p_align = 1;
}
else
{
- /* Fake sections -- don't need to be written. */
+ /* The rest are fake sections that shouldn't be written. */
sec->filepos = 0;
sec->size = 0;
- flags = sec->flags = 0;
+ sec->flags = 0;
+ continue;
}
- p->p_memsz = 0;
- p->p_align = 1;
}
else
{
- if ((sec->flags & SEC_LOAD) != 0
- || (sec->flags & SEC_THREAD_LOCAL) == 0
- || p->p_type == PT_TLS)
- p->p_memsz += sec->size;
-
- if ((flags & SEC_LOAD) != 0)
- p->p_filesz += sec->size;
+ if (IS_LOADED (flags))
+ {
+ p->p_filesz += sec->size;
+ p->p_memsz += sec->size;
+ }
+ /* .tbss is special. It doesn't contribute to p_memsz of
+ normal segments. */
+ else if ((flags & SEC_THREAD_LOCAL) == 0
+ || p->p_type == PT_TLS)
+ p->p_memsz += sec->size;
if (p->p_type == PT_TLS
&& sec->size == 0
@@ -4493,8 +4495,8 @@ assign_file_positions_except_relocs (bfd
else if ((hdr->sh_flags & SHF_ALLOC) != 0)
{
((*_bfd_error_handler)
- (_("%s: warning: allocated section `%s' not in segment"),
- bfd_get_filename (abfd),
+ (_("%B: warning: allocated section `%s' not in segment"),
+ abfd,
(hdr->bfd_section == NULL
? "*unknown*"
: hdr->bfd_section->name)));
--- cygnus.orig/ld/testsuite/ld-scripts/overlay-size.d 2002-05-07 20:38:57.000000000 +0930
+++ cygnus/ld/testsuite/ld-scripts/overlay-size.d 2004-09-20 12:58:55.000000000 +0930
@@ -13,7 +13,7 @@
#...
3 \.mtext +0+020 +0+10000 +0+30000 .*
#...
- 4 \.mbss +0+230 +0+20030 +0+20060 .*
+ 4 \.mbss +0+230 +0+20030 .*
#...
5 \.text1 +0+080 +0+10020 +0+30020 .*
#...
--
Alan Modra
IBM OzLabs - Linux Technology Centre