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.
832 lines
27 KiB
C++
832 lines
27 KiB
C++
// target-reloc.h -- target specific relocation support -*- 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_TARGET_RELOC_H
|
|
#define GOLD_TARGET_RELOC_H
|
|
|
|
#include "elfcpp.h"
|
|
#include "symtab.h"
|
|
#include "object.h"
|
|
#include "reloc.h"
|
|
#include "reloc-types.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// This function implements the generic part of reloc scanning. The
|
|
// template parameter Scan must be a class type which provides two
|
|
// functions: local() and global(). Those functions implement the
|
|
// machine specific part of scanning. We do it this way to
|
|
// avoid making a function call for each relocation, and to avoid
|
|
// repeating the generic code for each target.
|
|
|
|
template<int size, bool big_endian, typename Target_type, int sh_type,
|
|
typename Scan>
|
|
inline void
|
|
scan_relocs(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
Target_type* target,
|
|
Sized_relobj_file<size, big_endian>* object,
|
|
unsigned int data_shndx,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
bool needs_special_offset_handling,
|
|
size_t local_count,
|
|
const unsigned char* plocal_syms)
|
|
{
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
|
|
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
|
|
Scan scan;
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Reltype reloc(prelocs);
|
|
|
|
if (needs_special_offset_handling
|
|
&& !output_section->is_input_address_mapped(object, data_shndx,
|
|
reloc.get_r_offset()))
|
|
continue;
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
|
|
|
|
if (r_sym < local_count)
|
|
{
|
|
gold_assert(plocal_syms != NULL);
|
|
typename elfcpp::Sym<size, big_endian> lsym(plocal_syms
|
|
+ r_sym * sym_size);
|
|
unsigned int shndx = lsym.get_st_shndx();
|
|
bool is_ordinary;
|
|
shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
|
|
// If RELOC is a relocation against a local symbol in a
|
|
// section we are discarding then we can ignore it. It will
|
|
// eventually become a reloc against the value zero.
|
|
//
|
|
// FIXME: We should issue a warning if this is an
|
|
// allocated section; is this the best place to do it?
|
|
//
|
|
// FIXME: The old GNU linker would in some cases look
|
|
// for the linkonce section which caused this section to
|
|
// be discarded, and, if the other section was the same
|
|
// size, change the reloc to refer to the other section.
|
|
// That seems risky and weird to me, and I don't know of
|
|
// any case where it is actually required.
|
|
bool is_discarded = (is_ordinary
|
|
&& shndx != elfcpp::SHN_UNDEF
|
|
&& !object->is_section_included(shndx)
|
|
&& !symtab->is_section_folded(object, shndx));
|
|
scan.local(symtab, layout, target, object, data_shndx,
|
|
output_section, reloc, r_type, lsym, is_discarded);
|
|
}
|
|
else
|
|
{
|
|
Symbol* gsym = object->global_symbol(r_sym);
|
|
gold_assert(gsym != NULL);
|
|
if (gsym->is_forwarder())
|
|
gsym = symtab->resolve_forwards(gsym);
|
|
|
|
scan.global(symtab, layout, target, object, data_shndx,
|
|
output_section, reloc, r_type, gsym);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Behavior for relocations to discarded comdat sections.
|
|
|
|
enum Comdat_behavior
|
|
{
|
|
CB_UNDETERMINED, // Not yet determined -- need to look at section name.
|
|
CB_PRETEND, // Attempt to map to the corresponding kept section.
|
|
CB_IGNORE, // Ignore the relocation.
|
|
CB_WARNING // Print a warning.
|
|
};
|
|
|
|
class Default_comdat_behavior
|
|
{
|
|
public:
|
|
// Decide what the linker should do for relocations that refer to
|
|
// discarded comdat sections. This decision is based on the name of
|
|
// the section being relocated.
|
|
|
|
inline Comdat_behavior
|
|
get(const char* name)
|
|
{
|
|
if (Layout::is_debug_info_section(name))
|
|
return CB_PRETEND;
|
|
if (strcmp(name, ".eh_frame") == 0
|
|
|| strcmp(name, ".gcc_except_table") == 0)
|
|
return CB_IGNORE;
|
|
return CB_WARNING;
|
|
}
|
|
};
|
|
|
|
// Give an error for a symbol with non-default visibility which is not
|
|
// defined locally.
|
|
|
|
inline void
|
|
visibility_error(const Symbol* sym)
|
|
{
|
|
const char* v;
|
|
switch (sym->visibility())
|
|
{
|
|
case elfcpp::STV_INTERNAL:
|
|
v = _("internal");
|
|
break;
|
|
case elfcpp::STV_HIDDEN:
|
|
v = _("hidden");
|
|
break;
|
|
case elfcpp::STV_PROTECTED:
|
|
v = _("protected");
|
|
break;
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
gold_error(_("%s symbol '%s' is not defined locally"),
|
|
v, sym->name());
|
|
}
|
|
|
|
// Return true if we are should issue an error saying that SYM is an
|
|
// undefined symbol. This is called if there is a relocation against
|
|
// SYM.
|
|
|
|
inline bool
|
|
issue_undefined_symbol_error(const Symbol* sym)
|
|
{
|
|
// We only report global symbols.
|
|
if (sym == NULL)
|
|
return false;
|
|
|
|
// We only report undefined symbols.
|
|
if (!sym->is_undefined() && !sym->is_placeholder())
|
|
return false;
|
|
|
|
// We don't report weak symbols.
|
|
if (sym->is_weak_undefined())
|
|
return false;
|
|
|
|
// We don't report symbols defined in discarded sections.
|
|
if (sym->is_defined_in_discarded_section())
|
|
return false;
|
|
|
|
// If the target defines this symbol, don't report it here.
|
|
if (parameters->target().is_defined_by_abi(sym))
|
|
return false;
|
|
|
|
// See if we've been told to ignore whether this symbol is
|
|
// undefined.
|
|
const char* const u = parameters->options().unresolved_symbols();
|
|
if (u != NULL)
|
|
{
|
|
if (strcmp(u, "ignore-all") == 0)
|
|
return false;
|
|
if (strcmp(u, "report-all") == 0)
|
|
return true;
|
|
if (strcmp(u, "ignore-in-object-files") == 0 && !sym->in_dyn())
|
|
return false;
|
|
if (strcmp(u, "ignore-in-shared-libs") == 0 && !sym->in_reg())
|
|
return false;
|
|
}
|
|
|
|
// If the symbol is hidden, report it.
|
|
if (sym->visibility() == elfcpp::STV_HIDDEN)
|
|
return true;
|
|
|
|
// When creating a shared library, only report unresolved symbols if
|
|
// -z defs was used.
|
|
if (parameters->options().shared() && !parameters->options().defs())
|
|
return false;
|
|
|
|
// Otherwise issue a warning.
|
|
return true;
|
|
}
|
|
|
|
// This function implements the generic part of relocation processing.
|
|
// The template parameter Relocate must be a class type which provides
|
|
// a single function, relocate(), which implements the machine
|
|
// specific part of a relocation.
|
|
|
|
// The template parameter Relocate_comdat_behavior is a class type
|
|
// which provides a single function, get(), which determines what the
|
|
// linker should do for relocations that refer to discarded comdat
|
|
// sections.
|
|
|
|
// SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of
|
|
// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA.
|
|
// RELOCATE implements operator() to do a relocation.
|
|
|
|
// PRELOCS points to the relocation data. RELOC_COUNT is the number
|
|
// of relocs. OUTPUT_SECTION is the output section.
|
|
// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
|
|
// mapped to output offsets.
|
|
|
|
// VIEW is the section data, VIEW_ADDRESS is its memory address, and
|
|
// VIEW_SIZE is the size. These refer to the input section, unless
|
|
// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
|
|
// the output section.
|
|
|
|
// RELOC_SYMBOL_CHANGES is used for -fsplit-stack support. If it is
|
|
// not NULL, it is a vector indexed by relocation index. If that
|
|
// entry is not NULL, it points to a global symbol which used as the
|
|
// symbol for the relocation, ignoring the symbol index in the
|
|
// relocation.
|
|
|
|
template<int size, bool big_endian, typename Target_type, int sh_type,
|
|
typename Relocate,
|
|
typename Relocate_comdat_behavior>
|
|
inline void
|
|
relocate_section(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
Target_type* target,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
bool needs_special_offset_handling,
|
|
unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
|
|
section_size_type view_size,
|
|
const Reloc_symbol_changes* reloc_symbol_changes)
|
|
{
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
|
|
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
Relocate relocate;
|
|
Relocate_comdat_behavior relocate_comdat_behavior;
|
|
|
|
Sized_relobj_file<size, big_endian>* object = relinfo->object;
|
|
unsigned int local_count = object->local_symbol_count();
|
|
|
|
Comdat_behavior comdat_behavior = CB_UNDETERMINED;
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Reltype reloc(prelocs);
|
|
|
|
section_offset_type offset =
|
|
convert_to_section_size_type(reloc.get_r_offset());
|
|
|
|
if (needs_special_offset_handling)
|
|
{
|
|
offset = output_section->output_offset(relinfo->object,
|
|
relinfo->data_shndx,
|
|
offset);
|
|
if (offset == -1)
|
|
continue;
|
|
}
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
|
|
const Sized_symbol<size>* sym;
|
|
|
|
Symbol_value<size> symval;
|
|
const Symbol_value<size> *psymval;
|
|
bool is_defined_in_discarded_section;
|
|
unsigned int shndx;
|
|
if (r_sym < local_count
|
|
&& (reloc_symbol_changes == NULL
|
|
|| (*reloc_symbol_changes)[i] == NULL))
|
|
{
|
|
sym = NULL;
|
|
psymval = object->local_symbol(r_sym);
|
|
|
|
// If the local symbol belongs to a section we are discarding,
|
|
// and that section is a debug section, try to find the
|
|
// corresponding kept section and map this symbol to its
|
|
// counterpart in the kept section. The symbol must not
|
|
// correspond to a section we are folding.
|
|
bool is_ordinary;
|
|
shndx = psymval->input_shndx(&is_ordinary);
|
|
is_defined_in_discarded_section =
|
|
(is_ordinary
|
|
&& shndx != elfcpp::SHN_UNDEF
|
|
&& !object->is_section_included(shndx)
|
|
&& !relinfo->symtab->is_section_folded(object, shndx));
|
|
}
|
|
else
|
|
{
|
|
const Symbol* gsym;
|
|
if (reloc_symbol_changes != NULL
|
|
&& (*reloc_symbol_changes)[i] != NULL)
|
|
gsym = (*reloc_symbol_changes)[i];
|
|
else
|
|
{
|
|
gsym = object->global_symbol(r_sym);
|
|
gold_assert(gsym != NULL);
|
|
if (gsym->is_forwarder())
|
|
gsym = relinfo->symtab->resolve_forwards(gsym);
|
|
}
|
|
|
|
sym = static_cast<const Sized_symbol<size>*>(gsym);
|
|
if (sym->has_symtab_index() && sym->symtab_index() != -1U)
|
|
symval.set_output_symtab_index(sym->symtab_index());
|
|
else
|
|
symval.set_no_output_symtab_entry();
|
|
symval.set_output_value(sym->value());
|
|
if (gsym->type() == elfcpp::STT_TLS)
|
|
symval.set_is_tls_symbol();
|
|
else if (gsym->type() == elfcpp::STT_GNU_IFUNC)
|
|
symval.set_is_ifunc_symbol();
|
|
psymval = &symval;
|
|
|
|
is_defined_in_discarded_section =
|
|
(gsym->is_defined_in_discarded_section()
|
|
&& gsym->is_undefined());
|
|
shndx = 0;
|
|
}
|
|
|
|
Symbol_value<size> symval2;
|
|
if (is_defined_in_discarded_section)
|
|
{
|
|
if (comdat_behavior == CB_UNDETERMINED)
|
|
{
|
|
std::string name = object->section_name(relinfo->data_shndx);
|
|
comdat_behavior = relocate_comdat_behavior.get(name.c_str());
|
|
}
|
|
if (comdat_behavior == CB_PRETEND)
|
|
{
|
|
// FIXME: This case does not work for global symbols.
|
|
// We have no place to store the original section index.
|
|
// Fortunately this does not matter for comdat sections,
|
|
// only for sections explicitly discarded by a linker
|
|
// script.
|
|
bool found;
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value =
|
|
object->map_to_kept_section(shndx, &found);
|
|
if (found)
|
|
symval2.set_output_value(value + psymval->input_value());
|
|
else
|
|
symval2.set_output_value(0);
|
|
}
|
|
else
|
|
{
|
|
if (comdat_behavior == CB_WARNING)
|
|
gold_warning_at_location(relinfo, i, offset,
|
|
_("relocation refers to discarded "
|
|
"section"));
|
|
symval2.set_output_value(0);
|
|
}
|
|
symval2.set_no_output_symtab_entry();
|
|
psymval = &symval2;
|
|
}
|
|
|
|
// If OFFSET is out of range, still let the target decide to
|
|
// ignore the relocation. Pass in NULL as the VIEW argument so
|
|
// that it can return quickly without trashing an invalid memory
|
|
// address.
|
|
unsigned char *v = view + offset;
|
|
if (offset < 0 || static_cast<section_size_type>(offset) >= view_size)
|
|
v = NULL;
|
|
|
|
if (!relocate.relocate(relinfo, sh_type, target, output_section,
|
|
i, prelocs, sym, psymval,
|
|
v, view_address + offset, view_size))
|
|
continue;
|
|
|
|
if (v == NULL)
|
|
{
|
|
gold_error_at_location(relinfo, i, offset,
|
|
_("reloc has bad offset %zu"),
|
|
static_cast<size_t>(offset));
|
|
continue;
|
|
}
|
|
|
|
if (issue_undefined_symbol_error(sym))
|
|
gold_undefined_symbol_at_location(sym, relinfo, i, offset);
|
|
else if (sym != NULL
|
|
&& sym->visibility() != elfcpp::STV_DEFAULT
|
|
&& (sym->is_strong_undefined() || sym->is_from_dynobj()))
|
|
visibility_error(sym);
|
|
|
|
if (sym != NULL && sym->has_warning())
|
|
relinfo->symtab->issue_warning(sym, relinfo, i, offset);
|
|
}
|
|
}
|
|
|
|
// Apply an incremental relocation.
|
|
|
|
template<int size, bool big_endian, typename Target_type,
|
|
typename Relocate>
|
|
void
|
|
apply_relocation(const Relocate_info<size, big_endian>* relinfo,
|
|
Target_type* target,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr r_offset,
|
|
unsigned int r_type,
|
|
typename elfcpp::Elf_types<size>::Elf_Swxword r_addend,
|
|
const Symbol* gsym,
|
|
unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address,
|
|
section_size_type view_size)
|
|
{
|
|
// Construct the ELF relocation in a temporary buffer.
|
|
const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
|
|
unsigned char relbuf[reloc_size];
|
|
elfcpp::Rela_write<size, big_endian> orel(relbuf);
|
|
orel.put_r_offset(r_offset);
|
|
orel.put_r_info(elfcpp::elf_r_info<size>(0, r_type));
|
|
orel.put_r_addend(r_addend);
|
|
|
|
// Setup a Symbol_value for the global symbol.
|
|
const Sized_symbol<size>* sym = static_cast<const Sized_symbol<size>*>(gsym);
|
|
Symbol_value<size> symval;
|
|
gold_assert(sym->has_symtab_index() && sym->symtab_index() != -1U);
|
|
symval.set_output_symtab_index(sym->symtab_index());
|
|
symval.set_output_value(sym->value());
|
|
if (gsym->type() == elfcpp::STT_TLS)
|
|
symval.set_is_tls_symbol();
|
|
else if (gsym->type() == elfcpp::STT_GNU_IFUNC)
|
|
symval.set_is_ifunc_symbol();
|
|
|
|
Relocate relocate;
|
|
relocate.relocate(relinfo, elfcpp::SHT_RELA, target, NULL,
|
|
-1U, relbuf, sym, &symval,
|
|
view + r_offset, address + r_offset, view_size);
|
|
}
|
|
|
|
// This class may be used as a typical class for the
|
|
// Scan_relocatable_reloc parameter to scan_relocatable_relocs. The
|
|
// template parameter Classify_reloc must be a class type which
|
|
// provides a function get_size_for_reloc which returns the number of
|
|
// bytes to which a reloc applies. This class is intended to capture
|
|
// the most typical target behaviour, while still permitting targets
|
|
// to define their own independent class for Scan_relocatable_reloc.
|
|
|
|
template<int sh_type, typename Classify_reloc>
|
|
class Default_scan_relocatable_relocs
|
|
{
|
|
public:
|
|
// Return the strategy to use for a local symbol which is not a
|
|
// section symbol, given the relocation type.
|
|
inline Relocatable_relocs::Reloc_strategy
|
|
local_non_section_strategy(unsigned int r_type, Relobj*, unsigned int r_sym)
|
|
{
|
|
// We assume that relocation type 0 is NONE. Targets which are
|
|
// different must override.
|
|
if (r_type == 0 && r_sym == 0)
|
|
return Relocatable_relocs::RELOC_DISCARD;
|
|
return Relocatable_relocs::RELOC_COPY;
|
|
}
|
|
|
|
// Return the strategy to use for a local symbol which is a section
|
|
// symbol, given the relocation type.
|
|
inline Relocatable_relocs::Reloc_strategy
|
|
local_section_strategy(unsigned int r_type, Relobj* object)
|
|
{
|
|
if (sh_type == elfcpp::SHT_RELA)
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
|
|
else
|
|
{
|
|
Classify_reloc classify;
|
|
switch (classify.get_size_for_reloc(r_type, object))
|
|
{
|
|
case 0:
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0;
|
|
case 1:
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1;
|
|
case 2:
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2;
|
|
case 4:
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4;
|
|
case 8:
|
|
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8;
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the strategy to use for a global symbol, given the
|
|
// relocation type, the object, and the symbol index.
|
|
inline Relocatable_relocs::Reloc_strategy
|
|
global_strategy(unsigned int, Relobj*, unsigned int)
|
|
{ return Relocatable_relocs::RELOC_COPY; }
|
|
};
|
|
|
|
// Scan relocs during a relocatable link. This is a default
|
|
// definition which should work for most targets.
|
|
// Scan_relocatable_reloc must name a class type which provides three
|
|
// functions which return a Relocatable_relocs::Reloc_strategy code:
|
|
// global_strategy, local_non_section_strategy, and
|
|
// local_section_strategy. Most targets should be able to use
|
|
// Default_scan_relocatable_relocs as this class.
|
|
|
|
template<int size, bool big_endian, int sh_type,
|
|
typename Scan_relocatable_reloc>
|
|
void
|
|
scan_relocatable_relocs(
|
|
Symbol_table*,
|
|
Layout*,
|
|
Sized_relobj_file<size, big_endian>* object,
|
|
unsigned int data_shndx,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
bool needs_special_offset_handling,
|
|
size_t local_symbol_count,
|
|
const unsigned char* plocal_syms,
|
|
Relocatable_relocs* rr)
|
|
{
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
|
|
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
|
|
Scan_relocatable_reloc scan;
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Reltype reloc(prelocs);
|
|
|
|
Relocatable_relocs::Reloc_strategy strategy;
|
|
|
|
if (needs_special_offset_handling
|
|
&& !output_section->is_input_address_mapped(object, data_shndx,
|
|
reloc.get_r_offset()))
|
|
strategy = Relocatable_relocs::RELOC_DISCARD;
|
|
else
|
|
{
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info =
|
|
reloc.get_r_info();
|
|
const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
const unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
|
|
|
|
if (r_sym >= local_symbol_count)
|
|
strategy = scan.global_strategy(r_type, object, r_sym);
|
|
else
|
|
{
|
|
gold_assert(plocal_syms != NULL);
|
|
typename elfcpp::Sym<size, big_endian> lsym(plocal_syms
|
|
+ r_sym * sym_size);
|
|
unsigned int shndx = lsym.get_st_shndx();
|
|
bool is_ordinary;
|
|
shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
|
|
if (is_ordinary
|
|
&& shndx != elfcpp::SHN_UNDEF
|
|
&& !object->is_section_included(shndx))
|
|
{
|
|
// RELOC is a relocation against a local symbol
|
|
// defined in a section we are discarding. Discard
|
|
// the reloc. FIXME: Should we issue a warning?
|
|
strategy = Relocatable_relocs::RELOC_DISCARD;
|
|
}
|
|
else if (lsym.get_st_type() != elfcpp::STT_SECTION)
|
|
strategy = scan.local_non_section_strategy(r_type, object,
|
|
r_sym);
|
|
else
|
|
{
|
|
strategy = scan.local_section_strategy(r_type, object);
|
|
if (strategy != Relocatable_relocs::RELOC_DISCARD)
|
|
object->output_section(shndx)->set_needs_symtab_index();
|
|
}
|
|
|
|
if (strategy == Relocatable_relocs::RELOC_COPY)
|
|
object->set_must_have_output_symtab_entry(r_sym);
|
|
}
|
|
}
|
|
|
|
rr->set_next_reloc_strategy(strategy);
|
|
}
|
|
}
|
|
|
|
// Relocate relocs. Called for a relocatable link, and for --emit-relocs.
|
|
// This is a default definition which should work for most targets.
|
|
|
|
template<int size, bool big_endian, int sh_type>
|
|
void
|
|
relocate_relocs(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
const unsigned char* prelocs,
|
|
size_t reloc_count,
|
|
Output_section* output_section,
|
|
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
|
|
unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
|
|
section_size_type view_size,
|
|
unsigned char* reloc_view,
|
|
section_size_type reloc_view_size)
|
|
{
|
|
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
|
|
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc_write
|
|
Reltype_write;
|
|
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
|
|
const Address invalid_address = static_cast<Address>(0) - 1;
|
|
|
|
Sized_relobj_file<size, big_endian>* const object = relinfo->object;
|
|
const unsigned int local_count = object->local_symbol_count();
|
|
|
|
unsigned char* pwrite = reloc_view;
|
|
|
|
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
|
|
{
|
|
Relocatable_relocs::Reloc_strategy strategy = relinfo->rr->strategy(i);
|
|
if (strategy == Relocatable_relocs::RELOC_DISCARD)
|
|
continue;
|
|
|
|
if (strategy == Relocatable_relocs::RELOC_SPECIAL)
|
|
{
|
|
// Target wants to handle this relocation.
|
|
Sized_target<size, big_endian>* target =
|
|
parameters->sized_target<size, big_endian>();
|
|
target->relocate_special_relocatable(relinfo, sh_type, prelocs,
|
|
i, output_section,
|
|
offset_in_output_section,
|
|
view, view_address,
|
|
view_size, pwrite);
|
|
pwrite += reloc_size;
|
|
continue;
|
|
}
|
|
Reltype reloc(prelocs);
|
|
Reltype_write reloc_write(pwrite);
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
|
|
const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
|
|
const unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
|
|
|
|
// Get the new symbol index.
|
|
|
|
Output_section* os = NULL;
|
|
unsigned int new_symndx;
|
|
if (r_sym < local_count)
|
|
{
|
|
switch (strategy)
|
|
{
|
|
case Relocatable_relocs::RELOC_COPY:
|
|
if (r_sym == 0)
|
|
new_symndx = 0;
|
|
else
|
|
{
|
|
new_symndx = object->symtab_index(r_sym);
|
|
gold_assert(new_symndx != -1U);
|
|
}
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8:
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4_UNALIGNED:
|
|
{
|
|
// We are adjusting a section symbol. We need to find
|
|
// the symbol table index of the section symbol for
|
|
// the output section corresponding to input section
|
|
// in which this symbol is defined.
|
|
gold_assert(r_sym < local_count);
|
|
bool is_ordinary;
|
|
unsigned int shndx =
|
|
object->local_symbol_input_shndx(r_sym, &is_ordinary);
|
|
gold_assert(is_ordinary);
|
|
os = object->output_section(shndx);
|
|
gold_assert(os != NULL);
|
|
gold_assert(os->needs_symtab_index());
|
|
new_symndx = os->symtab_index();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const Symbol* gsym = object->global_symbol(r_sym);
|
|
gold_assert(gsym != NULL);
|
|
if (gsym->is_forwarder())
|
|
gsym = relinfo->symtab->resolve_forwards(gsym);
|
|
|
|
gold_assert(gsym->has_symtab_index());
|
|
new_symndx = gsym->symtab_index();
|
|
}
|
|
|
|
// Get the new offset--the location in the output section where
|
|
// this relocation should be applied.
|
|
|
|
Address offset = reloc.get_r_offset();
|
|
Address new_offset;
|
|
if (offset_in_output_section != invalid_address)
|
|
new_offset = offset + offset_in_output_section;
|
|
else
|
|
{
|
|
section_offset_type sot_offset =
|
|
convert_types<section_offset_type, Address>(offset);
|
|
section_offset_type new_sot_offset =
|
|
output_section->output_offset(object, relinfo->data_shndx,
|
|
sot_offset);
|
|
gold_assert(new_sot_offset != -1);
|
|
new_offset = new_sot_offset;
|
|
}
|
|
|
|
// In an object file, r_offset is an offset within the section.
|
|
// In an executable or dynamic object, generated by
|
|
// --emit-relocs, r_offset is an absolute address.
|
|
if (!parameters->options().relocatable())
|
|
{
|
|
new_offset += view_address;
|
|
if (offset_in_output_section != invalid_address)
|
|
new_offset -= offset_in_output_section;
|
|
}
|
|
|
|
reloc_write.put_r_offset(new_offset);
|
|
reloc_write.put_r_info(elfcpp::elf_r_info<size>(new_symndx, r_type));
|
|
|
|
// Handle the reloc addend based on the strategy.
|
|
|
|
if (strategy == Relocatable_relocs::RELOC_COPY)
|
|
{
|
|
if (sh_type == elfcpp::SHT_RELA)
|
|
Reloc_types<sh_type, size, big_endian>::
|
|
copy_reloc_addend(&reloc_write,
|
|
&reloc);
|
|
}
|
|
else
|
|
{
|
|
// The relocation uses a section symbol in the input file.
|
|
// We are adjusting it to use a section symbol in the output
|
|
// file. The input section symbol refers to some address in
|
|
// the input section. We need the relocation in the output
|
|
// file to refer to that same address. This adjustment to
|
|
// the addend is the same calculation we use for a simple
|
|
// absolute relocation for the input section symbol.
|
|
|
|
const Symbol_value<size>* psymval = object->local_symbol(r_sym);
|
|
|
|
unsigned char* padd = view + offset;
|
|
switch (strategy)
|
|
{
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA:
|
|
{
|
|
typename elfcpp::Elf_types<size>::Elf_Swxword addend;
|
|
addend = Reloc_types<sh_type, size, big_endian>::
|
|
get_reloc_addend(&reloc);
|
|
gold_assert(os != NULL);
|
|
addend = psymval->value(object, addend) - os->address();
|
|
Reloc_types<sh_type, size, big_endian>::
|
|
set_reloc_addend(&reloc_write, addend);
|
|
}
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_0:
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_1:
|
|
Relocate_functions<size, big_endian>::rel8(padd, object,
|
|
psymval);
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_2:
|
|
Relocate_functions<size, big_endian>::rel16(padd, object,
|
|
psymval);
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4:
|
|
Relocate_functions<size, big_endian>::rel32(padd, object,
|
|
psymval);
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_8:
|
|
Relocate_functions<size, big_endian>::rel64(padd, object,
|
|
psymval);
|
|
break;
|
|
|
|
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_4_UNALIGNED:
|
|
Relocate_functions<size, big_endian>::rel32_unaligned(padd,
|
|
object,
|
|
psymval);
|
|
break;
|
|
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
pwrite += reloc_size;
|
|
}
|
|
|
|
gold_assert(static_cast<section_size_type>(pwrite - reloc_view)
|
|
== reloc_view_size);
|
|
}
|
|
|
|
} // End namespace gold.
|
|
|
|
#endif // !defined(GOLD_TARGET_RELOC_H)
|