From 96a2b4e4bf67a7430e7ddd06a1d0730a49e6b07e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 19 Mar 2008 21:41:38 +0000 Subject: [PATCH] Make sure the start and size of the TLS segment are aligned. --- gold/layout.cc | 8 ++-- gold/output.cc | 87 ++++++++++++++++++++++++++++++++------ gold/output.h | 7 +-- gold/testsuite/tls_test.cc | 7 +++ 4 files changed, 89 insertions(+), 20 deletions(-) diff --git a/gold/layout.cc b/gold/layout.cc index 978828f16a..9758597af5 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -1535,8 +1535,8 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, } unsigned int shndx_hold = *pshndx; - uint64_t new_addr = (*p)->set_section_addresses(false, addr, &off, - pshndx); + uint64_t new_addr = (*p)->set_section_addresses(this, false, addr, + &off, pshndx); // Now that we know the size of this segment, we may be able // to save a page in memory, at the cost of wasting some @@ -1561,8 +1561,8 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, addr = align_address(aligned_addr, common_pagesize); addr = align_address(addr, (*p)->maximum_alignment()); off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); - new_addr = (*p)->set_section_addresses(true, addr, &off, - pshndx); + new_addr = (*p)->set_section_addresses(this, true, addr, + &off, pshndx); } } diff --git a/gold/output.cc b/gold/output.cc index ba4f6c8421..85cc2b257b 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. diff --git a/gold/output.h b/gold/output.h index ac812a0ab1..e3945381c2 100644 --- a/gold/output.h +++ b/gold/output.h @@ -2582,7 +2582,7 @@ class Output_segment // address of the immediately following segment. Update *POFF and // *PSHNDX. This should only be called for a PT_LOAD segment. uint64_t - set_section_addresses(bool reset, uint64_t addr, off_t* poff, + set_section_addresses(const Layout*, bool reset, uint64_t addr, off_t* poff, unsigned int* pshndx); // Set the minimum alignment of this segment. This may be adjusted @@ -2638,8 +2638,9 @@ class Output_segment // Set the section addresses in an Output_data_list. uint64_t - set_section_list_addresses(bool reset, Output_data_list*, uint64_t addr, - off_t* poff, unsigned int* pshndx); + set_section_list_addresses(const Layout*, bool reset, Output_data_list*, + uint64_t addr, off_t* poff, unsigned int* pshndx, + bool* in_tls); // Return the number of Output_sections in an Output_data_list. unsigned int diff --git a/gold/testsuite/tls_test.cc b/gold/testsuite/tls_test.cc index 4a701da405..418d0ea256 100644 --- a/gold/testsuite/tls_test.cc +++ b/gold/testsuite/tls_test.cc @@ -60,6 +60,13 @@ __thread int v1; static __thread int v2; + +// We don't use these pointers, but putting them in tests alignment on +// a 64-bit target. +__thread char* p1; +char dummy; +__thread char* p2 = &dummy; + __thread int v3 = 3; static __thread int v4 = 4; __thread int v5;