aboutsummaryrefslogtreecommitdiff
path: root/gold/output.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2008-03-19 21:41:38 +0000
committerIan Lance Taylor <iant@google.com>2008-03-19 21:41:38 +0000
commit96a2b4e4bf67a7430e7ddd06a1d0730a49e6b07e (patch)
tree071317d44af235c3687bd900897968735303ebf2 /gold/output.cc
parent49a834f99faef9fd60532e6601c33bbeac27142b (diff)
downloadbinutils-96a2b4e4bf67a7430e7ddd06a1d0730a49e6b07e.zip
binutils-96a2b4e4bf67a7430e7ddd06a1d0730a49e6b07e.tar.gz
binutils-96a2b4e4bf67a7430e7ddd06a1d0730a49e6b07e.tar.bz2
Make sure the start and size of the TLS segment are aligned.
Diffstat (limited to 'gold/output.cc')
-rw-r--r--gold/output.cc87
1 files changed, 74 insertions, 13 deletions
diff --git a/gold/output.cc b/gold/output.cc
index ba4f6c8..85cc2b2 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -2483,7 +2483,8 @@ Output_segment::dynamic_reloc_count_list(const Output_data_list* pdl) const
// *POFF and *PSHNDX.
uint64_t
-Output_segment::set_section_addresses(bool reset, uint64_t addr, off_t* poff,
+Output_segment::set_section_addresses(const Layout* layout, bool reset,
+ uint64_t addr, off_t* poff,
unsigned int* pshndx)
{
gold_assert(this->type_ == elfcpp::PT_LOAD);
@@ -2500,17 +2501,31 @@ Output_segment::set_section_addresses(bool reset, uint64_t addr, off_t* poff,
this->are_addresses_set_ = true;
}
+ bool in_tls = false;
+
off_t orig_off = *poff;
this->offset_ = orig_off;
- addr = this->set_section_list_addresses(reset, &this->output_data_,
- addr, poff, pshndx);
+ addr = this->set_section_list_addresses(layout, reset, &this->output_data_,
+ addr, poff, pshndx, &in_tls);
this->filesz_ = *poff - orig_off;
off_t off = *poff;
- uint64_t ret = this->set_section_list_addresses(reset, &this->output_bss_,
- addr, poff, pshndx);
+ uint64_t ret = this->set_section_list_addresses(layout, reset,
+ &this->output_bss_,
+ addr, poff, pshndx,
+ &in_tls);
+
+ // If the last section was a TLS section, align upward to the
+ // alignment of the TLS segment, so that the overall size of the TLS
+ // segment is aligned.
+ if (in_tls)
+ {
+ uint64_t segment_align = layout->tls_segment()->maximum_alignment();
+ *poff = align_address(*poff, segment_align);
+ }
+
this->memsz_ = *poff - orig_off;
// Ignore the file offset adjustments made by the BSS Output_data
@@ -2524,9 +2539,11 @@ Output_segment::set_section_addresses(bool reset, uint64_t addr, off_t* poff,
// structures.
uint64_t
-Output_segment::set_section_list_addresses(bool reset, Output_data_list* pdl,
+Output_segment::set_section_list_addresses(const Layout* layout, bool reset,
+ Output_data_list* pdl,
uint64_t addr, off_t* poff,
- unsigned int* pshndx)
+ unsigned int* pshndx,
+ bool* in_tls)
{
off_t startoff = *poff;
@@ -2542,7 +2559,42 @@ Output_segment::set_section_list_addresses(bool reset, Output_data_list* pdl,
// already have an address.
if (!(*p)->is_address_valid())
{
- off = align_address(off, (*p)->addralign());
+ uint64_t align = (*p)->addralign();
+
+ if ((*p)->is_section_flag_set(elfcpp::SHF_TLS))
+ {
+ // Give the first TLS section the alignment of the
+ // entire TLS segment. Otherwise the TLS segment as a
+ // whole may be misaligned.
+ if (!*in_tls)
+ {
+ Output_segment* tls_segment = layout->tls_segment();
+ gold_assert(tls_segment != NULL);
+ uint64_t segment_align = tls_segment->maximum_alignment();
+ gold_assert(segment_align >= align);
+ align = segment_align;
+
+ *in_tls = true;
+ }
+ }
+ else
+ {
+ // If this is the first section after the TLS segment,
+ // align it to at least the alignment of the TLS
+ // segment, so that the size of the overall TLS segment
+ // is aligned.
+ if (*in_tls)
+ {
+ uint64_t segment_align =
+ layout->tls_segment()->maximum_alignment();
+ if (segment_align > align)
+ align = segment_align;
+
+ *in_tls = false;
+ }
+ }
+
+ off = align_address(off, align);
(*p)->set_address_and_file_offset(addr + (off - startoff), off);
}
else
@@ -2555,11 +2607,10 @@ Output_segment::set_section_list_addresses(bool reset, Output_data_list* pdl,
(*p)->finalize_data_size();
}
- // Unless this is a PT_TLS segment, 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 (this->type_ == elfcpp::PT_TLS
- || !(*p)->is_section_flag_set(elfcpp::SHF_TLS)
+ // We want to ignore the size of a SHF_TLS or 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))
off += (*p)->data_size();
@@ -2626,6 +2677,16 @@ Output_segment::set_offset()
this->memsz_ = (last->address()
+ last->data_size()
- this->vaddr_);
+
+ // If this is a TLS segment, align the memory size. The code in
+ // set_section_list ensures that the section after the TLS segment
+ // is aligned to give us room.
+ if (this->type_ == elfcpp::PT_TLS)
+ {
+ uint64_t segment_align = this->maximum_alignment();
+ gold_assert(this->vaddr_ == align_address(this->vaddr_, segment_align));
+ this->memsz_ = align_address(this->memsz_, segment_align);
+ }
}
// Set the TLS offsets of the sections in the PT_TLS segment.