91a65d2fe8
Some linker code editing needs to change multiple insns. In some cases multiple relocations are involved and it is not sufficient to make the changes independently as relocations are processed, because doing so might lead to a partial edit. So in order to safely edit we need all the relocations available in relocate(). Also, to emit edited relocs corresponding to the edited code sequence we need some way to pass information from relocate() to relocate_relocs(), particularly if the edit depends on insns. We can't modify input relocs in relocate() as they are mmapped PROT_READ, nor it is particularly clean to write relocs to the output at that stage. So add a Relocatable_relocs* field to relinfo to mark edited relocs. Given that relocate is passed the raw reloc pointer, it makes sense to remove the rel/rela parameter and r_type too. However, that means the mips relocate() needs to know whether SHT_REL or SHT_RELA relocs are being processed. So add a rel_type for mips, which also has the benefit of removing relocate() overloading there. This patch adds the infrastructure without making use of it. Note that relinfo->rr will be NULL if not outputting relocations. * object.h (struct Relocate_info): Add "rr". * reloc.h (Relocatable_relocs::set_strategy): New accessor. * reloc.cc (Sized_relobj_file::do_relocate_sections): Init relinfo.rr for relocate_section and relocate_relocs. * powerpc.cc (relocate): Add rel_type and preloc parameters. Delete rela and r_type params, instead recalculate these from preloc. (relocate_relocs): Delete Relocatable_relocs* param, instead use relinfo->rr. * aarch64.cc: Likewise. * arm.cc: Likewise. * i386.cc: Likewise. * mips.cc: Likewise. * s390.cc: Likewise. * sparc.cc: Likewise. * target.h: Likewise. * tilegx.cc: Likewise. * x86_64.cc: Likewise. * testsuite/testfile.cc: Likewise. * target-reloc.h (relocate_section): Adjust to suit. (apply_relocation, relocate_relocs): Likewise.
1857 lines
52 KiB
C++
1857 lines
52 KiB
C++
// reloc.cc -- relocate input files for gold.
|
|
|
|
// Copyright (C) 2006-2015 Free Software Foundation, Inc.
|
|
// Written by Ian Lance Taylor <iant@google.com>.
|
|
|
|
// This file is part of gold.
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
// MA 02110-1301, USA.
|
|
|
|
#include "gold.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "workqueue.h"
|
|
#include "layout.h"
|
|
#include "symtab.h"
|
|
#include "output.h"
|
|
#include "merge.h"
|
|
#include "object.h"
|
|
#include "target-reloc.h"
|
|
#include "reloc.h"
|
|
#include "icf.h"
|
|
#include "compressed_output.h"
|
|
#include "incremental.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// Read_relocs methods.
|
|
|
|
// These tasks just read the relocation information from the file.
|
|
// After reading it, the start another task to process the
|
|
// information. These tasks requires access to the file.
|
|
|
|
Task_token*
|
|
Read_relocs::is_runnable()
|
|
{
|
|
return this->object_->is_locked() ? this->object_->token() : NULL;
|
|
}
|
|
|
|
// Lock the file.
|
|
|
|
void
|
|
Read_relocs::locks(Task_locker* tl)
|
|
{
|
|
Task_token* token = this->object_->token();
|
|
if (token != NULL)
|
|
tl->add(this, token);
|
|
}
|
|
|
|
// Read the relocations and then start a Scan_relocs_task.
|
|
|
|
void
|
|
Read_relocs::run(Workqueue* workqueue)
|
|
{
|
|
Read_relocs_data* rd = new Read_relocs_data;
|
|
this->object_->read_relocs(rd);
|
|
this->object_->set_relocs_data(rd);
|
|
this->object_->release();
|
|
|
|
// If garbage collection or identical comdat folding is desired, we
|
|
// process the relocs first before scanning them. Scanning of relocs is
|
|
// done only after garbage or identical sections is identified.
|
|
if (parameters->options().gc_sections()
|
|
|| parameters->options().icf_enabled())
|
|
{
|
|
workqueue->queue_next(new Gc_process_relocs(this->symtab_,
|
|
this->layout_,
|
|
this->object_, rd,
|
|
this->this_blocker_,
|
|
this->next_blocker_));
|
|
}
|
|
else
|
|
{
|
|
workqueue->queue_next(new Scan_relocs(this->symtab_, this->layout_,
|
|
this->object_, rd,
|
|
this->this_blocker_,
|
|
this->next_blocker_));
|
|
}
|
|
}
|
|
|
|
// Return a debugging name for the task.
|
|
|
|
std::string
|
|
Read_relocs::get_name() const
|
|
{
|
|
return "Read_relocs " + this->object_->name();
|
|
}
|
|
|
|
// Gc_process_relocs methods.
|
|
|
|
Gc_process_relocs::~Gc_process_relocs()
|
|
{
|
|
if (this->this_blocker_ != NULL)
|
|
delete this->this_blocker_;
|
|
}
|
|
|
|
// These tasks process the relocations read by Read_relocs and
|
|
// determine which sections are referenced and which are garbage.
|
|
// This task is done only when --gc-sections is used. This is blocked
|
|
// by THIS_BLOCKER_. It unblocks NEXT_BLOCKER_.
|
|
|
|
Task_token*
|
|
Gc_process_relocs::is_runnable()
|
|
{
|
|
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
|
|
return this->this_blocker_;
|
|
if (this->object_->is_locked())
|
|
return this->object_->token();
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
Gc_process_relocs::locks(Task_locker* tl)
|
|
{
|
|
tl->add(this, this->object_->token());
|
|
tl->add(this, this->next_blocker_);
|
|
}
|
|
|
|
void
|
|
Gc_process_relocs::run(Workqueue*)
|
|
{
|
|
this->object_->gc_process_relocs(this->symtab_, this->layout_, this->rd_);
|
|
this->object_->release();
|
|
}
|
|
|
|
// Return a debugging name for the task.
|
|
|
|
std::string
|
|
Gc_process_relocs::get_name() const
|
|
{
|
|
return "Gc_process_relocs " + this->object_->name();
|
|
}
|
|
|
|
// Scan_relocs methods.
|
|
|
|
Scan_relocs::~Scan_relocs()
|
|
{
|
|
if (this->this_blocker_ != NULL)
|
|
delete this->this_blocker_;
|
|
}
|
|
|
|
// These tasks scan the relocations read by Read_relocs and mark up
|
|
// the symbol table to indicate which relocations are required. We
|
|
// use a lock on the symbol table to keep them from interfering with
|
|
// each other.
|
|
|
|
Task_token*
|
|
Scan_relocs::is_runnable()
|
|
{
|
|
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
|
|
return this->this_blocker_;
|
|
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.
|
|
|
|
void
|
|
Scan_relocs::locks(Task_locker* tl)
|
|
{
|
|
Task_token* token = this->object_->token();
|
|
if (token != NULL)
|
|
tl->add(this, token);
|
|
tl->add(this, this->next_blocker_);
|
|
}
|
|
|
|
// Scan the relocs.
|
|
|
|
void
|
|
Scan_relocs::run(Workqueue*)
|
|
{
|
|
this->object_->scan_relocs(this->symtab_, this->layout_, this->rd_);
|
|
delete this->rd_;
|
|
this->rd_ = NULL;
|
|
this->object_->release();
|
|
}
|
|
|
|
// 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_token*
|
|
Relocate_task::is_runnable()
|
|
{
|
|
if (this->object_->relocs_must_follow_section_writes()
|
|
&& this->output_sections_blocker_->is_blocked())
|
|
return this->output_sections_blocker_;
|
|
|
|
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.
|
|
|
|
void
|
|
Relocate_task::locks(Task_locker* tl)
|
|
{
|
|
if (this->input_sections_blocker_ != NULL)
|
|
tl->add(this, this->input_sections_blocker_);
|
|
tl->add(this, this->final_blocker_);
|
|
Task_token* token = this->object_->token();
|
|
if (token != NULL)
|
|
tl->add(this, token);
|
|
}
|
|
|
|
// Run the task.
|
|
|
|
void
|
|
Relocate_task::run(Workqueue*)
|
|
{
|
|
this->object_->relocate(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
|
|
// the information in RD.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
|
|
{
|
|
rd->relocs.clear();
|
|
|
|
unsigned int shnum = this->shnum();
|
|
if (shnum == 0)
|
|
return;
|
|
|
|
rd->relocs.reserve(shnum / 2);
|
|
|
|
const Output_sections& out_sections(this->output_sections());
|
|
const std::vector<Address>& out_offsets(this->section_offsets());
|
|
|
|
const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(),
|
|
shnum * This::shdr_size,
|
|
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)
|
|
{
|
|
typename This::Shdr shdr(ps);
|
|
|
|
unsigned int sh_type = shdr.get_sh_type();
|
|
if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA)
|
|
continue;
|
|
|
|
unsigned int shndx = this->adjust_shndx(shdr.get_sh_info());
|
|
if (shndx >= shnum)
|
|
{
|
|
this->error(_("relocation section %u has bad info %u"),
|
|
i, shndx);
|
|
continue;
|
|
}
|
|
|
|
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 unless this is a
|
|
// relocatable link or we need to emit relocations. FIXME: What
|
|
// should we do if a linker script maps a section with SHF_ALLOC
|
|
// clear to a section with SHF_ALLOC set?
|
|
typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size);
|
|
bool is_section_allocated = ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC)
|
|
!= 0);
|
|
if (!is_section_allocated
|
|
&& !parameters->options().relocatable()
|
|
&& !parameters->options().emit_relocs()
|
|
&& !parameters->incremental())
|
|
continue;
|
|
|
|
if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_)
|
|
{
|
|
this->error(_("relocation section %u uses unexpected "
|
|
"symbol table %u"),
|
|
i, this->adjust_shndx(shdr.get_sh_link()));
|
|
continue;
|
|
}
|
|
|
|
off_t sh_size = shdr.get_sh_size();
|
|
|
|
if (sh_size == 0)
|
|
continue;
|
|
|
|
unsigned int reloc_size;
|
|
if (sh_type == elfcpp::SHT_REL)
|
|
reloc_size = elfcpp::Elf_sizes<size>::rel_size;
|
|
else
|
|
reloc_size = elfcpp::Elf_sizes<size>::rela_size;
|
|
if (reloc_size != shdr.get_sh_entsize())
|
|
{
|
|
this->error(_("unexpected entsize for reloc section %u: %lu != %u"),
|
|
i, static_cast<unsigned long>(shdr.get_sh_entsize()),
|
|
reloc_size);
|
|
continue;
|
|
}
|
|
|
|
size_t reloc_count = sh_size / reloc_size;
|
|
if (static_cast<off_t>(reloc_count * reloc_size) != sh_size)
|
|
{
|
|
this->error(_("reloc section %u size %lu uneven"),
|
|
i, static_cast<unsigned long>(sh_size));
|
|
continue;
|
|
}
|
|
|
|
rd->relocs.push_back(Section_relocs());
|
|
Section_relocs& sr(rd->relocs.back());
|
|
sr.reloc_shndx = i;
|
|
sr.data_shndx = shndx;
|
|
sr.contents = this->get_lasting_view(shdr.get_sh_offset(), sh_size,
|
|
true, true);
|
|
sr.sh_type = sh_type;
|
|
sr.reloc_count = reloc_count;
|
|
sr.output_section = os;
|
|
sr.needs_special_offset_handling = out_offsets[shndx] == invalid_address;
|
|
sr.is_data_section_allocated = is_section_allocated;
|
|
}
|
|
|
|
// Read the local symbols.
|
|
gold_assert(this->symtab_shndx_ != -1U);
|
|
if (this->symtab_shndx_ == 0 || this->local_symbol_count_ == 0)
|
|
rd->local_symbols = NULL;
|
|
else
|
|
{
|
|
typename This::Shdr symtabshdr(pshdrs
|
|
+ this->symtab_shndx_ * This::shdr_size);
|
|
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
|
const int sym_size = This::sym_size;
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
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, true);
|
|
}
|
|
}
|
|
|
|
// Process the relocs to generate mappings from source sections to referenced
|
|
// sections. This is used during garbage collection to determine garbage
|
|
// sections.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_gc_process_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd)
|
|
{
|
|
Sized_target<size, big_endian>* target =
|
|
parameters->sized_target<size, big_endian>();
|
|
|
|
const unsigned char* local_symbols;
|
|
if (rd->local_symbols == NULL)
|
|
local_symbols = NULL;
|
|
else
|
|
local_symbols = rd->local_symbols->data();
|
|
|
|
for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin();
|
|
p != rd->relocs.end();
|
|
++p)
|
|
{
|
|
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->gc_process_relocs(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Scan the relocs and adjust the symbol table. This looks for
|
|
// relocations which require GOT/PLT/COPY relocations.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_scan_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd)
|
|
{
|
|
Sized_target<size, big_endian>* target =
|
|
parameters->sized_target<size, big_endian>();
|
|
|
|
const unsigned char* local_symbols;
|
|
if (rd->local_symbols == NULL)
|
|
local_symbols = NULL;
|
|
else
|
|
local_symbols = rd->local_symbols->data();
|
|
|
|
// For incremental links, allocate the counters for incremental relocations.
|
|
if (layout->incremental_inputs() != NULL)
|
|
this->allocate_incremental_reloc_counts();
|
|
|
|
for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin();
|
|
p != rd->relocs.end();
|
|
++p)
|
|
{
|
|
// When garbage collection is on, unreferenced sections are not included
|
|
// in the link that would have been included normally. This is known only
|
|
// after Read_relocs hence this check has to be done again.
|
|
if (parameters->options().gc_sections()
|
|
|| parameters->options().icf_enabled())
|
|
{
|
|
if (p->output_section == NULL)
|
|
continue;
|
|
}
|
|
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(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(symtab, layout, local_symbols, p);
|
|
if (layout->incremental_inputs() != NULL)
|
|
this->incremental_relocs_scan(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(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;
|
|
}
|
|
|
|
// For incremental links, finalize the allocation of relocations.
|
|
if (layout->incremental_inputs() != NULL)
|
|
this->finalize_incremental_relocs(layout, true);
|
|
|
|
if (rd->local_symbols != NULL)
|
|
{
|
|
delete rd->local_symbols;
|
|
rd->local_symbols = NULL;
|
|
}
|
|
}
|
|
|
|
// This is a strategy class we use when scanning for --emit-relocs.
|
|
|
|
template<int sh_type>
|
|
class Emit_relocs_strategy
|
|
{
|
|
public:
|
|
// A local non-section symbol.
|
|
inline Relocatable_relocs::Reloc_strategy
|
|
local_non_section_strategy(unsigned int, Relobj*, unsigned int)
|
|
{ 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<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::emit_relocs_scan(
|
|
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<elfcpp::SHT_REL>(symtab, layout,
|
|
plocal_syms, p, rr);
|
|
else
|
|
{
|
|
gold_assert(p->sh_type == elfcpp::SHT_RELA);
|
|
this->emit_relocs_scan_reltype<elfcpp::SHT_RELA>(symtab, layout,
|
|
plocal_syms, p, rr);
|
|
}
|
|
}
|
|
|
|
// Scan the input relocation for --emit-relocs, templatized on the
|
|
// type of the relocation section.
|
|
|
|
template<int size, bool big_endian>
|
|
template<int sh_type>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::emit_relocs_scan_reltype(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
const unsigned char* plocal_syms,
|
|
const Read_relocs_data::Relocs_list::iterator& p,
|
|
Relocatable_relocs* rr)
|
|
{
|
|
scan_relocatable_relocs<size, big_endian, sh_type,
|
|
Emit_relocs_strategy<sh_type> >(
|
|
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);
|
|
}
|
|
|
|
// Scan the input relocations for --incremental.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::incremental_relocs_scan(
|
|
const Read_relocs_data::Relocs_list::iterator& p)
|
|
{
|
|
if (p->sh_type == elfcpp::SHT_REL)
|
|
this->incremental_relocs_scan_reltype<elfcpp::SHT_REL>(p);
|
|
else
|
|
{
|
|
gold_assert(p->sh_type == elfcpp::SHT_RELA);
|
|
this->incremental_relocs_scan_reltype<elfcpp::SHT_RELA>(p);
|
|
}
|
|
}
|
|
|
|
// Scan the input relocation for --incremental, templatized on the
|
|
// type of the relocation section.
|
|
|
|
template<int size, bool big_endian>
|
|
template<int sh_type>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::incremental_relocs_scan_reltype(
|
|
const Read_relocs_data::Relocs_list::iterator& p)
|
|
{
|
|
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 unsigned char* prelocs = p->contents->data();
|
|
size_t reloc_count = p->reloc_count;
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Reltype reloc(prelocs);
|
|
|
|
if (p->needs_special_offset_handling
|
|
&& !p->output_section->is_input_address_mapped(this, p->data_shndx,
|
|
reloc.get_r_offset()))
|
|
continue;
|
|
|
|
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);
|
|
|
|
if (r_sym >= this->local_symbol_count_)
|
|
this->count_incremental_reloc(r_sym - this->local_symbol_count_);
|
|
}
|
|
}
|
|
|
|
// Relocate the input sections and write out the local symbols.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_relocate(const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
Output_file* of)
|
|
{
|
|
unsigned int shnum = this->shnum();
|
|
|
|
// Read the section headers.
|
|
const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(),
|
|
shnum * This::shdr_size,
|
|
true, true);
|
|
|
|
Views views;
|
|
views.resize(shnum);
|
|
|
|
// Make two passes over the sections. The first one copies the
|
|
// section data to the output file. The second one applies
|
|
// relocations.
|
|
|
|
this->write_sections(layout, 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(symtab, layout, pshdrs, of, &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_ctors_reverse_view)
|
|
this->reverse_words(views[i].view, views[i].view_size);
|
|
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(), layout->dynpool(),
|
|
layout->symtab_xindex(), layout->dynsym_xindex(),
|
|
layout->symtab_section_offset());
|
|
}
|
|
|
|
// 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.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::write_sections(const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews)
|
|
{
|
|
unsigned int shnum = this->shnum();
|
|
const Output_sections& out_sections(this->output_sections());
|
|
const std::vector<Address>& 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)
|
|
{
|
|
View_size* pvs = &(*pviews)[i];
|
|
|
|
pvs->view = NULL;
|
|
|
|
const Output_section* os = out_sections[i];
|
|
if (os == NULL)
|
|
continue;
|
|
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;
|
|
pvs->is_ctors_reverse_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<Address, off_t>(os->data_size());
|
|
}
|
|
else
|
|
{
|
|
output_section_offset = 0;
|
|
output_section_size =
|
|
convert_types<Address, off_t>(os->postprocessing_buffer_size());
|
|
}
|
|
|
|
off_t view_start;
|
|
section_size_type view_size;
|
|
bool must_decompress = false;
|
|
if (output_offset != invalid_address)
|
|
{
|
|
view_start = output_section_offset + output_offset;
|
|
view_size = convert_to_section_size_type(shdr.get_sh_size());
|
|
section_size_type uncompressed_size;
|
|
if (this->section_is_compressed(i, &uncompressed_size))
|
|
{
|
|
view_size = uncompressed_size;
|
|
must_decompress = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
view_start = output_section_offset;
|
|
view_size = convert_to_section_size_type(output_section_size);
|
|
}
|
|
|
|
if (view_size == 0)
|
|
continue;
|
|
|
|
gold_assert(output_offset == invalid_address
|
|
|| output_offset + view_size <= output_section_size);
|
|
|
|
unsigned char* view;
|
|
if (os->requires_postprocessing())
|
|
{
|
|
unsigned char* buffer = os->postprocessing_buffer();
|
|
view = buffer + view_start;
|
|
if (output_offset != invalid_address && !must_decompress)
|
|
{
|
|
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
|
|
{
|
|
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);
|
|
if (!must_decompress)
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (must_decompress)
|
|
{
|
|
// Read and decompress the section.
|
|
section_size_type len;
|
|
const unsigned char* p = this->section_contents(i, &len, false);
|
|
if (!decompress_input_section(p, len, view, view_size,
|
|
size, big_endian,
|
|
shdr.get_sh_flags()))
|
|
this->error(_("could not decompress section %s"),
|
|
this->section_name(i).c_str());
|
|
}
|
|
|
|
pvs->view = view;
|
|
pvs->address = os->address();
|
|
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 == invalid_address;
|
|
pvs->is_postprocessing_view = os->requires_postprocessing();
|
|
pvs->is_ctors_reverse_view =
|
|
(!parameters->options().relocatable()
|
|
&& view_size > size / 8
|
|
&& (strcmp(os->name(), ".init_array") == 0
|
|
|| strcmp(os->name(), ".fini_array") == 0)
|
|
&& layout->is_ctors_in_init_array(this, i));
|
|
}
|
|
|
|
// Actually read the data.
|
|
if (!rm.empty())
|
|
{
|
|
if (!is_sorted)
|
|
std::sort(rm.begin(), rm.end(), Read_multiple_compare());
|
|
this->read_multiple(rm);
|
|
}
|
|
}
|
|
|
|
// Relocate section data. VIEWS points to the section data as views
|
|
// in the output file.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_relocate_sections(
|
|
const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews)
|
|
{
|
|
unsigned int shnum = this->shnum();
|
|
Sized_target<size, big_endian>* target =
|
|
parameters->sized_target<size, big_endian>();
|
|
|
|
const Output_sections& out_sections(this->output_sections());
|
|
const std::vector<Address>& out_offsets(this->section_offsets());
|
|
|
|
Relocate_info<size, big_endian> relinfo;
|
|
relinfo.symtab = symtab;
|
|
relinfo.layout = layout;
|
|
relinfo.object = this;
|
|
|
|
const unsigned char* p = pshdrs + This::shdr_size;
|
|
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
|
|
{
|
|
typename This::Shdr shdr(p);
|
|
|
|
unsigned int sh_type = shdr.get_sh_type();
|
|
if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA)
|
|
continue;
|
|
|
|
off_t sh_size = shdr.get_sh_size();
|
|
if (sh_size == 0)
|
|
continue;
|
|
|
|
unsigned int index = this->adjust_shndx(shdr.get_sh_info());
|
|
if (index >= this->shnum())
|
|
{
|
|
this->error(_("relocation section %u has bad info %u"),
|
|
i, index);
|
|
continue;
|
|
}
|
|
|
|
Output_section* os = out_sections[index];
|
|
if (os == NULL)
|
|
{
|
|
// This relocation section is against a section which we
|
|
// discarded.
|
|
continue;
|
|
}
|
|
Address output_offset = out_offsets[index];
|
|
|
|
gold_assert((*pviews)[index].view != NULL);
|
|
if (parameters->options().relocatable())
|
|
gold_assert((*pviews)[i].view != NULL);
|
|
|
|
if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_)
|
|
{
|
|
gold_error(_("relocation section %u uses unexpected "
|
|
"symbol table %u"),
|
|
i, this->adjust_shndx(shdr.get_sh_link()));
|
|
continue;
|
|
}
|
|
|
|
const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(),
|
|
sh_size, true, false);
|
|
|
|
unsigned int reloc_size;
|
|
if (sh_type == elfcpp::SHT_REL)
|
|
reloc_size = elfcpp::Elf_sizes<size>::rel_size;
|
|
else
|
|
reloc_size = elfcpp::Elf_sizes<size>::rela_size;
|
|
|
|
if (reloc_size != shdr.get_sh_entsize())
|
|
{
|
|
gold_error(_("unexpected entsize for reloc section %u: %lu != %u"),
|
|
i, static_cast<unsigned long>(shdr.get_sh_entsize()),
|
|
reloc_size);
|
|
continue;
|
|
}
|
|
|
|
size_t reloc_count = sh_size / reloc_size;
|
|
if (static_cast<off_t>(reloc_count * reloc_size) != sh_size)
|
|
{
|
|
gold_error(_("reloc section %u size %lu uneven"),
|
|
i, static_cast<unsigned long>(sh_size));
|
|
continue;
|
|
}
|
|
|
|
gold_assert(output_offset != invalid_address
|
|
|| this->relocs_must_follow_section_writes());
|
|
|
|
relinfo.reloc_shndx = i;
|
|
relinfo.reloc_shdr = p;
|
|
relinfo.data_shndx = index;
|
|
relinfo.data_shdr = pshdrs + index * This::shdr_size;
|
|
unsigned char* view = (*pviews)[index].view;
|
|
Address address = (*pviews)[index].address;
|
|
section_size_type view_size = (*pviews)[index].view_size;
|
|
|
|
Reloc_symbol_changes* reloc_map = NULL;
|
|
if (this->uses_split_stack() && output_offset != invalid_address)
|
|
{
|
|
typename This::Shdr data_shdr(pshdrs + index * This::shdr_size);
|
|
if ((data_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
|
|
this->split_stack_adjust(symtab, pshdrs, sh_type, index,
|
|
prelocs, reloc_count, view, view_size,
|
|
&reloc_map);
|
|
}
|
|
|
|
Relocatable_relocs* rr = NULL;
|
|
if (parameters->options().emit_relocs()
|
|
|| parameters->options().relocatable())
|
|
rr = this->relocatable_relocs(i);
|
|
relinfo.rr = rr;
|
|
|
|
if (!parameters->options().relocatable())
|
|
{
|
|
target->relocate_section(&relinfo, sh_type, prelocs, reloc_count, os,
|
|
output_offset == invalid_address,
|
|
view, address, view_size, reloc_map);
|
|
if (parameters->options().emit_relocs())
|
|
target->relocate_relocs(&relinfo, sh_type, prelocs, reloc_count,
|
|
os, output_offset,
|
|
view, address, view_size,
|
|
(*pviews)[i].view,
|
|
(*pviews)[i].view_size);
|
|
if (parameters->incremental())
|
|
this->incremental_relocs_write(&relinfo, sh_type, prelocs,
|
|
reloc_count, os, output_offset, of);
|
|
}
|
|
else
|
|
target->relocate_relocs(&relinfo, sh_type, prelocs, reloc_count,
|
|
os, output_offset,
|
|
view, address, view_size,
|
|
(*pviews)[i].view,
|
|
(*pviews)[i].view_size);
|
|
}
|
|
}
|
|
|
|
// Write the incremental relocs.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::incremental_relocs_write(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
unsigned int sh_type,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
Address output_offset,
|
|
Output_file* of)
|
|
{
|
|
if (sh_type == elfcpp::SHT_REL)
|
|
this->incremental_relocs_write_reltype<elfcpp::SHT_REL>(
|
|
relinfo,
|
|
prelocs,
|
|
reloc_count,
|
|
output_section,
|
|
output_offset,
|
|
of);
|
|
else
|
|
{
|
|
gold_assert(sh_type == elfcpp::SHT_RELA);
|
|
this->incremental_relocs_write_reltype<elfcpp::SHT_RELA>(
|
|
relinfo,
|
|
prelocs,
|
|
reloc_count,
|
|
output_section,
|
|
output_offset,
|
|
of);
|
|
}
|
|
}
|
|
|
|
// Write the incremental relocs, templatized on the type of the
|
|
// relocation section.
|
|
|
|
template<int size, bool big_endian>
|
|
template<int sh_type>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::incremental_relocs_write_reltype(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
Address output_offset,
|
|
Output_file* of)
|
|
{
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reloc;
|
|
const unsigned int reloc_size =
|
|
Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
const unsigned int sizeof_addr = size / 8;
|
|
const unsigned int incr_reloc_size =
|
|
Incremental_relocs_reader<size, big_endian>::reloc_size;
|
|
|
|
unsigned int out_shndx = output_section->out_shndx();
|
|
|
|
// Get a view for the .gnu_incremental_relocs section.
|
|
|
|
Incremental_inputs* inputs = relinfo->layout->incremental_inputs();
|
|
gold_assert(inputs != NULL);
|
|
const off_t relocs_off = inputs->relocs_section()->offset();
|
|
const off_t relocs_size = inputs->relocs_section()->data_size();
|
|
unsigned char* const view = of->get_output_view(relocs_off, relocs_size);
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Reloc reloc(prelocs);
|
|
|
|
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 < this->local_symbol_count_)
|
|
continue;
|
|
|
|
// Get the new offset--the location in the output section where
|
|
// this relocation should be applied.
|
|
|
|
Address offset = reloc.get_r_offset();
|
|
if (output_offset != invalid_address)
|
|
offset += output_offset;
|
|
else
|
|
{
|
|
section_offset_type sot_offset =
|
|
convert_types<section_offset_type, Address>(offset);
|
|
section_offset_type new_sot_offset =
|
|
output_section->output_offset(relinfo->object,
|
|
relinfo->data_shndx,
|
|
sot_offset);
|
|
gold_assert(new_sot_offset != -1);
|
|
offset += new_sot_offset;
|
|
}
|
|
|
|
// Get the addend.
|
|
typename elfcpp::Elf_types<size>::Elf_Swxword addend;
|
|
if (sh_type == elfcpp::SHT_RELA)
|
|
addend =
|
|
Reloc_types<sh_type, size, big_endian>::get_reloc_addend(&reloc);
|
|
else
|
|
{
|
|
// FIXME: Get the addend for SHT_REL.
|
|
addend = 0;
|
|
}
|
|
|
|
// Get the index of the output relocation.
|
|
|
|
unsigned int reloc_index =
|
|
this->next_incremental_reloc_index(r_sym - this->local_symbol_count_);
|
|
|
|
// Write the relocation.
|
|
|
|
unsigned char* pov = view + reloc_index * incr_reloc_size;
|
|
elfcpp::Swap<32, big_endian>::writeval(pov, r_type);
|
|
elfcpp::Swap<32, big_endian>::writeval(pov + 4, out_shndx);
|
|
elfcpp::Swap<size, big_endian>::writeval(pov + 8, offset);
|
|
elfcpp::Swap<size, big_endian>::writeval(pov + 8 + sizeof_addr, addend);
|
|
of->write_output_view(pov - view, incr_reloc_size, view);
|
|
}
|
|
}
|
|
|
|
// Create merge hash tables for the local symbols. These are used to
|
|
// speed up relocations.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::initialize_input_to_output_maps()
|
|
{
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
for (unsigned int i = 1; i < loccount; ++i)
|
|
{
|
|
Symbol_value<size>& lv(this->local_values_[i]);
|
|
lv.initialize_input_to_output_map(this);
|
|
}
|
|
}
|
|
|
|
// Free merge hash tables for the local symbols.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::free_input_to_output_maps()
|
|
{
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
for (unsigned int i = 1; i < loccount; ++i)
|
|
{
|
|
Symbol_value<size>& lv(this->local_values_[i]);
|
|
lv.free_input_to_output_map();
|
|
}
|
|
}
|
|
|
|
// If an object was compiled with -fsplit-stack, this is called to
|
|
// check whether any relocations refer to functions defined in objects
|
|
// which were not compiled with -fsplit-stack. If they were, then we
|
|
// need to apply some target-specific adjustments to request
|
|
// additional stack space.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::split_stack_adjust(
|
|
const Symbol_table* symtab,
|
|
const unsigned char* pshdrs,
|
|
unsigned int sh_type,
|
|
unsigned int shndx,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
unsigned char* view,
|
|
section_size_type view_size,
|
|
Reloc_symbol_changes** reloc_map)
|
|
{
|
|
if (sh_type == elfcpp::SHT_REL)
|
|
this->split_stack_adjust_reltype<elfcpp::SHT_REL>(symtab, pshdrs, shndx,
|
|
prelocs, reloc_count,
|
|
view, view_size,
|
|
reloc_map);
|
|
else
|
|
{
|
|
gold_assert(sh_type == elfcpp::SHT_RELA);
|
|
this->split_stack_adjust_reltype<elfcpp::SHT_RELA>(symtab, pshdrs, shndx,
|
|
prelocs, reloc_count,
|
|
view, view_size,
|
|
reloc_map);
|
|
}
|
|
}
|
|
|
|
// Adjust for -fsplit-stack, templatized on the type of the relocation
|
|
// section.
|
|
|
|
template<int size, bool big_endian>
|
|
template<int sh_type>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::split_stack_adjust_reltype(
|
|
const Symbol_table* symtab,
|
|
const unsigned char* pshdrs,
|
|
unsigned int shndx,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
unsigned char* view,
|
|
section_size_type view_size,
|
|
Reloc_symbol_changes** reloc_map)
|
|
{
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
|
|
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
|
|
size_t local_count = this->local_symbol_count();
|
|
|
|
std::vector<section_offset_type> non_split_refs;
|
|
|
|
const unsigned char* pr = prelocs;
|
|
for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
|
|
{
|
|
Reltype reloc(pr);
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
if (r_sym < local_count)
|
|
continue;
|
|
|
|
const Symbol* gsym = this->global_symbol(r_sym);
|
|
gold_assert(gsym != NULL);
|
|
if (gsym->is_forwarder())
|
|
gsym = symtab->resolve_forwards(gsym);
|
|
|
|
// See if this relocation refers to a function defined in an
|
|
// object compiled without -fsplit-stack. Note that we don't
|
|
// care about the type of relocation--this means that in some
|
|
// cases we will ask for a large stack unnecessarily, but this
|
|
// is not fatal. FIXME: Some targets have symbols which are
|
|
// functions but are not type STT_FUNC, e.g., STT_ARM_TFUNC.
|
|
if (!gsym->is_undefined()
|
|
&& gsym->source() == Symbol::FROM_OBJECT
|
|
&& !gsym->object()->uses_split_stack())
|
|
{
|
|
unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
|
|
if (parameters->target().is_call_to_non_split(gsym, r_type))
|
|
{
|
|
section_offset_type offset =
|
|
convert_to_section_size_type(reloc.get_r_offset());
|
|
non_split_refs.push_back(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (non_split_refs.empty())
|
|
return;
|
|
|
|
// At this point, every entry in NON_SPLIT_REFS indicates a
|
|
// relocation which refers to a function in an object compiled
|
|
// without -fsplit-stack. We now have to convert that list into a
|
|
// set of offsets to functions. First, we find all the functions.
|
|
|
|
Function_offsets function_offsets;
|
|
this->find_functions(pshdrs, shndx, &function_offsets);
|
|
if (function_offsets.empty())
|
|
return;
|
|
|
|
// Now get a list of the function with references to non split-stack
|
|
// code.
|
|
|
|
Function_offsets calls_non_split;
|
|
for (std::vector<section_offset_type>::const_iterator p
|
|
= non_split_refs.begin();
|
|
p != non_split_refs.end();
|
|
++p)
|
|
{
|
|
Function_offsets::const_iterator low = function_offsets.lower_bound(*p);
|
|
if (low == function_offsets.end())
|
|
--low;
|
|
else if (low->first == *p)
|
|
;
|
|
else if (low == function_offsets.begin())
|
|
continue;
|
|
else
|
|
--low;
|
|
|
|
calls_non_split.insert(*low);
|
|
}
|
|
if (calls_non_split.empty())
|
|
return;
|
|
|
|
// Now we have a set of functions to adjust. The adjustments are
|
|
// target specific. Besides changing the output section view
|
|
// however, it likes, the target may request a relocation change
|
|
// from one global symbol name to another.
|
|
|
|
for (Function_offsets::const_iterator p = calls_non_split.begin();
|
|
p != calls_non_split.end();
|
|
++p)
|
|
{
|
|
std::string from;
|
|
std::string to;
|
|
parameters->target().calls_non_split(this, shndx, p->first, p->second,
|
|
view, view_size, &from, &to);
|
|
if (!from.empty())
|
|
{
|
|
gold_assert(!to.empty());
|
|
Symbol* tosym = NULL;
|
|
|
|
// Find relocations in the relevant function which are for
|
|
// FROM.
|
|
pr = prelocs;
|
|
for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
|
|
{
|
|
Reltype reloc(pr);
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info =
|
|
reloc.get_r_info();
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
if (r_sym < local_count)
|
|
continue;
|
|
|
|
section_offset_type offset =
|
|
convert_to_section_size_type(reloc.get_r_offset());
|
|
if (offset < p->first
|
|
|| (offset
|
|
>= (p->first
|
|
+ static_cast<section_offset_type>(p->second))))
|
|
continue;
|
|
|
|
const Symbol* gsym = this->global_symbol(r_sym);
|
|
if (from == gsym->name())
|
|
{
|
|
if (tosym == NULL)
|
|
{
|
|
tosym = symtab->lookup(to.c_str());
|
|
if (tosym == NULL)
|
|
{
|
|
this->error(_("could not convert call "
|
|
"to '%s' to '%s'"),
|
|
from.c_str(), to.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*reloc_map == NULL)
|
|
*reloc_map = new Reloc_symbol_changes(reloc_count);
|
|
(*reloc_map)->set(i, tosym);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all the function in this object defined in section SHNDX.
|
|
// Store their offsets in the section in FUNCTION_OFFSETS.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::find_functions(
|
|
const unsigned char* pshdrs,
|
|
unsigned int shndx,
|
|
Sized_relobj_file<size, big_endian>::Function_offsets* function_offsets)
|
|
{
|
|
// We need to read the symbols to find the functions. If we wanted
|
|
// to, we could cache reading the symbols across all sections in the
|
|
// object.
|
|
const unsigned int symtab_shndx = this->symtab_shndx_;
|
|
typename This::Shdr symtabshdr(pshdrs + symtab_shndx * This::shdr_size);
|
|
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword sh_size =
|
|
symtabshdr.get_sh_size();
|
|
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
|
|
sh_size, true, true);
|
|
|
|
const int sym_size = This::sym_size;
|
|
const unsigned int symcount = sh_size / sym_size;
|
|
for (unsigned int i = 0; i < symcount; ++i, psyms += sym_size)
|
|
{
|
|
typename elfcpp::Sym<size, big_endian> isym(psyms);
|
|
|
|
// FIXME: Some targets can have functions which do not have type
|
|
// STT_FUNC, e.g., STT_ARM_TFUNC.
|
|
if (isym.get_st_type() != elfcpp::STT_FUNC
|
|
|| isym.get_st_size() == 0)
|
|
continue;
|
|
|
|
bool is_ordinary;
|
|
Symbol_location loc;
|
|
loc.shndx = this->adjust_sym_shndx(i, isym.get_st_shndx(),
|
|
&is_ordinary);
|
|
if (!is_ordinary)
|
|
continue;
|
|
|
|
loc.object = this;
|
|
loc.offset = isym.get_st_value();
|
|
parameters->target().function_location(&loc);
|
|
|
|
if (loc.shndx != shndx)
|
|
continue;
|
|
|
|
section_offset_type value =
|
|
convert_to_section_size_type(loc.offset);
|
|
section_size_type fnsize =
|
|
convert_to_section_size_type(isym.get_st_size());
|
|
|
|
(*function_offsets)[value] = fnsize;
|
|
}
|
|
}
|
|
|
|
// Reverse the words in a section. Used for .ctors sections mapped to
|
|
// .init_array sections. See ctors_sections_in_init_array in
|
|
// layout.cc.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::reverse_words(unsigned char* view,
|
|
section_size_type view_size)
|
|
{
|
|
typedef typename elfcpp::Swap<size, big_endian>::Valtype Valtype;
|
|
Valtype* vview = reinterpret_cast<Valtype*>(view);
|
|
section_size_type vview_size = view_size / (size / 8);
|
|
for (section_size_type i = 0; i < vview_size / 2; ++i)
|
|
{
|
|
Valtype tmp = vview[i];
|
|
vview[i] = vview[vview_size - 1 - i];
|
|
vview[vview_size - 1 - i] = tmp;
|
|
}
|
|
}
|
|
|
|
// Class Merged_symbol_value.
|
|
|
|
template<int size>
|
|
void
|
|
Merged_symbol_value<size>::initialize_input_to_output_map(
|
|
const Relobj* object,
|
|
unsigned int input_shndx)
|
|
{
|
|
object->initialize_input_to_output_map<size>(input_shndx,
|
|
this->output_start_address_,
|
|
&this->output_addresses_);
|
|
}
|
|
|
|
// Get the output value corresponding to an input offset if we
|
|
// couldn't find it in the hash table.
|
|
|
|
template<int size>
|
|
typename elfcpp::Elf_types<size>::Elf_Addr
|
|
Merged_symbol_value<size>::value_from_output_section(
|
|
const Relobj* object,
|
|
unsigned int input_shndx,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr input_offset) const
|
|
{
|
|
section_offset_type output_offset;
|
|
bool found = object->merge_output_offset(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.
|
|
|
|
// Initialize the class to track the relocs. This gets the object,
|
|
// the reloc section index, and the type of the relocs. This returns
|
|
// false if something goes wrong.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Track_relocs<size, big_endian>::initialize(
|
|
Object* object,
|
|
unsigned int reloc_shndx,
|
|
unsigned int reloc_type)
|
|
{
|
|
// If RELOC_SHNDX is -1U, it means there is more than one reloc
|
|
// section for the .eh_frame section. We can't handle that case.
|
|
if (reloc_shndx == -1U)
|
|
return false;
|
|
|
|
// If RELOC_SHNDX is 0, there is no reloc section.
|
|
if (reloc_shndx == 0)
|
|
return true;
|
|
|
|
// Get the contents of the reloc section.
|
|
this->prelocs_ = object->section_contents(reloc_shndx, &this->len_, false);
|
|
|
|
if (reloc_type == elfcpp::SHT_REL)
|
|
this->reloc_size_ = elfcpp::Elf_sizes<size>::rel_size;
|
|
else if (reloc_type == elfcpp::SHT_RELA)
|
|
this->reloc_size_ = elfcpp::Elf_sizes<size>::rela_size;
|
|
else
|
|
gold_unreachable();
|
|
|
|
if (this->len_ % this->reloc_size_ != 0)
|
|
{
|
|
object->error(_("reloc section size %zu is not a multiple of "
|
|
"reloc size %d\n"),
|
|
static_cast<size_t>(this->len_),
|
|
this->reloc_size_);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return the offset of the next reloc, or -1 if there isn't one.
|
|
|
|
template<int size, bool big_endian>
|
|
off_t
|
|
Track_relocs<size, big_endian>::next_offset() const
|
|
{
|
|
if (this->pos_ >= this->len_)
|
|
return -1;
|
|
|
|
// Rel and Rela start out the same, so we can always use Rel to find
|
|
// the r_offset value.
|
|
elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
|
|
return rel.get_r_offset();
|
|
}
|
|
|
|
// Return the index of the symbol referenced by the next reloc, or -1U
|
|
// if there aren't any more relocs.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Track_relocs<size, big_endian>::next_symndx() const
|
|
{
|
|
if (this->pos_ >= this->len_)
|
|
return -1U;
|
|
|
|
// Rel and Rela start out the same, so we can use Rel to find the
|
|
// symbol index.
|
|
elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
|
|
return elfcpp::elf_r_sym<size>(rel.get_r_info());
|
|
}
|
|
|
|
// Return the addend of the next reloc, or 0 if there isn't one.
|
|
|
|
template<int size, bool big_endian>
|
|
uint64_t
|
|
Track_relocs<size, big_endian>::next_addend() const
|
|
{
|
|
if (this->pos_ >= this->len_)
|
|
return 0;
|
|
if (this->reloc_size_ == elfcpp::Elf_sizes<size>::rel_size)
|
|
return 0;
|
|
elfcpp::Rela<size, big_endian> rela(this->prelocs_ + this->pos_);
|
|
return rela.get_r_addend();
|
|
}
|
|
|
|
// Advance to the next reloc whose r_offset is greater than or equal
|
|
// to OFFSET. Return the number of relocs we skip.
|
|
|
|
template<int size, bool big_endian>
|
|
int
|
|
Track_relocs<size, big_endian>::advance(off_t offset)
|
|
{
|
|
int ret = 0;
|
|
while (this->pos_ < this->len_)
|
|
{
|
|
// Rel and Rela start out the same, so we can always use Rel to
|
|
// find the r_offset value.
|
|
elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
|
|
if (static_cast<off_t>(rel.get_r_offset()) >= offset)
|
|
break;
|
|
++ret;
|
|
this->pos_ += this->reloc_size_;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Instantiate the templates we need.
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::do_read_relocs(Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::do_read_relocs(Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::do_read_relocs(Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::do_read_relocs(Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::do_gc_process_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::do_gc_process_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::do_gc_process_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::do_gc_process_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::do_scan_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::do_scan_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::do_scan_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::do_scan_relocs(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_relocs_data* rd);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::do_relocate(const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
Output_file* of);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::do_relocate(const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
Output_file* of);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::do_relocate(const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
Output_file* of);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::do_relocate(const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
Output_file* of);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::do_relocate_sections(
|
|
const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::do_relocate_sections(
|
|
const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::do_relocate_sections(
|
|
const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::do_relocate_sections(
|
|
const Symbol_table* symtab,
|
|
const Layout* layout,
|
|
const unsigned char* pshdrs,
|
|
Output_file* of,
|
|
Views* pviews);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::initialize_input_to_output_maps();
|
|
|
|
template
|
|
void
|
|
Sized_relobj_file<32, false>::free_input_to_output_maps();
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::initialize_input_to_output_maps();
|
|
|
|
template
|
|
void
|
|
Sized_relobj_file<32, true>::free_input_to_output_maps();
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::initialize_input_to_output_maps();
|
|
|
|
template
|
|
void
|
|
Sized_relobj_file<64, false>::free_input_to_output_maps();
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::initialize_input_to_output_maps();
|
|
|
|
template
|
|
void
|
|
Sized_relobj_file<64, true>::free_input_to_output_maps();
|
|
#endif
|
|
|
|
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
|
|
template
|
|
class Merged_symbol_value<32>;
|
|
#endif
|
|
|
|
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
|
|
template
|
|
class Merged_symbol_value<64>;
|
|
#endif
|
|
|
|
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
|
|
template
|
|
class Symbol_value<32>;
|
|
#endif
|
|
|
|
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
|
|
template
|
|
class Symbol_value<64>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Track_relocs<32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Track_relocs<32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Track_relocs<64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Track_relocs<64, true>;
|
|
#endif
|
|
|
|
} // End namespace gold.
|