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.
906 lines
29 KiB
C++
906 lines
29 KiB
C++
// reloc.h -- relocate input files for gold -*- C++ -*-
|
|
|
|
// 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.
|
|
|
|
#ifndef GOLD_RELOC_H
|
|
#define GOLD_RELOC_H
|
|
|
|
#include <vector>
|
|
#ifdef HAVE_BYTESWAP_H
|
|
#include <byteswap.h>
|
|
#endif
|
|
|
|
#include "elfcpp.h"
|
|
#include "workqueue.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
class General_options;
|
|
class Object;
|
|
class Relobj;
|
|
struct Read_relocs_data;
|
|
class Symbol;
|
|
class Layout;
|
|
class Output_data;
|
|
class Output_section;
|
|
|
|
template<int size>
|
|
class Sized_symbol;
|
|
|
|
template<int size, bool big_endian>
|
|
class Sized_relobj_file;
|
|
|
|
template<int size>
|
|
class Symbol_value;
|
|
|
|
template<int sh_type, bool dynamic, int size, bool big_endian>
|
|
class Output_data_reloc;
|
|
|
|
// A class to read the relocations for an object file, and then queue
|
|
// up a task to see if they require any GOT/PLT/COPY relocations in
|
|
// the symbol table.
|
|
|
|
class Read_relocs : public Task
|
|
{
|
|
public:
|
|
// THIS_BLOCKER and NEXT_BLOCKER are passed along to a Scan_relocs
|
|
// or Gc_process_relocs task, so that they run in a deterministic
|
|
// order.
|
|
Read_relocs(Symbol_table* symtab, Layout* layout, Relobj* object,
|
|
Task_token* this_blocker, Task_token* next_blocker)
|
|
: symtab_(symtab), layout_(layout), object_(object),
|
|
this_blocker_(this_blocker), next_blocker_(next_blocker)
|
|
{ }
|
|
|
|
// The standard Task methods.
|
|
|
|
Task_token*
|
|
is_runnable();
|
|
|
|
void
|
|
locks(Task_locker*);
|
|
|
|
void
|
|
run(Workqueue*);
|
|
|
|
std::string
|
|
get_name() const;
|
|
|
|
private:
|
|
Symbol_table* symtab_;
|
|
Layout* layout_;
|
|
Relobj* object_;
|
|
Task_token* this_blocker_;
|
|
Task_token* next_blocker_;
|
|
};
|
|
|
|
// Process the relocs to figure out which sections are garbage.
|
|
// Very similar to scan relocs.
|
|
|
|
class Gc_process_relocs : public Task
|
|
{
|
|
public:
|
|
// THIS_BLOCKER prevents this task from running until the previous
|
|
// one is finished. NEXT_BLOCKER prevents the next task from
|
|
// running.
|
|
Gc_process_relocs(Symbol_table* symtab, Layout* layout, Relobj* object,
|
|
Read_relocs_data* rd, Task_token* this_blocker,
|
|
Task_token* next_blocker)
|
|
: symtab_(symtab), layout_(layout), object_(object), rd_(rd),
|
|
this_blocker_(this_blocker), next_blocker_(next_blocker)
|
|
{ }
|
|
|
|
~Gc_process_relocs();
|
|
|
|
// The standard Task methods.
|
|
|
|
Task_token*
|
|
is_runnable();
|
|
|
|
void
|
|
locks(Task_locker*);
|
|
|
|
void
|
|
run(Workqueue*);
|
|
|
|
std::string
|
|
get_name() const;
|
|
|
|
private:
|
|
Symbol_table* symtab_;
|
|
Layout* layout_;
|
|
Relobj* object_;
|
|
Read_relocs_data* rd_;
|
|
Task_token* this_blocker_;
|
|
Task_token* next_blocker_;
|
|
};
|
|
|
|
// Scan the relocations for an object to see if they require any
|
|
// GOT/PLT/COPY relocations.
|
|
|
|
class Scan_relocs : public Task
|
|
{
|
|
public:
|
|
// THIS_BLOCKER prevents this task from running until the previous
|
|
// one is finished. NEXT_BLOCKER prevents the next task from
|
|
// running.
|
|
Scan_relocs(Symbol_table* symtab, Layout* layout, Relobj* object,
|
|
Read_relocs_data* rd, Task_token* this_blocker,
|
|
Task_token* next_blocker)
|
|
: symtab_(symtab), layout_(layout), object_(object), rd_(rd),
|
|
this_blocker_(this_blocker), next_blocker_(next_blocker)
|
|
{ }
|
|
|
|
~Scan_relocs();
|
|
|
|
// The standard Task methods.
|
|
|
|
Task_token*
|
|
is_runnable();
|
|
|
|
void
|
|
locks(Task_locker*);
|
|
|
|
void
|
|
run(Workqueue*);
|
|
|
|
std::string
|
|
get_name() const;
|
|
|
|
private:
|
|
Symbol_table* symtab_;
|
|
Layout* layout_;
|
|
Relobj* object_;
|
|
Read_relocs_data* rd_;
|
|
Task_token* this_blocker_;
|
|
Task_token* next_blocker_;
|
|
};
|
|
|
|
// A class to perform all the relocations for an object file.
|
|
|
|
class Relocate_task : public Task
|
|
{
|
|
public:
|
|
Relocate_task(const Symbol_table* symtab, const Layout* layout,
|
|
Relobj* object, Output_file* of,
|
|
Task_token* input_sections_blocker,
|
|
Task_token* output_sections_blocker, Task_token* final_blocker)
|
|
: symtab_(symtab), layout_(layout), object_(object), of_(of),
|
|
input_sections_blocker_(input_sections_blocker),
|
|
output_sections_blocker_(output_sections_blocker),
|
|
final_blocker_(final_blocker)
|
|
{ }
|
|
|
|
// The standard Task methods.
|
|
|
|
Task_token*
|
|
is_runnable();
|
|
|
|
void
|
|
locks(Task_locker*);
|
|
|
|
void
|
|
run(Workqueue*);
|
|
|
|
std::string
|
|
get_name() const;
|
|
|
|
private:
|
|
const Symbol_table* symtab_;
|
|
const Layout* layout_;
|
|
Relobj* object_;
|
|
Output_file* of_;
|
|
Task_token* input_sections_blocker_;
|
|
Task_token* output_sections_blocker_;
|
|
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 addend should not be
|
|
// adjusted.
|
|
RELOC_ADJUST_FOR_SECTION_0,
|
|
// 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,
|
|
// Like RELOC_ADJUST_FOR_SECTION_4 but for unaligned relocs.
|
|
RELOC_ADJUST_FOR_SECTION_4_UNALIGNED,
|
|
// 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]);
|
|
}
|
|
|
|
// Set the strategy for reloc I.
|
|
void
|
|
set_strategy(unsigned int i, Reloc_strategy strategy)
|
|
{
|
|
gold_assert(i < this->reloc_strategies_.size());
|
|
this->reloc_strategies_[i] = strategy;
|
|
}
|
|
|
|
// 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.
|
|
|
|
template<int size, bool big_endian>
|
|
class Relocate_functions
|
|
{
|
|
private:
|
|
// Do a simple relocation with the addend in the section contents.
|
|
// VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
rel(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value);
|
|
}
|
|
|
|
// Like the above but for relocs at unaligned addresses.
|
|
template<int valsize>
|
|
static inline void
|
|
rel_unaligned(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value)
|
|
{
|
|
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
|
|
Valtype;
|
|
Valtype x = elfcpp::Swap_unaligned<valsize, big_endian>::readval(view);
|
|
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, x + value);
|
|
}
|
|
|
|
// Do a simple relocation using a Symbol_value with the addend in
|
|
// the section contents. VALSIZE is the size of the value to
|
|
// relocate.
|
|
template<int valsize>
|
|
static inline void
|
|
rel(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
|
|
x = psymval->value(object, x);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x);
|
|
}
|
|
|
|
// Like the above but for relocs at unaligned addresses.
|
|
template<int valsize>
|
|
static inline void
|
|
rel_unaligned(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{
|
|
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
|
|
Valtype;
|
|
Valtype x = elfcpp::Swap_unaligned<valsize, big_endian>::readval(view);
|
|
x = psymval->value(object, x);
|
|
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, x);
|
|
}
|
|
|
|
// Do a simple relocation with the addend in the relocation.
|
|
// VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
rela(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype addend)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, value + addend);
|
|
}
|
|
|
|
// Do a simple relocation using a symbol value with the addend in
|
|
// the relocation. VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
rela(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype addend)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = psymval->value(object, addend);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x);
|
|
}
|
|
|
|
// Do a simple PC relative relocation with the addend in the section
|
|
// contents. VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
pcrel(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value - address);
|
|
}
|
|
|
|
// Like the above but for relocs at unaligned addresses.
|
|
template<int valsize>
|
|
static inline void
|
|
pcrel_unaligned(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype x = elfcpp::Swap_unaligned<valsize, big_endian>::readval(view);
|
|
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view,
|
|
x + value - address);
|
|
}
|
|
|
|
// Do a simple PC relative relocation with a Symbol_value with the
|
|
// addend in the section contents. VALSIZE is the size of the
|
|
// value.
|
|
template<int valsize>
|
|
static inline void
|
|
pcrel(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
|
|
x = psymval->value(object, x);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x - address);
|
|
}
|
|
|
|
// Do a simple PC relative relocation with the addend in the
|
|
// relocation. VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
pcrela(unsigned char* view,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype value,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, value + addend - address);
|
|
}
|
|
|
|
// Do a simple PC relative relocation with a Symbol_value with the
|
|
// addend in the relocation. VALSIZE is the size of the value.
|
|
template<int valsize>
|
|
static inline void
|
|
pcrela(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype x = psymval->value(object, addend);
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, x - address);
|
|
}
|
|
|
|
typedef Relocate_functions<size, big_endian> This;
|
|
|
|
public:
|
|
// Do a simple 8-bit REL relocation with the addend in the section
|
|
// contents.
|
|
static inline void
|
|
rel8(unsigned char* view, unsigned char value)
|
|
{ This::template rel<8>(view, value); }
|
|
|
|
static inline void
|
|
rel8(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{ This::template rel<8>(view, object, psymval); }
|
|
|
|
// Do an 8-bit RELA relocation with the addend in the relocation.
|
|
static inline void
|
|
rela8(unsigned char* view, unsigned char value, unsigned char addend)
|
|
{ This::template rela<8>(view, value, addend); }
|
|
|
|
static inline void
|
|
rela8(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
unsigned char addend)
|
|
{ This::template rela<8>(view, object, psymval, addend); }
|
|
|
|
// Do a simple 8-bit PC relative relocation with the addend in the
|
|
// section contents.
|
|
static inline void
|
|
pcrel8(unsigned char* view, unsigned char value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<8>(view, value, address); }
|
|
|
|
static inline void
|
|
pcrel8(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<8>(view, object, psymval, address); }
|
|
|
|
// Do a simple 8-bit PC relative RELA relocation with the addend in
|
|
// the reloc.
|
|
static inline void
|
|
pcrela8(unsigned char* view, unsigned char value, unsigned char addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<8>(view, value, addend, address); }
|
|
|
|
static inline void
|
|
pcrela8(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
unsigned char addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<8>(view, object, psymval, addend, address); }
|
|
|
|
// Do a simple 16-bit REL relocation with the addend in the section
|
|
// contents.
|
|
static inline void
|
|
rel16(unsigned char* view, elfcpp::Elf_Half value)
|
|
{ This::template rel<16>(view, value); }
|
|
|
|
static inline void
|
|
rel16(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{ This::template rel<16>(view, object, psymval); }
|
|
|
|
// Do an 16-bit RELA relocation with the addend in the relocation.
|
|
static inline void
|
|
rela16(unsigned char* view, elfcpp::Elf_Half value, elfcpp::Elf_Half addend)
|
|
{ This::template rela<16>(view, value, addend); }
|
|
|
|
static inline void
|
|
rela16(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Half addend)
|
|
{ This::template rela<16>(view, object, psymval, addend); }
|
|
|
|
// Do a simple 16-bit PC relative REL relocation with the addend in
|
|
// the section contents.
|
|
static inline void
|
|
pcrel16(unsigned char* view, elfcpp::Elf_Half value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<16>(view, value, address); }
|
|
|
|
static inline void
|
|
pcrel16(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<16>(view, object, psymval, address); }
|
|
|
|
// Do a simple 16-bit PC relative RELA relocation with the addend in
|
|
// the reloc.
|
|
static inline void
|
|
pcrela16(unsigned char* view, elfcpp::Elf_Half value,
|
|
elfcpp::Elf_Half addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<16>(view, value, addend, address); }
|
|
|
|
static inline void
|
|
pcrela16(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Half addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<16>(view, object, psymval, addend, address); }
|
|
|
|
// Do a simple 32-bit REL relocation with the addend in the section
|
|
// contents.
|
|
static inline void
|
|
rel32(unsigned char* view, elfcpp::Elf_Word value)
|
|
{ This::template rel<32>(view, value); }
|
|
|
|
// Like above but for relocs at unaligned addresses.
|
|
static inline void
|
|
rel32_unaligned(unsigned char* view, elfcpp::Elf_Word value)
|
|
{ This::template rel_unaligned<32>(view, value); }
|
|
|
|
static inline void
|
|
rel32(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{ This::template rel<32>(view, object, psymval); }
|
|
|
|
// Like above but for relocs at unaligned addresses.
|
|
static inline void
|
|
rel32_unaligned(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{ This::template rel_unaligned<32>(view, object, psymval); }
|
|
|
|
// Do an 32-bit RELA relocation with the addend in the relocation.
|
|
static inline void
|
|
rela32(unsigned char* view, elfcpp::Elf_Word value, elfcpp::Elf_Word addend)
|
|
{ This::template rela<32>(view, value, addend); }
|
|
|
|
static inline void
|
|
rela32(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Word addend)
|
|
{ This::template rela<32>(view, object, psymval, addend); }
|
|
|
|
// Do a simple 32-bit PC relative REL relocation with the addend in
|
|
// the section contents.
|
|
static inline void
|
|
pcrel32(unsigned char* view, elfcpp::Elf_Word value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<32>(view, value, address); }
|
|
|
|
// Unaligned version of the above.
|
|
static inline void
|
|
pcrel32_unaligned(unsigned char* view, elfcpp::Elf_Word value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel_unaligned<32>(view, value, address); }
|
|
|
|
static inline void
|
|
pcrel32(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<32>(view, object, psymval, address); }
|
|
|
|
// Do a simple 32-bit PC relative RELA relocation with the addend in
|
|
// the relocation.
|
|
static inline void
|
|
pcrela32(unsigned char* view, elfcpp::Elf_Word value,
|
|
elfcpp::Elf_Word addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<32>(view, value, addend, address); }
|
|
|
|
static inline void
|
|
pcrela32(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Word addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<32>(view, object, psymval, addend, address); }
|
|
|
|
// Do a simple 64-bit REL relocation with the addend in the section
|
|
// contents.
|
|
static inline void
|
|
rel64(unsigned char* view, elfcpp::Elf_Xword value)
|
|
{ This::template rel<64>(view, value); }
|
|
|
|
static inline void
|
|
rel64(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval)
|
|
{ This::template rel<64>(view, object, psymval); }
|
|
|
|
// Do a 64-bit RELA relocation with the addend in the relocation.
|
|
static inline void
|
|
rela64(unsigned char* view, elfcpp::Elf_Xword value,
|
|
elfcpp::Elf_Xword addend)
|
|
{ This::template rela<64>(view, value, addend); }
|
|
|
|
static inline void
|
|
rela64(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Xword addend)
|
|
{ This::template rela<64>(view, object, psymval, addend); }
|
|
|
|
// Do a simple 64-bit PC relative REL relocation with the addend in
|
|
// the section contents.
|
|
static inline void
|
|
pcrel64(unsigned char* view, elfcpp::Elf_Xword value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<64>(view, value, address); }
|
|
|
|
static inline void
|
|
pcrel64(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrel<64>(view, object, psymval, address); }
|
|
|
|
// Do a simple 64-bit PC relative RELA relocation with the addend in
|
|
// the relocation.
|
|
static inline void
|
|
pcrela64(unsigned char* view, elfcpp::Elf_Xword value,
|
|
elfcpp::Elf_Xword addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<64>(view, value, addend, address); }
|
|
|
|
static inline void
|
|
pcrela64(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Xword addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{ This::template pcrela<64>(view, object, psymval, addend, address); }
|
|
};
|
|
|
|
// Integer manipulation functions used by various targets when
|
|
// performing relocations.
|
|
|
|
template<int bits>
|
|
class Bits
|
|
{
|
|
public:
|
|
// Sign extend an n-bit unsigned integer stored in a uint32_t into
|
|
// an int32_t. BITS must be between 1 and 32.
|
|
static inline int32_t
|
|
sign_extend32(uint32_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 32);
|
|
if (bits == 32)
|
|
return static_cast<int32_t>(val);
|
|
uint32_t mask = (~static_cast<uint32_t>(0)) >> (32 - bits);
|
|
val &= mask;
|
|
uint32_t top_bit = 1U << (bits - 1);
|
|
int32_t as_signed = static_cast<int32_t>(val);
|
|
if ((val & top_bit) != 0)
|
|
as_signed -= static_cast<int32_t>(top_bit * 2);
|
|
return as_signed;
|
|
}
|
|
|
|
// Return true if VAL (stored in a uint32_t) has overflowed a signed
|
|
// value with BITS bits.
|
|
static inline bool
|
|
has_overflow32(uint32_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 32);
|
|
if (bits == 32)
|
|
return false;
|
|
int32_t max = (1 << (bits - 1)) - 1;
|
|
int32_t min = -(1 << (bits - 1));
|
|
int32_t as_signed = static_cast<int32_t>(val);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Return true if VAL (stored in a uint32_t) has overflowed both a
|
|
// signed and an unsigned value. E.g.,
|
|
// Bits<8>::has_signed_unsigned_overflow32 would check -128 <= VAL <
|
|
// 255.
|
|
static inline bool
|
|
has_signed_unsigned_overflow32(uint32_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 32);
|
|
if (bits == 32)
|
|
return false;
|
|
int32_t max = static_cast<int32_t>((1U << bits) - 1);
|
|
int32_t min = -(1 << (bits - 1));
|
|
int32_t as_signed = static_cast<int32_t>(val);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Select bits from A and B using bits in MASK. For each n in
|
|
// [0..31], the n-th bit in the result is chosen from the n-th bits
|
|
// of A and B. A zero selects A and a one selects B.
|
|
static inline uint32_t
|
|
bit_select32(uint32_t a, uint32_t b, uint32_t mask)
|
|
{ return (a & ~mask) | (b & mask); }
|
|
|
|
// Sign extend an n-bit unsigned integer stored in a uint64_t into
|
|
// an int64_t. BITS must be between 1 and 64.
|
|
static inline int64_t
|
|
sign_extend(uint64_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 64);
|
|
if (bits == 64)
|
|
return static_cast<int64_t>(val);
|
|
uint64_t mask = (~static_cast<uint64_t>(0)) >> (64 - bits);
|
|
val &= mask;
|
|
uint64_t top_bit = static_cast<uint64_t>(1) << (bits - 1);
|
|
int64_t as_signed = static_cast<int64_t>(val);
|
|
if ((val & top_bit) != 0)
|
|
as_signed -= static_cast<int64_t>(top_bit * 2);
|
|
return as_signed;
|
|
}
|
|
|
|
// Return true if VAL (stored in a uint64_t) has overflowed a signed
|
|
// value with BITS bits.
|
|
static inline bool
|
|
has_overflow(uint64_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 64);
|
|
if (bits == 64)
|
|
return false;
|
|
int64_t max = (static_cast<int64_t>(1) << (bits - 1)) - 1;
|
|
int64_t min = -(static_cast<int64_t>(1) << (bits - 1));
|
|
int64_t as_signed = static_cast<int64_t>(val);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Return true if VAL (stored in a uint64_t) has overflowed both a
|
|
// signed and an unsigned value. E.g.,
|
|
// Bits<8>::has_signed_unsigned_overflow would check -128 <= VAL <
|
|
// 255.
|
|
static inline bool
|
|
has_signed_unsigned_overflow64(uint64_t val)
|
|
{
|
|
gold_assert(bits > 0 && bits <= 64);
|
|
if (bits == 64)
|
|
return false;
|
|
int64_t max = static_cast<int64_t>((static_cast<uint64_t>(1) << bits) - 1);
|
|
int64_t min = -(static_cast<int64_t>(1) << (bits - 1));
|
|
int64_t as_signed = static_cast<int64_t>(val);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Select bits from A and B using bits in MASK. For each n in
|
|
// [0..31], the n-th bit in the result is chosen from the n-th bits
|
|
// of A and B. A zero selects A and a one selects B.
|
|
static inline uint64_t
|
|
bit_select64(uint64_t a, uint64_t b, uint64_t mask)
|
|
{ return (a & ~mask) | (b & mask); }
|
|
};
|
|
|
|
// Track relocations while reading a section. This lets you ask for
|
|
// the relocation at a certain offset, and see how relocs occur
|
|
// between points of interest.
|
|
|
|
template<int size, bool big_endian>
|
|
class Track_relocs
|
|
{
|
|
public:
|
|
Track_relocs()
|
|
: prelocs_(NULL), len_(0), pos_(0), reloc_size_(0)
|
|
{ }
|
|
|
|
// Initialize the Track_relocs object. OBJECT is the object holding
|
|
// the reloc section, RELOC_SHNDX is the section index of the reloc
|
|
// section, and RELOC_TYPE is the type of the reloc section
|
|
// (elfcpp::SHT_REL or elfcpp::SHT_RELA). This returns false if
|
|
// something went wrong.
|
|
bool
|
|
initialize(Object* object, unsigned int reloc_shndx,
|
|
unsigned int reloc_type);
|
|
|
|
// Return the offset in the data section to which the next reloc
|
|
// applies. This returns -1 if there is no next reloc.
|
|
off_t
|
|
next_offset() const;
|
|
|
|
// Return the symbol index of the next reloc. This returns -1U if
|
|
// there is no next reloc.
|
|
unsigned int
|
|
next_symndx() const;
|
|
|
|
// Return the addend of the next reloc. This returns 0 if there is
|
|
// no next reloc.
|
|
uint64_t
|
|
next_addend() const;
|
|
|
|
// Advance to OFFSET within the data section, and return the number
|
|
// of relocs which would be skipped.
|
|
int
|
|
advance(off_t offset);
|
|
|
|
// Checkpoint the current position in the reloc section.
|
|
section_size_type
|
|
checkpoint() const
|
|
{ return this->pos_; }
|
|
|
|
// Reset the position to CHECKPOINT.
|
|
void
|
|
reset(section_size_type checkpoint)
|
|
{ this->pos_ = checkpoint; }
|
|
|
|
private:
|
|
// The contents of the input object's reloc section.
|
|
const unsigned char* prelocs_;
|
|
// The length of the reloc section.
|
|
section_size_type len_;
|
|
// Our current position in the reloc section.
|
|
section_size_type pos_;
|
|
// The size of the relocs in the section.
|
|
int reloc_size_;
|
|
};
|
|
|
|
} // End namespace gold.
|
|
|
|
#endif // !defined(GOLD_RELOC_H)
|