43193fe9fc
Relocations referring to discarded sections are now treated as errors instead of warnings. Also with this patch, we will now print the section group signature and the object file with the prevailing definition of that group along with the name of the symbol that the relocation is referring to. This additional information should be much more useful to anyone trying to track down the source of such errors. To do so, we now map each discarded section to the Kept_section info in the Layout class, and defer the logic that maps a discarded section to its counterpart in the kept group. This gives us the information we need to identify the signature symbol given the discarded section, and the name of the object file that provided the prevailing (i.e., first) definition of that group. gold/ * object.cc (Sized_relobj_file::include_section_group): Store reference to Kept_section info for discarded comdat sections regardless of size. Move size checking to map_to_kept_section. (Sized_relobj_file::include_linkonce_section): Likewise. (Sized_relobj_file::map_to_kept_section): Add section name parameter. Insert size checking logic from above functions. (Sized_relobj_file::find_kept_section_object): New method. (Sized_relobj_file::get_symbol_name): New method. * object.h (Sized_relobj_file::map_to_kept_section): Add section_name parameter. Adjust all callers. (Sized_relobj_file::find_kept_section_object): New method. (Sized_relobj_file::get_symbol_name): New method. (Sized_relobj_file::Kept_comdat_section): Replace object and shndx fields with sh_size, kept_section, symndx, and is_comdat fields. (Sized_relobj_file::set_kept_comdat_section): Replace kept_object and kept_shndx parameters with is_comdat, symndx, sh_size, and kept_section. (Sized_relobj_file::get_kept_comdat_section): Likewise. * target-reloc.h (enum Comdat_behavior): Change CB_WARNING to CB_ERROR. Adjust all references. (issue_undefined_symbol_error): New function template. (relocate_section): Pass section name to map_to_kept_section. Move discarded section code to new function above. * aarch64.cc (Target_aarch64::scan_reloc_section_for_stubs): Move declaration for gsym out one level. Call issue_discarded_error. * arm.cc (Target_arm::scan_reloc_section_for_stubs): Likewise. * powerpc.cc (Relocate_comdat_behavior): Change CB_WARNING to CB_ERROR.
1000 lines
32 KiB
C++
1000 lines
32 KiB
C++
// target-reloc.h -- target specific relocation support -*- C++ -*-
|
|
|
|
// Copyright (C) 2006-2018 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,
|
|
typename Scan, typename Classify_reloc>
|
|
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 Classify_reloc::Reltype Reltype;
|
|
const int reloc_size = Classify_reloc::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;
|
|
|
|
unsigned int r_sym = Classify_reloc::get_r_sym(&reloc);
|
|
unsigned int r_type = Classify_reloc::get_r_type(&reloc);
|
|
|
|
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_ERROR // Print an error.
|
|
};
|
|
|
|
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_ERROR;
|
|
}
|
|
};
|
|
|
|
// 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,
|
|
// unless they're placeholder symbols that should have been
|
|
// provided by a plugin.
|
|
if (sym->is_defined_in_discarded_section() && !sym->is_placeholder())
|
|
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;
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
inline void
|
|
issue_discarded_error(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
size_t shndx,
|
|
section_offset_type offset,
|
|
unsigned int r_sym,
|
|
const Symbol* gsym)
|
|
{
|
|
Sized_relobj_file<size, big_endian>* object = relinfo->object;
|
|
|
|
if (gsym == NULL)
|
|
{
|
|
gold_error_at_location(
|
|
relinfo, shndx, offset,
|
|
_("relocation refers to local symbol \"%s\" [%u], "
|
|
"which is defined in a discarded section"),
|
|
object->get_symbol_name(r_sym), r_sym);
|
|
}
|
|
else
|
|
{
|
|
gold_error_at_location(
|
|
relinfo, shndx, offset,
|
|
_("relocation refers to global symbol \"%s\", "
|
|
"which is defined in a discarded section"),
|
|
gsym->demangled_name().c_str());
|
|
}
|
|
|
|
bool is_ordinary;
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value;
|
|
unsigned int orig_shndx = object->symbol_section_and_value(r_sym, &value,
|
|
&is_ordinary);
|
|
if (orig_shndx != elfcpp::SHN_UNDEF)
|
|
{
|
|
unsigned int key_symndx;
|
|
Relobj* kept_obj = object->find_kept_section_object(orig_shndx,
|
|
&key_symndx);
|
|
if (key_symndx != 0)
|
|
gold_info(_(" section group signature: \"%s\""),
|
|
object->get_symbol_name(key_symndx));
|
|
if (kept_obj != NULL)
|
|
gold_info(_(" prevailing definition is from %s"),
|
|
kept_obj->name().c_str());
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
typename Relocate,
|
|
typename Relocate_comdat_behavior,
|
|
typename Classify_reloc>
|
|
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 Classify_reloc::Reltype Reltype;
|
|
const int reloc_size = Classify_reloc::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;
|
|
}
|
|
|
|
unsigned int r_sym = Classify_reloc::get_r_sym(&reloc);
|
|
|
|
const Sized_symbol<size>* sym;
|
|
|
|
Symbol_value<size> symval;
|
|
const Symbol_value<size> *psymval;
|
|
bool is_defined_in_discarded_section;
|
|
unsigned int shndx;
|
|
const Symbol* gsym = NULL;
|
|
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
|
|
{
|
|
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)
|
|
{
|
|
std::string name = object->section_name(relinfo->data_shndx);
|
|
|
|
if (comdat_behavior == CB_UNDETERMINED)
|
|
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, name, &found);
|
|
if (found)
|
|
symval2.set_output_value(value + psymval->input_value());
|
|
else
|
|
symval2.set_output_value(0);
|
|
}
|
|
else
|
|
{
|
|
if (comdat_behavior == CB_ERROR)
|
|
issue_discarded_error(relinfo, i, offset, r_sym, gsym);
|
|
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, Classify_reloc::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);
|
|
}
|
|
|
|
// A class for inquiring about properties of a relocation,
|
|
// used while scanning relocs during a relocatable link and
|
|
// garbage collection. This class may be used as the default
|
|
// for SHT_RELA targets, but SHT_REL targets must implement
|
|
// a derived class that overrides get_size_for_reloc.
|
|
// The MIPS-64 target also needs to override the methods
|
|
// for accessing the r_sym and r_type fields of a relocation,
|
|
// due to its non-standard use of the r_info field.
|
|
|
|
template<int sh_type_, int size, bool big_endian>
|
|
class Default_classify_reloc
|
|
{
|
|
public:
|
|
typedef typename Reloc_types<sh_type_, size, big_endian>::Reloc
|
|
Reltype;
|
|
typedef typename Reloc_types<sh_type_, size, big_endian>::Reloc_write
|
|
Reltype_write;
|
|
static const int reloc_size =
|
|
Reloc_types<sh_type_, size, big_endian>::reloc_size;
|
|
static const int sh_type = sh_type_;
|
|
|
|
// Return the symbol referred to by the relocation.
|
|
static inline unsigned int
|
|
get_r_sym(const Reltype* reloc)
|
|
{ return elfcpp::elf_r_sym<size>(reloc->get_r_info()); }
|
|
|
|
// Return the type of the relocation.
|
|
static inline unsigned int
|
|
get_r_type(const Reltype* reloc)
|
|
{ return elfcpp::elf_r_type<size>(reloc->get_r_info()); }
|
|
|
|
// Return the explicit addend of the relocation (return 0 for SHT_REL).
|
|
static inline typename elfcpp::Elf_types<size>::Elf_Swxword
|
|
get_r_addend(const Reltype* reloc)
|
|
{ return Reloc_types<sh_type_, size, big_endian>::get_reloc_addend(reloc); }
|
|
|
|
// Write the r_info field to a new reloc, using the r_info field from
|
|
// the original reloc, replacing the r_sym field with R_SYM.
|
|
static inline void
|
|
put_r_info(Reltype_write* new_reloc, Reltype* reloc, unsigned int r_sym)
|
|
{
|
|
unsigned int r_type = elfcpp::elf_r_type<size>(reloc->get_r_info());
|
|
new_reloc->put_r_info(elfcpp::elf_r_info<size>(r_sym, r_type));
|
|
}
|
|
|
|
// Write the r_addend field to a new reloc.
|
|
static inline void
|
|
put_r_addend(Reltype_write* to,
|
|
typename elfcpp::Elf_types<size>::Elf_Swxword addend)
|
|
{ Reloc_types<sh_type_, size, big_endian>::set_reloc_addend(to, addend); }
|
|
|
|
// Return the size of the addend of the relocation (only used for SHT_REL).
|
|
static unsigned int
|
|
get_size_for_reloc(unsigned int, Relobj*)
|
|
{
|
|
gold_unreachable();
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
// This class may be used as a typical class for the
|
|
// Scan_relocatable_reloc parameter to scan_relocatable_relocs.
|
|
// 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<typename Classify_reloc>
|
|
class Default_scan_relocatable_relocs
|
|
{
|
|
public:
|
|
typedef typename Classify_reloc::Reltype Reltype;
|
|
static const int reloc_size = Classify_reloc::reloc_size;
|
|
static const int sh_type = Classify_reloc::sh_type;
|
|
|
|
// Return the symbol referred to by the relocation.
|
|
static inline unsigned int
|
|
get_r_sym(const Reltype* reloc)
|
|
{ return Classify_reloc::get_r_sym(reloc); }
|
|
|
|
// Return the type of the relocation.
|
|
static inline unsigned int
|
|
get_r_type(const Reltype* reloc)
|
|
{ return Classify_reloc::get_r_type(reloc); }
|
|
|
|
// 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
|
|
{
|
|
switch (Classify_reloc::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; }
|
|
};
|
|
|
|
// This is a strategy class used with scan_relocatable_relocs
|
|
// and --emit-relocs.
|
|
|
|
template<typename Classify_reloc>
|
|
class Default_emit_relocs_strategy
|
|
{
|
|
public:
|
|
typedef typename Classify_reloc::Reltype Reltype;
|
|
static const int reloc_size = Classify_reloc::reloc_size;
|
|
static const int sh_type = Classify_reloc::sh_type;
|
|
|
|
// Return the symbol referred to by the relocation.
|
|
static inline unsigned int
|
|
get_r_sym(const Reltype* reloc)
|
|
{ return Classify_reloc::get_r_sym(reloc); }
|
|
|
|
// Return the type of the relocation.
|
|
static inline unsigned int
|
|
get_r_type(const Reltype* reloc)
|
|
{ return Classify_reloc::get_r_type(reloc); }
|
|
|
|
// 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 relocs during a relocatable link. This is a default
|
|
// definition which should work for most targets.
|
|
// Scan_relocatable_reloc must name a class type which provides three
|
|
// functions which return a Relocatable_relocs::Reloc_strategy code:
|
|
// global_strategy, local_non_section_strategy, and
|
|
// local_section_strategy. Most targets should be able to use
|
|
// Default_scan_relocatable_relocs as this class.
|
|
|
|
template<int size, bool big_endian, typename 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 Scan_relocatable_reloc::Reltype Reltype;
|
|
const int reloc_size = Scan_relocatable_reloc::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
|
|
{
|
|
const unsigned int r_sym = Scan_relocatable_reloc::get_r_sym(&reloc);
|
|
const unsigned int r_type =
|
|
Scan_relocatable_reloc::get_r_type(&reloc);
|
|
|
|
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, typename Classify_reloc>
|
|
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 Classify_reloc::Reltype Reltype;
|
|
typedef typename Classify_reloc::Reltype_write Reltype_write;
|
|
const int reloc_size = Classify_reloc::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;
|
|
|
|
const bool relocatable = parameters->options().relocatable();
|
|
|
|
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, Classify_reloc::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);
|
|
|
|
const unsigned int r_sym = Classify_reloc::get_r_sym(&reloc);
|
|
|
|
// 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 (!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);
|
|
Classify_reloc::put_r_info(&reloc_write, &reloc, new_symndx);
|
|
|
|
// Handle the reloc addend based on the strategy.
|
|
|
|
if (strategy == Relocatable_relocs::RELOC_COPY)
|
|
{
|
|
if (Classify_reloc::sh_type == elfcpp::SHT_RELA)
|
|
Classify_reloc::put_r_addend(&reloc_write,
|
|
Classify_reloc::get_r_addend(&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
|
|
= Classify_reloc::get_r_addend(&reloc);
|
|
addend = psymval->value(object, addend);
|
|
// In a relocatable link, the symbol value is relative to
|
|
// the start of the output section. For a non-relocatable
|
|
// link, we need to adjust the addend.
|
|
if (!relocatable)
|
|
{
|
|
gold_assert(os != NULL);
|
|
addend -= os->address();
|
|
}
|
|
Classify_reloc::put_r_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)
|