X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gold%2Freloc.cc;h=481617df0b50f06f7bceca9df0e54fb616667d01;hb=ede76260a7201121e1dbed3a14e280bd3aa8c459;hp=ab74498d244f2d6e3198cecb61c1fc70ad8c9e80;hpb=4f4c5f80c7482fbd1c2fd5a3b96547f5099bf3fd;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/reloc.cc b/gold/reloc.cc index ab74498d24..481617df0b 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -1,6 +1,6 @@ // reloc.cc -- relocate input files for gold. -// Copyright 2006, 2007 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -22,10 +22,14 @@ #include "gold.h" +#include + #include "workqueue.h" -#include "object.h" #include "symtab.h" #include "output.h" +#include "merge.h" +#include "object.h" +#include "target-reloc.h" #include "reloc.h" namespace gold @@ -37,18 +41,18 @@ namespace gold // After reading it, the start another task to process the // information. These tasks requires access to the file. -Task::Is_runnable_type -Read_relocs::is_runnable(Workqueue*) +Task_token* +Read_relocs::is_runnable() { - return this->object_->is_locked() ? IS_LOCKED : IS_RUNNABLE; + return this->object_->is_locked() ? this->object_->token() : NULL; } // Lock the file. -Task_locker* -Read_relocs::locks(Workqueue*) +void +Read_relocs::locks(Task_locker* tl) { - return new Task_locker_obj(*this->object_); + tl->add(this, this->object_->token()); } // Read the relocations and then start a Scan_relocs_task. @@ -58,9 +62,19 @@ Read_relocs::run(Workqueue* workqueue) { Read_relocs_data *rd = new Read_relocs_data; this->object_->read_relocs(rd); - workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, - this->layout_, this->object_, rd, - this->symtab_lock_, this->blocker_)); + this->object_->release(); + + workqueue->queue_next(new Scan_relocs(this->options_, this->symtab_, + this->layout_, this->object_, rd, + this->symtab_lock_, this->blocker_)); +} + +// Return a debugging name for the task. + +std::string +Read_relocs::get_name() const +{ + return "Read_relocs " + this->object_->name(); } // Scan_relocs methods. @@ -70,37 +84,25 @@ Read_relocs::run(Workqueue* workqueue) // use a lock on the symbol table to keep them from interfering with // each other. -Task::Is_runnable_type -Scan_relocs::is_runnable(Workqueue*) +Task_token* +Scan_relocs::is_runnable() { - if (!this->symtab_lock_->is_writable() || this->object_->is_locked()) - return IS_LOCKED; - return IS_RUNNABLE; + if (!this->symtab_lock_->is_writable()) + return this->symtab_lock_; + if (this->object_->is_locked()) + return this->object_->token(); + return NULL; } // Return the locks we hold: one on the file, one on the symbol table // and one blocker. -class Scan_relocs::Scan_relocs_locker : public Task_locker -{ - public: - Scan_relocs_locker(Object* object, Task_token& symtab_lock, Task* task, - Task_token& blocker, Workqueue* workqueue) - : objlock_(*object), symtab_locker_(symtab_lock, task), - blocker_(blocker, workqueue) - { } - - private: - Task_locker_obj objlock_; - Task_locker_write symtab_locker_; - Task_locker_block blocker_; -}; - -Task_locker* -Scan_relocs::locks(Workqueue* workqueue) +void +Scan_relocs::locks(Task_locker* tl) { - return new Scan_relocs_locker(this->object_, *this->symtab_lock_, this, - *this->blocker_, workqueue); + tl->add(this, this->object_->token()); + tl->add(this, this->symtab_lock_); + tl->add(this, this->blocker_); } // Scan the relocs. @@ -110,51 +112,47 @@ Scan_relocs::run(Workqueue*) { this->object_->scan_relocs(this->options_, this->symtab_, this->layout_, this->rd_); + this->object_->release(); delete this->rd_; this->rd_ = NULL; } +// Return a debugging name for the task. + +std::string +Scan_relocs::get_name() const +{ + return "Scan_relocs " + this->object_->name(); +} + // Relocate_task methods. // We may have to wait for the output sections to be written. -Task::Is_runnable_type -Relocate_task::is_runnable(Workqueue*) +Task_token* +Relocate_task::is_runnable() { if (this->object_->relocs_must_follow_section_writes() && this->output_sections_blocker_->is_blocked()) - return IS_BLOCKED; + return this->output_sections_blocker_; - return IS_RUNNABLE; + if (this->object_->is_locked()) + return this->object_->token(); + + return NULL; } // We want to lock the file while we run. We want to unblock // INPUT_SECTIONS_BLOCKER and FINAL_BLOCKER when we are done. +// INPUT_SECTIONS_BLOCKER may be NULL. -class Relocate_task::Relocate_locker : public Task_locker -{ - public: - Relocate_locker(Task_token& input_sections_blocker, - Task_token& final_blocker, Workqueue* workqueue, - Object* object) - : input_sections_blocker_(input_sections_blocker, workqueue), - final_blocker_(final_blocker, workqueue), - objlock_(*object) - { } - - private: - Task_block_token input_sections_blocker_; - Task_block_token final_blocker_; - Task_locker_obj objlock_; -}; - -Task_locker* -Relocate_task::locks(Workqueue* workqueue) +void +Relocate_task::locks(Task_locker* tl) { - return new Relocate_locker(*this->input_sections_blocker_, - *this->final_blocker_, - workqueue, - this->object_); + if (this->input_sections_blocker_ != NULL) + tl->add(this, this->input_sections_blocker_); + tl->add(this, this->final_blocker_); + tl->add(this, this->object_->token()); } // Run the task. @@ -164,6 +162,20 @@ Relocate_task::run(Workqueue*) { this->object_->relocate(this->options_, this->symtab_, this->layout_, this->of_); + + // This is normally the last thing we will do with an object, so + // uncache all views. + this->object_->clear_view_cache_marks(); + + this->object_->release(); +} + +// Return a debugging name for the task. + +std::string +Relocate_task::get_name() const +{ + return "Relocate_task " + this->object_->name(); } // Read the relocs and local symbols from the object file and store @@ -181,11 +193,12 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) rd->relocs.reserve(shnum / 2); - std::vector& map_sections(this->map_to_output()); + const Output_sections& out_sections(this->output_sections()); + const std::vector
& out_offsets(this->section_offsets_); const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(), shnum * This::shdr_size, - true); + true, true); // Skip the first, dummy, section. const unsigned char *ps = pshdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, ps += This::shdr_size) @@ -196,7 +209,7 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) continue; - unsigned int shndx = shdr.get_sh_info(); + unsigned int shndx = this->adjust_shndx(shdr.get_sh_info()); if (shndx >= shnum) { this->error(_("relocation section %u has bad info %u"), @@ -204,23 +217,28 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) continue; } - Output_section* os = map_sections[shndx].output_section; + Output_section* os = out_sections[shndx]; if (os == NULL) continue; // 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. + // GOT and PLT entries. So we skip them unless this is a + // relocatable link or we need to emit relocations. typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size); - if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) + bool is_section_allocated = ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) + != 0); + if (!is_section_allocated + && !parameters->options().relocatable() + && !parameters->options().emit_relocs()) continue; - if (shdr.get_sh_link() != this->symtab_shndx_) + if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_) { this->error(_("relocation section %u uses unexpected " "symbol table %u"), - i, shdr.get_sh_link()); + i, this->adjust_shndx(shdr.get_sh_link())); continue; } @@ -252,11 +270,12 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) sr.reloc_shndx = i; sr.data_shndx = shndx; sr.contents = this->get_lasting_view(shdr.get_sh_offset(), sh_size, - true); + true, 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; + sr.needs_special_offset_handling = out_offsets[shndx] == -1U; + sr.is_data_section_allocated = is_section_allocated; } // Read the local symbols. @@ -273,7 +292,7 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) gold_assert(loccount == symtabshdr.get_sh_info()); off_t locsize = loccount * sym_size; rd->local_symbols = this->get_lasting_view(symtabshdr.get_sh_offset(), - locsize, true); + locsize, true, true); } } @@ -299,11 +318,37 @@ Sized_relobj::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->options().relocatable()) + { + // As noted above, when not generating an object file, we + // only scan allocated sections. We may see a non-allocated + // section here if we are emitting relocs. + if (p->is_data_section_allocated) + 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->options().emit_relocs()) + this->emit_relocs_scan(options, symtab, layout, local_symbols, p); + } + 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; } @@ -315,6 +360,98 @@ Sized_relobj::do_scan_relocs(const General_options& options, } } +// This is a strategy class we use when scanning for --emit-relocs. + +template +class Emit_relocs_strategy +{ + public: + // A local non-section symbol. + inline Relocatable_relocs::Reloc_strategy + local_non_section_strategy(unsigned int, Relobj*) + { return Relocatable_relocs::RELOC_COPY; } + + // A local section symbol. + inline Relocatable_relocs::Reloc_strategy + local_section_strategy(unsigned int, Relobj*) + { + if (sh_type == elfcpp::SHT_RELA) + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA; + else + { + // The addend is stored in the section contents. Since this + // is not a relocatable link, we are going to apply the + // relocation contents to the section as usual. This means + // that we have no way to record the original addend. If the + // original addend is not zero, there is basically no way for + // the user to handle this correctly. Caveat emptor. + return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0; + } + } + + // A global symbol. + inline Relocatable_relocs::Reloc_strategy + global_strategy(unsigned int, Relobj*, unsigned int) + { return Relocatable_relocs::RELOC_COPY; } +}; + +// Scan the input relocations for --emit-relocs. + +template +void +Sized_relobj::emit_relocs_scan( + const General_options& options, + Symbol_table* symtab, + Layout* layout, + const unsigned char* plocal_syms, + const Read_relocs_data::Relocs_list::iterator& p) +{ + Relocatable_relocs* rr = this->relocatable_relocs(p->reloc_shndx); + gold_assert(rr != NULL); + rr->set_reloc_count(p->reloc_count); + + if (p->sh_type == elfcpp::SHT_REL) + this->emit_relocs_scan_reltype(options, symtab, layout, + plocal_syms, p, rr); + else + { + gold_assert(p->sh_type == elfcpp::SHT_RELA); + this->emit_relocs_scan_reltype(options, symtab, + layout, plocal_syms, p, + rr); + } +} + +// Scan the input relocation for --emit-relocs, templatized on the +// type of the relocation section. + +template +template +void +Sized_relobj::emit_relocs_scan_reltype( + const General_options& options, + Symbol_table* symtab, + Layout* layout, + const unsigned char* plocal_syms, + const Read_relocs_data::Relocs_list::iterator& p, + Relocatable_relocs* rr) +{ + scan_relocatable_relocs >( + options, + symtab, + layout, + this, + p->data_shndx, + p->contents->data(), + p->reloc_count, + p->output_section, + p->needs_special_offset_handling, + this->local_symbol_count_, + plocal_syms, + rr); +} + // Relocate the input sections and write out the local symbols. template @@ -329,7 +466,7 @@ Sized_relobj::do_relocate(const General_options& options, // Read the section headers. const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(), shnum * This::shdr_size, - true); + true, true); Views views; views.resize(shnum); @@ -340,28 +477,53 @@ Sized_relobj::do_relocate(const General_options& options, this->write_sections(pshdrs, of, &views); + // To speed up relocations, we set up hash tables for fast lookup of + // input offsets to output addresses. + this->initialize_input_to_output_maps(); + // Apply relocations. this->relocate_sections(options, symtab, layout, pshdrs, &views); + // After we've done the relocations, we release the hash tables, + // since we no longer need them. + this->free_input_to_output_maps(); + // Write out the accumulated views. for (unsigned int i = 1; i < shnum; ++i) { if (views[i].view != NULL) { - 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); + if (!views[i].is_postprocessing_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. - this->write_local_symbols(of, layout->sympool()); + this->write_local_symbols(of, layout->sympool(), layout->dynpool(), + layout->symtab_xindex(), layout->dynsym_xindex()); + + // We should no longer need the local symbol values. + this->clear_local_symbols(); } +// Sort a Read_multiple vector by file offset. +struct Read_multiple_compare +{ + inline bool + operator()(const File_read::Read_multiple_entry& rme1, + const File_read::Read_multiple_entry& rme2) const + { return rme1.file_offset < rme2.file_offset; } +}; + // Write section data to the output file. PSHDRS points to the // section headers. Record the views in *PVIEWS for use when // relocating. @@ -373,7 +535,11 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, Views* pviews) { unsigned int shnum = this->shnum(); - std::vector& map_sections(this->map_to_output()); + const Output_sections& out_sections(this->output_sections()); + const std::vector
& out_offsets(this->section_offsets_); + + File_read::Read_multiple rm; + bool is_sorted = true; const unsigned char* p = pshdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) @@ -382,52 +548,139 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, pvs->view = NULL; - const Output_section* os = map_sections[i].output_section; + const Output_section* os = out_sections[i]; if (os == NULL) continue; - off_t output_offset = map_sections[i].offset; + Address output_offset = out_offsets[i]; typename This::Shdr shdr(p); if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) continue; + if ((parameters->options().relocatable() + || parameters->options().emit_relocs()) + && (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 or when + // emitting relocs. 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. + + // However, if OUTPUT_OFFSET == INVALID_ADDRESS, then input data is + // handled specially--e.g., a .eh_frame section. The relocation + // routines need to check for each reloc where it should be + // applied. For this case, we need an input/output view for the + // entire contents of the section in the output file. We don't + // want to copy the contents of the input section to the output + // section; the output section contents were already written, + // and we waited for them in Relocate_task::is_runnable because + // relocs_must_follow_section_writes is set for the object. + + // Regardless of which of the above cases is true, we have to + // check requires_postprocessing of the output section. If that + // is false, then we work with views of the output file + // directly. If it is true, then we work with a separate + // buffer, and the output section is responsible for writing the + // final data to the output file. + + off_t output_section_offset; + Address output_section_size; + if (!os->requires_postprocessing()) + { + output_section_offset = os->offset(); + output_section_size = convert_types(os->data_size()); + } + else + { + output_section_offset = 0; + output_section_size = + convert_types(os->postprocessing_buffer_size()); + } + off_t view_start; - off_t view_size; - if (output_offset != -1) + section_size_type view_size; + if (output_offset != invalid_address) { - view_start = os->offset() + output_offset; - view_size = shdr.get_sh_size(); + view_start = output_section_offset + output_offset; + view_size = convert_to_section_size_type(shdr.get_sh_size()); } else { - view_start = os->offset(); - view_size = os->data_size(); + view_start = output_section_offset; + view_size = convert_to_section_size_type(output_section_size); } if (view_size == 0) continue; - gold_assert(output_offset == -1 - || (output_offset >= 0 - && output_offset + view_size <= os->data_size())); + gold_assert(output_offset == invalid_address + || output_offset + view_size <= output_section_size); unsigned char* view; - if (output_offset == -1) - view = of->get_input_output_view(view_start, view_size); + if (os->requires_postprocessing()) + { + unsigned char* buffer = os->postprocessing_buffer(); + view = buffer + view_start; + if (output_offset != invalid_address) + { + off_t sh_offset = shdr.get_sh_offset(); + if (!rm.empty() && rm.back().file_offset > sh_offset) + is_sorted = false; + rm.push_back(File_read::Read_multiple_entry(sh_offset, + view_size, view)); + } + } else { - view = of->get_output_view(view_start, view_size); - this->read(shdr.get_sh_offset(), view_size, view); + if (output_offset == invalid_address) + view = of->get_input_output_view(view_start, view_size); + else + { + view = of->get_output_view(view_start, view_size); + off_t sh_offset = shdr.get_sh_offset(); + if (!rm.empty() && rm.back().file_offset > sh_offset) + is_sorted = false; + rm.push_back(File_read::Read_multiple_entry(sh_offset, + view_size, view)); + } } pvs->view = view; pvs->address = os->address(); - if (output_offset != -1) + if (output_offset != invalid_address) pvs->address += output_offset; pvs->offset = view_start; pvs->view_size = view_size; - pvs->is_input_output_view = output_offset == -1; + pvs->is_input_output_view = output_offset == invalid_address; + pvs->is_postprocessing_view = os->requires_postprocessing(); + } + + // Actually read the data. + if (!rm.empty()) + { + if (!is_sorted) + std::sort(rm.begin(), rm.end(), Read_multiple_compare()); + this->read_multiple(rm); } } @@ -446,7 +699,8 @@ Sized_relobj::relocate_sections( unsigned int shnum = this->shnum(); Sized_target* target = this->sized_target(); - std::vector& map_sections(this->map_to_output()); + const Output_sections& out_sections(this->output_sections()); + const std::vector
& out_offsets(this->section_offsets_); Relocate_info relinfo; relinfo.options = &options; @@ -463,7 +717,7 @@ Sized_relobj::relocate_sections( if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) continue; - unsigned int index = shdr.get_sh_info(); + unsigned int index = this->adjust_shndx(shdr.get_sh_info()); if (index >= this->shnum()) { this->error(_("relocation section %u has bad info %u"), @@ -471,28 +725,30 @@ Sized_relobj::relocate_sections( continue; } - Output_section* os = map_sections[index].output_section; + Output_section* os = out_sections[index]; if (os == NULL) { // This relocation section is against a section which we // discarded. continue; } - off_t output_offset = map_sections[index].offset; + Address output_offset = out_offsets[index]; gold_assert((*pviews)[index].view != NULL); + if (parameters->options().relocatable()) + gold_assert((*pviews)[i].view != NULL); - if (shdr.get_sh_link() != this->symtab_shndx_) + if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_) { gold_error(_("relocation section %u uses unexpected " "symbol table %u"), - i, shdr.get_sh_link()); + i, this->adjust_shndx(shdr.get_sh_link())); continue; } off_t sh_size = shdr.get_sh_size(); const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(), - sh_size, false); + sh_size, true, false); unsigned int reloc_size; if (sh_type == elfcpp::SHT_REL) @@ -516,156 +772,187 @@ Sized_relobj::relocate_sections( continue; } + gold_assert(output_offset != invalid_address + || this->relocs_must_follow_section_writes()); + 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->options().relocatable()) + { + target->relocate_section(&relinfo, + sh_type, + prelocs, + reloc_count, + os, + output_offset == invalid_address, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size); + if (parameters->options().emit_relocs()) + this->emit_relocs(&relinfo, i, sh_type, prelocs, reloc_count, + os, output_offset, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size, + (*pviews)[i].view, + (*pviews)[i].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); + } } } -// Copy_relocs::Copy_reloc_entry methods. - -// Return whether we should emit this reloc. We should emit it if the -// symbol is still defined in a dynamic object. If we should not emit -// it, we clear it, to save ourselves the test next time. - -template -bool -Copy_relocs::Copy_reloc_entry::should_emit() -{ - if (this->sym_ == NULL) - return false; - if (this->sym_->is_from_dynobj()) - return true; - this->sym_ = NULL; - return false; -} - -// Emit a reloc into a SHT_REL section. +// Emit the relocs for --emit-relocs. template void -Copy_relocs::Copy_reloc_entry::emit( - Output_data_reloc* reloc_data) +Sized_relobj::emit_relocs( + const Relocate_info* relinfo, + unsigned int i, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + typename elfcpp::Elf_types::Elf_Addr offset_in_output_section, + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) { - this->sym_->set_needs_dynsym_entry(); - reloc_data->add_global(this->sym_, this->reloc_type_, this->output_section_, - this->relobj_, this->shndx_, this->address_); + if (sh_type == elfcpp::SHT_REL) + this->emit_relocs_reltype(relinfo, i, prelocs, + reloc_count, output_section, + offset_in_output_section, + view, address, view_size, + reloc_view, reloc_view_size); + else + { + gold_assert(sh_type == elfcpp::SHT_RELA); + this->emit_relocs_reltype(relinfo, i, prelocs, + reloc_count, output_section, + offset_in_output_section, + view, address, view_size, + reloc_view, reloc_view_size); + } } -// Emit a reloc into a SHT_RELA section. +// Emit the relocs for --emit-relocs, templatized on the type of the +// relocation section. template +template void -Copy_relocs::Copy_reloc_entry::emit( - Output_data_reloc* reloc_data) -{ - this->sym_->set_needs_dynsym_entry(); - reloc_data->add_global(this->sym_, this->reloc_type_, this->output_section_, - this->relobj_, this->shndx_, this->address_, - this->addend_); -} - -// Copy_relocs methods. - -// Return whether we need a COPY reloc for a relocation against GSYM. -// The relocation is being applied to section SHNDX in OBJECT. - -template -bool -Copy_relocs::need_copy_reloc( - const General_options*, - Relobj* object, - unsigned int shndx, - Sized_symbol* sym) +Sized_relobj::emit_relocs_reltype( + const Relocate_info* relinfo, + unsigned int i, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + typename elfcpp::Elf_types::Elf_Addr offset_in_output_section, + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size) { - // FIXME: Handle -z nocopyrelocs. - - if (sym->symsize() == 0) - return false; - - // If this is a readonly section, then we need a COPY reloc. - // Otherwise we can use a dynamic reloc. - if ((object->section_flags(shndx) & elfcpp::SHF_WRITE) == 0) - return true; - - return false; + const Relocatable_relocs* rr = this->relocatable_relocs(i); + relocate_for_relocatable( + relinfo, + prelocs, + reloc_count, + output_section, + offset_in_output_section, + rr, + view, + address, + view_size, + reloc_view, + reloc_view_size); } -// Save a Rel reloc. +// Create merge hash tables for the local symbols. These are used to +// speed up relocations. template void -Copy_relocs::save( - Symbol* sym, - Relobj* relobj, - unsigned int shndx, - Output_section* output_section, - const elfcpp::Rel& rel) +Sized_relobj::initialize_input_to_output_maps() { - unsigned int reloc_type = elfcpp::elf_r_type(rel.get_r_info()); - this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, relobj, shndx, - output_section, - rel.get_r_offset(), 0)); + const unsigned int loccount = this->local_symbol_count_; + for (unsigned int i = 1; i < loccount; ++i) + { + Symbol_value& lv(this->local_values_[i]); + lv.initialize_input_to_output_map(this); + } } -// Save a Rela reloc. +// Free merge hash tables for the local symbols. template void -Copy_relocs::save( - Symbol* sym, - Relobj* relobj, - unsigned int shndx, - Output_section* output_section, - const elfcpp::Rela& rela) +Sized_relobj::free_input_to_output_maps() { - unsigned int reloc_type = elfcpp::elf_r_type(rela.get_r_info()); - this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, relobj, shndx, - output_section, - rela.get_r_offset(), - rela.get_r_addend())); + const unsigned int loccount = this->local_symbol_count_; + for (unsigned int i = 1; i < loccount; ++i) + { + Symbol_value& lv(this->local_values_[i]); + lv.free_input_to_output_map(); + } } -// Return whether there are any relocs to emit. We don't want to emit -// a reloc if the symbol is no longer defined in a dynamic object. +// Class Merged_symbol_value. -template -bool -Copy_relocs::any_to_emit() +template +void +Merged_symbol_value::initialize_input_to_output_map( + const Relobj* object, + unsigned int input_shndx) { - for (typename Copy_reloc_entries::iterator p = this->entries_.begin(); - p != this->entries_.end(); - ++p) - { - if (p->should_emit()) - return true; - } - return false; + Object_merge_map* map = object->merge_map(); + map->initialize_input_to_output_map(input_shndx, + this->output_start_address_, + &this->output_addresses_); } -// Emit relocs. +// Get the output value corresponding to an input offset if we +// couldn't find it in the hash table. -template -template -void -Copy_relocs::emit( - Output_data_reloc* reloc_data) +template +typename elfcpp::Elf_types::Elf_Addr +Merged_symbol_value::value_from_output_section( + const Relobj* object, + unsigned int input_shndx, + typename elfcpp::Elf_types::Elf_Addr input_offset) const { - for (typename Copy_reloc_entries::iterator p = this->entries_.begin(); - p != this->entries_.end(); - ++p) - { - if (p->should_emit()) - p->emit(reloc_data); - } + section_offset_type output_offset; + bool found = object->merge_map()->get_output_offset(NULL, input_shndx, + input_offset, + &output_offset); + + // If this assertion fails, it means that some relocation was + // against a portion of an input merge section which we didn't map + // to the output file and we didn't explicitly discard. We should + // always map all portions of input merge sections. + gold_assert(found); + + if (output_offset == -1) + return 0; + else + return this->output_start_address_ + output_offset; } // Track_relocs methods. @@ -764,8 +1051,7 @@ Track_relocs::advance(off_t offset) return ret; } -// Instantiate the templates we need. We could use the configure -// script to restrict this to only the ones for implemented targets. +// Instantiate the templates we need. #ifdef HAVE_TARGET_32_LITTLE template @@ -863,80 +1149,24 @@ Sized_relobj<64, true>::do_relocate(const General_options& options, Output_file* of); #endif -#ifdef HAVE_TARGET_32_LITTLE -template -class Copy_relocs<32, false>; -#endif - -#ifdef HAVE_TARGET_32_BIG +#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG) template -class Copy_relocs<32, true>; +class Merged_symbol_value<32>; #endif -#ifdef HAVE_TARGET_64_LITTLE +#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG) template -class Copy_relocs<64, false>; +class Merged_symbol_value<64>; #endif -#ifdef HAVE_TARGET_64_BIG +#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG) template -class Copy_relocs<64, true>; +class Symbol_value<32>; #endif -#ifdef HAVE_TARGET_32_LITTLE +#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG) template -void -Copy_relocs<32, false>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_32_BIG -template -void -Copy_relocs<32, true>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_64_LITTLE -template -void -Copy_relocs<64, false>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_64_BIG -template -void -Copy_relocs<64, true>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_32_LITTLE -template -void -Copy_relocs<32, false>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_32_BIG -template -void -Copy_relocs<32, true>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_64_LITTLE -template -void -Copy_relocs<64, false>::emit( - Output_data_reloc*); -#endif - -#ifdef HAVE_TARGET_64_BIG -template -void -Copy_relocs<64, true>::emit( - Output_data_reloc*); +class Symbol_value<64>; #endif #ifdef HAVE_TARGET_32_LITTLE