From 730cdc88f70c0804b5daf2259d3bd8ad29b6411b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 9 Nov 2007 07:00:15 +0000 Subject: Generate a complete exception frame header. Discard duplicate exception frame information. --- gold/dynobj.cc | 2 + gold/dynobj.h | 5 + gold/ehframe.cc | 1047 +++++++++++++++++++++++++++++++++++++++++++- gold/ehframe.h | 359 ++++++++++++++- gold/gold.cc | 24 + gold/i386.cc | 21 +- gold/layout.cc | 278 ++++++++++-- gold/layout.h | 130 +++++- gold/merge.cc | 85 ++-- gold/merge.h | 92 +++- gold/object.cc | 210 ++++++++- gold/object.h | 120 ++++- gold/output.cc | 153 +++++-- gold/output.h | 119 +++-- gold/po/gold.pot | 155 +++---- gold/reloc.cc | 218 +++++++-- gold/reloc.h | 58 ++- gold/stringpool.cc | 2 +- gold/symtab.cc | 12 +- gold/symtab.h | 2 +- gold/target-reloc.h | 44 +- gold/target.h | 31 +- gold/testsuite/testfile.cc | 9 +- gold/x86_64.cc | 21 +- 24 files changed, 2830 insertions(+), 367 deletions(-) diff --git a/gold/dynobj.cc b/gold/dynobj.cc index 95ffe08..9845194 100644 --- a/gold/dynobj.cc +++ b/gold/dynobj.cc @@ -288,6 +288,7 @@ Sized_dynobj::do_read_symbols(Read_symbols_data* sd) sd->symbols = NULL; sd->symbols_size = 0; + sd->external_symbols_offset = 0; sd->symbol_names = NULL; sd->symbol_names_size = 0; @@ -606,6 +607,7 @@ Sized_dynobj::do_add_symbols(Symbol_table* symtab, const int sym_size = This::sym_size; const size_t symcount = sd->symbols_size / sym_size; + gold_assert(sd->external_symbols_offset == 0); if (static_cast(symcount * sym_size) != sd->symbols_size) { this->error(_("size of dynamic symbols is not multiple of symbol size")); diff --git a/gold/dynobj.h b/gold/dynobj.h index d9c3e10..7895dda 100644 --- a/gold/dynobj.h +++ b/gold/dynobj.h @@ -146,6 +146,11 @@ class Sized_dynobj : public Dynobj do_section_flags(unsigned int shndx) { return this->elf_file_.section_flags(shndx); } + // Return section type. + unsigned int + do_section_type(unsigned int shndx) + { return this->elf_file_.section_type(shndx); } + // Return the section link field. unsigned int do_section_link(unsigned int shndx) diff --git a/gold/ehframe.cc b/gold/ehframe.cc index 607654b..1c7b714 100644 --- a/gold/ehframe.cc +++ b/gold/ehframe.cc @@ -22,8 +22,13 @@ #include "gold.h" +#include +#include + #include "elfcpp.h" #include "dwarf.h" +#include "symtab.h" +#include "reloc.h" #include "ehframe.h" namespace gold @@ -31,7 +36,8 @@ namespace gold // This file handles generation of the exception frame header that // gcc's runtime support libraries use to find unwind information at -// runtime. +// runtime. This file also handles discarding duplicate exception +// frame information. // The exception frame header starts with four bytes: @@ -68,16 +74,17 @@ namespace gold // the start of the exception frame header information. The entries // are in sorted order by starting PC. -// FIXME: We currently always generate an empty exception frame -// header. - const int eh_frame_hdr_size = 4; // Construct the exception frame header. -Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section) +Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section, + const Eh_frame* eh_frame_data) : Output_section_data(4), - eh_frame_section_(eh_frame_section) + eh_frame_section_(eh_frame_section), + eh_frame_data_(eh_frame_data), + fde_offsets_(), + any_unrecognized_eh_frame_sections_(false) { } @@ -86,7 +93,15 @@ Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section) void Eh_frame_hdr::do_set_address(uint64_t, off_t) { - this->set_data_size(eh_frame_hdr_size + 4); + unsigned int data_size = eh_frame_hdr_size + 4; + if (!this->any_unrecognized_eh_frame_sections_) + { + unsigned int fde_count = this->eh_frame_data_->fde_count(); + if (fde_count != 0) + data_size += 4 + 8 * fde_count; + this->fde_offsets_.reserve(fde_count); + } + this->set_data_size(data_size); } // Write the data to the flie. @@ -94,6 +109,54 @@ Eh_frame_hdr::do_set_address(uint64_t, off_t) void Eh_frame_hdr::do_write(Output_file* of) { + if (parameters->get_size() == 32) + { + if (!parameters->is_big_endian()) + { +#ifdef HAVE_TARGET_32_LITTLE + this->do_sized_write<32, false>(of); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_32_BIG + this->do_sized_write<32, true>(of); +#else + gold_unreachable(); +#endif + } + } + else if (parameters->get_size() == 64) + { + if (!parameters->is_big_endian()) + { +#ifdef HAVE_TARGET_64_LITTLE + this->do_sized_write<64, false>(of); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_64_BIG + this->do_sized_write<64, true>(of); +#else + gold_unreachable(); +#endif + } + } + else + gold_unreachable(); +} + +// Write the data to the file with the right endianness. + +template +void +Eh_frame_hdr::do_sized_write(Output_file* of) +{ const off_t off = this->offset(); const off_t oview_size = this->data_size(); unsigned char* const oview = of->get_output_view(off, oview_size); @@ -108,18 +171,974 @@ Eh_frame_hdr::do_write(Output_file* of) uint64_t eh_frame_hdr_address = this->address(); uint64_t eh_frame_offset = (eh_frame_address - (eh_frame_hdr_address + 4)); - if (parameters->is_big_endian()) - elfcpp::Swap<32, true>::writeval(oview + 4, eh_frame_offset); + elfcpp::Swap<32, big_endian>::writeval(oview + 4, eh_frame_offset); + + if (this->any_unrecognized_eh_frame_sections_ + || this->fde_offsets_.empty()) + { + // There are no FDEs, or we didn't recognize the format of the + // some of the .eh_frame sections, so we can't write out the + // sorted table. + oview[2] = elfcpp::DW_EH_PE_omit; + oview[3] = elfcpp::DW_EH_PE_omit; + + gold_assert(oview_size == 8); + } else - elfcpp::Swap<32, false>::writeval(oview + 4, eh_frame_offset); + { + oview[2] = elfcpp::DW_EH_PE_udata4; + oview[3] = elfcpp::DW_EH_PE_datarel | elfcpp::DW_EH_PE_sdata4; + + elfcpp::Swap<32, big_endian>::writeval(oview + 8, + this->fde_offsets_.size()); + + // We have the offsets of the FDEs in the .eh_frame section. We + // couldn't easily get the PC values before, as they depend on + // relocations which are, of course, target specific. This code + // is run after all those relocations have been applied to the + // output file. Here we read the output file again to find the + // PC values. Then we sort the list and write it out. - // We don't currently write out the sorted table. - oview[2] = elfcpp::DW_EH_PE_omit; - oview[3] = elfcpp::DW_EH_PE_omit; + Fde_addresses fde_addresses(this->fde_offsets_.size()); + this->get_fde_addresses(of, &this->fde_offsets_, + &fde_addresses); - gold_assert(oview_size == 8); + std::sort(fde_addresses.begin(), fde_addresses.end(), + Fde_address_compare()); + + typename elfcpp::Elf_types::Elf_Addr output_address; + output_address = this->address(); + + unsigned char* pfde = oview + 12; + for (typename Fde_addresses::iterator p = fde_addresses.begin(); + p != fde_addresses.end(); + ++p) + { + elfcpp::Swap<32, big_endian>::writeval(pfde, + p->first - output_address); + elfcpp::Swap<32, big_endian>::writeval(pfde + 4, + p->second - output_address); + pfde += 8; + } + + gold_assert(pfde - oview == oview_size); + } of->write_output_view(off, oview_size, oview); } +// Given the offset FDE_OFFSET of an FDE in the .eh_frame section, and +// the contents of the .eh_frame section EH_FRAME_CONTENTS, where the +// FDE's encoding is FDE_ENCODING, return the output address of the +// FDE's PC. + +template +typename elfcpp::Elf_types::Elf_Addr +Eh_frame_hdr::get_fde_pc(const unsigned char* eh_frame_contents, + off_t fde_offset, unsigned char fde_encoding) +{ + // The FDE starts with a 4 byte length and a 4 byte offset to the + // CIE. The PC follows. + const unsigned char* p = eh_frame_contents + fde_offset + 8; + + typename elfcpp::Elf_types::Elf_Addr pc; + bool is_signed = (fde_encoding & elfcpp::DW_EH_PE_signed) != 0; + int pc_size = fde_encoding & 7; + if (pc_size == elfcpp::DW_EH_PE_absptr) + { + if (size == 32) + pc_size = elfcpp::DW_EH_PE_udata4; + else if (size == 64) + pc_size = elfcpp::DW_EH_PE_udata8; + else + gold_unreachable(); + } + + switch (pc_size) + { + case elfcpp::DW_EH_PE_udata2: + pc = elfcpp::Swap<16, big_endian>::readval(p); + if (is_signed) + pc = (pc ^ 0x8000) - 0x8000; + break; + + case elfcpp::DW_EH_PE_udata4: + pc = elfcpp::Swap<32, big_endian>::readval(p); + if (size > 32 && is_signed) + pc = (pc ^ 0x80000000) - 0x80000000; + break; + + case elfcpp::DW_EH_PE_udata8: + gold_assert(size == 64); + pc = elfcpp::Swap_unaligned<64, big_endian>::readval(p); + break; + + default: + gold_unreachable(); + } + + return pc; +} + +// Given an array of FDE offsets in the .eh_frame section, return an +// array of offsets from the exception frame header to the FDE's +// output PC and to the output address of the FDE itself. We get the +// FDE's PC by actually looking in the .eh_frame section we just wrote +// to the output file. + +template +void +Eh_frame_hdr::get_fde_addresses(Output_file* of, + const Fde_offsets* fde_offsets, + Fde_addresses* fde_addresses) +{ + typename elfcpp::Elf_types::Elf_Addr eh_frame_address; + eh_frame_address = this->eh_frame_section_->address(); + off_t eh_frame_offset = this->eh_frame_section_->offset(); + off_t eh_frame_size = this->eh_frame_section_->data_size(); + const unsigned char* eh_frame_contents = of->get_input_view(eh_frame_offset, + eh_frame_size); + + for (Fde_offsets::const_iterator p = fde_offsets->begin(); + p != fde_offsets->end(); + ++p) + { + typename elfcpp::Elf_types::Elf_Addr fde_pc; + fde_pc = this->get_fde_pc(eh_frame_contents, + p->first, p->second); + fde_addresses->push_back(fde_pc, eh_frame_address + p->first); + } + + of->free_input_view(eh_frame_offset, eh_frame_size, eh_frame_contents); +} + +// Class Fde. + +// Write the FDE to OVIEW starting at OFFSET. CIE_OFFSET is the +// offset of the CIE in OVIEW. FDE_ENCODING is the encoding, from the +// CIE. Record the FDE pc for EH_FRAME_HDR. Return the new offset. + +template +off_t +Fde::write(unsigned char* oview, off_t offset, off_t cie_offset, + unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr) +{ + size_t length = this->contents_.length(); + + // Write the length of the FDE as a 32-bit word. The length word + // does not include the four bytes of the length word itself, but it + // does include the offset to the CIE. + elfcpp::Swap<32, big_endian>::writeval(oview + offset, + length + 4); + + // Write the offset to the CIE as a 32-bit word. This is the + // difference between the address of the offset word itself and the + // CIE address. + elfcpp::Swap<32, big_endian>::writeval(oview + offset + 4, + offset + 4 - cie_offset); + + // Copy the rest of the FDE. Note that this is run before + // relocation processing is done on this section, so the relocations + // will later be applied to the FDE data. + memcpy(oview + offset + 8, this->contents_.data(), length); + + // Tell the exception frame header about this FDE. + if (eh_frame_hdr != NULL) + eh_frame_hdr->record_fde(offset, fde_encoding); + + return offset + length + 8; +} + +// Class Cie. + +// Destructor. + +Cie::~Cie() +{ + for (std::vector::iterator p = this->fdes_.begin(); + p != this->fdes_.end(); + ++p) + delete *p; +} + +// Set the output offset of a CIE. Return the new output offset. + +off_t +Cie::set_output_offset(off_t output_offset, unsigned int addralign, + Merge_map* merge_map) +{ + size_t length = this->contents_.length(); + gold_assert((length & (addralign - 1)) == 0); + // Add 4 for length and 4 for zero CIE identifier tag. + length += 8; + + merge_map->add_mapping(this->object_, this->shndx_, this->input_offset_, + length, output_offset); + + for (std::vector::const_iterator p = this->fdes_.begin(); + p != this->fdes_.end(); + ++p) + { + (*p)->add_mapping(output_offset + length, merge_map); + + size_t fde_length = (*p)->length(); + gold_assert((fde_length & (addralign - 1)) == 0); + length += fde_length; + } + + return output_offset + length; +} + +// Write the CIE to OVIEW starting at OFFSET. EH_FRAME_HDR is for FDE +// recording. Return the new offset. + +template +off_t +Cie::write(unsigned char* oview, off_t offset, Eh_frame_hdr* eh_frame_hdr) +{ + off_t cie_offset = offset; + + size_t length = this->contents_.length(); + + // Write the length of the CIE as a 32-bit word. The length word + // does not include the four bytes of the length word itself. + elfcpp::Swap<32, big_endian>::writeval(oview + offset, length + 4); + + // Write the tag which marks this as a CIE: a 32-bit zero. + elfcpp::Swap<32, big_endian>::writeval(oview + offset + 4, 0); + + // Write out the CIE data. + memcpy(oview + offset + 8, this->contents_.data(), length); + offset += length + 8; + + // Write out the associated FDEs. + unsigned char fde_encoding = this->fde_encoding_; + for (std::vector::const_iterator p = this->fdes_.begin(); + p != this->fdes_.end(); + ++p) + offset = (*p)->write(oview, offset, cie_offset, + fde_encoding, eh_frame_hdr); + + return offset; +} + +// We track all the CIEs we see, and merge them when possible. This +// works because each FDE holds an offset to the relevant CIE: we +// rewrite the FDEs to point to the merged CIE. This is worthwhile +// because in a typical C++ program many FDEs in many different object +// files will use the same CIE. + +// An equality operator for Cie. + +bool +operator==(const Cie& cie1, const Cie& cie2) +{ + return (cie1.personality_name_ == cie2.personality_name_ + && cie1.contents_ == cie2.contents_); +} + +// A less-than operator for Cie. + +bool +operator<(const Cie& cie1, const Cie& cie2) +{ + if (cie1.personality_name_ != cie2.personality_name_) + return cie1.personality_name_ < cie2.personality_name_; + return cie1.contents_ < cie2.contents_; +} + +// Class Eh_frame. + +Eh_frame::Eh_frame() + : Output_section_data(Output_data::default_alignment()), + eh_frame_hdr_(NULL), + cie_offsets_(), + unmergeable_cie_offsets_(), + merge_map_() +{ +} + +// Skip an LEB128, updating *PP to point to the next character. +// Return false if we ran off the end of the string. + +bool +Eh_frame::skip_leb128(const unsigned char** pp, const unsigned char* pend) +{ + const unsigned char* p; + for (p = *pp; p < pend; ++p) + { + if ((*p & 0x80) == 0) + { + *pp = p + 1; + return true; + } + } + return false; +} + +// Add input section SHNDX in OBJECT to an exception frame section. +// SYMBOLS is the contents of the symbol table section (size +// SYMBOLS_SIZE), SYMBOL_NAMES is the symbol names section (size +// SYMBOL_NAMES_SIZE). RELOC_SHNDX is the index of a relocation +// section applying to SHNDX, or 0 if none, or -1U if more than one. +// RELOC_TYPE is the type of the reloc section if there is one, either +// SHT_REL or SHT_RELA. We try to parse the input exception frame +// data into our data structures. If we can't do it, we return false +// to mean that the section should be handled as a normal input +// section. + +template +bool +Eh_frame::add_ehframe_input_section( + Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type) +{ + // Get the section contents. + off_t contents_len; + const unsigned char* pcontents = object->section_contents(shndx, + &contents_len, + false); + if (contents_len == 0) + return false; + + // If this is the marker section for the end of the data, then + // return false to force it to be handled as an ordinary input + // section. If we don't do this, we won't correctly handle the case + // of unrecognized .eh_frame sections. + if (contents_len == 4 + && elfcpp::Swap<32, big_endian>::readval(pcontents) == 0) + return false; + + New_cies new_cies; + if (!this->do_add_ehframe_input_section(object, symbols, symbols_size, + symbol_names, symbol_names_size, + shndx, reloc_shndx, + reloc_type, pcontents, + contents_len, &new_cies)) + { + this->eh_frame_hdr_->found_unrecognized_eh_frame_section(); + + for (New_cies::iterator p = new_cies.begin(); + p != new_cies.end(); + ++p) + delete p->first; + + return false; + } + + // Now that we know we are using this section, record any new CIEs + // that we found. + for (New_cies::const_iterator p = new_cies.begin(); + p != new_cies.end(); + ++p) + { + uint64_t zero = 0; + if (p->second) + this->cie_offsets_.insert(std::make_pair(p->first, zero)); + else + this->unmergeable_cie_offsets_.push_back(std::make_pair(p->first, + zero)); + } + + return true; +} + +// The bulk of the implementation of add_ehframe_input_section. + +template +bool +Eh_frame::do_add_ehframe_input_section( + Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type, + const unsigned char* pcontents, + off_t contents_len, + New_cies* new_cies) +{ + typedef typename elfcpp::Elf_types::Elf_Addr Address; + Track_relocs relocs; + + const unsigned char* p = pcontents; + const unsigned char* pend = p + contents_len; + + // Get the contents of the reloc section if any. + if (!relocs.initialize(object, reloc_shndx, reloc_type)) + return false; + + // Keep track of which CIEs are at which offsets. + Offsets_to_cie cies; + + while (p < pend) + { + if (pend - p < 4) + return false; + + // There shouldn't be any relocations here. + if (relocs.advance(p + 4 - pcontents) > 0) + return false; + + unsigned int len = elfcpp::Swap<32, big_endian>::readval(p); + p += 4; + if (len == 0) + { + // We should only find a zero-length entry at the end of the + // section. + if (p < pend) + return false; + break; + } + // We don't support a 64-bit .eh_frame. + if (len == 0xffffffff) + return false; + if (static_cast(pend - p) < len) + return false; + + const unsigned char* const pentend = p + len; + + if (pend - p < 4) + return false; + if (relocs.advance(p + 4 - pcontents) > 0) + return false; + + unsigned int id = elfcpp::Swap<32, big_endian>::readval(p); + p += 4; + + if (id == 0) + { + // CIE. + if (!this->read_cie(object, shndx, symbols, symbols_size, + symbol_names, symbol_names_size, + pcontents, p, pentend, &relocs, &cies, + new_cies)) + return false; + } + else + { + // FDE. + if (!this->read_fde(object, shndx, symbols, symbols_size, + pcontents, id, p, pentend, &relocs, &cies)) + return false; + } + + p = pentend; + } + + return true; +} + +// Read a CIE. Return false if we can't parse the information. + +template +bool +Eh_frame::read_cie(Sized_relobj* object, + unsigned int shndx, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + const unsigned char* pcontents, + const unsigned char* pcie, + const unsigned char *pcieend, + Track_relocs* relocs, + Offsets_to_cie* cies, + New_cies* new_cies) +{ + bool mergeable = true; + + // We need to find the personality routine if there is one, since we + // can only merge CIEs which use the same routine. We also need to + // find the FDE encoding if there is one, so that we can read the PC + // from the FDE. + + const unsigned char* p = pcie; + + if (pcieend - p < 1) + return false; + unsigned char version = *p++; + if (version != 1 && version != 3) + return false; + + const unsigned char* paug = p; + const void* paugendv = memchr(p, '\0', pcieend - p); + const unsigned char* paugend = static_cast(paugendv); + if (paugend == NULL) + return false; + p = paugend + 1; + + if (paug[0] == 'e' && paug[1] == 'h') + { + // This is a CIE from gcc before version 3.0. We can't merge + // these. We can still read the FDEs. + mergeable = false; + paug += 2; + if (*paug != '\0') + return false; + if (pcieend - p < size / 8) + return false; + p += size / 8; + } + + // Skip the code alignment. + if (!skip_leb128(&p, pcieend)) + return false; + + // Skip the data alignment. + if (!skip_leb128(&p, pcieend)) + return false; + + // Skip the return column. + if (version == 1) + { + if (pcieend - p < 1) + return false; + ++p; + } + else + { + if (!skip_leb128(&p, pcieend)) + return false; + } + + if (*paug == 'z') + { + ++paug; + // Skip the augmentation size. + if (!skip_leb128(&p, pcieend)) + return false; + } + + unsigned char fde_encoding = elfcpp::DW_EH_PE_absptr; + int per_offset = -1; + while (*paug != '\0') + { + switch (*paug) + { + case 'L': // LSDA encoding. + if (pcieend - p < 1) + return false; + ++p; + break; + + case 'R': // FDE encoding. + if (pcieend - p < 1) + return false; + fde_encoding = *p; + switch (fde_encoding & 7) + { + case elfcpp::DW_EH_PE_absptr: + case elfcpp::DW_EH_PE_udata2: + case elfcpp::DW_EH_PE_udata4: + case elfcpp::DW_EH_PE_udata8: + break; + default: + return false; + } + ++p; + break; + + case 'S': + break; + + case 'P': + // Personality encoding. + { + if (pcieend - p < 1) + return false; + unsigned char per_encoding = *p; + ++p; + + if ((per_encoding & 0x60) == 0x60) + return false; + unsigned int per_width; + switch (per_encoding & 7) + { + case elfcpp::DW_EH_PE_udata2: + per_width = 2; + break; + case elfcpp::DW_EH_PE_udata4: + per_width = 4; + break; + case elfcpp::DW_EH_PE_udata8: + per_width = 8; + break; + case elfcpp::DW_EH_PE_absptr: + per_width = size / 8; + break; + default: + return false; + } + + if ((per_encoding & 0xf0) == elfcpp::DW_EH_PE_aligned) + { + unsigned int len = p - pcie; + len += per_width - 1; + len &= ~ (per_width - 1); + if (static_cast(pcieend - p) < len) + return false; + p += len; + } + + per_offset = p - pcontents; + + if (static_cast(pcieend - p) < per_width) + return false; + p += per_width; + } + break; + + default: + return false; + } + + ++paug; + } + + const char* personality_name = ""; + if (per_offset != -1) + { + if (relocs->advance(per_offset) > 0) + return false; + if (relocs->next_offset() != per_offset) + return false; + + unsigned int personality_symndx = relocs->next_symndx(); + if (personality_symndx == -1U) + return false; + + if (personality_symndx < object->local_symbol_count()) + { + // We can only merge this CIE if the personality routine is + // a global symbol. We can still read the FDEs. + mergeable = false; + } + else + { + const int sym_size = elfcpp::Elf_sizes::sym_size; + if (personality_symndx >= symbols_size / sym_size) + return false; + elfcpp::Sym sym(symbols + + (personality_symndx * sym_size)); + unsigned int name_offset = sym.get_st_name(); + if (name_offset >= symbol_names_size) + return false; + personality_name = (reinterpret_cast(symbol_names) + + name_offset); + } + + int r = relocs->advance(per_offset + 1); + gold_assert(r == 1); + } + + if (relocs->advance(pcieend - pcontents) > 0) + return false; + + Cie cie(object, shndx, (pcie - 8) - pcontents, fde_encoding, + personality_name, pcie, pcieend - pcie); + Cie* cie_pointer = NULL; + if (mergeable) + { + Cie_offsets::iterator find_cie = this->cie_offsets_.find(&cie); + if (find_cie != this->cie_offsets_.end()) + cie_pointer = find_cie->first; + else + { + // See if we already saw this CIE in this object file. + for (New_cies::const_iterator pc = new_cies->begin(); + pc != new_cies->end(); + ++pc) + { + if (*(pc->first) == cie) + { + cie_pointer = pc->first; + break; + } + } + } + } + + if (cie_pointer == NULL) + { + cie_pointer = new Cie(cie); + new_cies->push_back(std::make_pair(cie_pointer, mergeable)); + } + else + { + // We are deleting this CIE. Record that in our mapping from + // input sections to the output section. At this point we don't + // know for sure that we are doing a special mapping for this + // input section, but that's OK--if we don't do a special + // mapping, nobody will ever ask for the mapping we add here. + this->merge_map_.add_mapping(object, shndx, (pcie - 8) - pcontents, + pcieend - (pcie - 8), -1); + } + + // Record this CIE plus the offset in the input section. + cies->insert(std::make_pair(pcie - pcontents, cie_pointer)); + + return true; +} + +// Read an FDE. Return false if we can't parse the information. + +template +bool +Eh_frame::read_fde(Sized_relobj* object, + unsigned int shndx, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* pcontents, + unsigned int offset, + const unsigned char* pfde, + const unsigned char *pfdeend, + Track_relocs* relocs, + Offsets_to_cie* cies) +{ + // OFFSET is the distance between the 4 bytes before PFDE to the + // start of the CIE. The offset we recorded for the CIE is 8 bytes + // after the start of the CIE--after the length and the zero tag. + unsigned int cie_offset = (pfde - 4 - pcontents) - offset + 8; + Offsets_to_cie::const_iterator pcie = cies->find(cie_offset); + if (pcie == cies->end()) + return false; + Cie* cie = pcie->second; + + // The FDE should start with a reloc to the start of the code which + // it describes. + if (relocs->advance(pfde - pcontents) > 0) + return false; + + if (relocs->next_offset() != pfde - pcontents) + return false; + + unsigned int symndx = relocs->next_symndx(); + if (symndx == -1U) + return false; + + // There can be another reloc in the FDE, if the CIE specifies an + // LSDA (language specific data area). We currently don't care. We + // will care later if we want to optimize the LSDA from an absolute + // pointer to a PC relative offset when generating a shared library. + relocs->advance(pfdeend - pcontents); + + unsigned int fde_shndx; + const int sym_size = elfcpp::Elf_sizes::sym_size; + if (symndx >= symbols_size / sym_size) + return false; + elfcpp::Sym sym(symbols + symndx * sym_size); + fde_shndx = sym.get_st_shndx(); + + if (fde_shndx != elfcpp::SHN_UNDEF + && fde_shndx < object->shnum() + && !object->is_section_included(fde_shndx)) + { + // This FDE applies to a section which we are discarding. We + // can discard this FDE. + this->merge_map_.add_mapping(object, shndx, (pfde - 8) - pcontents, + pfdeend - (pfde - 8), -1); + return true; + } + + cie->add_fde(new Fde(object, shndx, (pfde - 8) - pcontents, + pfde, pfdeend - pfde)); + + return true; +} + +// Return the number of FDEs. + +unsigned int +Eh_frame::fde_count() const +{ + unsigned int ret = 0; + for (Unmergeable_cie_offsets::const_iterator p = + this->unmergeable_cie_offsets_.begin(); + p != this->unmergeable_cie_offsets_.end(); + ++p) + ret += p->first->fde_count(); + for (Cie_offsets::const_iterator p = this->cie_offsets_.begin(); + p != this->cie_offsets_.end(); + ++p) + ret += p->first->fde_count(); + return ret; +} + +// Set the final data size. + +void +Eh_frame::do_set_address(uint64_t, off_t start_file_offset) +{ + off_t output_offset = 0; + + for (Unmergeable_cie_offsets::iterator p = + this->unmergeable_cie_offsets_.begin(); + p != this->unmergeable_cie_offsets_.end(); + ++p) + { + p->second = start_file_offset + output_offset; + output_offset = p->first->set_output_offset(output_offset, + this->addralign(), + &this->merge_map_); + } + + for (Cie_offsets::iterator p = this->cie_offsets_.begin(); + p != this->cie_offsets_.end(); + ++p) + { + p->second = start_file_offset + output_offset; + output_offset = p->first->set_output_offset(output_offset, + this->addralign(), + &this->merge_map_); + } + + gold_assert((output_offset & (this->addralign() - 1)) == 0); + this->set_data_size(output_offset); +} + +// Return an output offset for an input offset. + +bool +Eh_frame::do_output_offset(const Relobj* object, unsigned int shndx, + off_t offset, off_t* poutput) const +{ + return this->merge_map_.get_output_offset(object, shndx, offset, poutput); +} + +// Write the data to the output file. + +void +Eh_frame::do_write(Output_file* of) +{ + const off_t offset = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + if (parameters->get_size() == 32) + { + if (!parameters->is_big_endian()) + { +#ifdef HAVE_TARGET_32_LITTLE + this->do_sized_write<32, false>(oview); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_32_BIG + this->do_sized_write<32, true>(oview); +#else + gold_unreachable(); +#endif + } + } + else if (parameters->get_size() == 64) + { + if (!parameters->is_big_endian()) + { +#ifdef HAVE_TARGET_64_LITTLE + this->do_sized_write<64, false>(oview); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_64_BIG + this->do_sized_write<64, true>(oview); +#else + gold_unreachable(); +#endif + } + } + else + gold_unreachable(); + + of->write_output_view(offset, oview_size, oview); +} + +// Write the data to the output file--template version. + +template +void +Eh_frame::do_sized_write(unsigned char* oview) +{ + off_t o = 0; + for (Unmergeable_cie_offsets::iterator p = + this->unmergeable_cie_offsets_.begin(); + p != this->unmergeable_cie_offsets_.end(); + ++p) + o = p->first->write(oview, o, this->eh_frame_hdr_); + for (Cie_offsets::iterator p = this->cie_offsets_.begin(); + p != this->cie_offsets_.end(); + ++p) + o = p->first->write(oview, o, this->eh_frame_hdr_); +} + +#ifdef HAVE_TARGET_32_LITTLE +template +bool +Eh_frame::add_ehframe_input_section<32, false>( + Sized_relobj<32, false>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type); +#endif + +#ifdef HAVE_TARGET_32_BIG +template +bool +Eh_frame::add_ehframe_input_section<32, true>( + Sized_relobj<32, true>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type); +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +bool +Eh_frame::add_ehframe_input_section<64, false>( + Sized_relobj<64, false>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type); +#endif + +#ifdef HAVE_TARGET_64_BIG +template +bool +Eh_frame::add_ehframe_input_section<64, true>( + Sized_relobj<64, true>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type); +#endif + } // End namespace gold. diff --git a/gold/ehframe.h b/gold/ehframe.h index 0641ac8..6a584f9 100644 --- a/gold/ehframe.h +++ b/gold/ehframe.h @@ -24,10 +24,16 @@ #define GOLD_EHFRAME_H #include "output.h" +#include "merge.h" namespace gold { +template +class Track_relocs; + +class Eh_frame; + // This class manages the .eh_frame_hdr section, which holds the data // for the PT_GNU_EH_FRAME segment. gcc's unwind support code uses // the PT_GNU_EH_FRAME segment to find the list of FDEs. This saves @@ -42,7 +48,20 @@ namespace gold class Eh_frame_hdr : public Output_section_data { public: - Eh_frame_hdr(Output_section* eh_frame_section); + Eh_frame_hdr(Output_section* eh_frame_section, const Eh_frame*); + + // Record that we found an unrecognized .eh_frame section. + void + found_unrecognized_eh_frame_section() + { this->any_unrecognized_eh_frame_sections_ = true; } + + // Record an FDE. + void + record_fde(off_t fde_offset, unsigned char fde_encoding) + { + if (!this->any_unrecognized_eh_frame_sections_) + this->fde_offsets_.push_back(std::make_pair(fde_offset, fde_encoding)); + } // Set the final data size. void @@ -53,8 +72,346 @@ class Eh_frame_hdr : public Output_section_data do_write(Output_file*); private: + // Write the data to the file with the right endianness. + template + void + do_sized_write(Output_file*); + + // The data we record for one FDE: the offset of the FDE within the + // .eh_frame section, and the FDE encoding. + typedef std::pair Fde_offset; + + // The list of information we record for an FDE. + typedef std::vector Fde_offsets; + + // When writing out the header, we convert the FDE offsets into FDE + // addresses. This is a list of pairs of the offset from the header + // to the FDE PC and to the FDE itself. + template + class Fde_addresses + { + public: + typedef typename elfcpp::Elf_types::Elf_Addr Address; + typedef typename std::pair Fde_address; + typedef typename std::vector Fde_address_list; + typedef typename Fde_address_list::iterator iterator; + + Fde_addresses(unsigned int reserve) + : fde_addresses_() + { this->fde_addresses_.reserve(reserve); } + + void + push_back(Address pc_address, Address fde_address) + { + this->fde_addresses_.push_back(std::make_pair(pc_address, fde_address)); + } + + iterator + begin() + { return this->fde_addresses_.begin(); } + + iterator + end() + { return this->fde_addresses_.end(); } + + private: + Fde_address_list fde_addresses_; + }; + + // Compare Fde_address objects. + template + struct Fde_address_compare + { + bool + operator()(const typename Fde_addresses::Fde_address& f1, + const typename Fde_addresses::Fde_address& f2) const + { return f1.first < f2.first; } + }; + + // Return the PC to which an FDE refers. + template + typename elfcpp::Elf_types::Elf_Addr + get_fde_pc(const unsigned char* eh_frame_contents, + off_t fde_offset, unsigned char fde_encoding); + + // Convert Fde_offsets to Fde_addresses. + template + void + get_fde_addresses(Output_file* of, + const Fde_offsets* fde_offsets, + Fde_addresses* fde_addresses); + // The .eh_frame section. Output_section* eh_frame_section_; + // The .eh_frame section data. + const Eh_frame* eh_frame_data_; + // Data from the FDEs in the .eh_frame sections. + Fde_offsets fde_offsets_; + // Whether we found any .eh_frame sections which we could not + // process. + bool any_unrecognized_eh_frame_sections_; +}; + +// This class holds an FDE. + +class Fde +{ + public: + Fde(Relobj* object, unsigned int shndx, off_t input_offset, + const unsigned char* contents, size_t length) + : object_(object), shndx_(shndx), input_offset_(input_offset), + contents_(reinterpret_cast(contents), length) + { } + + // Return the length of this FDE. Add 4 for the length and 4 for + // the offset to the CIE. + size_t + length() const + { return this->contents_.length() + 8; } + + // Add a mapping for this FDE to MERGE_MAP. + void + add_mapping(off_t output_offset, Merge_map* merge_map) const + { + merge_map->add_mapping(this->object_, this->shndx_, + this->input_offset_, this->length(), + output_offset); + } + + // Write the FDE to OVIEW starting at OFFSET. FDE_ENCODING is the + // encoding, from the CIE. Record the FDE in EH_FRAME_HDR. Return + // the new offset. + template + off_t + write(unsigned char* oview, off_t offset, off_t cie_offset, + unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr); + + private: + // The object in which this FDE was seen. + Relobj* object_; + // Input section index for this FDE. + unsigned int shndx_; + // Offset within the input section for this FDE. + off_t input_offset_; + // FDE data. + std::string contents_; +}; + +// This class holds a CIE. + +class Cie +{ + public: + Cie(Relobj* object, unsigned int shndx, off_t input_offset, + unsigned char fde_encoding, const char* personality_name, + const unsigned char* contents, size_t length) + : object_(object), + shndx_(shndx), + input_offset_(input_offset), + fde_encoding_(fde_encoding), + personality_name_(personality_name), + fdes_(), + contents_(reinterpret_cast(contents), length) + { } + + ~Cie(); + + // We permit copying a CIE when there are no FDEs. This is + // convenient in the code which creates them. + Cie(const Cie& cie) + : object_(cie.object_), + shndx_(cie.shndx_), + input_offset_(cie.input_offset_), + fde_encoding_(cie.fde_encoding_), + personality_name_(cie.personality_name_), + fdes_(), + contents_(cie.contents_) + { gold_assert(cie.fdes_.empty()); } + + // Add an FDE associated with this CIE. + void + add_fde(Fde* fde) + { this->fdes_.push_back(fde); } + + // Return the number of FDEs. + unsigned int + fde_count() const + { return this->fdes_.size(); } + + // Set the output offset of this CIE to OUTPUT_OFFSET. It will be + // followed by all its FDEs. ADDRALIGN is the required address + // alignment, typically 4 or 8. This updates MERGE_MAP with the + // mapping. It returns the new output offset. + off_t + set_output_offset(off_t output_offset, unsigned int addralign, Merge_map*); + + // Write the CIE to OVIEW starting at OFFSET. EH_FRAME_HDR is the + // exception frame header for FDE recording. Return the new offset. + template + off_t + write(unsigned char* oview, off_t offset, Eh_frame_hdr* eh_frame_hdr); + + friend bool operator<(const Cie&, const Cie&); + friend bool operator==(const Cie&, const Cie&); + + private: + // The class is not assignable. + Cie& operator=(const Cie&); + + // The object in which this CIE was first seen. + Relobj* object_; + // Input section index for this CIE. + unsigned int shndx_; + // Offset within the input section for this CIE. + off_t input_offset_; + // The encoding of the FDE. This is a DW_EH_PE code. + unsigned char fde_encoding_; + // The name of the personality routine. This will be the name of a + // global symbol, or will be the empty string. + std::string personality_name_; + // List of FDEs. + std::vector fdes_; + // CIE data. + std::string contents_; +}; + +extern bool operator<(const Cie&, const Cie&); +extern bool operator==(const Cie&, const Cie&); + +// This class manages .eh_frame sections. It discards duplicate +// exception information. + +class Eh_frame : public Output_section_data +{ + public: + Eh_frame(); + + // Record the associated Eh_frame_hdr, if any. + void + set_eh_frame_hdr(Eh_frame_hdr* hdr) + { this->eh_frame_hdr_ = hdr; } + + // Add the input section SHNDX in OBJECT. SYMBOLS is the contents + // of the symbol table section (size SYMBOLS_SIZE), SYMBOL_NAMES is + // the symbol names section (size SYMBOL_NAMES_SIZE). RELOC_SHNDX + // is the relocation section if any (0 for none, -1U for multiple). + // RELOC_TYPE is the type of the relocation section if any. This + // returns whether the section was incorporated into the .eh_frame + // data. + template + bool + add_ehframe_input_section(Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, unsigned int reloc_shndx, + unsigned int reloc_type); + + // Return the number of FDEs. + unsigned int + fde_count() const; + + // Set the final data size. + void + do_set_address(uint64_t, off_t); + + // Return the output address for an input address. + bool + do_output_offset(const Relobj*, unsigned int shndx, off_t offset, + off_t* poutput) const; + + // Write the data to the file. + void + do_write(Output_file*); + + private: + // The comparison routine for the CIE map. + struct Cie_less + { + bool + operator()(const Cie* cie1, const Cie* cie2) const + { return *cie1 < *cie2; } + }; + + // A mapping from unique CIEs to their offset in the output file. + typedef std::map Cie_offsets; + + // A list of unmergeable CIEs with their offsets. + typedef std::vector > Unmergeable_cie_offsets; + + // A mapping from offsets to CIEs. This is used while reading an + // input section. + typedef std::map Offsets_to_cie; + + // A list of CIEs, and a bool indicating whether the CIE is + // mergeable. + typedef std::vector > New_cies; + + // Skip an LEB128. + static bool + skip_leb128(const unsigned char**, const unsigned char*); + + // The implementation of add_ehframe_input_section. + template + bool + do_add_ehframe_input_section(Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + unsigned int reloc_shndx, + unsigned int reloc_type, + const unsigned char* pcontents, + off_t contents_len, + New_cies*); + + // Read a CIE. + template + bool + read_cie(Sized_relobj* object, + unsigned int shndx, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + const unsigned char* pcontents, + const unsigned char* pcie, + const unsigned char *pcieend, + Track_relocs* relocs, + Offsets_to_cie* cies, + New_cies* new_cies); + + // Read an FDE. + template + bool + read_fde(Sized_relobj* object, + unsigned int shndx, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* pcontents, + unsigned int offset, + const unsigned char* pfde, + const unsigned char *pfdeend, + Track_relocs* relocs, + Offsets_to_cie* cies); + + // Template version of write function. + template + void + do_sized_write(unsigned char* oview); + + // The exception frame header, if any. + Eh_frame_hdr* eh_frame_hdr_; + // A mapping from all unique CIEs to their offset in the output + // file. + Cie_offsets cie_offsets_; + // A mapping from unmergeable CIEs to their offset in the output + // file. + Unmergeable_cie_offsets unmergeable_cie_offsets_; + // A mapping from input sections to the output section. + Merge_map merge_map_; }; } // End namespace gold. diff --git a/gold/gold.cc b/gold/gold.cc index 9a3a1e6..83bb3f6 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -250,6 +250,14 @@ queue_final_tasks(const General_options& options, thread_count = input_objects->number_of_input_objects(); workqueue->set_thread_count(thread_count); + // Use a blocker to wait until all the input sections have been + // written out. + Task_token* input_sections_blocker = new Task_token(); + + // Use a blocker to block any objects which have to wait for the + // output sections to complete before they can apply relocations. + Task_token* output_sections_blocker = new Task_token(); + // Use a blocker to block the final cleanup task. Task_token* final_blocker = new Task_token(); @@ -259,8 +267,11 @@ queue_final_tasks(const General_options& options, p != input_objects->relobj_end(); ++p) { + input_sections_blocker->add_blocker(); final_blocker->add_blocker(); workqueue->queue(new Relocate_task(options, symtab, layout, *p, of, + input_sections_blocker, + output_sections_blocker, final_blocker)); } @@ -273,10 +284,23 @@ queue_final_tasks(const General_options& options, of, final_blocker)); + // Queue a task to write out the output sections. + output_sections_blocker->add_blocker(); + final_blocker->add_blocker(); + workqueue->queue(new Write_sections_task(layout, of, output_sections_blocker, + final_blocker)); + // Queue a task to write out everything else. final_blocker->add_blocker(); workqueue->queue(new Write_data_task(layout, symtab, of, final_blocker)); + // Queue a task to write out the output sections which depend on + // input sections. + final_blocker->add_blocker(); + workqueue->queue(new Write_after_input_sections_task(layout, of, + input_sections_blocker, + final_blocker)); + // Queue a task to close the output file. This will be blocked by // FINAL_BLOCKER. workqueue->queue(new Task_function(new Close_task_runner(of), diff --git a/gold/i386.cc b/gold/i386.cc index 699aa68..d0b0b51 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -70,9 +70,10 @@ class Target_i386 : public Sized_target<32, false> unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_symbol_count, - const unsigned char* plocal_symbols, - Symbol** global_symbols); + const unsigned char* plocal_symbols); // Finalize the sections. void @@ -89,6 +90,8 @@ class Target_i386 : public Sized_target<32, false> unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, off_t view_size); @@ -1157,9 +1160,10 @@ Target_i386::scan_relocs(const General_options& options, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_symbol_count, - const unsigned char* plocal_symbols, - Symbol** global_symbols) + const unsigned char* plocal_symbols) { if (sh_type == elfcpp::SHT_RELA) { @@ -1178,9 +1182,10 @@ Target_i386::scan_relocs(const General_options& options, data_shndx, prelocs, reloc_count, + output_section, + needs_special_offset_handling, local_symbol_count, - plocal_symbols, - global_symbols); + plocal_symbols); } // Finalize the sections. @@ -1770,6 +1775,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr address, off_t view_size) @@ -1782,6 +1789,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, this, prelocs, reloc_count, + output_section, + needs_special_offset_handling, view, address, view_size); diff --git a/gold/layout.cc b/gold/layout.cc index 84e8722..6894264 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -101,7 +101,7 @@ is_prefix_of(const char* prefix, const char* str) template bool -Layout::include_section(Object*, const char* name, +Layout::include_section(Sized_relobj*, const char* name, const elfcpp::Shdr& shdr) { // Some section types are never linked. Some are only linked when @@ -202,13 +202,20 @@ Layout::get_output_section(const char* name, Stringpool::Key name_key, } // Return the output section to use for input section SHNDX, with name -// NAME, with header HEADER, from object OBJECT. Set *OFF to the -// offset of this input section without the output section. +// NAME, with header HEADER, from object OBJECT. RELOC_SHNDX is the +// index of a relocation section which applies to this section, or 0 +// if none, or -1U if more than one. RELOC_TYPE is the type of the +// relocation section if there is one. Set *OFF to the offset of this +// input section without the output section. Return NULL if the +// section should be discarded. Set *OFF to -1 if the section +// contents should not be written directly to the output file, but +// will instead receive special handling. template Output_section* -Layout::layout(Relobj* object, unsigned int shndx, const char* name, - const elfcpp::Shdr& shdr, off_t* off) +Layout::layout(Sized_relobj* object, unsigned int shndx, + const char* name, const elfcpp::Shdr& shdr, + unsigned int reloc_shndx, unsigned int, off_t* off) { if (!this->include_section(object, name, shdr)) return NULL; @@ -231,38 +238,44 @@ Layout::layout(Relobj* object, unsigned int shndx, const char* name, shdr.get_sh_type(), shdr.get_sh_flags()); - // Special GNU handling of sections named .eh_frame. - if (!parameters->output_is_object() - && strcmp(name, ".eh_frame") == 0 - && shdr.get_sh_size() > 0 - && shdr.get_sh_type() == elfcpp::SHT_PROGBITS - && shdr.get_sh_flags() == elfcpp::SHF_ALLOC) - { - this->layout_eh_frame(object, shndx, name, shdr, os, off); - return os; - } - // FIXME: Handle SHF_LINK_ORDER somewhere. - *off = os->add_input_section(object, shndx, name, shdr); + *off = os->add_input_section(object, shndx, name, shdr, reloc_shndx); return os; } -// Special GNU handling of sections named .eh_frame. They will -// normally hold exception frame data. +// Special GNU handling of sections name .eh_frame. They will +// normally hold exception frame data as defined by the C++ ABI +// (http://codesourcery.com/cxx-abi/). template -void -Layout::layout_eh_frame(Relobj* object, +Output_section* +Layout::layout_eh_frame(Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, unsigned int shndx, - const char* name, const elfcpp::Shdr& shdr, - Output_section* os, off_t* off) + unsigned int reloc_shndx, unsigned int reloc_type, + off_t* off) { + gold_assert(shdr.get_sh_type() == elfcpp::SHT_PROGBITS); + gold_assert(shdr.get_sh_flags() == elfcpp::SHF_ALLOC); + + Stringpool::Key name_key; + const char* name = this->namepool_.add(".eh_frame", false, &name_key); + + Output_section* os = this->get_output_section(name, name_key, + elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC); + if (this->eh_frame_section_ == NULL) { this->eh_frame_section_ = os; + this->eh_frame_data_ = new Eh_frame(); + os->add_output_section_data(this->eh_frame_data_); if (this->options_.create_eh_frame_hdr()) { @@ -275,19 +288,39 @@ Layout::layout_eh_frame(Relobj* object, elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC); - Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os); + Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os, this->eh_frame_data_); hdr_os->add_output_section_data(hdr_posd); + hdr_os->set_after_input_sections(); + Output_segment* hdr_oseg = new Output_segment(elfcpp::PT_GNU_EH_FRAME, elfcpp::PF_R); this->segment_list_.push_back(hdr_oseg); hdr_oseg->add_output_section(hdr_os, elfcpp::PF_R); + + this->eh_frame_data_->set_eh_frame_hdr(hdr_posd); } } gold_assert(this->eh_frame_section_ == os); - *off = os->add_input_section(object, shndx, name, shdr); + if (this->eh_frame_data_->add_ehframe_input_section(object, + symbols, + symbols_size, + symbol_names, + symbol_names_size, + shndx, + reloc_shndx, + reloc_type)) + *off = -1; + else + { + // We couldn't handle this .eh_frame section for some reason. + // Add it as a normal section. + *off = os->add_input_section(object, shndx, name, shdr, reloc_shndx); + } + + return os; } // Add POSD to an output section using NAME, TYPE, and FLAGS. @@ -1724,6 +1757,22 @@ Layout::add_comdat(const char* signature, bool group) } } +// Write out the Output_sections. Most won't have anything to write, +// since most of the data will come from input sections which are +// handled elsewhere. But some Output_sections do have Output_data. + +void +Layout::write_output_sections(Output_file* of) const +{ + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if (!(*p)->after_input_sections()) + (*p)->write(of); + } +} + // Write out data not associated with a section or the symbol table. void @@ -1764,15 +1813,6 @@ Layout::write_data(const Symbol_table* symtab, Output_file* of) const } } - // Write out the Output_sections. Most won't have anything to - // write, since most of the data will come from input sections which - // are handled elsewhere. But some Output_sections do have - // Output_data. - for (Section_list::const_iterator p = this->section_list_.begin(); - p != this->section_list_.end(); - ++p) - (*p)->write(of); - // Write out the Output_data which are not in an Output_section. for (Data_list::const_iterator p = this->special_output_list_.begin(); p != this->special_output_list_.end(); @@ -1780,6 +1820,65 @@ Layout::write_data(const Symbol_table* symtab, Output_file* of) const (*p)->write(of); } +// Write out the Output_sections which can only be written after the +// input sections are complete. + +void +Layout::write_sections_after_input_sections(Output_file* of) const +{ + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if ((*p)->after_input_sections()) + (*p)->write(of); + } +} + +// Write_sections_task methods. + +// We can always run this task. + +Task::Is_runnable_type +Write_sections_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We need to unlock both OUTPUT_SECTIONS_BLOCKER and FINAL_BLOCKER +// when finished. + +class Write_sections_task::Write_sections_locker : public Task_locker +{ + public: + Write_sections_locker(Task_token& output_sections_blocker, + Task_token& final_blocker, + Workqueue* workqueue) + : output_sections_block_(output_sections_blocker, workqueue), + final_block_(final_blocker, workqueue) + { } + + private: + Task_block_token output_sections_block_; + Task_block_token final_block_; +}; + +Task_locker* +Write_sections_task::locks(Workqueue* workqueue) +{ + return new Write_sections_locker(*this->output_sections_blocker_, + *this->final_blocker_, + workqueue); +} + +// Run the task--write out the data. + +void +Write_sections_task::run(Workqueue*) +{ + this->layout_->write_output_sections(this->of_); +} + // Write_data_task methods. // We can always run this task. @@ -1833,6 +1932,34 @@ Write_symbols_task::run(Workqueue*) this->of_); } +// Write_after_input_sections_task methods. + +// We can only run this task after the input sections have completed. + +Task::Is_runnable_type +Write_after_input_sections_task::is_runnable(Workqueue*) +{ + if (this->input_sections_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +// We need to unlock FINAL_BLOCKER when finished. + +Task_locker* +Write_after_input_sections_task::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->final_blocker_, workqueue); +} + +// Run the task. + +void +Write_after_input_sections_task::run(Workqueue*) +{ + this->layout_->write_sections_after_input_sections(this->of_); +} + // Close_task_runner methods. // Run the task--close the file. @@ -1849,30 +1976,97 @@ Close_task_runner::run(Workqueue*) #ifdef HAVE_TARGET_32_LITTLE template Output_section* -Layout::layout<32, false>(Relobj* object, unsigned int shndx, const char* name, - const elfcpp::Shdr<32, false>& shdr, off_t*); +Layout::layout<32, false>(Sized_relobj<32, false>* object, unsigned int shndx, + const char* name, + const elfcpp::Shdr<32, false>& shdr, + unsigned int, unsigned int, off_t*); #endif #ifdef HAVE_TARGET_32_BIG template Output_section* -Layout::layout<32, true>(Relobj* object, unsigned int shndx, const char* name, - const elfcpp::Shdr<32, true>& shdr, off_t*); +Layout::layout<32, true>(Sized_relobj<32, true>* object, unsigned int shndx, + const char* name, + const elfcpp::Shdr<32, true>& shdr, + unsigned int, unsigned int, off_t*); #endif #ifdef HAVE_TARGET_64_LITTLE template Output_section* -Layout::layout<64, false>(Relobj* object, unsigned int shndx, const char* name, - const elfcpp::Shdr<64, false>& shdr, off_t*); +Layout::layout<64, false>(Sized_relobj<64, false>* object, unsigned int shndx, + const char* name, + const elfcpp::Shdr<64, false>& shdr, + unsigned int, unsigned int, off_t*); #endif #ifdef HAVE_TARGET_64_BIG template Output_section* -Layout::layout<64, true>(Relobj* object, unsigned int shndx, const char* name, - const elfcpp::Shdr<64, true>& shdr, off_t*); +Layout::layout<64, true>(Sized_relobj<64, true>* object, unsigned int shndx, + const char* name, + const elfcpp::Shdr<64, true>& shdr, + unsigned int, unsigned int, off_t*); #endif +#ifdef HAVE_TARGET_32_LITTLE +template +Output_section* +Layout::layout_eh_frame<32, false>(Sized_relobj<32, false>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + const elfcpp::Shdr<32, false>& shdr, + unsigned int reloc_shndx, + unsigned int reloc_type, + off_t* off); +#endif + +#ifdef HAVE_TARGET_32_BIG +template +Output_section* +Layout::layout_eh_frame<32, true>(Sized_relobj<32, true>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + const elfcpp::Shdr<32, true>& shdr, + unsigned int reloc_shndx, + unsigned int reloc_type, + off_t* off); +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +Output_section* +Layout::layout_eh_frame<64, false>(Sized_relobj<64, false>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + const elfcpp::Shdr<64, false>& shdr, + unsigned int reloc_shndx, + unsigned int reloc_type, + off_t* off); +#endif + +#ifdef HAVE_TARGET_64_BIG +template +Output_section* +Layout::layout_eh_frame<64, true>(Sized_relobj<64, true>* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + const elfcpp::Shdr<64, true>& shdr, + unsigned int reloc_shndx, + unsigned int reloc_type, + off_t* off); +#endif } // End namespace gold. diff --git a/gold/layout.h b/gold/layout.h index 5e9871e..fa804ad 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -45,6 +45,7 @@ class Output_section_headers; class Output_segment; class Output_data; class Output_data_dynamic; +class Eh_frame; class Target; // This task function handles mapping the input sections to output @@ -87,12 +88,37 @@ class Layout // Given an input section SHNDX, named NAME, with data in SHDR, from // the object file OBJECT, return the output section where this - // input section should go. Set *OFFSET to the offset within the - // output section. + // input section should go. RELOC_SHNDX is the index of a + // relocation section which applies to this section, or 0 if none, + // or -1U if more than one. RELOC_TYPE is the type of the + // relocation section if there is one. Set *OFFSET to the offset + // within the output section. template Output_section* - layout(Relobj *object, unsigned int shndx, const char* name, - const elfcpp::Shdr& shdr, off_t* offset); + layout(Sized_relobj *object, unsigned int shndx, + const char* name, const elfcpp::Shdr& shdr, + unsigned int reloc_shndx, unsigned int reloc_type, off_t* offset); + + // Like layout, only for exception frame sections. OBJECT is an + // object file. SYMBOLS is the contents of the symbol table + // section, with size SYMBOLS_SIZE. SYMBOL_NAMES is the contents of + // the symbol name section, with size SYMBOL_NAMES_SIZE. SHNDX is a + // .eh_frame section in OBJECT. SHDR is the section header. + // RELOC_SHNDX is the index of a relocation section which applies to + // this section, or 0 if none, or -1U if more than one. RELOC_TYPE + // is the type of the relocation section if there is one. This + // returns the output section, and sets *OFFSET to the offset. + template + Output_section* + layout_eh_frame(Sized_relobj* object, + const unsigned char* symbols, + off_t symbols_size, + const unsigned char* symbol_names, + off_t symbol_names_size, + unsigned int shndx, + const elfcpp::Shdr& shdr, + unsigned int reloc_shndx, unsigned int reloc_type, + off_t* offset); // Handle a GNU stack note. This is called once per input object // file. SEEN_GNU_STACK is true if the object file has a @@ -176,11 +202,20 @@ class Layout dynamic_data() const { return this->dynamic_data_; } + // Write out the output sections. + void + write_output_sections(Output_file* of) const; + // Write out data not associated with an input file or the symbol // table. void write_data(const Symbol_table*, Output_file*) const; + // Write out output sections which can not be written until all the + // input sections are complete. + void + write_sections_after_input_sections(Output_file* of) const; + // Return an output section named NAME, or NULL if there is none. Output_section* find_output_section(const char* name) const; @@ -218,13 +253,6 @@ class Layout static const Linkonce_mapping linkonce_mapping[]; static const int linkonce_mapping_count; - // Handle an exception frame section. - template - void - layout_eh_frame(Relobj*, unsigned int, const char*, - const elfcpp::Shdr&, - Output_section*, off_t*); - // Create a .note section for gold. void create_gold_note(); @@ -285,7 +313,7 @@ class Layout // Return whether to include this section in the link. template bool - include_section(Object* object, const char* name, + include_section(Sized_relobj* object, const char* name, const elfcpp::Shdr&); // Return the output section name to use given an input section @@ -389,8 +417,12 @@ class Layout Output_section* dynamic_section_; // The dynamic data which goes into dynamic_section_. Output_data_dynamic* dynamic_data_; - // The exception frame section. + // The exception frame output section if there is one. Output_section* eh_frame_section_; + // The exception frame data for eh_frame_section_. + Eh_frame* eh_frame_data_; + // The exception frame header output section if there is one. + Output_section* eh_frame_hdr_section_; // The size of the output file. off_t output_file_size_; // Whether we have seen an object file marked to require an @@ -404,6 +436,42 @@ class Layout bool input_without_gnu_stack_note_; }; +// This task handles writing out data in output sections which is not +// part of an input section, or which requires special handling. When +// this is done, it unblocks both output_sections_blocker and +// final_blocker. + +class Write_sections_task : public Task +{ + public: + Write_sections_task(const Layout* layout, Output_file* of, + Task_token* output_sections_blocker, + Task_token* final_blocker) + : layout_(layout), of_(of), + output_sections_blocker_(output_sections_blocker), + final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Write_sections_locker; + + const Layout* layout_; + Output_file* of_; + Task_token* output_sections_blocker_; + Task_token* final_blocker_; +}; + // This task handles writing out data which is not part of a section // or segment. @@ -465,6 +533,42 @@ class Write_symbols_task : public Task Task_token* final_blocker_; }; +// This task handles writing out data in output sections which can't +// be written out until all the input sections have been handled. +// This is for sections whose contents is based on the contents of +// other output sections. + +class Write_after_input_sections_task : public Task +{ + public: + Write_after_input_sections_task(const Layout* layout, Output_file* of, + Task_token* input_sections_blocker, + Task_token* final_blocker) + : layout_(layout), of_(of), + input_sections_blocker_(input_sections_blocker), + final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Write_sections_locker; + + const Layout* layout_; + Output_file* of_; + Task_token* input_sections_blocker_; + Task_token* final_blocker_; +}; + // This task function handles closing the file. class Close_task_runner : public Task_function_runner diff --git a/gold/merge.cc b/gold/merge.cc index 13bfc17..15d3b7c 100644 --- a/gold/merge.cc +++ b/gold/merge.cc @@ -30,12 +30,14 @@ namespace gold { +// Class Merge_map::Merge_key_less. + // Sort the entries in a merge mapping. The key is an input object, a // section index in that object, and an offset in that section. bool -Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1, - const Merge_key& mk2) const +Merge_map::Merge_key_less::operator()(const Merge_key& mk1, + const Merge_key& mk2) const { // The order of different objects and different sections doesn't // matter. We want to get consistent results across links so we @@ -55,43 +57,44 @@ Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1, return mk1.offset < mk2.offset; } -// Add a mapping from an OFFSET in input section SHNDX in object -// OBJECT to an OUTPUT_OFFSET in a merged output section. This -// manages the mapping used to resolve relocations against merged -// sections. +// Class Merge_map. + +// Add a mapping for the bytes from OFFSET to OFFSET + LENGTH in input +// section SHNDX in object OBJECT to an OUTPUT_OFFSET in a merged +// output section. void -Output_merge_base::add_mapping(Relobj* object, unsigned int shndx, - off_t offset, off_t output_offset) +Merge_map::add_mapping(Relobj* object, unsigned int shndx, + off_t offset, off_t length, off_t output_offset) { Merge_key mk; mk.object = object; mk.shndx = shndx; mk.offset = offset; - std::pair ins = - this->merge_map_.insert(std::make_pair(mk, output_offset)); + + Merge_value mv; + mv.length = length; + mv.output_offset = output_offset; + + std::pair ins = + this->merge_map_.insert(std::make_pair(mk, mv)); gold_assert(ins.second); } -// Return the output address for an input address. The input address -// is at offset OFFSET in section SHNDX in OBJECT. -// OUTPUT_SECTION_ADDRESS is the address of the output section. If we -// know the address, set *POUTPUT and return true. Otherwise return -// false. +// Return the output offset for an input address. The input address +// is at offset OFFSET in section SHNDX in OBJECT. This sets +// *OUTPUT_OFFSET to the offset in the output section. This returns +// true if the mapping is known, false otherwise. bool -Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx, - off_t offset, - uint64_t output_section_address, - uint64_t* poutput) const +Merge_map::get_output_offset(const Relobj* object, unsigned int shndx, + off_t offset, off_t* output_offset) const { - gold_assert(output_section_address == this->address()); - Merge_key mk; mk.object = object; mk.shndx = shndx; mk.offset = offset; - Merge_map::const_iterator p = this->merge_map_.lower_bound(mk); + Merge_mapping::const_iterator p = this->merge_map_.lower_bound(mk); // If MK is not in the map, lower_bound returns the next iterator // larger than it. @@ -108,12 +111,32 @@ Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx, if (p->first.object != object || p->first.shndx != shndx) return false; - // Any input section is fully mapped: we don't need to know the size - // of the range starting at P->FIRST.OFFSET. - *poutput = output_section_address + p->second + (offset - p->first.offset); + if (offset - p->first.offset >= p->second.length) + return false; + + *output_offset = p->second.output_offset; + if (*output_offset != -1) + *output_offset += (offset - p->first.offset); return true; } +// Class Output_merge_base. + +// Return the output offset for an input offset. The input address is +// at offset OFFSET in section SHNDX in OBJECT. If we know the +// offset, set *POUTPUT and return true. Otherwise return false. + +bool +Output_merge_base::do_output_offset(const Relobj* object, + unsigned int shndx, + off_t offset, + off_t* poutput) const +{ + return this->merge_map_.get_output_offset(object, shndx, offset, poutput); +} + +// Class Output_merge_data. + // Compute the hash code for a fixed-size constant. size_t @@ -214,7 +237,7 @@ Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx) } // Record the offset of this constant in the output section. - this->add_mapping(object, shndx, i, k); + this->add_mapping(object, shndx, i, entsize, k); } return true; @@ -241,6 +264,8 @@ Output_merge_data::do_write(Output_file* of) of->write(this->offset(), this->p_, this->len_); } +// Class Output_merge_string. + // Add an input section to a merged string section. template @@ -279,10 +304,12 @@ Output_merge_string::do_add_input_section(Relobj* object, const Char_type* str = this->stringpool_.add(p, true, NULL); - this->merged_strings_.push_back(Merged_string(object, shndx, i, str)); + off_t bytelen_with_null = (plen + 1) * sizeof(Char_type); + this->merged_strings_.push_back(Merged_string(object, shndx, i, str, + bytelen_with_null)); p += plen + 1; - i += (plen + 1) * sizeof(Char_type); + i += bytelen_with_null; } return true; @@ -302,7 +329,7 @@ Output_merge_string::do_set_address(uint64_t, off_t) this->merged_strings_.begin(); p != this->merged_strings_.end(); ++p) - this->add_mapping(p->object, p->shndx, p->offset, + this->add_mapping(p->object, p->shndx, p->offset, p->length, this->stringpool_.get_offset(p->string)); this->set_data_size(this->stringpool_.get_strtab_size()); diff --git a/gold/merge.h b/gold/merge.h index cc554f2..630b593 100644 --- a/gold/merge.h +++ b/gold/merge.h @@ -31,6 +31,62 @@ namespace gold { +// This class manages mappings from input sections to offsets in an +// output section. This is used where input sections are merged. + +class Merge_map +{ + public: + Merge_map() + : merge_map_() + { } + + // Add a mapping for the bytes from OFFSET to OFFSET + LENGTH in the + // input section SHNDX in object OBJECT to OUTPUT_OFFSET in the + // output section. An OUTPUT_OFFSET of -1 means that the bytes are + // discarded. + void + add_mapping(Relobj* object, unsigned int shndx, off_t offset, off_t length, + off_t output_offset); + + // Return the output offset for an input address. The input address + // is at offset OFFSET in section SHNDX in OBJECT. This sets + // *OUTPUT_OFFSET to the offset in the output section; this will be + // -1 if the bytes are not being copied to the output. This returns + // true if the mapping is known, false otherwise. + bool + get_output_offset(const Relobj* object, unsigned int shndx, off_t offset, + off_t *output_offset) const; + + private: + // We build a mapping from OBJECT/SHNDX/OFFSET to an offset and + // length in the output section. + struct Merge_key + { + const Relobj* object; + unsigned int shndx; + off_t offset; + }; + + struct Merge_key_less + { + bool + operator()(const Merge_key&, const Merge_key&) const; + }; + + struct Merge_value + { + off_t length; + off_t output_offset; + }; + + typedef std::map Merge_mapping; + + // A mapping from input object/section/offset to offset in output + // section. + Merge_mapping merge_map_; +}; + // A general class for SHF_MERGE data, to hold functions shared by // fixed-size constant data and string data. @@ -41,10 +97,10 @@ class Output_merge_base : public Output_section_data : Output_section_data(addralign), merge_map_(), entsize_(entsize) { } - // Return the output address for an input address. + // Return the output offset for an input offset. bool - do_output_address(const Relobj* object, unsigned int shndx, off_t offset, - uint64_t output_section_address, uint64_t* poutput) const; + do_output_offset(const Relobj* object, unsigned int shndx, off_t offset, + off_t* poutput) const; protected: // Return the entry size. @@ -56,30 +112,15 @@ class Output_merge_base : public Output_section_data // OBJECT to an OUTPUT_OFFSET in the output section. void add_mapping(Relobj* object, unsigned int shndx, off_t offset, - off_t output_offset); - - private: - // We build a mapping from OBJECT/SHNDX/OFFSET to an offset in the - // output section. - struct Merge_key + off_t length, off_t output_offset) { - const Relobj* object; - unsigned int shndx; - off_t offset; - }; - - struct Merge_key_less - { - bool - operator()(const Merge_key&, const Merge_key&) const; - }; - - typedef std::map Merge_map; + this->merge_map_.add_mapping(object, shndx, offset, length, output_offset); + } + private: // A mapping from input object/section/offset to offset in output // section. Merge_map merge_map_; - // The entry size. For fixed-size constants, this is the size of // the constants. For strings, this is the size of a character. uint64_t entsize_; @@ -221,10 +262,13 @@ class Output_merge_string : public Output_merge_base off_t offset; // The string itself, a pointer into a Stringpool. const Char_type* string; + // The length of the string in bytes, including the null terminator. + size_t length; Merged_string(Relobj *objecta, unsigned int shndxa, off_t offseta, - const Char_type* stringa) - : object(objecta), shndx(shndxa), offset(offseta), string(stringa) + const Char_type* stringa, size_t lengtha) + : object(objecta), shndx(shndxa), offset(offseta), string(stringa), + length(lengtha) { } }; diff --git a/gold/object.cc b/gold/object.cc index 0b71be1..d334b17 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -139,10 +139,11 @@ Sized_relobj::Sized_relobj( symtab_shndx_(-1U), local_symbol_count_(0), output_local_symbol_count_(0), - symbols_(NULL), + symbols_(), local_symbol_offset_(0), local_values_(), - local_got_offsets_() + local_got_offsets_(), + has_eh_frame_(false) { } @@ -198,6 +199,50 @@ Sized_relobj::find_symtab(const unsigned char* pshdrs) } } +// Return whether SHDR has the right type and flags to be a GNU +// .eh_frame section. + +template +bool +Sized_relobj::check_eh_frame_flags( + const elfcpp::Shdr* shdr) const +{ + return (shdr->get_sh_size() > 0 + && shdr->get_sh_type() == elfcpp::SHT_PROGBITS + && shdr->get_sh_flags() == elfcpp::SHF_ALLOC); +} + +// Return whether there is a GNU .eh_frame section, given the section +// headers and the section names. + +template +bool +Sized_relobj::find_eh_frame(const unsigned char* pshdrs, + const char* names, + off_t names_size) const +{ + const unsigned int shnum = this->shnum(); + const unsigned char* p = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + { + typename This::Shdr shdr(p); + if (this->check_eh_frame_flags(&shdr)) + { + if (shdr.get_sh_name() >= names_size) + { + this->error(_("bad section name offset for section %u: %lu"), + i, static_cast(shdr.get_sh_name())); + continue; + } + + const char* name = names + shdr.get_sh_name(); + if (strcmp(name, ".eh_frame") == 0) + return true; + } + } + return false; +} + // Read the sections and symbols from an object file. template @@ -210,8 +255,14 @@ Sized_relobj::do_read_symbols(Read_symbols_data* sd) this->find_symtab(pshdrs); + const unsigned char* namesu = sd->section_names->data(); + const char* names = reinterpret_cast(namesu); + if (this->find_eh_frame(pshdrs, names, sd->section_names_size)) + this->has_eh_frame_ = true; + sd->symbols = NULL; sd->symbols_size = 0; + sd->external_symbols_offset = 0; sd->symbol_names = NULL; sd->symbol_names_size = 0; @@ -226,16 +277,26 @@ Sized_relobj::do_read_symbols(Read_symbols_data* sd) + this->symtab_shndx_ * This::shdr_size); gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); - // We only need the external symbols. + // If this object has a .eh_frame section, we need all the symbols. + // Otherwise we only need the external symbols. While it would be + // simpler to just always read all the symbols, I've seen object + // files with well over 2000 local symbols, which for a 64-bit + // object file format is over 5 pages that we don't need to read + // now. + const int sym_size = This::sym_size; const unsigned int loccount = symtabshdr.get_sh_info(); this->local_symbol_count_ = loccount; off_t locsize = loccount * sym_size; - off_t extoff = symtabshdr.get_sh_offset() + locsize; - off_t extsize = symtabshdr.get_sh_size() - locsize; + off_t dataoff = symtabshdr.get_sh_offset(); + off_t datasize = symtabshdr.get_sh_size(); + off_t extoff = dataoff + locsize; + off_t extsize = datasize - locsize; + + off_t readoff = this->has_eh_frame_ ? dataoff : extoff; + off_t readsize = this->has_eh_frame_ ? datasize : extsize; - // Read the symbol table. - File_view* fvsymtab = this->get_lasting_view(extoff, extsize, false); + File_view* fvsymtab = this->get_lasting_view(readoff, readsize, false); // Read the section header for the symbol names. unsigned int strtab_shndx = symtabshdr.get_sh_link(); @@ -257,11 +318,36 @@ Sized_relobj::do_read_symbols(Read_symbols_data* sd) strtabshdr.get_sh_size(), true); sd->symbols = fvsymtab; - sd->symbols_size = extsize; + sd->symbols_size = readsize; + sd->external_symbols_offset = this->has_eh_frame_ ? locsize : 0; sd->symbol_names = fvstrtab; sd->symbol_names_size = strtabshdr.get_sh_size(); } +// Return the section index of symbol SYM. Set *VALUE to its value in +// the object file. Note that for a symbol which is not defined in +// this object file, this will set *VALUE to 0 and return SHN_UNDEF; +// it will not return the final value of the symbol in the link. + +template +unsigned int +Sized_relobj::symbol_section_and_value(unsigned int sym, + Address* value) +{ + off_t symbols_size; + const unsigned char* symbols = this->section_contents(this->symtab_shndx_, + &symbols_size, + false); + + const size_t count = symbols_size / This::sym_size; + gold_assert(sym < count); + + elfcpp::Sym elfsym(symbols + sym * This::sym_size); + *value = elfsym.get_st_value(); + // FIXME: Handle SHN_XINDEX. + return elfsym.get_st_shndx(); +} + // Return whether to include a section group in the link. LAYOUT is // used to keep track of which section groups we have already seen. // INDEX is the index of the section group and SHDR is the section @@ -425,6 +511,38 @@ Sized_relobj::do_layout(Symbol_table* symtab, const unsigned char* pnamesu = sd->section_names->data(); const char* pnames = reinterpret_cast(pnamesu); + // For each section, record the index of the reloc section if any. + // Use 0 to mean that there is no reloc section, -1U to mean that + // there is more than one. + std::vector reloc_shndx(shnum, 0); + std::vector reloc_type(shnum, elfcpp::SHT_NULL); + // Skip the first, dummy, section. + pshdrs += This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) + { + typename This::Shdr shdr(pshdrs); + + unsigned int sh_type = shdr.get_sh_type(); + if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA) + { + unsigned int target_shndx = shdr.get_sh_info(); + if (target_shndx == 0 || target_shndx >= shnum) + { + this->error(_("relocation section %u has bad info %u"), + i, target_shndx); + continue; + } + + if (reloc_shndx[target_shndx] != 0) + reloc_shndx[target_shndx] = -1U; + else + { + reloc_shndx[target_shndx] = i; + reloc_type[target_shndx] = sh_type; + } + } + } + std::vector& map_sections(this->map_to_output()); map_sections.resize(shnum); @@ -436,8 +554,11 @@ Sized_relobj::do_layout(Symbol_table* symtab, // Keep track of which sections to omit. std::vector omit(shnum, false); + // Keep track of .eh_frame sections. + std::vector eh_frame_sections; + // Skip the first, dummy, section. - pshdrs += This::shdr_size; + pshdrs = sd->section_headers->data() + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) { typename This::Shdr shdr(pshdrs); @@ -490,15 +611,70 @@ Sized_relobj::do_layout(Symbol_table* symtab, continue; } + // The .eh_frame section is special. It holds exception frame + // information that we need to read in order to generate the + // exception frame header. We process these after all the other + // sections so that the exception frame reader can reliably + // determine which sections are being discarded, and discard the + // corresponding information. + if (!parameters->output_is_object() + && strcmp(name, ".eh_frame") == 0 + && this->check_eh_frame_flags(&shdr)) + { + eh_frame_sections.push_back(i); + continue; + } + off_t offset; - Output_section* os = layout->layout(this, i, name, shdr, &offset); + Output_section* os = layout->layout(this, i, name, shdr, + reloc_shndx[i], reloc_type[i], + &offset); map_sections[i].output_section = os; map_sections[i].offset = offset; + + // If this section requires special handling, and if there are + // relocs that apply to it, then we must do the special handling + // before we apply the relocs. + if (offset == -1 && reloc_shndx[i] != 0) + this->set_relocs_must_follow_section_writes(); } layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags); + // Handle the .eh_frame sections at the end. + for (std::vector::const_iterator p = eh_frame_sections.begin(); + p != eh_frame_sections.end(); + ++p) + { + gold_assert(this->has_eh_frame_); + gold_assert(sd->external_symbols_offset != 0); + + unsigned int i = *p; + const unsigned char *pshdr; + pshdr = sd->section_headers->data() + i * This::shdr_size; + typename This::Shdr shdr(pshdr); + + off_t offset; + Output_section* os = layout->layout_eh_frame(this, + sd->symbols->data(), + sd->symbols_size, + sd->symbol_names->data(), + sd->symbol_names_size, + i, shdr, + reloc_shndx[i], + reloc_type[i], + &offset); + map_sections[i].output_section = os; + map_sections[i].offset = offset; + + // If this section requires special handling, and if there are + // relocs that apply to it, then we must do the special handling + // before we apply the relocs. + if (offset == -1 && reloc_shndx[i] != 0) + this->set_relocs_must_follow_section_writes(); + } + delete sd->section_headers; sd->section_headers = NULL; delete sd->section_names; @@ -519,19 +695,23 @@ Sized_relobj::do_add_symbols(Symbol_table* symtab, } const int sym_size = This::sym_size; - size_t symcount = sd->symbols_size / sym_size; - if (static_cast(symcount * sym_size) != sd->symbols_size) + size_t symcount = ((sd->symbols_size - sd->external_symbols_offset) + / sym_size); + if (static_cast(symcount * sym_size) + != sd->symbols_size - sd->external_symbols_offset) { this->error(_("size of symbols is not multiple of symbol size")); return; } - this->symbols_ = new Symbol*[symcount]; + this->symbols_.resize(symcount); const char* sym_names = reinterpret_cast(sd->symbol_names->data()); - symtab->add_from_relobj(this, sd->symbols->data(), symcount, sym_names, - sd->symbol_names_size, this->symbols_); + symtab->add_from_relobj(this, + sd->symbols->data() + sd->external_symbols_offset, + symcount, sym_names, sd->symbol_names_size, + &this->symbols_); delete sd->symbols; sd->symbols = NULL; diff --git a/gold/object.h b/gold/object.h index 9fdb9a3..8aa40a2 100644 --- a/gold/object.h +++ b/gold/object.h @@ -57,6 +57,10 @@ struct Read_symbols_data File_view* symbols; // Size of symbol data in bytes. off_t symbols_size; + // Offset of external symbols within symbol data. This structure + // sometimes contains only external symbols, in which case this will + // be zero. Sometimes it contains all symbols. + off_t external_symbols_offset; // Symbol names. File_view* symbol_names; // Size of symbol name data in bytes. @@ -100,6 +104,10 @@ struct Section_relocs unsigned int sh_type; // Number of reloc entries. size_t reloc_count; + // Output section. + Output_section* output_section; + // Whether this section has special handling for offsets. + bool needs_special_offset_handling; }; // Relocations in an object file. This is read in read_relocs and @@ -197,6 +205,11 @@ class Object section_flags(unsigned int shndx) { return this->do_section_flags(shndx); } + // Return the section type given a section index. + unsigned int + section_type(unsigned int shndx) + { return this->do_section_type(shndx); } + // Return the section link field given a section index. unsigned int section_link(unsigned int shndx) @@ -291,6 +304,10 @@ class Object virtual uint64_t do_section_flags(unsigned int shndx) = 0; + // Get section type--implemented by child class. + virtual unsigned int + do_section_type(unsigned int shndx) = 0; + // Get section link field--implemented by child class. virtual unsigned int do_section_link(unsigned int shndx) = 0; @@ -421,9 +438,21 @@ class Relobj : public Object return this->map_to_output_[shndx].output_section != NULL; } + // Return whether an input section requires special + // handling--whether it is not simply mapped from the input file to + // the output file. + bool + is_section_specially_mapped(unsigned int shndx) const + { + gold_assert(shndx < this->map_to_output_.size()); + return (this->map_to_output_[shndx].output_section != NULL + && this->map_to_output_[shndx].offset == -1); + } + // Given a section index, return the corresponding Output_section // (which will be NULL if the section is not included in the link) - // and set *POFF to the offset within that section. + // and set *POFF to the offset within that section. *POFF will be + // set to -1 if the section requires special handling. inline Output_section* output_section(unsigned int shndx, off_t* poff) const; @@ -435,6 +464,14 @@ class Relobj : public Object this->map_to_output_[shndx].offset = off; } + // Return true if we need to wait for output sections to be written + // before we can apply relocations. This is true if the object has + // any relocations for sections which require special handling, such + // as the exception frame section. + bool + relocs_must_follow_section_writes() + { return this->relocs_must_follow_section_writes_; } + protected: // What we need to know to map an input section to an output // section. We keep an array of these, one for each input section, @@ -478,9 +515,18 @@ class Relobj : public Object map_to_output() const { return this->map_to_output_; } + // Record that we must wait for the output sections to be written + // before applying relocations. + void + set_relocs_must_follow_section_writes() + { this->relocs_must_follow_section_writes_ = true; } + private: // Mapping from input sections to output section. std::vector map_to_output_; + // Whether we need to wait for output sections to be written before + // we can apply relocations. + bool relocs_must_follow_section_writes_; }; // Implement Object::output_section inline for efficiency. @@ -495,8 +541,8 @@ Relobj::output_section(unsigned int shndx, off_t* poff) const // This POD class is holds the value of a symbol. This is used for // local symbols, and for all symbols during relocation processing. -// In order to process relocs we need to be able to handle SHF_MERGE -// sections correctly. +// For special sections, such as SHF_MERGE sections, this calls a +// function to get the final symbol value. template class Symbol_value @@ -577,7 +623,10 @@ class Symbol_value // Set the index of the input section in the input file. void set_input_shndx(unsigned int i) - { this->input_shndx_ = i; } + { + this->input_shndx_ = i; + gold_assert(this->input_shndx_ == i); + } // Record that this is a section symbol. void @@ -610,6 +659,7 @@ class Sized_relobj : public Relobj { public: typedef typename elfcpp::Elf_types::Elf_Addr Address; + typedef std::vector Symbols; typedef std::vector > Local_values; Sized_relobj(const std::string& name, Input_file* input_file, off_t offset, @@ -621,6 +671,41 @@ class Sized_relobj : public Relobj void setup(const typename elfcpp::Ehdr&); + // Return the number of local symbols. + unsigned int + local_symbol_count() const + { return this->local_symbol_count_; } + + // If SYM is the index of a global symbol in the object file's + // symbol table, return the Symbol object. Otherwise, return NULL. + Symbol* + global_symbol(unsigned int sym) const + { + if (sym >= this->local_symbol_count_) + { + gold_assert(sym - this->local_symbol_count_ < this->symbols_.size()); + return this->symbols_[sym - this->local_symbol_count_]; + } + return NULL; + } + + // Return the section index of symbol SYM. Set *VALUE to its value + // in the object file. Note that for a symbol which is not defined + // in this object file, this will set *VALUE to 0 and return + // SHN_UNDEF; it will not return the final value of the symbol in + // the link. + unsigned int + symbol_section_and_value(unsigned int sym, Address* value); + + // Return a pointer to the Symbol_value structure which holds the + // value of a local symbol. + const Symbol_value* + local_symbol(unsigned int sym) const + { + gold_assert(sym < this->local_values_.size()); + return &this->local_values_[sym]; + } + // Return the index of local symbol SYM in the ordinary symbol // table. A value of -1U means that the symbol is not being output. unsigned int @@ -731,6 +816,11 @@ class Sized_relobj : public Relobj do_section_flags(unsigned int shndx) { return this->elf_file_.section_flags(shndx); } + // Return section type. + unsigned int + do_section_type(unsigned int shndx) + { return this->elf_file_.section_type(shndx); } + // Return the section link field. unsigned int do_section_link(unsigned int shndx) @@ -748,6 +838,17 @@ class Sized_relobj : public Relobj void find_symtab(const unsigned char* pshdrs); + // Return whether SHDR has the right flags for a GNU style exception + // frame section. + bool + check_eh_frame_flags(const elfcpp::Shdr* shdr) const; + + // Return whether there is a section named .eh_frame which might be + // a GNU style exception frame section. + bool + find_eh_frame(const unsigned char* pshdrs, const char* names, + off_t names_size) const; + // Whether to include a section group in the link. bool include_section_group(Layout*, unsigned int, @@ -766,6 +867,7 @@ class Sized_relobj : public Relobj typename elfcpp::Elf_types::Elf_Addr address; off_t offset; off_t view_size; + bool is_input_output_view; }; typedef std::vector Views; @@ -797,13 +899,15 @@ class Sized_relobj : public Relobj // The number of local symbols which go into the output file. unsigned int output_local_symbol_count_; // The entries in the symbol table for the external symbols. - Symbol** symbols_; + Symbols symbols_; // File offset for local symbols. off_t local_symbol_offset_; // Values of local symbols. Local_values local_values_; // GOT offsets for local symbols, indexed by symbol number. Local_got_offsets local_got_offsets_; + // Whether this object has a GNU style .eh_frame section. + bool has_eh_frame_; }; // A class to manage the list of all objects. @@ -891,12 +995,6 @@ struct Relocate_info const Layout* layout; // Object being relocated. Sized_relobj* object; - // Number of local symbols. - unsigned int local_symbol_count; - // Values of local symbols. - const typename Sized_relobj::Local_values* local_values; - // Global symbols. - const Symbol* const * symbols; // Section index of relocation section. unsigned int reloc_shndx; // Section index of section being relocated. diff --git a/gold/output.cc b/gold/output.cc index 64aa4dc..ca99097 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -63,10 +63,18 @@ Output_data::set_address(uint64_t addr, off_t off) this->do_set_address(addr, off); } +// Return the default alignment for the target size. + +uint64_t +Output_data::default_alignment() +{ + return Output_data::default_alignment_for_size(parameters->get_size()); +} + // Return the default alignment for a size--32 or 64. uint64_t -Output_data::default_alignment(int size) +Output_data::default_alignment_for_size(int size) { if (size == 32) return 4; @@ -569,7 +577,14 @@ Output_reloc::write_rel( Output_section* os = this->u2_.relobj->output_section(this->shndx_, &off); gold_assert(os != NULL); - address += os->address() + off; + if (off != -1) + address += os->address() + off; + else + { + address = os->output_address(this->u2_.relobj, this->shndx_, + address); + gold_assert(address != -1U); + } } else if (this->u2_.od != NULL) address += this->u2_.od->address(); @@ -941,27 +956,25 @@ Output_section::Input_section::set_address(uint64_t addr, off_t off, this->u2_.posd->set_address(addr, off); } -// Try to turn an input address into an output address. +// Try to turn an input offset into an output offset. bool -Output_section::Input_section::output_address(const Relobj* object, - unsigned int shndx, - off_t offset, - uint64_t output_section_address, - uint64_t *poutput) const +Output_section::Input_section::output_offset(const Relobj* object, + unsigned int shndx, + off_t offset, + off_t *poutput) const { if (!this->is_input_section()) - return this->u2_.posd->output_address(object, shndx, offset, - output_section_address, poutput); + return this->u2_.posd->output_offset(object, shndx, offset, poutput); else { - if (this->shndx_ != shndx - || this->u2_.object != object) + if (this->shndx_ != shndx || this->u2_.object != object) return false; off_t output_offset; Output_section* os = object->output_section(shndx, &output_offset); gold_assert(os != NULL); - *poutput = output_section_address + output_offset + offset; + gold_assert(output_offset != -1); + *poutput = output_offset + offset; return true; } } @@ -1001,7 +1014,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, needs_symtab_index_(false), needs_dynsym_index_(false), should_link_to_symtab_(false), - should_link_to_dynsym_(false) + should_link_to_dynsym_(false), + after_input_sections_(false) { } @@ -1021,16 +1035,22 @@ Output_section::set_entsize(uint64_t v) } // Add the input section SHNDX, with header SHDR, named SECNAME, in -// OBJECT, to the Output_section. Return the offset of the input -// section within the output section. We don't always keep track of -// input sections for an Output_section. Instead, each Object keeps -// track of the Output_section for each of its input sections. +// OBJECT, to the Output_section. RELOC_SHNDX is the index of a +// relocation section which applies to this section, or 0 if none, or +// -1U if more than one. Return the offset of the input section +// within the output section. Return -1 if the input section will +// receive special handling. In the normal case we don't always keep +// track of input sections for an Output_section. Instead, each +// Object keeps track of the Output_section for each of its input +// sections. template off_t -Output_section::add_input_section(Relobj* object, unsigned int shndx, +Output_section::add_input_section(Sized_relobj* object, + unsigned int shndx, const char* secname, - const elfcpp::Shdr& shdr) + const elfcpp::Shdr& shdr, + unsigned int reloc_shndx) { elfcpp::Elf_Xword addralign = shdr.get_sh_addralign(); if ((addralign & (addralign - 1)) != 0) @@ -1044,15 +1064,17 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx, this->addralign_ = addralign; // If this is a SHF_MERGE section, we pass all the input sections to - // a Output_data_merge. - if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0) + // a Output_data_merge. We don't try to handle relocations for such + // a section. + if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0 + && reloc_shndx == 0) { if (this->add_merge_input_section(object, shndx, shdr.get_sh_flags(), shdr.get_sh_entsize(), addralign)) { // Tell the relocation routines that they need to call the - // output_address method to determine the final address. + // output_offset method to determine the final address. return -1; } } @@ -1176,6 +1198,57 @@ Output_section::add_merge_input_section(Relobj* object, unsigned int shndx, return true; } +// Given an address OFFSET relative to the start of input section +// SHNDX in OBJECT, return whether this address is being included in +// the final link. This should only be called if SHNDX in OBJECT has +// a special mapping. + +bool +Output_section::is_input_address_mapped(const Relobj* object, + unsigned int shndx, + off_t offset) const +{ + gold_assert(object->is_section_specially_mapped(shndx)); + + for (Input_section_list::const_iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off_t output_offset; + if (p->output_offset(object, shndx, offset, &output_offset)) + return output_offset != -1; + } + + // By default we assume that the address is mapped. This should + // only be called after we have passed all sections to Layout. At + // that point we should know what we are discarding. + return true; +} + +// Given an address OFFSET relative to the start of input section +// SHNDX in object OBJECT, return the output offset relative to the +// start of the section. This should only be called if SHNDX in +// OBJECT has a special mapping. + +off_t +Output_section::output_offset(const Relobj* object, unsigned int shndx, + off_t offset) const +{ + gold_assert(object->is_section_specially_mapped(shndx)); + // This can only be called meaningfully when layout is complete. + gold_assert(Output_data::is_layout_complete()); + + for (Input_section_list::const_iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off_t output_offset; + if (p->output_offset(object, shndx, offset, &output_offset)) + return output_offset; + } + gold_unreachable(); +} + // Return the output virtual address of OFFSET relative to the start // of input section SHNDX in object OBJECT. @@ -1183,15 +1256,23 @@ uint64_t Output_section::output_address(const Relobj* object, unsigned int shndx, off_t offset) const { + gold_assert(object->is_section_specially_mapped(shndx)); + // This can only be called meaningfully when layout is complete. + gold_assert(Output_data::is_layout_complete()); + uint64_t addr = this->address() + this->first_input_offset_; for (Input_section_list::const_iterator p = this->input_sections_.begin(); p != this->input_sections_.end(); ++p) { addr = align_address(addr, p->addralign()); - uint64_t output; - if (p->output_address(object, shndx, offset, addr, &output)) - return output; + off_t output_offset; + if (p->output_offset(object, shndx, offset, &output_offset)) + { + if (output_offset == -1) + return -1U; + return addr + output_offset; + } addr += p->data_size(); } @@ -1739,40 +1820,44 @@ Output_file::close() template off_t Output_section::add_input_section<32, false>( - Relobj* object, + Sized_relobj<32, false>* object, unsigned int shndx, const char* secname, - const elfcpp::Shdr<32, false>& shdr); + const elfcpp::Shdr<32, false>& shdr, + unsigned int reloc_shndx); #endif #ifdef HAVE_TARGET_32_BIG template off_t Output_section::add_input_section<32, true>( - Relobj* object, + Sized_relobj<32, true>* object, unsigned int shndx, const char* secname, - const elfcpp::Shdr<32, true>& shdr); + const elfcpp::Shdr<32, true>& shdr, + unsigned int reloc_shndx); #endif #ifdef HAVE_TARGET_64_LITTLE template off_t Output_section::add_input_section<64, false>( - Relobj* object, + Sized_relobj<64, false>* object, unsigned int shndx, const char* secname, - const elfcpp::Shdr<64, false>& shdr); + const elfcpp::Shdr<64, false>& shdr, + unsigned int reloc_shndx); #endif #ifdef HAVE_TARGET_64_BIG template off_t Output_section::add_input_section<64, true>( - Relobj* object, + Sized_relobj<64, true>* object, unsigned int shndx, const char* secname, - const elfcpp::Shdr<64, true>& shdr); + const elfcpp::Shdr<64, true>& shdr, + unsigned int reloc_shndx); #endif #ifdef HAVE_TARGET_32_LITTLE diff --git a/gold/output.h b/gold/output.h index b6c1245..053579f 100644 --- a/gold/output.h +++ b/gold/output.h @@ -29,7 +29,6 @@ #include "elfcpp.h" #include "layout.h" #include "reloc-types.h" -#include "parameters.h" namespace gold { @@ -124,6 +123,11 @@ class Output_data layout_complete() { Output_data::sizes_are_fixed = true; } + // Used to check that layout has been done. + static bool + is_layout_complete() + { return Output_data::sizes_are_fixed; } + protected: // Functions that child classes may or in some cases must implement. @@ -179,9 +183,13 @@ class Output_data this->data_size_ = data_size; } - // Return default alignment for a size--32 or 64. + // Return default alignment for the target size. + static uint64_t + default_alignment(); + + // Return default alignment for a specified size--32 or 64. static uint64_t - default_alignment(int size); + default_alignment_for_size(int size); private: Output_data(const Output_data&); @@ -216,7 +224,7 @@ class Output_section_headers : public Output_data // Return the required alignment. uint64_t do_addralign() const - { return Output_data::default_alignment(parameters->get_size()); } + { return Output_data::default_alignment(); } private: // Write the data to the file with the right size and endianness. @@ -244,7 +252,7 @@ class Output_segment_headers : public Output_data // Return the required alignment. uint64_t do_addralign() const - { return Output_data::default_alignment(parameters->get_size()); } + { return Output_data::default_alignment(); } private: // Write the data to the file with the right size and endianness. @@ -276,7 +284,7 @@ class Output_file_header : public Output_data // Return the required alignment. uint64_t do_addralign() const - { return Output_data::default_alignment(parameters->get_size()); } + { return Output_data::default_alignment(); } // Set the address and offset--we only implement this for error // checking. @@ -330,17 +338,14 @@ class Output_section_data : public Output_data // Given an input OBJECT, an input section index SHNDX within that // object, and an OFFSET relative to the start of that input - // section, return whether or not the output address is known. - // OUTPUT_SECTION_ADDRESS is the address of the output section which - // this is a part of. If this function returns true, it sets - // *POUTPUT to the output address. + // section, return whether or not the corresponding offset within + // the output section is known. If this function returns true, it + // sets *POUTPUT to the output offset. The value -1 indicates that + // this input offset is being discarded. virtual bool - output_address(const Relobj* object, unsigned int shndx, off_t offset, - uint64_t output_section_address, uint64_t *poutput) const - { - return this->do_output_address(object, shndx, offset, - output_section_address, poutput); - } + output_offset(const Relobj* object, unsigned int shndx, off_t offset, + off_t *poutput) const + { return this->do_output_offset(object, shndx, offset, poutput); } protected: // The child class must implement do_write. @@ -357,10 +362,9 @@ class Output_section_data : public Output_data do_add_input_section(Relobj*, unsigned int) { gold_unreachable(); } - // The child class may implement output_address. + // The child class may implement output_offset. virtual bool - do_output_address(const Relobj*, unsigned int, off_t, uint64_t, - uint64_t*) const + do_output_offset(const Relobj*, unsigned int, off_t, off_t*) const { return false; } // Return the required alignment. @@ -736,7 +740,7 @@ class Output_data_reloc_base : public Output_section_data // Construct the section. Output_data_reloc_base() - : Output_section_data(Output_data::default_alignment(size)) + : Output_section_data(Output_data::default_alignment_for_size(size)) { } // Write out the data. @@ -901,7 +905,8 @@ class Output_data_got : public Output_section_data typedef typename elfcpp::Elf_types::Elf_Addr Valtype; Output_data_got() - : Output_section_data(Output_data::default_alignment(size)), entries_() + : Output_section_data(Output_data::default_alignment_for_size(size)), + entries_() { } // Add an entry for a global symbol to the GOT. Return true if this @@ -1013,8 +1018,7 @@ class Output_data_dynamic : public Output_section_data { public: Output_data_dynamic(Stringpool* pool) - : Output_section_data(Output_data::default_alignment( - parameters->get_size())), + : Output_section_data(Output_data::default_alignment()), entries_(), pool_(pool) { } @@ -1155,11 +1159,15 @@ class Output_section : public Output_data virtual ~Output_section(); // Add a new input section SHNDX, named NAME, with header SHDR, from - // object OBJECT. Return the offset within the output section. + // object OBJECT. RELOC_SHNDX is the index of a relocation section + // which applies to this section, or 0 if none, or -1U if more than + // one. Return the offset within the output section. template off_t - add_input_section(Relobj* object, unsigned int shndx, const char *name, - const elfcpp::Shdr& shdr); + add_input_section(Sized_relobj* object, unsigned int shndx, + const char *name, + const elfcpp::Shdr& shdr, + unsigned int reloc_shndx); // Add generated data POSD to this output section. void @@ -1326,6 +1334,29 @@ class Output_section : public Output_data this->dynsym_index_ = index; } + // Return whether this section should be written after all the input + // sections are complete. + bool + after_input_sections() const + { return this->after_input_sections_; } + + // Record that this section should be written after all the input + // sections are complete. + void + set_after_input_sections() + { this->after_input_sections_ = true; } + + // Return whether the offset OFFSET in the input section SHNDX in + // object OBJECT is being included in the link. + bool + is_input_address_mapped(const Relobj* object, unsigned int shndx, + off_t offset) const; + + // Return the offset within the output section of OFFSET relative to + // the start of input section SHNDX in object OBJECT. + off_t + output_offset(const Relobj* object, unsigned int shndx, off_t offset) const; + // Return the output virtual address of OFFSET relative to the start // of input section SHNDX in object OBJECT. uint64_t @@ -1477,13 +1508,12 @@ class Output_section : public Output_data // Given an input OBJECT, an input section index SHNDX within that // object, and an OFFSET relative to the start of that input - // section, return whether or not the output address is known. - // OUTPUT_SECTION_ADDRESS is the address of the output section - // which this is a part of. If this function returns true, it - // sets *POUTPUT to the output address. + // section, return whether or not the output offset is known. If + // this function returns true, it sets *POUTPUT to the output + // offset. bool - output_address(const Relobj* object, unsigned int shndx, off_t offset, - uint64_t output_section_address, uint64_t *poutput) const; + output_offset(const Relobj* object, unsigned int shndx, off_t offset, + off_t *poutput) const; // Write out the data. This does nothing for an input section. void @@ -1648,6 +1678,9 @@ class Output_section : public Output_data // Whether the link field of this output section should point to the // dynamic symbol table. bool should_link_to_dynsym_ : 1; + // Whether this section should be written after all the input + // sections are complete. + bool after_input_sections_ : 1; }; // An output segment. PT_LOAD segments are built from collections of @@ -1847,6 +1880,28 @@ class Output_file write_output_view(off_t, off_t, unsigned char*) { } + // Get a read/write buffer. This is used when we want to write part + // of the file, read it in, and write it again. + unsigned char* + get_input_output_view(off_t start, off_t size) + { return this->get_output_view(start, size); } + + // Write a read/write buffer back to the file. + void + write_input_output_view(off_t, off_t, unsigned char*) + { } + + // Get a read buffer. This is used when we just want to read part + // of the file back it in. + const unsigned char* + get_input_view(off_t start, off_t size) + { return this->get_output_view(start, size); } + + // Release a read bfufer. + void + free_input_view(off_t, off_t, const unsigned char*) + { } + private: // General options. const General_options& options_; diff --git a/gold/po/gold.pot b/gold/po/gold.pot index d79701c..140ee86 100644 --- a/gold/po/gold.pot +++ b/gold/po/gold.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-02 16:01-0700\n" +"POT-Creation-Date: 2007-11-08 22:56-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -66,110 +66,110 @@ msgstr "" msgid "%s: can not read directory: %s" msgstr "" -#: dynobj.cc:128 +#: dynobj.cc:151 #, c-format msgid "unexpected duplicate type %u section: %u, %u" msgstr "" -#: dynobj.cc:164 +#: dynobj.cc:187 #, c-format msgid "unexpected link in section %u header: %u != %u" msgstr "" -#: dynobj.cc:199 +#: dynobj.cc:222 #, c-format msgid "DYNAMIC section %u link out of range: %u" msgstr "" -#: dynobj.cc:207 +#: dynobj.cc:230 #, c-format msgid "DYNAMIC section %u link %u is not a strtab" msgstr "" -#: dynobj.cc:227 +#: dynobj.cc:250 #, c-format msgid "DT_SONAME value out of range: %lld >= %lld" msgstr "" -#: dynobj.cc:242 +#: dynobj.cc:265 msgid "missing DT_NULL in dynamic segment" msgstr "" -#: dynobj.cc:285 +#: dynobj.cc:309 #, c-format msgid "invalid dynamic symbol table name index: %u" msgstr "" -#: dynobj.cc:292 +#: dynobj.cc:316 #, c-format msgid "dynamic symbol table name section has wrong type: %u" msgstr "" -#: dynobj.cc:365 object.cc:447 +#: dynobj.cc:389 object.cc:233 object.cc:568 #, c-format msgid "bad section name offset for section %u: %lu" msgstr "" -#: dynobj.cc:394 +#: dynobj.cc:418 #, c-format msgid "duplicate definition for version %u" msgstr "" -#: dynobj.cc:423 +#: dynobj.cc:447 #, c-format msgid "unexpected verdef version %u" msgstr "" -#: dynobj.cc:439 +#: dynobj.cc:463 #, c-format msgid "verdef vd_cnt field too small: %u" msgstr "" -#: dynobj.cc:446 +#: dynobj.cc:470 #, c-format msgid "verdef vd_aux field out of range: %u" msgstr "" -#: dynobj.cc:456 +#: dynobj.cc:480 #, c-format msgid "verdaux vda_name field out of range: %u" msgstr "" -#: dynobj.cc:465 +#: dynobj.cc:489 #, c-format msgid "verdef vd_next field out of range: %u" msgstr "" -#: dynobj.cc:498 +#: dynobj.cc:522 #, c-format msgid "unexpected verneed version %u" msgstr "" -#: dynobj.cc:507 +#: dynobj.cc:531 #, c-format msgid "verneed vn_aux field out of range: %u" msgstr "" -#: dynobj.cc:520 +#: dynobj.cc:544 #, c-format msgid "vernaux vna_name field out of range: %u" msgstr "" -#: dynobj.cc:531 +#: dynobj.cc:555 #, c-format msgid "verneed vna_next field out of range: %u" msgstr "" -#: dynobj.cc:542 +#: dynobj.cc:566 #, c-format msgid "verneed vn_next field out of range: %u" msgstr "" -#: dynobj.cc:588 +#: dynobj.cc:613 msgid "size of dynamic symbols is not multiple of symbol size" msgstr "" -#: dynobj.cc:1265 +#: dynobj.cc:1290 #, c-format msgid "symbol %s has undefined version %s" msgstr "" @@ -317,54 +317,54 @@ msgid "pthread_cond_signal failed: %s" msgstr "" #. FIXME: This needs to specify the location somehow. -#: i386.cc:150 i386.cc:1296 x86_64.cc:162 x86_64.cc:1228 +#: i386.cc:153 i386.cc:1301 x86_64.cc:165 x86_64.cc:1233 msgid "missing expected TLS relocation" msgstr "" -#: i386.cc:746 x86_64.cc:709 x86_64.cc:876 +#: i386.cc:749 x86_64.cc:712 x86_64.cc:879 #, c-format msgid "%s: unsupported reloc %u against local symbol" msgstr "" -#: i386.cc:840 i386.cc:1070 x86_64.cc:817 x86_64.cc:1055 +#: i386.cc:843 i386.cc:1073 x86_64.cc:820 x86_64.cc:1058 #, c-format msgid "%s: unexpected reloc %u in object file" msgstr "" -#: i386.cc:926 x86_64.cc:890 x86_64.cc:1114 +#: i386.cc:929 x86_64.cc:893 x86_64.cc:1117 #, c-format msgid "%s: unsupported reloc %u against global symbol %s" msgstr "" -#: i386.cc:1166 +#: i386.cc:1170 #, c-format msgid "%s: unsupported RELA reloc section" msgstr "" -#: i386.cc:1423 x86_64.cc:1426 +#: i386.cc:1428 x86_64.cc:1431 #, c-format msgid "unexpected reloc %u in object file" msgstr "" -#: i386.cc:1455 i386.cc:1502 i386.cc:1509 i386.cc:1529 i386.cc:1558 -#: x86_64.cc:1447 x86_64.cc:1496 x86_64.cc:1507 +#: i386.cc:1460 i386.cc:1507 i386.cc:1514 i386.cc:1534 i386.cc:1563 +#: x86_64.cc:1452 x86_64.cc:1501 x86_64.cc:1512 #, c-format msgid "unsupported reloc %u" msgstr "" -#: i386.cc:1480 x86_64.cc:1472 +#: i386.cc:1485 x86_64.cc:1477 msgid "TLS reloc but no TLS segment" msgstr "" -#: i386.cc:1517 +#: i386.cc:1522 msgid "both SUN and GNU model TLS relocations" msgstr "" -#: merge.cc:258 +#: merge.cc:283 msgid "mergeable string section length not multiple of character size" msgstr "" -#: merge.cc:274 +#: merge.cc:299 msgid "entry in mergeable string section not null terminated" msgstr "" @@ -383,112 +383,117 @@ msgstr "" msgid "section name section has wrong type: %u" msgstr "" -#: object.cc:244 +#: object.cc:305 #, c-format msgid "invalid symbol table name index: %u" msgstr "" -#: object.cc:250 +#: object.cc:311 #, c-format msgid "symbol table name section has wrong type: %u" msgstr "" -#: object.cc:305 +#: object.cc:391 #, c-format msgid "section group %u info %u out of range" msgstr "" -#: object.cc:323 +#: object.cc:409 #, c-format msgid "symbol %u name offset %u out of range" msgstr "" -#: object.cc:355 +#: object.cc:441 #, c-format msgid "section %u in section group %u out of range" msgstr "" -#: object.cc:525 +#: object.cc:531 reloc.cc:202 reloc.cc:469 +#, c-format +msgid "relocation section %u has bad info %u" +msgstr "" + +#: object.cc:703 msgid "size of symbols is not multiple of symbol size" msgstr "" #. FIXME: Handle SHN_XINDEX. -#: object.cc:615 +#: object.cc:795 #, c-format msgid "unknown section index %u for local symbol %u" msgstr "" -#: object.cc:624 +#: object.cc:804 #, c-format msgid "local symbol %u section index %u out of range" msgstr "" -#: object.cc:656 +#: object.cc:836 #, c-format msgid "local symbol %u section name out of range: %u >= %u" msgstr "" -#: object.cc:892 +#: object.cc:1054 #, c-format msgid "%s: incompatible target" msgstr "" -#: object.cc:994 +#: object.cc:1175 #, c-format msgid "%s: unsupported ELF file type %d" msgstr "" -#: object.cc:1013 object.cc:1059 object.cc:1093 +#: object.cc:1194 object.cc:1240 object.cc:1274 #, c-format msgid "%s: ELF file too short" msgstr "" -#: object.cc:1021 +#: object.cc:1202 #, c-format msgid "%s: invalid ELF version 0" msgstr "" -#: object.cc:1023 +#: object.cc:1204 #, c-format msgid "%s: unsupported ELF version %d" msgstr "" -#: object.cc:1030 +#: object.cc:1211 #, c-format msgid "%s: invalid ELF class 0" msgstr "" -#: object.cc:1036 +#: object.cc:1217 #, c-format msgid "%s: unsupported ELF class %d" msgstr "" -#: object.cc:1043 +#: object.cc:1224 #, c-format msgid "%s: invalid ELF data encoding" msgstr "" -#: object.cc:1049 +#: object.cc:1230 #, c-format msgid "%s: unsupported ELF data encoding %d" msgstr "" -#: object.cc:1069 +#: object.cc:1250 #, c-format msgid "%s: not configured to support 32-bit big-endian object" msgstr "" -#: object.cc:1082 +#: object.cc:1263 #, c-format msgid "%s: not configured to support 32-bit little-endian object" msgstr "" -#: object.cc:1103 +#: object.cc:1284 #, c-format msgid "%s: not configured to support 64-bit big-endian object" msgstr "" -#: object.cc:1116 +#: object.cc:1297 #, c-format msgid "%s: not configured to support 64-bit little-endian object" msgstr "" @@ -772,37 +777,37 @@ msgstr "" msgid "%s: invalid thread count: %s\n" msgstr "" -#: output.cc:1038 +#: output.cc:1058 #, c-format msgid "invalid alignment %lu for section \"%s\"" msgstr "" -#: output.cc:1703 +#: output.cc:1784 #, c-format msgid "%s: open: %s" msgstr "" -#: output.cc:1708 +#: output.cc:1789 #, c-format msgid "%s: lseek: %s" msgstr "" -#: output.cc:1711 +#: output.cc:1792 #, c-format msgid "%s: write: %s" msgstr "" -#: output.cc:1717 +#: output.cc:1798 #, c-format msgid "%s: mmap: %s" msgstr "" -#: output.cc:1727 +#: output.cc:1808 #, c-format msgid "%s: munmap: %s" msgstr "" -#: output.cc:1731 +#: output.cc:1812 #, c-format msgid "%s: close: %s" msgstr "" @@ -823,26 +828,26 @@ msgstr "" msgid "%s: not an object or archive" msgstr "" -#: reloc.cc:190 reloc.cc:431 -#, c-format -msgid "relocation section %u has bad info %u" -msgstr "" - -#: reloc.cc:208 reloc.cc:447 +#: reloc.cc:221 reloc.cc:487 #, c-format msgid "relocation section %u uses unexpected symbol table %u" msgstr "" -#: reloc.cc:223 reloc.cc:465 +#: reloc.cc:236 reloc.cc:505 #, c-format msgid "unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:232 reloc.cc:474 +#: reloc.cc:245 reloc.cc:514 #, c-format msgid "reloc section %u size %lu uneven" msgstr "" +#: reloc.cc:702 +#, c-format +msgid "reloc section size %zu is not a multiple of reloc size %d\n" +msgstr "" + #: resolve.cc:165 #, c-format msgid "%s: invalid STB_LOCAL symbol %s in external symbols" @@ -907,7 +912,7 @@ msgstr "" msgid "%s: unsupported symbol section 0x%x" msgstr "" -#: target-reloc.h:191 +#: target-reloc.h:211 #, c-format msgid "reloc has bad offset %zu" msgstr "" @@ -935,12 +940,12 @@ msgid "" "This program has absolutely no warranty.\n" msgstr "" -#: x86_64.cc:1137 +#: x86_64.cc:1141 #, c-format msgid "%s: unsupported REL reloc section" msgstr "" -#: x86_64.cc:1535 +#: x86_64.cc:1540 #, c-format msgid "unsupported reloc type %u" msgstr "" diff --git a/gold/reloc.cc b/gold/reloc.cc index 7647edf..e72c134 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -116,34 +116,44 @@ Scan_relocs::run(Workqueue*) // Relocate_task methods. -// These tasks are always runnable. +// We may have to wait for the output sections to be written. Task::Is_runnable_type Relocate_task::is_runnable(Workqueue*) { + if (this->object_->relocs_must_follow_section_writes() + && this->output_sections_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; } // We want to lock the file while we run. We want to unblock -// FINAL_BLOCKER when we are done. +// INPUT_SECTIONS_BLOCKER and FINAL_BLOCKER when we are done. class Relocate_task::Relocate_locker : public Task_locker { public: - Relocate_locker(Task_token& token, Workqueue* workqueue, + Relocate_locker(Task_token& input_sections_blocker, + Task_token& final_blocker, Workqueue* workqueue, Object* object) - : blocker_(token, workqueue), objlock_(*object) + : input_sections_blocker_(input_sections_blocker, workqueue), + final_blocker_(final_blocker, workqueue), + objlock_(*object) { } private: - Task_locker_block blocker_; + Task_block_token input_sections_blocker_; + Task_block_token final_blocker_; Task_locker_obj objlock_; }; Task_locker* Relocate_task::locks(Workqueue* workqueue) { - return new Relocate_locker(*this->final_blocker_, workqueue, + return new Relocate_locker(*this->input_sections_blocker_, + *this->final_blocker_, + workqueue, this->object_); } @@ -171,6 +181,8 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) rd->relocs.reserve(shnum / 2); + std::vector& map_sections(this->map_to_output()); + const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(), shnum * This::shdr_size, true); @@ -192,7 +204,8 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) continue; } - if (!this->is_section_included(shndx)) + Output_section* os = map_sections[shndx].output_section; + if (os == NULL) continue; // We are scanning relocations in order to fill out the GOT and @@ -242,6 +255,8 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) true); sr.sh_type = sh_type; sr.reloc_count = reloc_count; + sr.output_section = os; + sr.needs_special_offset_handling = map_sections[shndx].offset == -1; } // Read the local symbols. @@ -286,9 +301,9 @@ Sized_relobj::do_scan_relocs(const General_options& options, { target->scan_relocs(options, symtab, layout, this, p->data_shndx, p->sh_type, p->contents->data(), p->reloc_count, + p->output_section, p->needs_special_offset_handling, this->local_symbol_count_, - local_symbols, - this->symbols_); + local_symbols); delete p->contents; p->contents = NULL; } @@ -333,8 +348,14 @@ Sized_relobj::do_relocate(const General_options& options, for (unsigned int i = 1; i < shnum; ++i) { if (views[i].view != NULL) - of->write_output_view(views[i].offset, views[i].view_size, - views[i].view); + { + if (views[i].is_input_output_view) + of->write_input_output_view(views[i].offset, views[i].view_size, + views[i].view); + else + of->write_output_view(views[i].offset, views[i].view_size, + views[i].view); + } } // Write out the local symbols. @@ -361,34 +382,52 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, pvs->view = NULL; - if (map_sections[i].offset == -1) - continue; - const Output_section* os = map_sections[i].output_section; if (os == NULL) continue; + off_t output_offset = map_sections[i].offset; typename This::Shdr shdr(p); if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) continue; - off_t start = os->offset() + map_sections[i].offset; - off_t sh_size = shdr.get_sh_size(); + off_t view_start; + off_t view_size; + if (output_offset != -1) + { + view_start = os->offset() + output_offset; + view_size = shdr.get_sh_size(); + } + else + { + view_start = os->offset(); + view_size = os->data_size(); + } - if (sh_size == 0) + if (view_size == 0) continue; - gold_assert(map_sections[i].offset >= 0 - && map_sections[i].offset + sh_size <= os->data_size()); + gold_assert(output_offset == -1 + || (output_offset >= 0 + && output_offset + view_size <= os->data_size())); - unsigned char* view = of->get_output_view(start, sh_size); - this->read(shdr.get_sh_offset(), sh_size, view); + unsigned char* view; + if (output_offset == -1) + view = of->get_input_output_view(view_start, view_size); + else + { + view = of->get_output_view(view_start, view_size); + this->read(shdr.get_sh_offset(), view_size, view); + } pvs->view = view; - pvs->address = os->address() + map_sections[i].offset; - pvs->offset = start; - pvs->view_size = sh_size; + pvs->address = os->address(); + if (output_offset != -1) + pvs->address += output_offset; + pvs->offset = view_start; + pvs->view_size = view_size; + pvs->is_input_output_view = output_offset == -1; } } @@ -407,14 +446,13 @@ Sized_relobj::relocate_sections( unsigned int shnum = this->shnum(); Sized_target* target = this->sized_target(); + std::vector& map_sections(this->map_to_output()); + Relocate_info relinfo; relinfo.options = &options; relinfo.symtab = symtab; relinfo.layout = layout; relinfo.object = this; - relinfo.local_symbol_count = this->local_symbol_count_; - relinfo.local_values = &this->local_values_; - relinfo.symbols = this->symbols_; const unsigned char* p = pshdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) @@ -433,12 +471,14 @@ Sized_relobj::relocate_sections( continue; } - if (!this->is_section_included(index)) + Output_section* os = map_sections[index].output_section; + if (os == NULL) { // This relocation section is against a section which we // discarded. continue; } + off_t output_offset = map_sections[index].offset; gold_assert((*pviews)[index].view != NULL); @@ -464,7 +504,7 @@ Sized_relobj::relocate_sections( { gold_error(_("unexpected entsize for reloc section %u: %lu != %u"), i, static_cast(shdr.get_sh_entsize()), - reloc_size); + reloc_size); continue; } @@ -482,6 +522,8 @@ Sized_relobj::relocate_sections( sh_type, prelocs, reloc_count, + os, + output_offset == -1, (*pviews)[index].view, (*pviews)[index].address, (*pviews)[index].view_size); @@ -621,6 +663,104 @@ Copy_relocs::emit( } } +// Track_relocs methods. + +// Initialize the class to track the relocs. This gets the object, +// the reloc section index, and the type of the relocs. This returns +// false if something goes wrong. + +template +bool +Track_relocs::initialize( + Sized_relobj* object, + unsigned int reloc_shndx, + unsigned int reloc_type) +{ + this->object_ = object; + + // If RELOC_SHNDX is -1U, it means there is more than one reloc + // section for the .eh_frame section. We can't handle that case. + if (reloc_shndx == -1U) + return false; + + // If RELOC_SHNDX is 0, there is no reloc section. + if (reloc_shndx == 0) + return true; + + // Get the contents of the reloc section. + this->prelocs_ = object->section_contents(reloc_shndx, &this->len_, false); + + if (reloc_type == elfcpp::SHT_REL) + this->reloc_size_ = elfcpp::Elf_sizes::rel_size; + else if (reloc_type == elfcpp::SHT_RELA) + this->reloc_size_ = elfcpp::Elf_sizes::rela_size; + else + gold_unreachable(); + + if (this->len_ % this->reloc_size_ != 0) + { + object->error(_("reloc section size %zu is not a multiple of " + "reloc size %d\n"), + static_cast(this->len_), + this->reloc_size_); + return false; + } + + return true; +} + +// Return the offset of the next reloc, or -1 if there isn't one. + +template +off_t +Track_relocs::next_offset() const +{ + if (this->pos_ >= this->len_) + return -1; + + // Rel and Rela start out the same, so we can always use Rel to find + // the r_offset value. + elfcpp::Rel rel(this->prelocs_ + this->pos_); + return rel.get_r_offset(); +} + +// Return the index of the symbol referenced by the next reloc, or -1U +// if there aren't any more relocs. + +template +unsigned int +Track_relocs::next_symndx() const +{ + if (this->pos_ >= this->len_) + return -1U; + + // Rel and Rela start out the same, so we can use Rel to find the + // symbol index. + elfcpp::Rel rel(this->prelocs_ + this->pos_); + return elfcpp::elf_r_sym(rel.get_r_info()); +} + +// Advance to the next reloc whose r_offset is greater than or equal +// to OFFSET. Return the number of relocs we skip. + +template +int +Track_relocs::advance(off_t offset) +{ + int ret = 0; + while (this->pos_ < this->len_) + { + // Rel and Rela start out the same, so we can always use Rel to + // find the r_offset value. + elfcpp::Rel rel(this->prelocs_ + this->pos_); + if (static_cast(rel.get_r_offset()) >= offset) + break; + ++ret; + this->pos_ += this->reloc_size_; + } + return ret; +} + // Instantiate the templates we need. We could use the configure // script to restrict this to only the ones for implemented targets. @@ -796,4 +936,24 @@ Copy_relocs<64, true>::emit( Output_data_reloc*); #endif +#ifdef HAVE_TARGET_32_LITTLE +template +class Track_relocs<32, false>; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Track_relocs<32, true>; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Track_relocs<64, false>; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Track_relocs<64, true>; +#endif + } // End namespace gold. diff --git a/gold/reloc.h b/gold/reloc.h index 1bf0457..5d2a160 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -129,9 +129,12 @@ class Relocate_task : public Task public: Relocate_task(const General_options& options, const Symbol_table* symtab, const Layout* layout, Relobj* object, Output_file* of, - Task_token* final_blocker) + Task_token* input_sections_blocker, + Task_token* output_sections_blocker, Task_token* final_blocker) : options_(options), symtab_(symtab), layout_(layout), object_(object), - of_(of), final_blocker_(final_blocker) + of_(of), input_sections_blocker_(input_sections_blocker), + output_sections_blocker_(output_sections_blocker), + final_blocker_(final_blocker) { } // The standard Task methods. @@ -153,6 +156,8 @@ class Relocate_task : public Task const Layout* layout_; Relobj* object_; Output_file* of_; + Task_token* input_sections_blocker_; + Task_token* output_sections_blocker_; Task_token* final_blocker_; }; @@ -594,6 +599,55 @@ class Copy_relocs Copy_reloc_entries entries_; }; +// Track relocations while reading a section. This lets you ask for +// the relocation at a certain offset, and see how relocs occur +// between points of interest. + +template +class Track_relocs +{ + public: + Track_relocs() + : object_(NULL), prelocs_(NULL), len_(0), pos_(0), reloc_size_(0) + { } + + // Initialize the Track_relocs object. OBJECT is the object holding + // the reloc section, RELOC_SHNDX is the section index of the reloc + // section, and RELOC_TYPE is the type of the reloc section + // (elfcpp::SHT_REL or elfcpp::SHT_RELA). This returns false if + // something went wrong. + bool + initialize(Sized_relobj* object, unsigned int reloc_shndx, + unsigned int reloc_type); + + // Return the offset in the data section to which the next reloc + // applies. THis returns -1 if there is no next reloc. + off_t + next_offset() const; + + // Return the symbol index of the next reloc. This returns -1U if + // there is no next reloc. + unsigned int + next_symndx() const; + + // Advance to OFFSET within the data section, and return the number + // of relocs which would be skipped. + int + advance(off_t offset); + + private: + // The object file. + Sized_relobj* object_; + // The contents of the reloc section. + const unsigned char* prelocs_; + // The length of the reloc section. + off_t len_; + // Our current position in the reloc section. + off_t pos_; + // The size of the relocs in the section. + int reloc_size_; +}; + } // End namespace gold. #endif // !defined(GOLD_RELOC_H) diff --git a/gold/stringpool.cc b/gold/stringpool.cc index bb571f0f..db04b0c 100644 --- a/gold/stringpool.cc +++ b/gold/stringpool.cc @@ -103,7 +103,7 @@ Stringpool_template::Stringpool_hash::operator()( const Stringpool_char* s) const { // Fowler/Noll/Vo (FNV) hash (type FNV-1a). - if (sizeof(size_t) == 8) + if (sizeof(size_t) > 4) { size_t result = static_cast(14695981039346656037ULL); while (*s != 0) diff --git a/gold/symtab.cc b/gold/symtab.cc index 3108433..3c2a976 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -521,7 +521,7 @@ Symbol_table::add_from_relobj( size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers) + typename Sized_relobj::Symbols* sympointers) { gold_assert(size == relobj->target()->get_size()); gold_assert(size == parameters->get_size()); @@ -592,7 +592,7 @@ Symbol_table::add_from_relobj( def, *psym); } - *sympointers++ = res; + (*sympointers)[i] = res; } } @@ -1870,7 +1870,7 @@ Symbol_table::add_from_relobj<32, false>( size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers); + Sized_relobj<32, true>::Symbols* sympointers); #endif #ifdef HAVE_TARGET_32_BIG @@ -1882,7 +1882,7 @@ Symbol_table::add_from_relobj<32, true>( size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers); + Sized_relobj<32, false>::Symbols* sympointers); #endif #ifdef HAVE_TARGET_64_LITTLE @@ -1894,7 +1894,7 @@ Symbol_table::add_from_relobj<64, false>( size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers); + Sized_relobj<64, true>::Symbols* sympointers); #endif #ifdef HAVE_TARGET_64_BIG @@ -1906,7 +1906,7 @@ Symbol_table::add_from_relobj<64, true>( size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers); + Sized_relobj<64, false>::Symbols* sympointers); #endif #ifdef HAVE_TARGET_32_LITTLE diff --git a/gold/symtab.h b/gold/symtab.h index 05c8d7a..38320f9 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -833,7 +833,7 @@ class Symbol_table add_from_relobj(Sized_relobj* relobj, const unsigned char* syms, size_t count, const char* sym_names, size_t sym_name_size, - Symbol** sympointers); + typename Sized_relobj::Symbols*); // Add COUNT dynamic symbols from the dynamic object DYNOBJ to the // symbol table. SYMS is the symbols. SYM_NAMES is their names. diff --git a/gold/target-reloc.h b/gold/target-reloc.h index c38d5f6..0498e9a 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -24,7 +24,6 @@ #define GOLD_TARGET_RELOC_H #include "elfcpp.h" -#include "object.h" #include "symtab.h" #include "reloc-types.h" @@ -49,9 +48,10 @@ scan_relocs( unsigned int data_shndx, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_count, - const unsigned char* plocal_syms, - Symbol** global_syms) + const unsigned char* plocal_syms) { typedef typename Reloc_types::Reloc Reltype; const int reloc_size = Reloc_types::reloc_size; @@ -62,6 +62,11 @@ scan_relocs( { Reltype reloc(prelocs); + if (needs_special_offset_handling + && !output_section->is_input_address_mapped(object, data_shndx, + reloc.get_r_offset())) + continue; + typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym(r_info); unsigned int r_type = elfcpp::elf_r_type(r_info); @@ -99,7 +104,7 @@ scan_relocs( } else { - Symbol* gsym = global_syms[r_sym - local_count]; + Symbol* gsym = object->global_symbol(r_sym); gold_assert(gsym != NULL); if (gsym->is_forwarder()) gsym = symtab->resolve_forwards(gsym); @@ -122,8 +127,14 @@ scan_relocs( // RELOCATE implements operator() to do a relocation. // PRELOCS points to the relocation data. RELOC_COUNT is the number -// of relocs. VIEW is the section data, VIEW_ADDRESS is its memory -// address, and VIEW_SIZE is the size. +// of relocs. OUTPUT_SECTION is the output section. +// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be +// mapped to output offsets. + +// VIEW is the section data, VIEW_ADDRESS is its memory address, and +// VIEW_SIZE is the size. These refer to the input section, unless +// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to +// the output section. template @@ -133,6 +144,8 @@ relocate_section( Target_type* target, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, off_t view_size) @@ -141,10 +154,8 @@ relocate_section( const int reloc_size = Reloc_types::reloc_size; Relocate relocate; - unsigned int local_count = relinfo->local_symbol_count; - const typename Sized_relobj::Local_values* local_values = - relinfo->local_values; - const Symbol* const * global_syms = relinfo->symbols; + Sized_relobj* object = relinfo->object; + unsigned int local_count = object->local_symbol_count(); for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) { @@ -152,6 +163,15 @@ relocate_section( off_t offset = reloc.get_r_offset(); + if (needs_special_offset_handling) + { + offset = output_section->output_offset(relinfo->object, + relinfo->data_shndx, + offset); + if (offset == -1) + continue; + } + typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym(r_info); unsigned int r_type = elfcpp::elf_r_type(r_info); @@ -163,11 +183,11 @@ relocate_section( if (r_sym < local_count) { sym = NULL; - psymval = &(*local_values)[r_sym]; + psymval = object->local_symbol(r_sym); } else { - const Symbol* gsym = global_syms[r_sym - local_count]; + const Symbol* gsym = object->global_symbol(r_sym); gold_assert(gsym != NULL); if (gsym->is_forwarder()) gsym = relinfo->symtab->resolve_forwards(gsym); diff --git a/gold/target.h b/gold/target.h index 32166d1..8ecc078 100644 --- a/gold/target.h +++ b/gold/target.h @@ -43,11 +43,12 @@ class Object; template class Sized_relobj; template -struct Relocate_info; +class Relocate_info; class Symbol; template class Sized_symbol; class Symbol_table; +class Output_section; // The abstract class for target specific handling. @@ -228,9 +229,11 @@ class Sized_target : public Target // relocs apply to. SH_TYPE is the type of the relocation section, // SHT_REL or SHT_RELA. PRELOCS points to the relocation data. // RELOC_COUNT is the number of relocs. LOCAL_SYMBOL_COUNT is the - // number of local symbols. PLOCAL_SYMBOLS points to the local - // symbol data from OBJECT. GLOBAL_SYMBOLS is the array of pointers - // to the global symbol table from OBJECT. + // number of local symbols. OUTPUT_SECTION is the output section. + // NEEDS_SPECIAL_OFFSET_HANDLING is true if offsets to the output + // sections are not mapped as usual. PLOCAL_SYMBOLS points to the + // local symbol data from OBJECT. GLOBAL_SYMBOLS is the array of + // pointers to the global symbol table from OBJECT. virtual void scan_relocs(const General_options& options, Symbol_table* symtab, @@ -240,21 +243,29 @@ class Sized_target : public Target unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_symbol_count, - const unsigned char* plocal_symbols, - Symbol** global_symbols) = 0; + const unsigned char* plocal_symbols) = 0; // Relocate section data. SH_TYPE is the type of the relocation // section, SHT_REL or SHT_RELA. PRELOCS points to the relocation - // information. RELOC_COUNT is the number of relocs. VIEW is a - // view into the output file holding the section contents, - // VIEW_ADDRESS is the virtual address of the view, and VIEW_SIZE is - // the size of the view. + // information. RELOC_COUNT is the number of relocs. + // OUTPUT_SECTION is the output section. + // NEEDS_SPECIAL_OFFSET_HANDLING is true if offsets must be mapped + // to correspond to the output section. VIEW is a view into the + // output file holding the section contents, VIEW_ADDRESS is the + // virtual address of the view, and VIEW_SIZE is the size of the + // view. If NEEDS_SPECIAL_OFFSET_HANDLING is true, the VIEW_xx + // parameters refer to the complete output section data, not just + // the input section data. virtual void relocate_section(const Relocate_info*, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, off_t view_size) = 0; diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc index 8e6a48d..89bad1d 100644 --- a/gold/testsuite/testfile.cc +++ b/gold/testsuite/testfile.cc @@ -46,14 +46,15 @@ class Target_test : public Sized_target void scan_relocs(const General_options&, Symbol_table*, Layout*, Sized_relobj*, unsigned int, - unsigned int, const unsigned char*, size_t, size_t, - const unsigned char*, Symbol**) + unsigned int, const unsigned char*, size_t, Output_section*, + bool, size_t, const unsigned char*) { ERROR("call to Target_test::scan_relocs"); } void relocate_section(const Relocate_info*, unsigned int, - const unsigned char*, size_t, unsigned char*, - typename elfcpp::Elf_types::Elf_Addr, off_t) + const unsigned char*, size_t, Output_section*, bool, + unsigned char*, typename elfcpp::Elf_types::Elf_Addr, + off_t) { ERROR("call to Target_test::relocate_section"); } static const Target::Target_info test_target_info; diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 223523e..3be64cf 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -83,9 +83,10 @@ class Target_x86_64 : public Sized_target<64, false> unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_symbol_count, - const unsigned char* plocal_symbols, - Symbol** global_symbols); + const unsigned char* plocal_symbols); // Finalize the sections. void @@ -102,6 +103,8 @@ class Target_x86_64 : public Sized_target<64, false> unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<64>::Elf_Addr view_address, off_t view_size); @@ -1128,9 +1131,10 @@ Target_x86_64::scan_relocs(const General_options& options, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, size_t local_symbol_count, - const unsigned char* plocal_symbols, - Symbol** global_symbols) + const unsigned char* plocal_symbols) { if (sh_type == elfcpp::SHT_REL) { @@ -1149,9 +1153,10 @@ Target_x86_64::scan_relocs(const General_options& options, data_shndx, prelocs, reloc_count, + output_section, + needs_special_offset_handling, local_symbol_count, - plocal_symbols, - global_symbols); + plocal_symbols); } // Finalize the sections. @@ -1670,6 +1675,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<64>::Elf_Addr address, off_t view_size) @@ -1682,6 +1689,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, this, prelocs, reloc_count, + output_section, + needs_special_offset_handling, view, address, view_size); -- cgit v1.1