aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCary Coutant <ccoutant@gmail.com>2016-12-11 17:31:25 -0800
committerCary Coutant <ccoutant@gmail.com>2016-12-11 17:31:25 -0800
commit5d9f66cb84b9f228c1082d3a1037355edceddded (patch)
treeb7eeb9bb9e1bee8c9f3160a7356343560a774169 /gold/output.cc
parentRegenerate Makefile.in to get rid of annoying diffs caused by non-determinist... (diff)
downloadbinutils-gdb-5d9f66cb84b9f228c1082d3a1037355edceddded.tar.gz
binutils-gdb-5d9f66cb84b9f228c1082d3a1037355edceddded.tar.bz2
binutils-gdb-5d9f66cb84b9f228c1082d3a1037355edceddded.zip
Fix problems with bss handling in linker scripts.
PR 16711 noted that gold allocates file space for BSS sections when using a linker script. I've fixed that by rewriting set_section_addresses and set_section_list_addresses to track the file offset separate from the current virtual address, so that BSS sections do not move the file offset. Now, if a series of BSS sections come at the end of a segment, we do not allocate file space; but if a script forces them into the middle of a segment, we will still allocate file space (matching Gnu ld behavior). I've also added a warning when that happens. That exposed another problem where orphan .bss sections were sometimes placed in the middle of a segment. For example, if the script mentions the .got section, but both .data and .bss are orphans, gold would put both .data and .bss in front of .got. I've fixed that by ensuring that orphan BSS sections are always placed after all other allocated sections. It also exposed a problem where the SUBALIGN property is not handled properly. The ld manual clearly states that it should override input section alignment, whether greater or less than the given alignment, but gold would only increase an input section's alignment. Gold would also place the output section based on its original alignment before the SUBALIGN property took effect, leading to a misaligned output section (where the input section was properly aligned in memory, but was not aligned relative to the start of the section), in violation of the ELF/gABI spec. I've fixed that by making sure that the SUBALIGN property overrides the internal alignment of the input sections as well as the external alignment of the output section. This affected the behavior of script_test_2, which was written to expect a misaligned section. The net effect is, I think, improved compatibility with the BFD linker. There are still cases where orphan placement differs, but the differences should be rarer and less important. ALIGN and SUBALIGN behavior is closer, but still not an exact match -- I still found cases where ld would create a misaligned output section, and where gold will properly align it. gold/ PR gold/16711 * output.cc (Output_section::set_final_data_size): Calculate data size based on relative offset rather than file offset. (Output_segment::set_section_addresses): Track file offset separately from address offset. (Output_segment::set_section_list_addresses): Add pfoff parameter. Track file offset separately. Don't move file offset for BSS sections. * output.h (Output_segment::set_section_list_addresses): Add pfoff parameter. * script-sections.cc (Orphan_section_placement): Add PLACE_LAST_ALLOC. (Orphan_section_placement::Orphan_section_placement): Initialize it. (Orphan_section_placement::output_section_init): Track last allocated section. (Orphan_section_placement::find_place): Place BSS after last allocated section. (Output_section_element_input::set_section_addresses): Always override input section alignment when SUBALIGN is specified. (Output_section_definition::set_section_addresses): Override alignment of output section when SUBALIGN is specified. * testsuite/Makefile.am (script_test_15a, script_test_15b) (script_test_15c): New test cases. * testsuite/Makefile.in: Regenerate. * testsuite/script_test_2.cc: Adjust expected layout. * testsuite/script_test_15.c: New source file. * testsuite/script_test_15a.sh: New shell script. * testsuite/script_test_15a.t: New linker script. * testsuite/script_test_15b.sh: New shell script. * testsuite/script_test_15b.t: New linker script. * testsuite/script_test_15c.sh: New shell script. * testsuite/script_test_15c.t: New linker script.
Diffstat (limited to 'gold/output.cc')
-rw-r--r--gold/output.cc65
1 files changed, 49 insertions, 16 deletions
diff --git a/gold/output.cc b/gold/output.cc
index 8e043d7cedc..cf934fb61a3 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -3165,17 +3165,17 @@ Output_section::set_final_data_size()
uint64_t address = this->address();
off_t startoff = this->offset();
- off_t off = startoff + this->first_input_offset_;
+ off_t off = this->first_input_offset_;
for (Input_section_list::iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
++p)
{
off = align_address(off, p->addralign());
- p->set_address_and_file_offset(address + (off - startoff), off,
+ p->set_address_and_file_offset(address + off, startoff + off,
startoff);
off += p->data_size();
}
- data_size = off - startoff;
+ data_size = off;
}
// For full incremental links, we want to allocate some patch space
@@ -4398,12 +4398,14 @@ Output_segment::set_section_addresses(const Target* target,
this->offset_ = orig_off;
off_t off = 0;
+ off_t foff = *poff;
uint64_t ret = 0;
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
{
if (i == static_cast<int>(ORDER_RELRO_LAST))
{
*poff += last_relro_pad;
+ foff += last_relro_pad;
addr += last_relro_pad;
if (this->output_lists_[i].empty())
{
@@ -4415,12 +4417,20 @@ Output_segment::set_section_addresses(const Target* target,
}
addr = this->set_section_list_addresses(layout, reset,
&this->output_lists_[i],
- addr, poff, pshndx, &in_tls);
- if (i < static_cast<int>(ORDER_SMALL_BSS))
- {
- this->filesz_ = *poff - orig_off;
- off = *poff;
- }
+ addr, poff, &foff, pshndx,
+ &in_tls);
+
+ // FOFF tracks the last offset used for the file image,
+ // and *POFF tracks the last offset used for the memory image.
+ // When not using a linker script, bss sections should all
+ // be processed in the ORDER_SMALL_BSS and later buckets.
+ gold_assert(*poff == foff
+ || i == static_cast<int>(ORDER_TLS_BSS)
+ || i >= static_cast<int>(ORDER_SMALL_BSS)
+ || layout->script_options()->saw_sections_clause());
+
+ this->filesz_ = foff - orig_off;
+ off = foff;
ret = addr;
}
@@ -4485,6 +4495,7 @@ uint64_t
Output_segment::set_section_list_addresses(Layout* layout, bool reset,
Output_data_list* pdl,
uint64_t addr, off_t* poff,
+ off_t* pfoff,
unsigned int* pshndx,
bool* in_tls)
{
@@ -4494,10 +4505,14 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
off_t maxoff = startoff;
off_t off = startoff;
+ off_t foff = *pfoff;
for (Output_data_list::iterator p = pdl->begin();
p != pdl->end();
++p)
{
+ bool is_bss = (*p)->is_section_type(elfcpp::SHT_NOBITS);
+ bool is_tls = (*p)->is_section_flag_set(elfcpp::SHF_TLS);
+
if (reset)
(*p)->reset_address_and_file_offset();
@@ -4507,7 +4522,7 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
{
uint64_t align = (*p)->addralign();
- if ((*p)->is_section_flag_set(elfcpp::SHF_TLS))
+ if (is_tls)
{
// Give the first TLS section the alignment of the
// entire TLS segment. Otherwise the TLS segment as a
@@ -4542,8 +4557,11 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
if (!parameters->incremental_update())
{
+ gold_assert(off == foff || is_bss);
off = align_address(off, align);
- (*p)->set_address_and_file_offset(addr + (off - startoff), off);
+ if (is_tls || !is_bss)
+ foff = off;
+ (*p)->set_address_and_file_offset(addr + (off - startoff), foff);
}
else
{
@@ -4551,6 +4569,7 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
(*p)->pre_finalize_data_size();
off_t current_size = (*p)->current_data_size();
off = layout->allocate(current_size, align, startoff);
+ foff = off;
if (off == -1)
{
gold_assert((*p)->output_section() != NULL);
@@ -4558,7 +4577,7 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
"relink with --incremental-full"),
(*p)->output_section()->name());
}
- (*p)->set_address_and_file_offset(addr + (off - startoff), off);
+ (*p)->set_address_and_file_offset(addr + (off - startoff), foff);
if ((*p)->data_size() > current_size)
{
gold_assert((*p)->output_section() != NULL);
@@ -4573,13 +4592,22 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
// For incremental updates, use the fixed offset for the
// high-water mark computation.
off = (*p)->offset();
+ foff = off;
}
else
{
// The script may have inserted a skip forward, but it
// better not have moved backward.
if ((*p)->address() >= addr + (off - startoff))
- off += (*p)->address() - (addr + (off - startoff));
+ {
+ if (!is_bss && off > foff)
+ gold_warning(_("script places BSS section in the middle "
+ "of a LOAD segment; space will be allocated "
+ "in the file"));
+ off += (*p)->address() - (addr + (off - startoff));
+ if (is_tls || !is_bss)
+ foff = off;
+ }
else
{
if (!layout->script_options()->saw_sections_clause())
@@ -4603,7 +4631,7 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
os->name(), previous_dot, dot);
}
}
- (*p)->set_file_offset(off);
+ (*p)->set_file_offset(foff);
(*p)->finalize_data_size();
}
@@ -4618,10 +4646,14 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
// We want to ignore the size of a SHF_TLS SHT_NOBITS
// section. Such a section does not affect the size of a
// PT_LOAD segment.
- if (!(*p)->is_section_flag_set(elfcpp::SHF_TLS)
- || !(*p)->is_section_type(elfcpp::SHT_NOBITS))
+ if (!is_tls || !is_bss)
off += (*p)->data_size();
+ // We don't allocate space in the file for SHT_NOBITS sections,
+ // unless a script has force-placed one in the middle of a segment.
+ if (!is_bss)
+ foff = off;
+
if (off > maxoff)
maxoff = off;
@@ -4633,6 +4665,7 @@ Output_segment::set_section_list_addresses(Layout* layout, bool reset,
}
*poff = maxoff;
+ *pfoff = foff;
return addr + (maxoff - startoff);
}