diff options
-rw-r--r-- | gold/gold.cc | 27 | ||||
-rw-r--r-- | gold/i386.cc | 182 | ||||
-rw-r--r-- | gold/layout.cc | 295 | ||||
-rw-r--r-- | gold/layout.h | 28 | ||||
-rw-r--r-- | gold/object.cc | 82 | ||||
-rw-r--r-- | gold/object.h | 46 | ||||
-rw-r--r-- | gold/options.cc | 3 | ||||
-rw-r--r-- | gold/output.cc | 233 | ||||
-rw-r--r-- | gold/output.h | 69 | ||||
-rw-r--r-- | gold/reloc-types.h | 28 | ||||
-rw-r--r-- | gold/reloc.cc | 100 | ||||
-rw-r--r-- | gold/reloc.h | 95 | ||||
-rw-r--r-- | gold/target-reloc.h | 330 | ||||
-rw-r--r-- | gold/target.h | 37 | ||||
-rw-r--r-- | gold/testsuite/Makefile.am | 9 | ||||
-rw-r--r-- | gold/testsuite/Makefile.in | 24 | ||||
-rw-r--r-- | gold/testsuite/testfile.cc | 18 | ||||
-rw-r--r-- | gold/x86_64.cc | 180 |
18 files changed, 1696 insertions, 90 deletions
diff --git a/gold/gold.cc b/gold/gold.cc index aa6f32a..2aee1b7 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -182,6 +182,9 @@ queue_middle_tasks(const General_options& options, gold_error(_("cannot mix -static with dynamic object %s"), (*input_objects->dynobj_begin())->name().c_str()); } + if (!doing_static_link && parameters->output_is_object()) + gold_error(_("cannot mix -r with dynamic object %s"), + (*input_objects->dynobj_begin())->name().c_str()); if (is_debugging_enabled(DEBUG_SCRIPT)) layout->script_options()->print(stderr); @@ -201,12 +204,15 @@ queue_middle_tasks(const General_options& options, // Define symbols from any linker scripts. layout->define_script_symbols(symtab); - // Predefine standard symbols. - define_standard_symbols(symtab, layout); + if (!parameters->output_is_object()) + { + // Predefine standard symbols. + define_standard_symbols(symtab, layout); - // Define __start and __stop symbols for output sections where - // appropriate. - layout->define_section_symbols(symtab); + // Define __start and __stop symbols for output sections where + // appropriate. + layout->define_section_symbols(symtab); + } // Read the relocations of the input files. We do this to find // which symbols are used by relocations which require a GOT and/or @@ -237,9 +243,14 @@ queue_middle_tasks(const General_options& options, // Allocate common symbols. This requires write access to the // symbol table, but is independent of the relocation processing. - blocker->add_blocker(); - workqueue->queue(new Allocate_commons_task(options, symtab, layout, - symtab_lock, blocker)); + // FIXME: We should have an option to do this even for a relocatable + // link. + if (!parameters->output_is_object()) + { + blocker->add_blocker(); + workqueue->queue(new Allocate_commons_task(options, symtab, layout, + symtab_lock, blocker)); + } // When all those tasks are complete, we can start laying out the // output file. diff --git a/gold/i386.cc b/gold/i386.cc index ca68a3f..3bcfa4c 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -96,6 +96,37 @@ class Target_i386 : public Sized_target<32, false> elfcpp::Elf_types<32>::Elf_Addr view_address, section_size_type view_size); + // Scan the relocs during a relocatable link. + void + scan_relocatable_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + 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, + Relocatable_relocs*); + + // Relocate a section during a relocatable link. + void + relocate_for_relocatable(const Relocate_info<32, false>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs*, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size); + // Return a string used to fill a code section with nops. std::string do_code_fill(section_size_type length); @@ -240,6 +271,15 @@ class Target_i386 : public Sized_target<32, false> Local_dynamic_type local_dynamic_type_; }; + // A class which returns the size required for a relocation type, + // used while scanning relocs during a relocatable link. + class Relocatable_size_for_reloc + { + public: + unsigned int + get_size_for_reloc(unsigned int, Relobj*); + }; + // Adjust TLS relocation type based on the options and whether this // is a local symbol. static tls::Tls_optimization @@ -2151,6 +2191,148 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, view_size); } +// Return the size of a relocation while scanning during a relocatable +// link. + +unsigned int +Target_i386::Relocatable_size_for_reloc::get_size_for_reloc( + unsigned int r_type, + Relobj* object) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + case elfcpp::R_386_TLS_GD: // Global-dynamic + case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva url) + case elfcpp::R_386_TLS_DESC_CALL: + case elfcpp::R_386_TLS_LDM: // Local-dynamic + case elfcpp::R_386_TLS_LDO_32: // Alternate local-dynamic + case elfcpp::R_386_TLS_IE: // Initial-exec + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: // Local-exec + case elfcpp::R_386_TLS_LE_32: + return 0; + + case elfcpp::R_386_32: + case elfcpp::R_386_PC32: + case elfcpp::R_386_GOT32: + case elfcpp::R_386_PLT32: + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + return 4; + + case elfcpp::R_386_16: + case elfcpp::R_386_PC16: + return 2; + + case elfcpp::R_386_8: + case elfcpp::R_386_PC8: + return 1; + + // These are relocations which should only be seen by the + // dynamic linker, and should never be seen here. + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + object->error(_("unexpected reloc %u in object file"), r_type); + return 0; + + case elfcpp::R_386_32PLT: + case elfcpp::R_386_TLS_GD_32: + case elfcpp::R_386_TLS_GD_PUSH: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + object->error(_("unsupported reloc %u in object file"), r_type); + return 0; + } +} + +// Scan the relocs during a relocatable link. + +void +Target_i386::scan_relocatable_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + 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, + Relocatable_relocs* rr) +{ + gold_assert(sh_type == elfcpp::SHT_REL); + + typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_REL, + Relocatable_size_for_reloc> Scan_relocatable_relocs; + + gold::scan_relocatable_relocs<32, false, Target_i386, elfcpp::SHT_REL, + Scan_relocatable_relocs>( + options, + symtab, + layout, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_symbols, + rr); +} + +// Relocate a section during a relocatable link. + +void +Target_i386::relocate_for_relocatable( + const Relocate_info<32, false>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs* rr, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) +{ + gold_assert(sh_type == elfcpp::SHT_REL); + + gold::relocate_for_relocatable<32, false, Target_i386, elfcpp::SHT_REL>( + relinfo, + prelocs, + reloc_count, + output_section, + offset_in_output_section, + rr, + view, + view_address, + view_size, + reloc_view, + reloc_view_size); +} + // Return the value to use for a dynamic which requires special // treatment. This is how we support equality comparisons of function // pointers across shared library boundaries, as described in the diff --git a/gold/layout.cc b/gold/layout.cc index a532e09..006a384 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -36,6 +36,7 @@ #include "dynobj.h" #include "ehframe.h" #include "compressed_output.h" +#include "reloc.h" #include "layout.h" namespace gold @@ -138,8 +139,6 @@ bool Layout::include_section(Sized_relobj<size, big_endian>*, const char* name, const elfcpp::Shdr<size, big_endian>& shdr) { - // Some section types are never linked. Some are only linked when - // doing a relocateable link. switch (shdr.get_sh_type()) { case elfcpp::SHT_NULL: @@ -154,7 +153,9 @@ Layout::include_section(Sized_relobj<size, big_endian>*, const char* name, case elfcpp::SHT_RELA: case elfcpp::SHT_REL: case elfcpp::SHT_GROUP: - return parameters->output_is_object(); + // For a relocatable link these should be handled elsewhere. + gold_assert(!parameters->output_is_object()); + return false; case elfcpp::SHT_PROGBITS: if (parameters->strip_debug() @@ -330,13 +331,24 @@ Layout::layout(Sized_relobj<size, big_endian>* object, unsigned int shndx, if (!this->include_section(object, name, shdr)) return NULL; - Output_section* os = this->choose_output_section(object, - name, - shdr.get_sh_type(), - shdr.get_sh_flags(), - true); - if (os == NULL) - return NULL; + Output_section* os; + + // In a relocatable link a grouped section must not be combined with + // any other sections. + if (parameters->output_is_object() + && (shdr.get_sh_flags() & elfcpp::SHF_GROUP) != 0) + { + name = this->namepool_.add(name, true, NULL); + os = this->make_output_section(name, shdr.get_sh_type(), + shdr.get_sh_flags()); + } + else + { + os = this->choose_output_section(object, name, shdr.get_sh_type(), + shdr.get_sh_flags(), true); + if (os == NULL) + return NULL; + } // FIXME: Handle SHF_LINK_ORDER somewhere. @@ -346,6 +358,100 @@ Layout::layout(Sized_relobj<size, big_endian>* object, unsigned int shndx, return os; } +// Handle a relocation section when doing a relocatable link. + +template<int size, bool big_endian> +Output_section* +Layout::layout_reloc(Sized_relobj<size, big_endian>* object, + unsigned int, + const elfcpp::Shdr<size, big_endian>& shdr, + Output_section* data_section, + Relocatable_relocs* rr) +{ + gold_assert(parameters->output_is_object()); + + int sh_type = shdr.get_sh_type(); + + std::string name; + if (sh_type == elfcpp::SHT_REL) + name = ".rel"; + else if (sh_type == elfcpp::SHT_RELA) + name = ".rela"; + else + gold_unreachable(); + name += data_section->name(); + + Output_section* os = this->choose_output_section(object, name.c_str(), + sh_type, + shdr.get_sh_flags(), + false); + + os->set_should_link_to_symtab(); + os->set_info_section(data_section); + + Output_section_data* posd; + if (sh_type == elfcpp::SHT_REL) + { + os->set_entsize(elfcpp::Elf_sizes<size>::rel_size); + posd = new Output_relocatable_relocs<elfcpp::SHT_REL, + size, + big_endian>(rr); + } + else if (sh_type == elfcpp::SHT_RELA) + { + os->set_entsize(elfcpp::Elf_sizes<size>::rela_size); + posd = new Output_relocatable_relocs<elfcpp::SHT_RELA, + size, + big_endian>(rr); + } + else + gold_unreachable(); + + os->add_output_section_data(posd); + rr->set_output_data(posd); + + return os; +} + +// Handle a group section when doing a relocatable link. + +template<int size, bool big_endian> +void +Layout::layout_group(Symbol_table* symtab, + Sized_relobj<size, big_endian>* object, + unsigned int, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<size, big_endian>& shdr, + const elfcpp::Elf_Word* contents) +{ + gold_assert(parameters->output_is_object()); + gold_assert(shdr.get_sh_type() == elfcpp::SHT_GROUP); + group_section_name = this->namepool_.add(group_section_name, true, NULL); + Output_section* os = this->make_output_section(group_section_name, + elfcpp::SHT_GROUP, + shdr.get_sh_flags()); + + // We need to find a symbol with the signature in the symbol table. + // This is a hack to force that to happen. + Symbol* sym = symtab->lookup(signature, NULL); + if (sym == NULL) + sym = symtab->define_as_constant(signature, NULL, 0, 0, + elfcpp::STT_NOTYPE, + elfcpp::STB_WEAK, + elfcpp::STV_HIDDEN, 0, false); + + os->set_should_link_to_symtab(); + os->set_info_symndx(sym); + os->set_entsize(4); + + section_size_type entry_count = + convert_to_section_size_type(shdr.get_sh_size() / 4); + Output_section_data* posd = + new Output_data_group<size, big_endian>(object, entry_count, contents); + os->add_output_section_data(posd); +} + // 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/). @@ -496,6 +602,9 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, this->unattached_section_list_.push_back(os); else { + if (parameters->output_is_object()) + return os; + // If we have a SECTIONS clause, we can't handle the attachment // to segments until after we've seen all the sections. if (this->script_options_->saw_sections_clause()) @@ -799,7 +908,9 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, // If there is a SECTIONS clause, put all the input sections into // the required order. Output_segment* load_seg; - if (this->script_options_->saw_sections_clause()) + if (parameters->output_is_object()) + load_seg = NULL; + else if (this->script_options_->saw_sections_clause()) load_seg = this->set_section_addresses_from_script(symtab); else load_seg = this->find_first_load_seg(); @@ -808,11 +919,16 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, // Lay out the segment headers. Output_segment_headers* segment_headers; - segment_headers = new Output_segment_headers(this->segment_list_); - if (load_seg != NULL) - load_seg->add_initial_output_data(segment_headers); - if (phdr_seg != NULL) - phdr_seg->add_initial_output_data(segment_headers); + if (parameters->output_is_object()) + segment_headers = NULL; + else + { + segment_headers = new Output_segment_headers(this->segment_list_); + if (load_seg != NULL) + load_seg->add_initial_output_data(segment_headers); + if (phdr_seg != NULL) + phdr_seg->add_initial_output_data(segment_headers); + } // Lay out the file header. Output_file_header* file_header; @@ -822,9 +938,11 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, load_seg->add_initial_output_data(file_header); this->special_output_list_.push_back(file_header); - this->special_output_list_.push_back(segment_headers); + if (segment_headers != NULL) + this->special_output_list_.push_back(segment_headers); - if (this->script_options_->saw_phdrs_clause()) + if (this->script_options_->saw_phdrs_clause() + && !parameters->output_is_object()) { // Support use of FILEHDRS and PHDRS attachments in a PHDRS // clause in a linker script. @@ -838,7 +956,11 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, // Set the file offsets of all the segments, and all the sections // they contain. - off_t off = this->set_segment_offsets(target, load_seg, &shndx); + off_t off; + if (!parameters->output_is_object()) + off = this->set_segment_offsets(target, load_seg, &shndx); + else + off = this->set_relocatable_section_offsets(file_header, &shndx); // Set the file offsets of all the non-data sections we've seen so // far which don't have to wait for the input sections. We need @@ -1272,6 +1394,45 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, return off; } +// Set the offsets of all the allocated sections when doing a +// relocatable link. This does the same jobs as set_segment_offsets, +// only for a relocatable link. + +off_t +Layout::set_relocatable_section_offsets(Output_data* file_header, + unsigned int *pshndx) +{ + off_t off = 0; + + file_header->set_address_and_file_offset(0, 0); + off += file_header->data_size(); + + for (Section_list::iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + // We skip unallocated sections here, except that group sections + // have to come first. + if (((*p)->flags() & elfcpp::SHF_ALLOC) == 0 + && (*p)->type() != elfcpp::SHT_GROUP) + continue; + + off = align_address(off, (*p)->addralign()); + + // The linker script might have set the address. + if (!(*p)->is_address_valid()) + (*p)->set_address(0); + (*p)->set_file_offset(off); + (*p)->finalize_data_size(); + off += (*p)->data_size(); + + (*p)->set_out_shndx(*pshndx); + ++*pshndx; + } + + return off; +} + // Set the file offset of all the sections not associated with a // segment. @@ -1327,10 +1488,16 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass) unsigned int Layout::set_section_indexes(unsigned int shndx) { + const bool output_is_object = parameters->output_is_object(); for (Section_list::iterator p = this->unattached_section_list_.begin(); p != this->unattached_section_list_.end(); ++p) { + // In a relocatable link, we already did group sections. + if (output_is_object + && (*p)->type() == elfcpp::SHT_GROUP) + continue; + (*p)->set_out_shndx(shndx); ++shndx; } @@ -1545,6 +1712,7 @@ Layout::create_shdrs(off_t* poff) Output_section_headers* oshdrs; oshdrs = new Output_section_headers(this, &this->segment_list_, + &this->section_list_, &this->unattached_section_list_, &this->namepool_); off_t off = align_address(*poff, oshdrs->addralign()); @@ -2177,6 +2345,7 @@ Layout::get_allocated_sections(Section_list* section_list) const Output_segment* Layout::make_output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags) { + gold_assert(!parameters->output_is_object()); Output_segment* oseg = new Output_segment(type, flags); this->segment_list_.push_back(oseg); return oseg; @@ -2459,6 +2628,94 @@ Layout::layout<64, true>(Sized_relobj<64, true>* object, unsigned int shndx, #ifdef HAVE_TARGET_32_LITTLE template Output_section* +Layout::layout_reloc<32, false>(Sized_relobj<32, false>* object, + unsigned int reloc_shndx, + const elfcpp::Shdr<32, false>& shdr, + Output_section* data_section, + Relocatable_relocs* rr); +#endif + +#ifdef HAVE_TARGET_32_BIG +template +Output_section* +Layout::layout_reloc<32, true>(Sized_relobj<32, true>* object, + unsigned int reloc_shndx, + const elfcpp::Shdr<32, true>& shdr, + Output_section* data_section, + Relocatable_relocs* rr); +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +Output_section* +Layout::layout_reloc<64, false>(Sized_relobj<64, false>* object, + unsigned int reloc_shndx, + const elfcpp::Shdr<64, false>& shdr, + Output_section* data_section, + Relocatable_relocs* rr); +#endif + +#ifdef HAVE_TARGET_64_BIG +template +Output_section* +Layout::layout_reloc<64, true>(Sized_relobj<64, true>* object, + unsigned int reloc_shndx, + const elfcpp::Shdr<64, true>& shdr, + Output_section* data_section, + Relocatable_relocs* rr); +#endif + +#ifdef HAVE_TARGET_32_LITTLE +template +void +Layout::layout_group<32, false>(Symbol_table* symtab, + Sized_relobj<32, false>* object, + unsigned int, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<32, false>& shdr, + const elfcpp::Elf_Word* contents); +#endif + +#ifdef HAVE_TARGET_32_BIG +template +void +Layout::layout_group<32, true>(Symbol_table* symtab, + Sized_relobj<32, true>* object, + unsigned int, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<32, true>& shdr, + const elfcpp::Elf_Word* contents); +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +void +Layout::layout_group<64, false>(Symbol_table* symtab, + Sized_relobj<64, false>* object, + unsigned int, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<64, false>& shdr, + const elfcpp::Elf_Word* contents); +#endif + +#ifdef HAVE_TARGET_64_BIG +template +void +Layout::layout_group<64, true>(Symbol_table* symtab, + Sized_relobj<64, true>* object, + unsigned int, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<64, true>& shdr, + const elfcpp::Elf_Word* contents); +#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, diff --git a/gold/layout.h b/gold/layout.h index d7f5965..e050df3 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -100,6 +100,29 @@ class Layout const char* name, const elfcpp::Shdr<size, big_endian>& shdr, unsigned int reloc_shndx, unsigned int reloc_type, off_t* offset); + // Layout an input reloc section when doing a relocatable link. The + // section is RELOC_SHNDX in OBJECT, with data in SHDR. + // DATA_SECTION is the reloc section to which it refers. RR is the + // relocatable information. + template<int size, bool big_endian> + Output_section* + layout_reloc(Sized_relobj<size, big_endian>* object, + unsigned int reloc_shndx, + const elfcpp::Shdr<size, big_endian>& shdr, + Output_section* data_section, + Relocatable_relocs* rr); + + // Layout a group section when doing a relocatable link. + template<int size, bool big_endian> + void + layout_group(Symbol_table* symtab, + Sized_relobj<size, big_endian>* object, + unsigned int group_shndx, + const char* group_section_name, + const char* signature, + const elfcpp::Shdr<size, big_endian>& shdr, + const elfcpp::Elf_Word* contents); + // 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 @@ -413,6 +436,11 @@ class Layout off_t set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx); + // Set the file offsets of the sections when doing a relocatable + // link. + off_t + set_relocatable_section_offsets(Output_data*, unsigned int* pshndx); + // Set the final file offsets of all the sections not associated // with a segment. We set section offsets in three passes: the // first handles all allocated sections, the second sections that diff --git a/gold/object.cc b/gold/object.cc index e56f6a4..433fbc8 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -378,8 +378,10 @@ Sized_relobj<size, big_endian>::symbol_section_and_value(unsigned int sym, template<int size, bool big_endian> bool Sized_relobj<size, big_endian>::include_section_group( + Symbol_table* symtab, Layout* layout, unsigned int index, + const char* name, const elfcpp::Shdr<size, big_endian>& shdr, std::vector<bool>* omit) { @@ -393,13 +395,11 @@ Sized_relobj<size, big_endian>::include_section_group( // groups. Other section groups are always included in the link // just like ordinary sections. elfcpp::Elf_Word flags = elfcpp::Swap<32, big_endian>::readval(pword); - if ((flags & elfcpp::GRP_COMDAT) == 0) - return true; // Look up the group signature, which is the name of a symbol. This // is a lot of effort to go to to read a string. Why didn't they - // just use the name of the SHT_GROUP section as the group - // signature? + // just have the group signature point into the string table, rather + // than indirect through a symbol? // Get the appropriate symbol table header (this will normally be // the single SHT_SYMTAB section, but in principle it need not be). @@ -447,8 +447,15 @@ Sized_relobj<size, big_endian>::include_section_group( // Record this section group, and see whether we've already seen one // with the same signature. - if (layout->add_comdat(signature, true)) - return true; + + if ((flags & elfcpp::GRP_COMDAT) == 0 + || layout->add_comdat(signature, true)) + { + if (parameters->output_is_object()) + layout->layout_group(symtab, this, index, name, signature, shdr, + pword); + return true; + } // This is a duplicate. We want to discard the sections in this // group. @@ -575,6 +582,10 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, // Keep track of which sections to omit. std::vector<bool> omit(shnum, false); + // Keep track of reloc sections when doing a relocatable link. + const bool output_is_object = parameters->output_is_object(); + std::vector<unsigned int> reloc_sections; + // Keep track of .eh_frame sections. std::vector<unsigned int> eh_frame_sections; @@ -595,7 +606,7 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, if (this->handle_gnu_warning_section(name, i, symtab)) { - if (!parameters->output_is_object()) + if (!output_is_object) omit[i] = true; } @@ -614,7 +625,8 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, { if (shdr.get_sh_type() == elfcpp::SHT_GROUP) { - if (!this->include_section_group(layout, i, shdr, &omit)) + if (!this->include_section_group(symtab, layout, i, name, shdr, + &omit)) discard = true; } else if ((shdr.get_sh_flags() & elfcpp::SHF_GROUP) == 0 @@ -632,13 +644,29 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, continue; } + // When doing a relocatable link we are going to copy input + // reloc sections into the output. We only want to copy the + // ones associated with sections which are not being discarded. + // However, we don't know that yet for all sections. So save + // reloc sections and process them later. + if (output_is_object + && (shdr.get_sh_type() == elfcpp::SHT_REL + || shdr.get_sh_type() == elfcpp::SHT_RELA)) + { + reloc_sections.push_back(i); + continue; + } + + if (output_is_object && shdr.get_sh_type() == elfcpp::SHT_GROUP) + 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() + if (!output_is_object && strcmp(name, ".eh_frame") == 0 && this->check_eh_frame_flags(&shdr)) { @@ -663,6 +691,42 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags); + // When doing a relocatable link handle the reloc sections at the + // end. + if (output_is_object) + this->size_relocatable_relocs(); + for (std::vector<unsigned int>::const_iterator p = reloc_sections.begin(); + p != reloc_sections.end(); + ++p) + { + unsigned int i = *p; + const unsigned char* pshdr; + pshdr = sd->section_headers->data() + i * This::shdr_size; + typename This::Shdr shdr(pshdr); + + unsigned int data_shndx = shdr.get_sh_info(); + if (data_shndx >= shnum) + { + // We already warned about this above. + continue; + } + + Output_section* data_section = map_sections[data_shndx].output_section; + if (data_section == NULL) + { + map_sections[i].output_section = NULL; + continue; + } + + Relocatable_relocs* rr = new Relocatable_relocs(); + this->set_relocatable_relocs(i, rr); + + Output_section* os = layout->layout_reloc(this, i, shdr, data_section, + rr); + map_sections[i].output_section = os; + map_sections[i].offset = -1; + } + // Handle the .eh_frame sections at the end. for (std::vector<unsigned int>::const_iterator p = eh_frame_sections.begin(); p != eh_frame_sections.end(); diff --git a/gold/object.h b/gold/object.h index 5a62546..dbe0ca1 100644 --- a/gold/object.h +++ b/gold/object.h @@ -41,6 +41,7 @@ class Output_section; class Output_file; class Dynobj; class Object_merge_map; +class Relocatable_relocs; template<typename Stringpool_char> class Stringpool_template; @@ -456,6 +457,7 @@ class Relobj : public Object Relobj(const std::string& name, Input_file* input_file, off_t offset = 0) : Object(name, input_file, false, offset), map_to_output_(), + map_to_relocatable_relocs_(NULL), object_merge_map_(NULL), relocs_must_follow_section_writes_(false) { } @@ -562,6 +564,22 @@ class Relobj : public Object this->object_merge_map_ = object_merge_map; } + // Record the relocatable reloc info for an input reloc section. + void + set_relocatable_relocs(unsigned int reloc_shndx, Relocatable_relocs* rr) + { + gold_assert(reloc_shndx < this->shnum()); + (*this->map_to_relocatable_relocs_)[reloc_shndx] = rr; + } + + // Get the relocatable reloc info for an input reloc section. + Relocatable_relocs* + relocatable_relocs(unsigned int reloc_shndx) + { + gold_assert(reloc_shndx < this->shnum()); + return (*this->map_to_relocatable_relocs_)[reloc_shndx]; + } + 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, @@ -592,10 +610,11 @@ class Relobj : public Object // Count local symbols--implemented by child class. virtual void do_count_local_symbols(Stringpool_template<char>*, - Stringpool_template<char>*) = 0; + Stringpool_template<char>*) = 0; - // Finalize the local symbols. Set the output symbol table indexes for the local variables, and set the - // offset where local symbol information will be stored. + // Finalize the local symbols. Set the output symbol table indexes + // for the local variables, and set the offset where local symbol + // information will be stored. virtual unsigned int do_finalize_local_symbols(unsigned int, off_t) = 0; @@ -622,6 +641,14 @@ class Relobj : public Object map_to_output() const { return this->map_to_output_; } + // Set the size of the relocatable relocs array. + void + size_relocatable_relocs() + { + this->map_to_relocatable_relocs_ = + new std::vector<Relocatable_relocs*>(this->shnum()); + } + // Record that we must wait for the output sections to be written // before applying relocations. void @@ -631,6 +658,9 @@ class Relobj : public Object private: // Mapping from input sections to output section. std::vector<Map_to_output> map_to_output_; + // Mapping from input section index to the information recorded for + // the relocations. This is only used for a relocatable link. + std::vector<Relocatable_relocs*>* map_to_relocatable_relocs_; // Mappings for merge sections. This is managed by the code in the // Merge_map class. Object_merge_map* object_merge_map_; @@ -992,6 +1022,14 @@ class Sized_relobj : public Relobj return this->local_values_[sym].output_dynsym_index(); } + // Return the input section index of local symbol SYM. + unsigned int + local_symbol_input_shndx(unsigned int sym) const + { + gold_assert(sym < this->local_values_.size()); + return this->local_values_[sym].input_shndx(); + } + // Return the appropriate Sized_target structure. Sized_target<size, big_endian>* sized_target() @@ -1218,7 +1256,7 @@ class Sized_relobj : public Relobj // Whether to include a section group in the link. bool - include_section_group(Layout*, unsigned int, + include_section_group(Symbol_table*, Layout*, unsigned int, const char*, const elfcpp::Shdr<size, big_endian>&, std::vector<bool>*); diff --git a/gold/options.cc b/gold/options.cc index b1b428f..a4b4c2f 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -977,6 +977,9 @@ Command_line::get_special_argument(const char* longname, int argc, char** argv, void Command_line::normalize_options() { + if (this->options_.is_shared() && this->options_.is_relocatable()) + gold_fatal(_("-shared and -r are incompatible")); + // If the user specifies both -s and -r, convert the -s as -S. // -r requires us to keep externally visible symbols! if (this->options_.strip_all() && this->options_.is_relocatable()) diff --git a/gold/output.cc b/gold/output.cc index f0f4de2..0e05ff7 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -83,20 +83,33 @@ Output_data::default_alignment_for_size(int size) Output_section_headers::Output_section_headers( const Layout* layout, const Layout::Segment_list* segment_list, + const Layout::Section_list* section_list, const Layout::Section_list* unattached_section_list, const Stringpool* secnamepool) : layout_(layout), segment_list_(segment_list), + section_list_(section_list), unattached_section_list_(unattached_section_list), secnamepool_(secnamepool) { // Count all the sections. Start with 1 for the null section. off_t count = 1; - for (Layout::Segment_list::const_iterator p = segment_list->begin(); - p != segment_list->end(); - ++p) - if ((*p)->type() == elfcpp::PT_LOAD) - count += (*p)->output_section_count(); + if (!parameters->output_is_object()) + { + for (Layout::Segment_list::const_iterator p = segment_list->begin(); + p != segment_list->end(); + ++p) + if ((*p)->type() == elfcpp::PT_LOAD) + count += (*p)->output_section_count(); + } + else + { + for (Layout::Section_list::const_iterator p = section_list->begin(); + p != section_list->end(); + ++p) + if (((*p)->flags() & elfcpp::SHF_ALLOC) != 0) + ++count; + } count += unattached_section_list->size(); const int size = parameters->get_size(); @@ -184,18 +197,48 @@ Output_section_headers::do_sized_write(Output_file* of) v += shdr_size; - unsigned shndx = 1; - for (Layout::Segment_list::const_iterator p = this->segment_list_->begin(); - p != this->segment_list_->end(); - ++p) - v = (*p)->write_section_headers SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( - this->layout_, this->secnamepool_, v, &shndx - SELECT_SIZE_ENDIAN(size, big_endian)); + unsigned int shndx = 1; + if (!parameters->output_is_object()) + { + for (Layout::Segment_list::const_iterator p = + this->segment_list_->begin(); + p != this->segment_list_->end(); + ++p) + v = (*p)->write_section_headers<size, big_endian>(this->layout_, + this->secnamepool_, + v, + &shndx); + } + else + { + for (Layout::Section_list::const_iterator p = + this->section_list_->begin(); + p != this->section_list_->end(); + ++p) + { + // We do unallocated sections below, except that group + // sections have to come first. + if (((*p)->flags() & elfcpp::SHF_ALLOC) == 0 + && (*p)->type() != elfcpp::SHT_GROUP) + continue; + gold_assert(shndx == (*p)->out_shndx()); + elfcpp::Shdr_write<size, big_endian> oshdr(v); + (*p)->write_header(this->layout_, this->secnamepool_, &oshdr); + v += shdr_size; + ++shndx; + } + } + for (Layout::Section_list::const_iterator p = this->unattached_section_list_->begin(); p != this->unattached_section_list_->end(); ++p) { + // For a relocatable link, we did unallocated group sections + // above, since they have to come first. + if ((*p)->type() == elfcpp::SHT_GROUP + && parameters->output_is_object()) + continue; gold_assert(shndx == (*p)->out_shndx()); elfcpp::Shdr_write<size, big_endian> oshdr(v); (*p)->write_header(this->layout_, this->secnamepool_, &oshdr); @@ -422,16 +465,30 @@ Output_file_header::do_sized_write(Output_file* of) oehdr.put_e_entry(this->entry<size>()); - oehdr.put_e_phoff(this->segment_header_->offset()); + if (this->segment_header_ == NULL) + oehdr.put_e_phoff(0); + else + oehdr.put_e_phoff(this->segment_header_->offset()); + oehdr.put_e_shoff(this->section_header_->offset()); // FIXME: The target needs to set the flags. oehdr.put_e_flags(0); oehdr.put_e_ehsize(elfcpp::Elf_sizes<size>::ehdr_size); - oehdr.put_e_phentsize(elfcpp::Elf_sizes<size>::phdr_size); - oehdr.put_e_phnum(this->segment_header_->data_size() - / elfcpp::Elf_sizes<size>::phdr_size); + + if (this->segment_header_ == NULL) + { + oehdr.put_e_phentsize(0); + oehdr.put_e_phnum(0); + } + else + { + oehdr.put_e_phentsize(elfcpp::Elf_sizes<size>::phdr_size); + oehdr.put_e_phnum(this->segment_header_->data_size() + / elfcpp::Elf_sizes<size>::phdr_size); + } + oehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size); oehdr.put_e_shnum(this->section_header_->data_size() / elfcpp::Elf_sizes<size>::shdr_size); @@ -822,6 +879,80 @@ Output_data_reloc_base<sh_type, dynamic, size, big_endian>::do_write( this->relocs_.clear(); } +// Class Output_relocatable_relocs. + +template<int sh_type, int size, bool big_endian> +void +Output_relocatable_relocs<sh_type, size, big_endian>::set_final_data_size() +{ + this->set_data_size(this->rr_->output_reloc_count() + * Reloc_types<sh_type, size, big_endian>::reloc_size); +} + +// class Output_data_group. + +template<int size, bool big_endian> +Output_data_group<size, big_endian>::Output_data_group( + Sized_relobj<size, big_endian>* relobj, + section_size_type entry_count, + const elfcpp::Elf_Word* contents) + : Output_section_data(entry_count * 4, 4), + relobj_(relobj) +{ + this->flags_ = elfcpp::Swap<32, big_endian>::readval(contents); + for (section_size_type i = 1; i < entry_count; ++i) + { + unsigned int shndx = elfcpp::Swap<32, big_endian>::readval(contents + i); + this->input_sections_.push_back(shndx); + } +} + +// Write out the section group, which means translating the section +// indexes to apply to the output file. + +template<int size, bool big_endian> +void +Output_data_group<size, big_endian>::do_write(Output_file* of) +{ + const off_t off = this->offset(); + const section_size_type oview_size = + convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(off, oview_size); + + elfcpp::Elf_Word* contents = reinterpret_cast<elfcpp::Elf_Word*>(oview); + elfcpp::Swap<32, big_endian>::writeval(contents, this->flags_); + ++contents; + + for (std::vector<unsigned int>::const_iterator p = + this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p, ++contents) + { + section_offset_type dummy; + Output_section* os = this->relobj_->output_section(*p, &dummy); + + unsigned int output_shndx; + if (os != NULL) + output_shndx = os->out_shndx(); + else + { + this->relobj_->error(_("section group retained but " + "group element discarded")); + output_shndx = 0; + } + + elfcpp::Swap<32, big_endian>::writeval(contents, output_shndx); + } + + size_t wrote = reinterpret_cast<unsigned char*>(contents) - oview; + gold_assert(wrote == oview_size); + + of->write_output_view(off, oview_size, oview); + + // We no longer need this information. + this->input_sections_.clear(); +} + // Output_data_got::Got_entry methods. // Write out the entry. @@ -1460,6 +1591,7 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, link_section_(NULL), link_(0), info_section_(NULL), + info_symndx_(NULL), info_(0), type_(type), flags_(flags), @@ -1871,7 +2003,12 @@ Output_section::write_header(const Layout* layout, { oshdr->put_sh_name(secnamepool->get_offset(this->name_)); oshdr->put_sh_type(this->type_); - oshdr->put_sh_flags(this->flags_); + + elfcpp::Elf_Xword flags = this->flags_; + if (this->info_section_ != NULL) + flags |= elfcpp::SHF_INFO_LINK; + oshdr->put_sh_flags(flags); + oshdr->put_sh_addr(this->address()); oshdr->put_sh_offset(this->offset()); oshdr->put_sh_size(this->data_size()); @@ -1885,6 +2022,8 @@ Output_section::write_header(const Layout* layout, oshdr->put_sh_link(this->link_); if (this->info_section_ != NULL) oshdr->put_sh_info(this->info_section_->out_shndx()); + else if (this->info_symndx_ != NULL) + oshdr->put_sh_info(this->info_symndx_->symtab_index()); else oshdr->put_sh_info(this->info_); oshdr->put_sh_addralign(this->addralign_); @@ -2885,6 +3024,66 @@ class Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>; #ifdef HAVE_TARGET_32_LITTLE template +class Output_relocatable_relocs<elfcpp::SHT_REL, 32, false>; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Output_relocatable_relocs<elfcpp::SHT_REL, 32, true>; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Output_relocatable_relocs<elfcpp::SHT_REL, 64, false>; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Output_relocatable_relocs<elfcpp::SHT_REL, 64, true>; +#endif + +#ifdef HAVE_TARGET_32_LITTLE +template +class Output_relocatable_relocs<elfcpp::SHT_RELA, 32, false>; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Output_relocatable_relocs<elfcpp::SHT_RELA, 32, true>; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Output_relocatable_relocs<elfcpp::SHT_RELA, 64, false>; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Output_relocatable_relocs<elfcpp::SHT_RELA, 64, true>; +#endif + +#ifdef HAVE_TARGET_32_LITTLE +template +class Output_data_group<32, false>; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Output_data_group<32, true>; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Output_data_group<64, false>; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Output_data_group<64, true>; +#endif + +#ifdef HAVE_TARGET_32_LITTLE +template class Output_data_got<32, false>; #endif diff --git a/gold/output.h b/gold/output.h index 032a2bb..b327eb2 100644 --- a/gold/output.h +++ b/gold/output.h @@ -38,6 +38,7 @@ class Object; class Symbol; class Output_file; class Output_section; +class Relocatable_relocs; class Target; template<int size, bool big_endian> class Sized_target; @@ -372,6 +373,7 @@ class Output_section_headers : public Output_data Output_section_headers(const Layout*, const Layout::Segment_list*, const Layout::Section_list*, + const Layout::Section_list*, const Stringpool*); protected: @@ -392,6 +394,7 @@ class Output_section_headers : public Output_data const Layout* layout_; const Layout::Segment_list* segment_list_; + const Layout::Section_list* section_list_; const Layout::Section_list* unattached_section_list_; const Stringpool* secnamepool_; }; @@ -1184,6 +1187,54 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> addend)); } }; +// Output_relocatable_relocs represents a relocation section in a +// relocatable link. The actual data is written out in the target +// hook relocate_for_relocatable. This just saves space for it. + +template<int sh_type, int size, bool big_endian> +class Output_relocatable_relocs : public Output_section_data +{ + public: + Output_relocatable_relocs(Relocatable_relocs* rr) + : Output_section_data(Output_data::default_alignment_for_size(size)), + rr_(rr) + { } + + void + set_final_data_size(); + + // Write out the data. There is nothing to do here. + void + do_write(Output_file*) + { } + + private: + // The relocs associated with this input section. + Relocatable_relocs* rr_; +}; + +// Handle a GROUP section. + +template<int size, bool big_endian> +class Output_data_group : public Output_section_data +{ + public: + Output_data_group(Sized_relobj<size, big_endian>* relobj, + section_size_type entry_count, + const elfcpp::Elf_Word* contents); + + void + do_write(Output_file*); + + private: + // The input object. + Sized_relobj<size, big_endian>* relobj_; + // The group flag word. + elfcpp::Elf_Word flags_; + // The section indexes of the input sections in this group. + std::vector<unsigned int> input_sections_; +}; + // Output_data_got is used to manage a GOT. Each entry in the GOT is // for one symbol--either a global symbol or a local symbol in an // object. The target specific code adds entries to the GOT as @@ -1626,15 +1677,23 @@ class Output_section : public Output_data void set_info_section(const Output_data* od) { - gold_assert(this->info_ == 0); + gold_assert(this->info_symndx_ == NULL && this->info_ == 0); this->info_section_ = od; } + // Set the info field to the symbol table index of a symbol. + void + set_info_symndx(const Symbol* sym) + { + gold_assert(this->info_section_ == NULL && this->info_ == 0); + this->info_symndx_ = sym; + } + // Set the info field to a constant. void set_info(unsigned int v) { - gold_assert(this->info_section_ == NULL); + gold_assert(this->info_section_ == NULL && this->info_symndx_ == NULL); this->info_ = v; } @@ -2201,7 +2260,11 @@ class Output_section : public Output_data unsigned int link_; // Set the section info field to the index of this section. const Output_data* info_section_; - // If info_section_ is NULL, this is the section info field. + // If info_section_ is NULL, set the info field to the symbol table + // index of this symbol. + const Symbol* info_symndx_; + // If info_section_ and info_symndx_ are NULL, this is the section + // info field. unsigned int info_; // The section type. const elfcpp::Elf_Word type_; diff --git a/gold/reloc-types.h b/gold/reloc-types.h index e8a0689..01f561e 100644 --- a/gold/reloc-types.h +++ b/gold/reloc-types.h @@ -41,14 +41,42 @@ template<int size, bool big_endian> struct Reloc_types<elfcpp::SHT_REL, size, big_endian> { typedef typename elfcpp::Rel<size, big_endian> Reloc; + typedef typename elfcpp::Rel_write<size, big_endian> Reloc_write; static const int reloc_size = elfcpp::Elf_sizes<size>::rel_size; + + static inline typename elfcpp::Elf_types<size>::Elf_Swxword + get_reloc_addend(const Reloc*) + { gold_unreachable(); } + + static inline void + set_reloc_addend(Reloc_write*, + typename elfcpp::Elf_types<size>::Elf_Swxword) + { gold_unreachable(); } + + static inline void + copy_reloc_addend(Reloc_write*, const Reloc*) + { gold_unreachable(); } }; template<int size, bool big_endian> struct Reloc_types<elfcpp::SHT_RELA, size, big_endian> { typedef typename elfcpp::Rela<size, big_endian> Reloc; + typedef typename elfcpp::Rela_write<size, big_endian> Reloc_write; static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; + + static inline typename elfcpp::Elf_types<size>::Elf_Swxword + get_reloc_addend(const Reloc* p) + { return p->get_r_addend(); } + + static inline void + set_reloc_addend(Reloc_write* p, + typename elfcpp::Elf_types<size>::Elf_Swxword val) + { p->put_r_addend(val); } + + static inline void + copy_reloc_addend(Reloc_write* to, const Reloc* from) + { to->put_r_addend(from->get_r_addend()); } }; }; // End namespace gold. diff --git a/gold/reloc.cc b/gold/reloc.cc index d361f16..1920032 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -222,10 +222,14 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd) // We are scanning relocations in order to fill out the GOT and // PLT sections. Relocations for sections which are not // allocated (typically debugging sections) should not add new - // GOT and PLT entries. So we skip them. - typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size); - if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) - continue; + // GOT and PLT entries. So we skip them unless this is a + // relocatable link. + if (!parameters->output_is_object()) + { + typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size); + if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) + continue; + } if (shdr.get_sh_link() != this->symtab_shndx_) { @@ -310,11 +314,29 @@ Sized_relobj<size, big_endian>::do_scan_relocs(const General_options& options, p != rd->relocs.end(); ++p) { - 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); + if (!parameters->output_is_object()) + 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); + else + { + Relocatable_relocs* rr = this->relocatable_relocs(p->reloc_shndx); + gold_assert(rr != NULL); + rr->set_reloc_count(p->reloc_count); + target->scan_relocatable_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, + rr); + } + delete p->contents; p->contents = NULL; } @@ -430,6 +452,29 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs, if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) continue; + if (parameters->output_is_object() + && (shdr.get_sh_type() == elfcpp::SHT_REL + || shdr.get_sh_type() == elfcpp::SHT_RELA) + && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) + { + // This is a reloc section in a relocatable link. We don't + // need to read the input file. The size and file offset + // are stored in the Relocatable_relocs structure. + Relocatable_relocs* rr = this->relocatable_relocs(i); + gold_assert(rr != NULL); + Output_data* posd = rr->output_data(); + gold_assert(posd != NULL); + + pvs->offset = posd->offset(); + pvs->view_size = posd->data_size(); + pvs->view = of->get_output_view(pvs->offset, pvs->view_size); + pvs->address = posd->address(); + pvs->is_input_output_view = false; + pvs->is_postprocessing_view = false; + + continue; + } + // In the normal case, this input section is simply mapped to // the output section at offset OUTPUT_OFFSET. @@ -582,6 +627,8 @@ Sized_relobj<size, big_endian>::relocate_sections( off_t output_offset = map_sections[index].offset; gold_assert((*pviews)[index].view != NULL); + if (parameters->output_is_object()) + gold_assert((*pviews)[i].view != NULL); if (shdr.get_sh_link() != this->symtab_shndx_) { @@ -622,15 +669,32 @@ Sized_relobj<size, big_endian>::relocate_sections( relinfo.reloc_shndx = i; relinfo.data_shndx = index; - target->relocate_section(&relinfo, - sh_type, - prelocs, - reloc_count, - os, - output_offset == -1, - (*pviews)[index].view, - (*pviews)[index].address, - (*pviews)[index].view_size); + if (!parameters->output_is_object()) + target->relocate_section(&relinfo, + sh_type, + prelocs, + reloc_count, + os, + output_offset == -1, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size); + else + { + Relocatable_relocs* rr = this->relocatable_relocs(i); + target->relocate_for_relocatable(&relinfo, + sh_type, + prelocs, + reloc_count, + os, + output_offset, + rr, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size, + (*pviews)[i].view, + (*pviews)[i].view_size); + } } } diff --git a/gold/reloc.h b/gold/reloc.h index 39ffe4a..438e861 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -38,6 +38,7 @@ class Relobj; class Read_relocs_data; class Symbol; class Layout; +class Output_data; class Output_section; template<int size> @@ -170,6 +171,100 @@ class Relocate_task : public Task Task_token* final_blocker_; }; +// During a relocatable link, this class records how relocations +// should be handled for a single input reloc section. An instance of +// this class is created while scanning relocs, and it is used while +// processing relocs. + +class Relocatable_relocs +{ + public: + // We use a vector of unsigned char to indicate how the input relocs + // should be handled. Each element is one of the following values. + // We create this vector when we initially scan the relocations. + enum Reloc_strategy + { + // Copy the input reloc. Don't modify it other than updating the + // r_offset field and the r_sym part of the r_info field. + RELOC_COPY, + // Copy the input reloc which is against an STT_SECTION symbol. + // Update the r_offset and r_sym part of the r_info field. Adjust + // the addend by subtracting the value of the old local symbol and + // adding the value of the new local symbol. The addend is in the + // SHT_RELA reloc and the contents of the data section do not need + // to be changed. + RELOC_ADJUST_FOR_SECTION_RELA, + // Like RELOC_ADJUST_FOR_SECTION_RELA but the contents of the + // section need to be changed. The number indicates the number of + // bytes in the addend in the section contents. + RELOC_ADJUST_FOR_SECTION_1, + RELOC_ADJUST_FOR_SECTION_2, + RELOC_ADJUST_FOR_SECTION_4, + RELOC_ADJUST_FOR_SECTION_8, + // Discard the input reloc--process it completely when relocating + // the data section contents. + RELOC_DISCARD, + // An input reloc which is not discarded, but which requires + // target specific processing in order to update it. + RELOC_SPECIAL + }; + + Relocatable_relocs() + : reloc_strategies_(), output_reloc_count_(0), posd_(NULL) + { } + + // Record the number of relocs. + void + set_reloc_count(size_t reloc_count) + { this->reloc_strategies_.reserve(reloc_count); } + + // Record what to do for the next reloc. + void + set_next_reloc_strategy(Reloc_strategy strategy) + { + this->reloc_strategies_.push_back(static_cast<unsigned char>(strategy)); + if (strategy != RELOC_DISCARD) + ++this->output_reloc_count_; + } + + // Record the Output_data associated with this reloc section. + void + set_output_data(Output_data* posd) + { + gold_assert(this->posd_ == NULL); + this->posd_ = posd; + } + + // Return the Output_data associated with this reloc section. + Output_data* + output_data() const + { return this->posd_; } + + // Return what to do for reloc I. + Reloc_strategy + strategy(unsigned int i) const + { + gold_assert(i < this->reloc_strategies_.size()); + return static_cast<Reloc_strategy>(this->reloc_strategies_[i]); + } + + // Return the number of relocations to create in the output file. + size_t + output_reloc_count() const + { return this->output_reloc_count_; } + + private: + typedef std::vector<unsigned char> Reloc_strategies; + + // The strategies for the input reloc. There is one entry in this + // vector for each relocation in the input section. + Reloc_strategies reloc_strategies_; + // The number of relocations to be created in the output file. + size_t output_reloc_count_; + // The output data structure associated with this relocation. + Output_data* posd_; +}; + // Standard relocation routines which are used on many targets. Here // SIZE and BIG_ENDIAN refer to the target, not the relocation type. diff --git a/gold/target-reloc.h b/gold/target-reloc.h index 7718691..3fd96c3 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -25,16 +25,18 @@ #include "elfcpp.h" #include "symtab.h" +#include "reloc.h" #include "reloc-types.h" namespace gold { -// This function implements the generic part of reloc scanning. This -// is an inline function which takes a class whose member functions -// local() and global() implement the machine specific part of scanning. -// We do it this way to avoidmaking a function call for each relocation, -// and to avoid repeating the generic code for each target. +// This function implements the generic part of reloc scanning. The +// template parameter Scan must be a class type which provides two +// functions: local() and global(). Those functions implement the +// machine specific part of scanning. We do it this way to +// avoidmaking a function call for each relocation, and to avoid +// repeating the generic code for each target. template<int size, bool big_endian, typename Target_type, int sh_type, typename Scan> @@ -116,11 +118,9 @@ scan_relocs( } // This function implements the generic part of relocation processing. -// This is an inline function which take a class whose relocate() -// implements the machine specific part of relocation. We do it this -// way to avoid making a function call for each relocation, and to -// avoid repeating the generic relocation handling code for each -// target. +// The template parameter Relocate must be a class type which provides +// a single function, relocate(), which implements the machine +// specific part of a relocation. // SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of // the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. @@ -225,6 +225,316 @@ relocate_section( } } +// This class may be used as a typical class for the +// Scan_relocatable_reloc parameter to scan_relocatable_relocs. The +// template parameter Classify_reloc must be a class type which +// provides a function get_size_for_reloc which returns the number of +// bytes to which a reloc applies. This class is intended to capture +// the most typical target behaviour, while still permitting targets +// to define their own independent class for Scan_relocatable_reloc. + +template<int sh_type, typename Classify_reloc> +class Default_scan_relocatable_relocs +{ + public: + // Return the strategy to use for a local symbol which is not a + // section symbol, given the relocation type. + inline Relocatable_relocs::Reloc_strategy + local_non_section_strategy(unsigned int, Relobj*) + { return Relocatable_relocs::RELOC_COPY; } + + // Return the strategy to use for a local symbol which is a section + // symbol, given the relocation type. + inline Relocatable_relocs::Reloc_strategy + local_section_strategy(unsigned int r_type, Relobj* object) + { + if (sh_type == elfcpp::SHT_RELA) + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA; + else + { + Classify_reloc classify; + switch (classify.get_size_for_reloc(r_type, object)) + { + case 0: + return Relocatable_relocs::RELOC_COPY; + case 1: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1; + case 2: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2; + case 4: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4; + case 8: + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8; + default: + gold_unreachable(); + } + } + } + + // Return the strategy to use for a global symbol, given the + // relocation type, the object, and the symbol index. + inline Relocatable_relocs::Reloc_strategy + global_strategy(unsigned int, Relobj*, unsigned int) + { return Relocatable_relocs::RELOC_COPY; } +}; + +// Scan relocs during a relocatable link. This is a default +// definition which should work for most targets. +// Scan_relocatable_reloc must name a class type which provides three +// functions which return a Relocatable_relocs::Reloc_strategy code: +// global_strategy, local_non_section_strategy, and +// local_section_strategy. Most targets should be able to use +// Default_scan_relocatable_relocs as this class. + +template<int size, bool big_endian, typename Target_type, int sh_type, + typename Scan_relocatable_reloc> +void +scan_relocatable_relocs( + const General_options&, + Symbol_table*, + Layout*, + Sized_relobj<size, big_endian>* object, + unsigned int data_shndx, + 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_syms, + Relocatable_relocs* rr) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + Scan_relocatable_reloc scan; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + Relocatable_relocs::Reloc_strategy strategy; + + if (needs_special_offset_handling + && !output_section->is_input_address_mapped(object, data_shndx, + reloc.get_r_offset())) + strategy = Relocatable_relocs::RELOC_DISCARD; + else + { + typename elfcpp::Elf_types<size>::Elf_WXword r_info = + reloc.get_r_info(); + const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + const unsigned int r_type = elfcpp::elf_r_type<size>(r_info); + + if (r_sym >= local_symbol_count) + strategy = scan.global_strategy(r_type, object, r_sym); + else + { + gold_assert(plocal_syms != NULL); + typename elfcpp::Sym<size, big_endian> lsym(plocal_syms + + r_sym * sym_size); + const unsigned int shndx = lsym.get_st_shndx(); + if (shndx < elfcpp::SHN_LORESERVE + && shndx != elfcpp::SHN_UNDEF + && !object->is_section_included(lsym.get_st_shndx())) + { + // RELOC is a relocation against a local symbol + // defined in a section we are discarding. Discard + // the reloc. FIXME: Should we issue a warning? + strategy = Relocatable_relocs::RELOC_DISCARD; + } + else if (lsym.get_st_type() != elfcpp::STT_SECTION) + strategy = scan.local_non_section_strategy(r_type, object); + else + { + strategy = scan.local_section_strategy(r_type, object); + if (strategy != Relocatable_relocs::RELOC_DISCARD) + { + section_offset_type dummy; + Output_section* os = object->output_section(shndx, + &dummy); + os->set_needs_symtab_index(); + } + } + } + } + + rr->set_next_reloc_strategy(strategy); + } +} + +// Relocate relocs during a relocatable link. This is a default +// definition which should work for most targets. + +template<int size, bool big_endian, typename Target_type, int sh_type> +void +relocate_for_relocatable( + const Relocate_info<size, big_endian>* relinfo, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs* rr, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr, + section_size_type, + unsigned char* reloc_view, + section_size_type reloc_view_size) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc_write + Reltype_write; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + + Sized_relobj<size, big_endian>* const object = relinfo->object; + const unsigned int local_count = object->local_symbol_count(); + + unsigned char* pwrite = reloc_view; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i); + if (strategy == Relocatable_relocs::RELOC_DISCARD) + continue; + + Reltype reloc(prelocs); + Reltype_write reloc_write(pwrite); + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); + const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + const unsigned int r_type = elfcpp::elf_r_type<size>(r_info); + + // Get the new symbol index. + + unsigned int new_symndx; + if (r_sym < local_count) + { + switch (strategy) + { + case Relocatable_relocs::RELOC_COPY: + new_symndx = object->symtab_index(r_sym); + gold_assert(new_symndx != -1U); + break; + + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA: + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1: + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2: + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4: + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8: + { + // We are adjusting a section symbol. We need to find + // the symbol table index of the section symbol for + // the output section corresponding to input section + // in which this symbol is defined. + gold_assert(r_sym < local_count); + unsigned int shndx = object->local_symbol_input_shndx(r_sym); + section_offset_type dummy; + Output_section* os = object->output_section(shndx, &dummy); + gold_assert(os != NULL); + gold_assert(os->needs_symtab_index()); + new_symndx = os->symtab_index(); + } + break; + + default: + gold_unreachable(); + } + } + else + { + const Symbol* gsym = object->global_symbol(r_sym); + gold_assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = relinfo->symtab->resolve_forwards(gsym); + + gold_assert(gsym->has_symtab_index()); + new_symndx = gsym->symtab_index(); + } + + // Get the new offset--the location in the output section where + // this relocation should be applied. + + off_t offset = reloc.get_r_offset(); + off_t new_offset; + if (offset_in_output_section != -1) + new_offset = offset + offset_in_output_section; + else + { + new_offset = output_section->output_offset(object, + relinfo->data_shndx, + offset); + gold_assert(new_offset != -1); + } + + reloc_write.put_r_offset(new_offset); + reloc_write.put_r_info(elfcpp::elf_r_info<size>(new_symndx, r_type)); + + // Handle the reloc addend based on the strategy. + + if (strategy == Relocatable_relocs::RELOC_COPY) + { + if (sh_type == elfcpp::SHT_RELA) + Reloc_types<sh_type, size, big_endian>:: + copy_reloc_addend(&reloc_write, + &reloc); + } + else + { + // The relocation uses a section symbol in the input file. + // We are adjusting it to use a section symbol in the output + // file. The input section symbol refers to some address in + // the input section. We need the relocation in the output + // file to refer to that same address. This adjustment to + // the addend is the same calculation we use for a simple + // absolute relocation for the input section symbol. + + const Symbol_value<size>* psymval = object->local_symbol(r_sym); + + unsigned char* padd = view + offset; + switch (strategy) + { + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA: + { + typename elfcpp::Elf_types<size>::Elf_Swxword addend; + addend = Reloc_types<sh_type, size, big_endian>:: + get_reloc_addend(&reloc); + addend = psymval->value(object, addend); + Reloc_types<sh_type, size, big_endian>:: + set_reloc_addend(&reloc_write, addend); + } + break; + + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1: + Relocate_functions<size, big_endian>::rel8(padd, object, + psymval); + break; + + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2: + Relocate_functions<size, big_endian>::rel16(padd, object, + psymval); + break; + + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4: + Relocate_functions<size, big_endian>::rel32(padd, object, + psymval); + break; + + case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8: + Relocate_functions<size, big_endian>::rel64(padd, object, + psymval); + break; + + default: + gold_unreachable(); + } + } + + pwrite += reloc_size; + } + + gold_assert(static_cast<section_size_type>(pwrite - reloc_view) + == reloc_view_size); +} + } // End namespace gold. #endif // !defined(GOLD_TARGET_RELOC_H) diff --git a/gold/target.h b/gold/target.h index 218d9f7..c05c821 100644 --- a/gold/target.h +++ b/gold/target.h @@ -42,6 +42,7 @@ class General_options; class Object; template<int size, bool big_endian> class Sized_relobj; +class Relocatable_relocs; template<int size, bool big_endian> class Relocate_info; class Symbol; @@ -281,6 +282,42 @@ class Sized_target : public Target typename elfcpp::Elf_types<size>::Elf_Addr view_address, section_size_type view_size) = 0; + // Scan the relocs during a relocatable link. The parameters are + // like scan_relocs, with an additional Relocatable_relocs + // parameter, used to record the disposition of the relocs. + virtual void + scan_relocatable_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<size, big_endian>* object, + unsigned int data_shndx, + 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, + Relocatable_relocs*) = 0; + + // Relocate a section during a relocatable link. The parameters are + // like relocate_section, with additional parameters for the view of + // the output reloc section. + virtual void + relocate_for_relocatable(const Relocate_info<size, big_endian>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs*, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr + view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) = 0; + protected: Sized_target(const Target::Target_info* pti) : Target(pti) diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index b72e300..b0b149d 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -197,6 +197,15 @@ two_file_separate_shared_21_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. two_file_separate_shared_21_test_LDADD = \ two_file_shared_2.so two_file_shared_1.so +check_PROGRAMS += two_file_relocatable_test +two_file_relocatable_test_SOURCES = two_file_test_main.cc +two_file_relocatable_test_DEPENDENCIES = \ + gcctestdir/ld two_file_relocatable.o +two_file_relocatable_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +two_file_relocatable_test_LDADD = two_file_relocatable.o +two_file_relocatable.o: gcctestdir/ld two_file_test_1.o two_file_test_1b.o two_file_test_2.o + gcctestdir/ld -r -o $@ two_file_test_1.o two_file_test_1b.o two_file_test_2.o + # The nonpic tests will fail on platforms which can not put non-PIC # code into shared libraries, so we just don't run them in that case. if FN_PTRS_IN_SO_WITHOUT_PIC diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 182c89e..fe30a06 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -60,7 +60,8 @@ check_PROGRAMS = object_unittest$(EXEEXT) $(am__EXEEXT_1) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_2_pic_1_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_same_shared_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_12_test \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_21_test +@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_21_test \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_relocatable_test @GCC_FALSE@constructor_test_DEPENDENCIES = libgoldtest.a ../libgold.a \ @GCC_FALSE@ ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \ @GCC_FALSE@ $(am__DEPENDENCIES_1) @@ -233,7 +234,8 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_2_pic_1_test$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_same_shared_test$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_12_test$(EXEEXT) \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_21_test$(EXEEXT) +@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_separate_shared_21_test$(EXEEXT) \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_relocatable_test$(EXEEXT) @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_2 = two_file_shared_1_nonpic_test$(EXEEXT) \ @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_2_nonpic_test$(EXEEXT) \ @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_same_shared_nonpic_test$(EXEEXT) \ @@ -444,6 +446,11 @@ am__two_file_pic_test_SOURCES_DIST = two_file_test_main.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@am_two_file_pic_test_OBJECTS = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_test_main.$(OBJEXT) two_file_pic_test_OBJECTS = $(am_two_file_pic_test_OBJECTS) +am__two_file_relocatable_test_SOURCES_DIST = two_file_test_main.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@am_two_file_relocatable_test_OBJECTS = \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_test_main.$(OBJEXT) +two_file_relocatable_test_OBJECTS = \ + $(am_two_file_relocatable_test_OBJECTS) am__two_file_same_shared_nonpic_test_SOURCES_DIST = \ two_file_test_main.cc @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am_two_file_same_shared_nonpic_test_OBJECTS = two_file_test_main.$(OBJEXT) @@ -574,6 +581,7 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(tls_test_SOURCES) $(two_file_mixed_2_shared_test_SOURCES) \ $(two_file_mixed_shared_test_SOURCES) \ $(two_file_pic_test_SOURCES) \ + $(two_file_relocatable_test_SOURCES) \ $(two_file_same_shared_nonpic_test_SOURCES) \ $(two_file_same_shared_test_SOURCES) \ $(two_file_separate_shared_12_nonpic_test_SOURCES) \ @@ -613,6 +621,7 @@ DIST_SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(am__two_file_mixed_2_shared_test_SOURCES_DIST) \ $(am__two_file_mixed_shared_test_SOURCES_DIST) \ $(am__two_file_pic_test_SOURCES_DIST) \ + $(am__two_file_relocatable_test_SOURCES_DIST) \ $(am__two_file_same_shared_nonpic_test_SOURCES_DIST) \ $(am__two_file_same_shared_test_SOURCES_DIST) \ $(am__two_file_separate_shared_12_nonpic_test_SOURCES_DIST) \ @@ -866,6 +875,12 @@ object_unittest_SOURCES = object_unittest.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_separate_shared_21_test_LDADD = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_2.so two_file_shared_1.so +@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_relocatable_test_SOURCES = two_file_test_main.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_relocatable_test_DEPENDENCIES = \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ gcctestdir/ld two_file_relocatable.o + +@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_relocatable_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_relocatable_test_LDADD = two_file_relocatable.o @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1_nonpic_test_SOURCES = \ @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_test_2.cc two_file_test_main.cc @@ -1152,6 +1167,9 @@ two_file_mixed_shared_test$(EXEEXT): $(two_file_mixed_shared_test_OBJECTS) $(two two_file_pic_test$(EXEEXT): $(two_file_pic_test_OBJECTS) $(two_file_pic_test_DEPENDENCIES) @rm -f two_file_pic_test$(EXEEXT) $(CXXLINK) $(two_file_pic_test_LDFLAGS) $(two_file_pic_test_OBJECTS) $(two_file_pic_test_LDADD) $(LIBS) +two_file_relocatable_test$(EXEEXT): $(two_file_relocatable_test_OBJECTS) $(two_file_relocatable_test_DEPENDENCIES) + @rm -f two_file_relocatable_test$(EXEEXT) + $(CXXLINK) $(two_file_relocatable_test_LDFLAGS) $(two_file_relocatable_test_OBJECTS) $(two_file_relocatable_test_LDADD) $(LIBS) two_file_same_shared_nonpic_test$(EXEEXT): $(two_file_same_shared_nonpic_test_OBJECTS) $(two_file_same_shared_nonpic_test_DEPENDENCIES) @rm -f two_file_same_shared_nonpic_test$(EXEEXT) $(CXXLINK) $(two_file_same_shared_nonpic_test_LDFLAGS) $(two_file_same_shared_nonpic_test_OBJECTS) $(two_file_same_shared_nonpic_test_LDADD) $(LIBS) @@ -1557,6 +1575,8 @@ uninstall-am: uninstall-info-am @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_2_pic.o @GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared.so: two_file_test_1_pic.o two_file_test_1b_pic.o two_file_test_2_pic.o gcctestdir/ld @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1_pic.o two_file_test_1b_pic.o two_file_test_2_pic.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_relocatable.o: gcctestdir/ld two_file_test_1.o two_file_test_1b.o two_file_test_2.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@ gcctestdir/ld -r -o $@ two_file_test_1.o two_file_test_1b.o two_file_test_2.o @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1_nonpic.so: two_file_test_1.o gcctestdir/ld @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1.o two_file_test_1b.o @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_2_nonpic.so: two_file_test_2.o gcctestdir/ld diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc index 75029ad..3c98937 100644 --- a/gold/testsuite/testfile.cc +++ b/gold/testsuite/testfile.cc @@ -57,6 +57,24 @@ class Target_test : public Sized_target<size, big_endian> section_size_type) { ERROR("call to Target_test::relocate_section"); } + void + scan_relocatable_relocs(const General_options&, Symbol_table*, Layout*, + Sized_relobj<size, big_endian>*, unsigned int, + unsigned int, const unsigned char*, + size_t, Output_section*, bool, size_t, + const unsigned char*, Relocatable_relocs*) + { ERROR("call to Target_test::scan_relocatable_relocs"); } + + void + relocate_for_relocatable(const Relocate_info<size, big_endian>*, + unsigned int, const unsigned char*, size_t, + Output_section*, off_t, const Relocatable_relocs*, + unsigned char*, + typename elfcpp::Elf_types<size>::Elf_Addr, + section_size_type, unsigned char*, + section_size_type) + { ERROR("call to Target_test::relocate_for_relocatable"); } + static const Target::Target_info test_target_info; }; diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 14d2957..51a8d84 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -109,6 +109,37 @@ class Target_x86_64 : public Sized_target<64, false> elfcpp::Elf_types<64>::Elf_Addr view_address, section_size_type view_size); + // Scan the relocs during a relocatable link. + void + scan_relocatable_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + 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, + Relocatable_relocs*); + + // Relocate a section during a relocatable link. + void + relocate_for_relocatable(const Relocate_info<64, false>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs*, + unsigned char* view, + elfcpp::Elf_types<64>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size); + // Return a string used to fill a code section with nops. std::string do_code_fill(section_size_type length); @@ -234,6 +265,15 @@ class Target_x86_64 : public Sized_target<64, false> bool skip_call_tls_get_addr_; }; + // A class which returns the size required for a relocation type, + // used while scanning relocs during a relocatable link. + class Relocatable_size_for_reloc + { + public: + unsigned int + get_size_for_reloc(unsigned int, Relobj*); + }; + // Adjust TLS relocation type based on the options and whether this // is a local symbol. static tls::Tls_optimization @@ -1941,6 +1981,146 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, view_size); } +// Return the size of a relocation while scanning during a relocatable +// link. + +unsigned int +Target_x86_64::Relocatable_size_for_reloc::get_size_for_reloc( + unsigned int r_type, + Relobj* object) +{ + switch (r_type) + { + case elfcpp::R_X86_64_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + case elfcpp::R_X86_64_TLSGD: // Global-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url) + case elfcpp::R_X86_64_TLSDESC_CALL: + case elfcpp::R_X86_64_TLSLD: // Local-dynamic + case elfcpp::R_X86_64_DTPOFF32: + case elfcpp::R_X86_64_DTPOFF64: + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + case elfcpp::R_X86_64_TPOFF32: // Local-exec + return 0; + + case elfcpp::R_X86_64_64: + case elfcpp::R_X86_64_PC64: + case elfcpp::R_X86_64_GOTOFF64: + case elfcpp::R_X86_64_GOTPC64: + case elfcpp::R_X86_64_PLTOFF64: + case elfcpp::R_X86_64_GOT64: + case elfcpp::R_X86_64_GOTPCREL64: + case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPLT64: + return 8; + + case elfcpp::R_X86_64_32: + case elfcpp::R_X86_64_32S: + case elfcpp::R_X86_64_PC32: + case elfcpp::R_X86_64_PLT32: + case elfcpp::R_X86_64_GOTPC32: + case elfcpp::R_X86_64_GOT32: + return 4; + + case elfcpp::R_X86_64_16: + case elfcpp::R_X86_64_PC16: + return 2; + + case elfcpp::R_X86_64_8: + case elfcpp::R_X86_64_PC8: + return 1; + + case elfcpp::R_X86_64_COPY: + case elfcpp::R_X86_64_GLOB_DAT: + case elfcpp::R_X86_64_JUMP_SLOT: + case elfcpp::R_X86_64_RELATIVE: + // These are outstanding tls relocs, which are unexpected when linking + case elfcpp::R_X86_64_TPOFF64: + case elfcpp::R_X86_64_DTPMOD64: + case elfcpp::R_X86_64_TLSDESC: + object->error(_("unexpected reloc %u in object file"), r_type); + return 0; + + case elfcpp::R_X86_64_SIZE32: + case elfcpp::R_X86_64_SIZE64: + default: + object->error(_("unsupported reloc %u against local symbol"), r_type); + return 0; + } +} + +// Scan the relocs during a relocatable link. + +void +Target_x86_64::scan_relocatable_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + 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, + Relocatable_relocs* rr) +{ + gold_assert(sh_type == elfcpp::SHT_RELA); + + typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_RELA, + Relocatable_size_for_reloc> Scan_relocatable_relocs; + + gold::scan_relocatable_relocs<64, false, Target_x86_64, elfcpp::SHT_RELA, + Scan_relocatable_relocs>( + options, + symtab, + layout, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_symbols, + rr); +} + +// Relocate a section during a relocatable link. + +void +Target_x86_64::relocate_for_relocatable( + const Relocate_info<64, false>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + off_t offset_in_output_section, + const Relocatable_relocs* rr, + unsigned char* view, + elfcpp::Elf_types<64>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) +{ + gold_assert(sh_type == elfcpp::SHT_RELA); + + gold::relocate_for_relocatable<64, false, Target_x86_64, elfcpp::SHT_RELA>( + relinfo, + prelocs, + reloc_count, + output_section, + offset_in_output_section, + rr, + view, + view_address, + view_size, + reloc_view, + reloc_view_size); +} + // Return the value to use for a dynamic which requires special // treatment. This is how we support equality comparisons of function // pointers across shared library boundaries, as described in the |