mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-01 05:55:23 +08:00
b45e00b3ed
If a symbol is defined with ".symver foo,foo@VER", the assembler creates two symbols in the object: one unversioned, and one with the (non-default) version "VER". If foo is listed in a version script, gold would then make the first of those symbols the default version, and would ignore the second symbol as a duplicate, without making it a non-default version. While this is arguably reasonable behavior, it doesn't match Gnu ld behavior, so this patch fixes that by allowing the second definition to override the first by resetting the "default version" indication. Several test cases from the Gnu ld testsuite also exposed another related problem, where a symbol defined with ".symver foo,foo@", placed into a shared library, is not handled properly by gold. This patch also fixes that case, binding the symbol to the base version. gold/ PR gold/18703 * dynobj.cc (Versions::record_version): Handle symbol defined with base version. (Versions::symbol_section_contents): Likewise. * symtab.h (Symbol::set_is_not_default): New class method. (Symbol_table::resolve): Add is_default_version parameter. (Symbol_table::should_override): Likewise. * resolve.cc (Symbol_table::resolve): Add is_default_version parameter, and pass to should_override. Adjust all callers and explicit instantiations. (Symbol_table::should_override): Add is_default_value parameter; allow default version in a dynamic object to override existing definition from same object. * symtab.cc (Symbol_table::add_from_object): Handle case where same symbol is defined as unversioned and non-default version in the same object. * testsuite/Makefile.am (ver_test_13): New test case. * testsuite/Makefile.in: Regenerate. * testsuite/ver_test_4.cc: Add test for symbol with base version. * testsuite/ver_test_4.sh: Likewise. * testsuite/ver_test_13.c: New source file. * testsuite/ver_test_13.script: New version script. * testsuite/ver_test_13.sh: New test case.
1977 lines
62 KiB
C++
1977 lines
62 KiB
C++
// symtab.h -- the gold symbol table -*- 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.
|
|
|
|
// Symbol_table
|
|
// The symbol table.
|
|
|
|
#ifndef GOLD_SYMTAB_H
|
|
#define GOLD_SYMTAB_H
|
|
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "elfcpp.h"
|
|
#include "parameters.h"
|
|
#include "stringpool.h"
|
|
#include "object.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
class Mapfile;
|
|
class Object;
|
|
class Relobj;
|
|
template<int size, bool big_endian>
|
|
class Sized_relobj_file;
|
|
template<int size, bool big_endian>
|
|
class Sized_pluginobj;
|
|
class Dynobj;
|
|
template<int size, bool big_endian>
|
|
class Sized_dynobj;
|
|
template<int size, bool big_endian>
|
|
class Sized_incrobj;
|
|
class Versions;
|
|
class Version_script_info;
|
|
class Input_objects;
|
|
class Output_data;
|
|
class Output_section;
|
|
class Output_segment;
|
|
class Output_file;
|
|
class Output_symtab_xindex;
|
|
class Garbage_collection;
|
|
class Icf;
|
|
|
|
// The base class of an entry in the symbol table. The symbol table
|
|
// can have a lot of entries, so we don't want this class too big.
|
|
// Size dependent fields can be found in the template class
|
|
// Sized_symbol. Targets may support their own derived classes.
|
|
|
|
class Symbol
|
|
{
|
|
public:
|
|
// Because we want the class to be small, we don't use any virtual
|
|
// functions. But because symbols can be defined in different
|
|
// places, we need to classify them. This enum is the different
|
|
// sources of symbols we support.
|
|
enum Source
|
|
{
|
|
// Symbol defined in a relocatable or dynamic input file--this is
|
|
// the most common case.
|
|
FROM_OBJECT,
|
|
// Symbol defined in an Output_data, a special section created by
|
|
// the target.
|
|
IN_OUTPUT_DATA,
|
|
// Symbol defined in an Output_segment, with no associated
|
|
// section.
|
|
IN_OUTPUT_SEGMENT,
|
|
// Symbol value is constant.
|
|
IS_CONSTANT,
|
|
// Symbol is undefined.
|
|
IS_UNDEFINED
|
|
};
|
|
|
|
// When the source is IN_OUTPUT_SEGMENT, we need to describe what
|
|
// the offset means.
|
|
enum Segment_offset_base
|
|
{
|
|
// From the start of the segment.
|
|
SEGMENT_START,
|
|
// From the end of the segment.
|
|
SEGMENT_END,
|
|
// From the filesz of the segment--i.e., after the loaded bytes
|
|
// but before the bytes which are allocated but zeroed.
|
|
SEGMENT_BSS
|
|
};
|
|
|
|
// Return the symbol name.
|
|
const char*
|
|
name() const
|
|
{ return this->name_; }
|
|
|
|
// Return the (ANSI) demangled version of the name, if
|
|
// parameters.demangle() is true. Otherwise, return the name. This
|
|
// is intended to be used only for logging errors, so it's not
|
|
// super-efficient.
|
|
std::string
|
|
demangled_name() const;
|
|
|
|
// Return the symbol version. This will return NULL for an
|
|
// unversioned symbol.
|
|
const char*
|
|
version() const
|
|
{ return this->version_; }
|
|
|
|
void
|
|
clear_version()
|
|
{ this->version_ = NULL; }
|
|
|
|
// Return whether this version is the default for this symbol name
|
|
// (eg, "foo@@V2" is a default version; "foo@V1" is not). Only
|
|
// meaningful for versioned symbols.
|
|
bool
|
|
is_default() const
|
|
{
|
|
gold_assert(this->version_ != NULL);
|
|
return this->is_def_;
|
|
}
|
|
|
|
// Set that this version is the default for this symbol name.
|
|
void
|
|
set_is_default()
|
|
{ this->is_def_ = true; }
|
|
|
|
// Set that this version is not the default for this symbol name.
|
|
void
|
|
set_is_not_default()
|
|
{ this->is_def_ = false; }
|
|
|
|
// Return the symbol's name as name@version (or name@@version).
|
|
std::string
|
|
versioned_name() const;
|
|
|
|
// Return the symbol source.
|
|
Source
|
|
source() const
|
|
{ return this->source_; }
|
|
|
|
// Return the object with which this symbol is associated.
|
|
Object*
|
|
object() const
|
|
{
|
|
gold_assert(this->source_ == FROM_OBJECT);
|
|
return this->u_.from_object.object;
|
|
}
|
|
|
|
// Return the index of the section in the input relocatable or
|
|
// dynamic object file.
|
|
unsigned int
|
|
shndx(bool* is_ordinary) const
|
|
{
|
|
gold_assert(this->source_ == FROM_OBJECT);
|
|
*is_ordinary = this->is_ordinary_shndx_;
|
|
return this->u_.from_object.shndx;
|
|
}
|
|
|
|
// Return the output data section with which this symbol is
|
|
// associated, if the symbol was specially defined with respect to
|
|
// an output data section.
|
|
Output_data*
|
|
output_data() const
|
|
{
|
|
gold_assert(this->source_ == IN_OUTPUT_DATA);
|
|
return this->u_.in_output_data.output_data;
|
|
}
|
|
|
|
// If this symbol was defined with respect to an output data
|
|
// section, return whether the value is an offset from end.
|
|
bool
|
|
offset_is_from_end() const
|
|
{
|
|
gold_assert(this->source_ == IN_OUTPUT_DATA);
|
|
return this->u_.in_output_data.offset_is_from_end;
|
|
}
|
|
|
|
// Return the output segment with which this symbol is associated,
|
|
// if the symbol was specially defined with respect to an output
|
|
// segment.
|
|
Output_segment*
|
|
output_segment() const
|
|
{
|
|
gold_assert(this->source_ == IN_OUTPUT_SEGMENT);
|
|
return this->u_.in_output_segment.output_segment;
|
|
}
|
|
|
|
// If this symbol was defined with respect to an output segment,
|
|
// return the offset base.
|
|
Segment_offset_base
|
|
offset_base() const
|
|
{
|
|
gold_assert(this->source_ == IN_OUTPUT_SEGMENT);
|
|
return this->u_.in_output_segment.offset_base;
|
|
}
|
|
|
|
// Return the symbol binding.
|
|
elfcpp::STB
|
|
binding() const
|
|
{ return this->binding_; }
|
|
|
|
// Return the symbol type.
|
|
elfcpp::STT
|
|
type() const
|
|
{ return this->type_; }
|
|
|
|
// Set the symbol type.
|
|
void
|
|
set_type(elfcpp::STT type)
|
|
{ this->type_ = type; }
|
|
|
|
// Return true for function symbol.
|
|
bool
|
|
is_func() const
|
|
{
|
|
return (this->type_ == elfcpp::STT_FUNC
|
|
|| this->type_ == elfcpp::STT_GNU_IFUNC);
|
|
}
|
|
|
|
// Return the symbol visibility.
|
|
elfcpp::STV
|
|
visibility() const
|
|
{ return this->visibility_; }
|
|
|
|
// Set the visibility.
|
|
void
|
|
set_visibility(elfcpp::STV visibility)
|
|
{ this->visibility_ = visibility; }
|
|
|
|
// Override symbol visibility.
|
|
void
|
|
override_visibility(elfcpp::STV);
|
|
|
|
// Set whether the symbol was originally a weak undef or a regular undef
|
|
// when resolved by a dynamic def or by a special symbol.
|
|
inline void
|
|
set_undef_binding(elfcpp::STB bind)
|
|
{
|
|
if (!this->undef_binding_set_ || this->undef_binding_weak_)
|
|
{
|
|
this->undef_binding_weak_ = bind == elfcpp::STB_WEAK;
|
|
this->undef_binding_set_ = true;
|
|
}
|
|
}
|
|
|
|
// Return TRUE if a weak undef was resolved by a dynamic def or
|
|
// by a special symbol.
|
|
inline bool
|
|
is_undef_binding_weak() const
|
|
{ return this->undef_binding_weak_; }
|
|
|
|
// Return the non-visibility part of the st_other field.
|
|
unsigned char
|
|
nonvis() const
|
|
{ return this->nonvis_; }
|
|
|
|
// Set the non-visibility part of the st_other field.
|
|
void
|
|
set_nonvis(unsigned int nonvis)
|
|
{ this->nonvis_ = nonvis; }
|
|
|
|
// Return whether this symbol is a forwarder. This will never be
|
|
// true of a symbol found in the hash table, but may be true of
|
|
// symbol pointers attached to object files.
|
|
bool
|
|
is_forwarder() const
|
|
{ return this->is_forwarder_; }
|
|
|
|
// Mark this symbol as a forwarder.
|
|
void
|
|
set_forwarder()
|
|
{ this->is_forwarder_ = true; }
|
|
|
|
// Return whether this symbol has an alias in the weak aliases table
|
|
// in Symbol_table.
|
|
bool
|
|
has_alias() const
|
|
{ return this->has_alias_; }
|
|
|
|
// Mark this symbol as having an alias.
|
|
void
|
|
set_has_alias()
|
|
{ this->has_alias_ = true; }
|
|
|
|
// Return whether this symbol needs an entry in the dynamic symbol
|
|
// table.
|
|
bool
|
|
needs_dynsym_entry() const
|
|
{
|
|
return (this->needs_dynsym_entry_
|
|
|| (this->in_reg()
|
|
&& this->in_dyn()
|
|
&& this->is_externally_visible()));
|
|
}
|
|
|
|
// Mark this symbol as needing an entry in the dynamic symbol table.
|
|
void
|
|
set_needs_dynsym_entry()
|
|
{ this->needs_dynsym_entry_ = true; }
|
|
|
|
// Return whether this symbol should be added to the dynamic symbol
|
|
// table.
|
|
bool
|
|
should_add_dynsym_entry(Symbol_table*) const;
|
|
|
|
// Return whether this symbol has been seen in a regular object.
|
|
bool
|
|
in_reg() const
|
|
{ return this->in_reg_; }
|
|
|
|
// Mark this symbol as having been seen in a regular object.
|
|
void
|
|
set_in_reg()
|
|
{ this->in_reg_ = true; }
|
|
|
|
// Return whether this symbol has been seen in a dynamic object.
|
|
bool
|
|
in_dyn() const
|
|
{ return this->in_dyn_; }
|
|
|
|
// Mark this symbol as having been seen in a dynamic object.
|
|
void
|
|
set_in_dyn()
|
|
{ this->in_dyn_ = true; }
|
|
|
|
// Return whether this symbol has been seen in a real ELF object.
|
|
// (IN_REG will return TRUE if the symbol has been seen in either
|
|
// a real ELF object or an object claimed by a plugin.)
|
|
bool
|
|
in_real_elf() const
|
|
{ return this->in_real_elf_; }
|
|
|
|
// Mark this symbol as having been seen in a real ELF object.
|
|
void
|
|
set_in_real_elf()
|
|
{ this->in_real_elf_ = true; }
|
|
|
|
// Return whether this symbol was defined in a section that was
|
|
// discarded from the link. This is used to control some error
|
|
// reporting.
|
|
bool
|
|
is_defined_in_discarded_section() const
|
|
{ return this->is_defined_in_discarded_section_; }
|
|
|
|
// Mark this symbol as having been defined in a discarded section.
|
|
void
|
|
set_is_defined_in_discarded_section()
|
|
{ this->is_defined_in_discarded_section_ = true; }
|
|
|
|
// Return the index of this symbol in the output file symbol table.
|
|
// A value of -1U means that this symbol is not going into the
|
|
// output file. This starts out as zero, and is set to a non-zero
|
|
// value by Symbol_table::finalize. It is an error to ask for the
|
|
// symbol table index before it has been set.
|
|
unsigned int
|
|
symtab_index() const
|
|
{
|
|
gold_assert(this->symtab_index_ != 0);
|
|
return this->symtab_index_;
|
|
}
|
|
|
|
// Set the index of the symbol in the output file symbol table.
|
|
void
|
|
set_symtab_index(unsigned int index)
|
|
{
|
|
gold_assert(index != 0);
|
|
this->symtab_index_ = index;
|
|
}
|
|
|
|
// Return whether this symbol already has an index in the output
|
|
// file symbol table.
|
|
bool
|
|
has_symtab_index() const
|
|
{ return this->symtab_index_ != 0; }
|
|
|
|
// Return the index of this symbol in the dynamic symbol table. A
|
|
// value of -1U means that this symbol is not going into the dynamic
|
|
// symbol table. This starts out as zero, and is set to a non-zero
|
|
// during Layout::finalize. It is an error to ask for the dynamic
|
|
// symbol table index before it has been set.
|
|
unsigned int
|
|
dynsym_index() const
|
|
{
|
|
gold_assert(this->dynsym_index_ != 0);
|
|
return this->dynsym_index_;
|
|
}
|
|
|
|
// Set the index of the symbol in the dynamic symbol table.
|
|
void
|
|
set_dynsym_index(unsigned int index)
|
|
{
|
|
gold_assert(index != 0);
|
|
this->dynsym_index_ = index;
|
|
}
|
|
|
|
// Return whether this symbol already has an index in the dynamic
|
|
// symbol table.
|
|
bool
|
|
has_dynsym_index() const
|
|
{ return this->dynsym_index_ != 0; }
|
|
|
|
// Return whether this symbol has an entry in the GOT section.
|
|
// For a TLS symbol, this GOT entry will hold its tp-relative offset.
|
|
bool
|
|
has_got_offset(unsigned int got_type) const
|
|
{ return this->got_offsets_.get_offset(got_type) != -1U; }
|
|
|
|
// Return the offset into the GOT section of this symbol.
|
|
unsigned int
|
|
got_offset(unsigned int got_type) const
|
|
{
|
|
unsigned int got_offset = this->got_offsets_.get_offset(got_type);
|
|
gold_assert(got_offset != -1U);
|
|
return got_offset;
|
|
}
|
|
|
|
// Set the GOT offset of this symbol.
|
|
void
|
|
set_got_offset(unsigned int got_type, unsigned int got_offset)
|
|
{ this->got_offsets_.set_offset(got_type, got_offset); }
|
|
|
|
// Return the GOT offset list.
|
|
const Got_offset_list*
|
|
got_offset_list() const
|
|
{ return this->got_offsets_.get_list(); }
|
|
|
|
// Return whether this symbol has an entry in the PLT section.
|
|
bool
|
|
has_plt_offset() const
|
|
{ return this->plt_offset_ != -1U; }
|
|
|
|
// Return the offset into the PLT section of this symbol.
|
|
unsigned int
|
|
plt_offset() const
|
|
{
|
|
gold_assert(this->has_plt_offset());
|
|
return this->plt_offset_;
|
|
}
|
|
|
|
// Set the PLT offset of this symbol.
|
|
void
|
|
set_plt_offset(unsigned int plt_offset)
|
|
{
|
|
gold_assert(plt_offset != -1U);
|
|
this->plt_offset_ = plt_offset;
|
|
}
|
|
|
|
// Return whether this dynamic symbol needs a special value in the
|
|
// dynamic symbol table.
|
|
bool
|
|
needs_dynsym_value() const
|
|
{ return this->needs_dynsym_value_; }
|
|
|
|
// Set that this dynamic symbol needs a special value in the dynamic
|
|
// symbol table.
|
|
void
|
|
set_needs_dynsym_value()
|
|
{
|
|
gold_assert(this->object()->is_dynamic());
|
|
this->needs_dynsym_value_ = true;
|
|
}
|
|
|
|
// Return true if the final value of this symbol is known at link
|
|
// time.
|
|
bool
|
|
final_value_is_known() const;
|
|
|
|
// Return true if SHNDX represents a common symbol. This depends on
|
|
// the target.
|
|
static bool
|
|
is_common_shndx(unsigned int shndx);
|
|
|
|
// Return whether this is a defined symbol (not undefined or
|
|
// common).
|
|
bool
|
|
is_defined() const
|
|
{
|
|
bool is_ordinary;
|
|
if (this->source_ != FROM_OBJECT)
|
|
return this->source_ != IS_UNDEFINED;
|
|
unsigned int shndx = this->shndx(&is_ordinary);
|
|
return (is_ordinary
|
|
? shndx != elfcpp::SHN_UNDEF
|
|
: !Symbol::is_common_shndx(shndx));
|
|
}
|
|
|
|
// Return true if this symbol is from a dynamic object.
|
|
bool
|
|
is_from_dynobj() const
|
|
{
|
|
return this->source_ == FROM_OBJECT && this->object()->is_dynamic();
|
|
}
|
|
|
|
// Return whether this is a placeholder symbol from a plugin object.
|
|
bool
|
|
is_placeholder() const
|
|
{
|
|
return this->source_ == FROM_OBJECT && this->object()->pluginobj() != NULL;
|
|
}
|
|
|
|
// Return whether this is an undefined symbol.
|
|
bool
|
|
is_undefined() const
|
|
{
|
|
bool is_ordinary;
|
|
return ((this->source_ == FROM_OBJECT
|
|
&& this->shndx(&is_ordinary) == elfcpp::SHN_UNDEF
|
|
&& is_ordinary)
|
|
|| this->source_ == IS_UNDEFINED);
|
|
}
|
|
|
|
// Return whether this is a weak undefined symbol.
|
|
bool
|
|
is_weak_undefined() const
|
|
{
|
|
return (this->is_undefined()
|
|
&& (this->binding() == elfcpp::STB_WEAK
|
|
|| this->is_undef_binding_weak()
|
|
|| parameters->options().weak_unresolved_symbols()));
|
|
}
|
|
|
|
// Return whether this is a strong undefined symbol.
|
|
bool
|
|
is_strong_undefined() const
|
|
{
|
|
return (this->is_undefined()
|
|
&& this->binding() != elfcpp::STB_WEAK
|
|
&& !this->is_undef_binding_weak()
|
|
&& !parameters->options().weak_unresolved_symbols());
|
|
}
|
|
|
|
// Return whether this is an absolute symbol.
|
|
bool
|
|
is_absolute() const
|
|
{
|
|
bool is_ordinary;
|
|
return ((this->source_ == FROM_OBJECT
|
|
&& this->shndx(&is_ordinary) == elfcpp::SHN_ABS
|
|
&& !is_ordinary)
|
|
|| this->source_ == IS_CONSTANT);
|
|
}
|
|
|
|
// Return whether this is a common symbol.
|
|
bool
|
|
is_common() const
|
|
{
|
|
if (this->source_ != FROM_OBJECT)
|
|
return false;
|
|
bool is_ordinary;
|
|
unsigned int shndx = this->shndx(&is_ordinary);
|
|
return !is_ordinary && Symbol::is_common_shndx(shndx);
|
|
}
|
|
|
|
// Return whether this symbol can be seen outside this object.
|
|
bool
|
|
is_externally_visible() const
|
|
{
|
|
return ((this->visibility_ == elfcpp::STV_DEFAULT
|
|
|| this->visibility_ == elfcpp::STV_PROTECTED)
|
|
&& !this->is_forced_local_);
|
|
}
|
|
|
|
// Return true if this symbol can be preempted by a definition in
|
|
// another link unit.
|
|
bool
|
|
is_preemptible() const
|
|
{
|
|
// It doesn't make sense to ask whether a symbol defined in
|
|
// another object is preemptible.
|
|
gold_assert(!this->is_from_dynobj());
|
|
|
|
// It doesn't make sense to ask whether an undefined symbol
|
|
// is preemptible.
|
|
gold_assert(!this->is_undefined());
|
|
|
|
// If a symbol does not have default visibility, it can not be
|
|
// seen outside this link unit and therefore is not preemptible.
|
|
if (this->visibility_ != elfcpp::STV_DEFAULT)
|
|
return false;
|
|
|
|
// If this symbol has been forced to be a local symbol by a
|
|
// version script, then it is not visible outside this link unit
|
|
// and is not preemptible.
|
|
if (this->is_forced_local_)
|
|
return false;
|
|
|
|
// If we are not producing a shared library, then nothing is
|
|
// preemptible.
|
|
if (!parameters->options().shared())
|
|
return false;
|
|
|
|
// If the symbol was named in a --dynamic-list script, it is preemptible.
|
|
if (parameters->options().in_dynamic_list(this->name()))
|
|
return true;
|
|
|
|
// If the user used -Bsymbolic, then nothing (else) is preemptible.
|
|
if (parameters->options().Bsymbolic())
|
|
return false;
|
|
|
|
// If the user used -Bsymbolic-functions, then functions are not
|
|
// preemptible. We explicitly check for not being STT_OBJECT,
|
|
// rather than for being STT_FUNC, because that is what the GNU
|
|
// linker does.
|
|
if (this->type() != elfcpp::STT_OBJECT
|
|
&& parameters->options().Bsymbolic_functions())
|
|
return false;
|
|
|
|
// Otherwise the symbol is preemptible.
|
|
return true;
|
|
}
|
|
|
|
// Return true if this symbol is a function that needs a PLT entry.
|
|
bool
|
|
needs_plt_entry() const
|
|
{
|
|
// An undefined symbol from an executable does not need a PLT entry.
|
|
if (this->is_undefined() && !parameters->options().shared())
|
|
return false;
|
|
|
|
// An STT_GNU_IFUNC symbol always needs a PLT entry, even when
|
|
// doing a static link.
|
|
if (this->type() == elfcpp::STT_GNU_IFUNC)
|
|
return true;
|
|
|
|
// We only need a PLT entry for a function.
|
|
if (!this->is_func())
|
|
return false;
|
|
|
|
// If we're doing a static link or a -pie link, we don't create
|
|
// PLT entries.
|
|
if (parameters->doing_static_link()
|
|
|| parameters->options().pie())
|
|
return false;
|
|
|
|
// We need a PLT entry if the function is defined in a dynamic
|
|
// object, or is undefined when building a shared object, or if it
|
|
// is subject to pre-emption.
|
|
return (this->is_from_dynobj()
|
|
|| this->is_undefined()
|
|
|| this->is_preemptible());
|
|
}
|
|
|
|
// When determining whether a reference to a symbol needs a dynamic
|
|
// relocation, we need to know several things about the reference.
|
|
// These flags may be or'ed together. 0 means that the symbol
|
|
// isn't referenced at all.
|
|
enum Reference_flags
|
|
{
|
|
// A reference to the symbol's absolute address. This includes
|
|
// references that cause an absolute address to be stored in the GOT.
|
|
ABSOLUTE_REF = 1,
|
|
// A reference that calculates the offset of the symbol from some
|
|
// anchor point, such as the PC or GOT.
|
|
RELATIVE_REF = 2,
|
|
// A TLS-related reference.
|
|
TLS_REF = 4,
|
|
// A reference that can always be treated as a function call.
|
|
FUNCTION_CALL = 8,
|
|
// When set, says that dynamic relocations are needed even if a
|
|
// symbol has a plt entry.
|
|
FUNC_DESC_ABI = 16,
|
|
};
|
|
|
|
// Given a direct absolute or pc-relative static relocation against
|
|
// the global symbol, this function returns whether a dynamic relocation
|
|
// is needed.
|
|
|
|
bool
|
|
needs_dynamic_reloc(int flags) const
|
|
{
|
|
// No dynamic relocations in a static link!
|
|
if (parameters->doing_static_link())
|
|
return false;
|
|
|
|
// A reference to an undefined symbol from an executable should be
|
|
// statically resolved to 0, and does not need a dynamic relocation.
|
|
// This matches gnu ld behavior.
|
|
if (this->is_undefined() && !parameters->options().shared())
|
|
return false;
|
|
|
|
// A reference to an absolute symbol does not need a dynamic relocation.
|
|
if (this->is_absolute())
|
|
return false;
|
|
|
|
// An absolute reference within a position-independent output file
|
|
// will need a dynamic relocation.
|
|
if ((flags & ABSOLUTE_REF)
|
|
&& parameters->options().output_is_position_independent())
|
|
return true;
|
|
|
|
// A function call that can branch to a local PLT entry does not need
|
|
// a dynamic relocation.
|
|
if ((flags & FUNCTION_CALL) && this->has_plt_offset())
|
|
return false;
|
|
|
|
// A reference to any PLT entry in a non-position-independent executable
|
|
// does not need a dynamic relocation.
|
|
if (!(flags & FUNC_DESC_ABI)
|
|
&& !parameters->options().output_is_position_independent()
|
|
&& this->has_plt_offset())
|
|
return false;
|
|
|
|
// A reference to a symbol defined in a dynamic object or to a
|
|
// symbol that is preemptible will need a dynamic relocation.
|
|
if (this->is_from_dynobj()
|
|
|| this->is_undefined()
|
|
|| this->is_preemptible())
|
|
return true;
|
|
|
|
// For all other cases, return FALSE.
|
|
return false;
|
|
}
|
|
|
|
// Whether we should use the PLT offset associated with a symbol for
|
|
// a relocation. FLAGS is a set of Reference_flags.
|
|
|
|
bool
|
|
use_plt_offset(int flags) const
|
|
{
|
|
// If the symbol doesn't have a PLT offset, then naturally we
|
|
// don't want to use it.
|
|
if (!this->has_plt_offset())
|
|
return false;
|
|
|
|
// For a STT_GNU_IFUNC symbol we always have to use the PLT entry.
|
|
if (this->type() == elfcpp::STT_GNU_IFUNC)
|
|
return true;
|
|
|
|
// If we are going to generate a dynamic relocation, then we will
|
|
// wind up using that, so no need to use the PLT entry.
|
|
if (this->needs_dynamic_reloc(flags))
|
|
return false;
|
|
|
|
// If the symbol is from a dynamic object, we need to use the PLT
|
|
// entry.
|
|
if (this->is_from_dynobj())
|
|
return true;
|
|
|
|
// If we are generating a shared object, and this symbol is
|
|
// undefined or preemptible, we need to use the PLT entry.
|
|
if (parameters->options().shared()
|
|
&& (this->is_undefined() || this->is_preemptible()))
|
|
return true;
|
|
|
|
// If this is a call to a weak undefined symbol, we need to use
|
|
// the PLT entry; the symbol may be defined by a library loaded
|
|
// at runtime.
|
|
if ((flags & FUNCTION_CALL) && this->is_weak_undefined())
|
|
return true;
|
|
|
|
// Otherwise we can use the regular definition.
|
|
return false;
|
|
}
|
|
|
|
// Given a direct absolute static relocation against
|
|
// the global symbol, where a dynamic relocation is needed, this
|
|
// function returns whether a relative dynamic relocation can be used.
|
|
// The caller must determine separately whether the static relocation
|
|
// is compatible with a relative relocation.
|
|
|
|
bool
|
|
can_use_relative_reloc(bool is_function_call) const
|
|
{
|
|
// A function call that can branch to a local PLT entry can
|
|
// use a RELATIVE relocation.
|
|
if (is_function_call && this->has_plt_offset())
|
|
return true;
|
|
|
|
// A reference to a symbol defined in a dynamic object or to a
|
|
// symbol that is preemptible can not use a RELATIVE relocation.
|
|
if (this->is_from_dynobj()
|
|
|| this->is_undefined()
|
|
|| this->is_preemptible())
|
|
return false;
|
|
|
|
// For all other cases, return TRUE.
|
|
return true;
|
|
}
|
|
|
|
// Return the output section where this symbol is defined. Return
|
|
// NULL if the symbol has an absolute value.
|
|
Output_section*
|
|
output_section() const;
|
|
|
|
// Set the symbol's output section. This is used for symbols
|
|
// defined in scripts. This should only be called after the symbol
|
|
// table has been finalized.
|
|
void
|
|
set_output_section(Output_section*);
|
|
|
|
// Set the symbol's output segment. This is used for pre-defined
|
|
// symbols whose segments aren't known until after layout is done
|
|
// (e.g., __ehdr_start).
|
|
void
|
|
set_output_segment(Output_segment*, Segment_offset_base);
|
|
|
|
// Set the symbol to undefined. This is used for pre-defined
|
|
// symbols whose segments aren't known until after layout is done
|
|
// (e.g., __ehdr_start).
|
|
void
|
|
set_undefined();
|
|
|
|
// Return whether there should be a warning for references to this
|
|
// symbol.
|
|
bool
|
|
has_warning() const
|
|
{ return this->has_warning_; }
|
|
|
|
// Mark this symbol as having a warning.
|
|
void
|
|
set_has_warning()
|
|
{ this->has_warning_ = true; }
|
|
|
|
// Return whether this symbol is defined by a COPY reloc from a
|
|
// dynamic object.
|
|
bool
|
|
is_copied_from_dynobj() const
|
|
{ return this->is_copied_from_dynobj_; }
|
|
|
|
// Mark this symbol as defined by a COPY reloc.
|
|
void
|
|
set_is_copied_from_dynobj()
|
|
{ this->is_copied_from_dynobj_ = true; }
|
|
|
|
// Return whether this symbol is forced to visibility STB_LOCAL
|
|
// by a "local:" entry in a version script.
|
|
bool
|
|
is_forced_local() const
|
|
{ return this->is_forced_local_; }
|
|
|
|
// Mark this symbol as forced to STB_LOCAL visibility.
|
|
void
|
|
set_is_forced_local()
|
|
{ this->is_forced_local_ = true; }
|
|
|
|
// Return true if this may need a COPY relocation.
|
|
// References from an executable object to non-function symbols
|
|
// defined in a dynamic object may need a COPY relocation.
|
|
bool
|
|
may_need_copy_reloc() const
|
|
{
|
|
return (parameters->options().copyreloc()
|
|
&& this->is_from_dynobj()
|
|
&& !this->is_func());
|
|
}
|
|
|
|
// Return true if this symbol was predefined by the linker.
|
|
bool
|
|
is_predefined() const
|
|
{ return this->is_predefined_; }
|
|
|
|
// Return true if this is a C++ vtable symbol.
|
|
bool
|
|
is_cxx_vtable() const
|
|
{ return is_prefix_of("_ZTV", this->name_); }
|
|
|
|
protected:
|
|
// Instances of this class should always be created at a specific
|
|
// size.
|
|
Symbol()
|
|
{ memset(this, 0, sizeof *this); }
|
|
|
|
// Initialize the general fields.
|
|
void
|
|
init_fields(const char* name, const char* version,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis);
|
|
|
|
// Initialize fields from an ELF symbol in OBJECT. ST_SHNDX is the
|
|
// section index, IS_ORDINARY is whether it is a normal section
|
|
// index rather than a special code.
|
|
template<int size, bool big_endian>
|
|
void
|
|
init_base_object(const char* name, const char* version, Object* object,
|
|
const elfcpp::Sym<size, big_endian>&, unsigned int st_shndx,
|
|
bool is_ordinary);
|
|
|
|
// Initialize fields for an Output_data.
|
|
void
|
|
init_base_output_data(const char* name, const char* version, Output_data*,
|
|
elfcpp::STT, elfcpp::STB, elfcpp::STV,
|
|
unsigned char nonvis, bool offset_is_from_end,
|
|
bool is_predefined);
|
|
|
|
// Initialize fields for an Output_segment.
|
|
void
|
|
init_base_output_segment(const char* name, const char* version,
|
|
Output_segment* os, elfcpp::STT type,
|
|
elfcpp::STB binding, elfcpp::STV visibility,
|
|
unsigned char nonvis,
|
|
Segment_offset_base offset_base,
|
|
bool is_predefined);
|
|
|
|
// Initialize fields for a constant.
|
|
void
|
|
init_base_constant(const char* name, const char* version, elfcpp::STT type,
|
|
elfcpp::STB binding, elfcpp::STV visibility,
|
|
unsigned char nonvis, bool is_predefined);
|
|
|
|
// Initialize fields for an undefined symbol.
|
|
void
|
|
init_base_undefined(const char* name, const char* version, elfcpp::STT type,
|
|
elfcpp::STB binding, elfcpp::STV visibility,
|
|
unsigned char nonvis);
|
|
|
|
// Override existing symbol.
|
|
template<int size, bool big_endian>
|
|
void
|
|
override_base(const elfcpp::Sym<size, big_endian>&, unsigned int st_shndx,
|
|
bool is_ordinary, Object* object, const char* version);
|
|
|
|
// Override existing symbol with a special symbol.
|
|
void
|
|
override_base_with_special(const Symbol* from);
|
|
|
|
// Override symbol version.
|
|
void
|
|
override_version(const char* version);
|
|
|
|
// Allocate a common symbol by giving it a location in the output
|
|
// file.
|
|
void
|
|
allocate_base_common(Output_data*);
|
|
|
|
private:
|
|
Symbol(const Symbol&);
|
|
Symbol& operator=(const Symbol&);
|
|
|
|
// Symbol name (expected to point into a Stringpool).
|
|
const char* name_;
|
|
// Symbol version (expected to point into a Stringpool). This may
|
|
// be NULL.
|
|
const char* version_;
|
|
|
|
union
|
|
{
|
|
// This struct is used if SOURCE_ == FROM_OBJECT.
|
|
struct
|
|
{
|
|
// Object in which symbol is defined, or in which it was first
|
|
// seen.
|
|
Object* object;
|
|
// Section number in object_ in which symbol is defined.
|
|
unsigned int shndx;
|
|
} from_object;
|
|
|
|
// This struct is used if SOURCE_ == IN_OUTPUT_DATA.
|
|
struct
|
|
{
|
|
// Output_data in which symbol is defined. Before
|
|
// Layout::finalize the symbol's value is an offset within the
|
|
// Output_data.
|
|
Output_data* output_data;
|
|
// True if the offset is from the end, false if the offset is
|
|
// from the beginning.
|
|
bool offset_is_from_end;
|
|
} in_output_data;
|
|
|
|
// This struct is used if SOURCE_ == IN_OUTPUT_SEGMENT.
|
|
struct
|
|
{
|
|
// Output_segment in which the symbol is defined. Before
|
|
// Layout::finalize the symbol's value is an offset.
|
|
Output_segment* output_segment;
|
|
// The base to use for the offset before Layout::finalize.
|
|
Segment_offset_base offset_base;
|
|
} in_output_segment;
|
|
} u_;
|
|
|
|
// The index of this symbol in the output file. If the symbol is
|
|
// not going into the output file, this value is -1U. This field
|
|
// starts as always holding zero. It is set to a non-zero value by
|
|
// Symbol_table::finalize.
|
|
unsigned int symtab_index_;
|
|
|
|
// The index of this symbol in the dynamic symbol table. If the
|
|
// symbol is not going into the dynamic symbol table, this value is
|
|
// -1U. This field starts as always holding zero. It is set to a
|
|
// non-zero value during Layout::finalize.
|
|
unsigned int dynsym_index_;
|
|
|
|
// The GOT section entries for this symbol. A symbol may have more
|
|
// than one GOT offset (e.g., when mixing modules compiled with two
|
|
// different TLS models), but will usually have at most one.
|
|
Got_offset_list got_offsets_;
|
|
|
|
// If this symbol has an entry in the PLT section, then this is the
|
|
// offset from the start of the PLT section. This is -1U if there
|
|
// is no PLT entry.
|
|
unsigned int plt_offset_;
|
|
|
|
// Symbol type (bits 0 to 3).
|
|
elfcpp::STT type_ : 4;
|
|
// Symbol binding (bits 4 to 7).
|
|
elfcpp::STB binding_ : 4;
|
|
// Symbol visibility (bits 8 to 9).
|
|
elfcpp::STV visibility_ : 2;
|
|
// Rest of symbol st_other field (bits 10 to 15).
|
|
unsigned int nonvis_ : 6;
|
|
// The type of symbol (bits 16 to 18).
|
|
Source source_ : 3;
|
|
// True if this is the default version of the symbol (bit 19).
|
|
bool is_def_ : 1;
|
|
// True if this symbol really forwards to another symbol. This is
|
|
// used when we discover after the fact that two different entries
|
|
// in the hash table really refer to the same symbol. This will
|
|
// never be set for a symbol found in the hash table, but may be set
|
|
// for a symbol found in the list of symbols attached to an Object.
|
|
// It forwards to the symbol found in the forwarders_ map of
|
|
// Symbol_table (bit 20).
|
|
bool is_forwarder_ : 1;
|
|
// True if the symbol has an alias in the weak_aliases table in
|
|
// Symbol_table (bit 21).
|
|
bool has_alias_ : 1;
|
|
// True if this symbol needs to be in the dynamic symbol table (bit
|
|
// 22).
|
|
bool needs_dynsym_entry_ : 1;
|
|
// True if we've seen this symbol in a regular object (bit 23).
|
|
bool in_reg_ : 1;
|
|
// True if we've seen this symbol in a dynamic object (bit 24).
|
|
bool in_dyn_ : 1;
|
|
// True if this is a dynamic symbol which needs a special value in
|
|
// the dynamic symbol table (bit 25).
|
|
bool needs_dynsym_value_ : 1;
|
|
// True if there is a warning for this symbol (bit 26).
|
|
bool has_warning_ : 1;
|
|
// True if we are using a COPY reloc for this symbol, so that the
|
|
// real definition lives in a dynamic object (bit 27).
|
|
bool is_copied_from_dynobj_ : 1;
|
|
// True if this symbol was forced to local visibility by a version
|
|
// script (bit 28).
|
|
bool is_forced_local_ : 1;
|
|
// True if the field u_.from_object.shndx is an ordinary section
|
|
// index, not one of the special codes from SHN_LORESERVE to
|
|
// SHN_HIRESERVE (bit 29).
|
|
bool is_ordinary_shndx_ : 1;
|
|
// True if we've seen this symbol in a "real" ELF object (bit 30).
|
|
// If the symbol has been seen in a relocatable, non-IR, object file,
|
|
// it's known to be referenced from outside the IR. A reference from
|
|
// a dynamic object doesn't count as a "real" ELF, and we'll simply
|
|
// mark the symbol as "visible" from outside the IR. The compiler
|
|
// can use this distinction to guide its handling of COMDAT symbols.
|
|
bool in_real_elf_ : 1;
|
|
// True if this symbol is defined in a section which was discarded
|
|
// (bit 31).
|
|
bool is_defined_in_discarded_section_ : 1;
|
|
// True if UNDEF_BINDING_WEAK_ has been set (bit 32).
|
|
bool undef_binding_set_ : 1;
|
|
// True if this symbol was a weak undef resolved by a dynamic def
|
|
// or by a special symbol (bit 33).
|
|
bool undef_binding_weak_ : 1;
|
|
// True if this symbol is a predefined linker symbol (bit 34).
|
|
bool is_predefined_ : 1;
|
|
};
|
|
|
|
// The parts of a symbol which are size specific. Using a template
|
|
// derived class like this helps us use less space on a 32-bit system.
|
|
|
|
template<int size>
|
|
class Sized_symbol : public Symbol
|
|
{
|
|
public:
|
|
typedef typename elfcpp::Elf_types<size>::Elf_Addr Value_type;
|
|
typedef typename elfcpp::Elf_types<size>::Elf_WXword Size_type;
|
|
|
|
Sized_symbol()
|
|
{ }
|
|
|
|
// Initialize fields from an ELF symbol in OBJECT. ST_SHNDX is the
|
|
// section index, IS_ORDINARY is whether it is a normal section
|
|
// index rather than a special code.
|
|
template<bool big_endian>
|
|
void
|
|
init_object(const char* name, const char* version, Object* object,
|
|
const elfcpp::Sym<size, big_endian>&, unsigned int st_shndx,
|
|
bool is_ordinary);
|
|
|
|
// Initialize fields for an Output_data.
|
|
void
|
|
init_output_data(const char* name, const char* version, Output_data*,
|
|
Value_type value, Size_type symsize, elfcpp::STT,
|
|
elfcpp::STB, elfcpp::STV, unsigned char nonvis,
|
|
bool offset_is_from_end, bool is_predefined);
|
|
|
|
// Initialize fields for an Output_segment.
|
|
void
|
|
init_output_segment(const char* name, const char* version, Output_segment*,
|
|
Value_type value, Size_type symsize, elfcpp::STT,
|
|
elfcpp::STB, elfcpp::STV, unsigned char nonvis,
|
|
Segment_offset_base offset_base, bool is_predefined);
|
|
|
|
// Initialize fields for a constant.
|
|
void
|
|
init_constant(const char* name, const char* version, Value_type value,
|
|
Size_type symsize, elfcpp::STT, elfcpp::STB, elfcpp::STV,
|
|
unsigned char nonvis, bool is_predefined);
|
|
|
|
// Initialize fields for an undefined symbol.
|
|
void
|
|
init_undefined(const char* name, const char* version, elfcpp::STT,
|
|
elfcpp::STB, elfcpp::STV, unsigned char nonvis);
|
|
|
|
// Override existing symbol.
|
|
template<bool big_endian>
|
|
void
|
|
override(const elfcpp::Sym<size, big_endian>&, unsigned int st_shndx,
|
|
bool is_ordinary, Object* object, const char* version);
|
|
|
|
// Override existing symbol with a special symbol.
|
|
void
|
|
override_with_special(const Sized_symbol<size>*);
|
|
|
|
// Return the symbol's value.
|
|
Value_type
|
|
value() const
|
|
{ return this->value_; }
|
|
|
|
// Return the symbol's size (we can't call this 'size' because that
|
|
// is a template parameter).
|
|
Size_type
|
|
symsize() const
|
|
{ return this->symsize_; }
|
|
|
|
// Set the symbol size. This is used when resolving common symbols.
|
|
void
|
|
set_symsize(Size_type symsize)
|
|
{ this->symsize_ = symsize; }
|
|
|
|
// Set the symbol value. This is called when we store the final
|
|
// values of the symbols into the symbol table.
|
|
void
|
|
set_value(Value_type value)
|
|
{ this->value_ = value; }
|
|
|
|
// Allocate a common symbol by giving it a location in the output
|
|
// file.
|
|
void
|
|
allocate_common(Output_data*, Value_type value);
|
|
|
|
private:
|
|
Sized_symbol(const Sized_symbol&);
|
|
Sized_symbol& operator=(const Sized_symbol&);
|
|
|
|
// Symbol value. Before Layout::finalize this is the offset in the
|
|
// input section. This is set to the final value during
|
|
// Layout::finalize.
|
|
Value_type value_;
|
|
// Symbol size.
|
|
Size_type symsize_;
|
|
};
|
|
|
|
// A struct describing a symbol defined by the linker, where the value
|
|
// of the symbol is defined based on an output section. This is used
|
|
// for symbols defined by the linker, like "_init_array_start".
|
|
|
|
struct Define_symbol_in_section
|
|
{
|
|
// The symbol name.
|
|
const char* name;
|
|
// The name of the output section with which this symbol should be
|
|
// associated. If there is no output section with that name, the
|
|
// symbol will be defined as zero.
|
|
const char* output_section;
|
|
// The offset of the symbol within the output section. This is an
|
|
// offset from the start of the output section, unless start_at_end
|
|
// is true, in which case this is an offset from the end of the
|
|
// output section.
|
|
uint64_t value;
|
|
// The size of the symbol.
|
|
uint64_t size;
|
|
// The symbol type.
|
|
elfcpp::STT type;
|
|
// The symbol binding.
|
|
elfcpp::STB binding;
|
|
// The symbol visibility.
|
|
elfcpp::STV visibility;
|
|
// The rest of the st_other field.
|
|
unsigned char nonvis;
|
|
// If true, the value field is an offset from the end of the output
|
|
// section.
|
|
bool offset_is_from_end;
|
|
// If true, this symbol is defined only if we see a reference to it.
|
|
bool only_if_ref;
|
|
};
|
|
|
|
// A struct describing a symbol defined by the linker, where the value
|
|
// of the symbol is defined based on a segment. This is used for
|
|
// symbols defined by the linker, like "_end". We describe the
|
|
// segment with which the symbol should be associated by its
|
|
// characteristics. If no segment meets these characteristics, the
|
|
// symbol will be defined as zero. If there is more than one segment
|
|
// which meets these characteristics, we will use the first one.
|
|
|
|
struct Define_symbol_in_segment
|
|
{
|
|
// The symbol name.
|
|
const char* name;
|
|
// The segment type where the symbol should be defined, typically
|
|
// PT_LOAD.
|
|
elfcpp::PT segment_type;
|
|
// Bitmask of segment flags which must be set.
|
|
elfcpp::PF segment_flags_set;
|
|
// Bitmask of segment flags which must be clear.
|
|
elfcpp::PF segment_flags_clear;
|
|
// The offset of the symbol within the segment. The offset is
|
|
// calculated from the position set by offset_base.
|
|
uint64_t value;
|
|
// The size of the symbol.
|
|
uint64_t size;
|
|
// The symbol type.
|
|
elfcpp::STT type;
|
|
// The symbol binding.
|
|
elfcpp::STB binding;
|
|
// The symbol visibility.
|
|
elfcpp::STV visibility;
|
|
// The rest of the st_other field.
|
|
unsigned char nonvis;
|
|
// The base from which we compute the offset.
|
|
Symbol::Segment_offset_base offset_base;
|
|
// If true, this symbol is defined only if we see a reference to it.
|
|
bool only_if_ref;
|
|
};
|
|
|
|
// Specify an object/section/offset location. Used by ODR code.
|
|
|
|
struct Symbol_location
|
|
{
|
|
// Object where the symbol is defined.
|
|
Object* object;
|
|
// Section-in-object where the symbol is defined.
|
|
unsigned int shndx;
|
|
// For relocatable objects, offset-in-section where the symbol is defined.
|
|
// For dynamic objects, address where the symbol is defined.
|
|
off_t offset;
|
|
bool operator==(const Symbol_location& that) const
|
|
{
|
|
return (this->object == that.object
|
|
&& this->shndx == that.shndx
|
|
&& this->offset == that.offset);
|
|
}
|
|
};
|
|
|
|
// This class manages warnings. Warnings are a GNU extension. When
|
|
// we see a section named .gnu.warning.SYM in an object file, and if
|
|
// we wind using the definition of SYM from that object file, then we
|
|
// will issue a warning for any relocation against SYM from a
|
|
// different object file. The text of the warning is the contents of
|
|
// the section. This is not precisely the definition used by the old
|
|
// GNU linker; the old GNU linker treated an occurrence of
|
|
// .gnu.warning.SYM as defining a warning symbol. A warning symbol
|
|
// would trigger a warning on any reference. However, it was
|
|
// inconsistent in that a warning in a dynamic object only triggered
|
|
// if there was no definition in a regular object. This linker is
|
|
// different in that we only issue a warning if we use the symbol
|
|
// definition from the same object file as the warning section.
|
|
|
|
class Warnings
|
|
{
|
|
public:
|
|
Warnings()
|
|
: warnings_()
|
|
{ }
|
|
|
|
// Add a warning for symbol NAME in object OBJ. WARNING is the text
|
|
// of the warning.
|
|
void
|
|
add_warning(Symbol_table* symtab, const char* name, Object* obj,
|
|
const std::string& warning);
|
|
|
|
// For each symbol for which we should give a warning, make a note
|
|
// on the symbol.
|
|
void
|
|
note_warnings(Symbol_table* symtab);
|
|
|
|
// Issue a warning for a reference to SYM at RELINFO's location.
|
|
template<int size, bool big_endian>
|
|
void
|
|
issue_warning(const Symbol* sym, const Relocate_info<size, big_endian>*,
|
|
size_t relnum, off_t reloffset) const;
|
|
|
|
private:
|
|
Warnings(const Warnings&);
|
|
Warnings& operator=(const Warnings&);
|
|
|
|
// What we need to know to get the warning text.
|
|
struct Warning_location
|
|
{
|
|
// The object the warning is in.
|
|
Object* object;
|
|
// The warning text.
|
|
std::string text;
|
|
|
|
Warning_location()
|
|
: object(NULL), text()
|
|
{ }
|
|
|
|
void
|
|
set(Object* o, const std::string& t)
|
|
{
|
|
this->object = o;
|
|
this->text = t;
|
|
}
|
|
};
|
|
|
|
// A mapping from warning symbol names (canonicalized in
|
|
// Symbol_table's namepool_ field) to warning information.
|
|
typedef Unordered_map<const char*, Warning_location> Warning_table;
|
|
|
|
Warning_table warnings_;
|
|
};
|
|
|
|
// The main linker symbol table.
|
|
|
|
class Symbol_table
|
|
{
|
|
public:
|
|
// The different places where a symbol definition can come from.
|
|
enum Defined
|
|
{
|
|
// Defined in an object file--the normal case.
|
|
OBJECT,
|
|
// Defined for a COPY reloc.
|
|
COPY,
|
|
// Defined on the command line using --defsym.
|
|
DEFSYM,
|
|
// Defined (so to speak) on the command line using -u.
|
|
UNDEFINED,
|
|
// Defined in a linker script.
|
|
SCRIPT,
|
|
// Predefined by the linker.
|
|
PREDEFINED,
|
|
// Defined by the linker during an incremental base link, but not
|
|
// a predefined symbol (e.g., common, defined in script).
|
|
INCREMENTAL_BASE,
|
|
};
|
|
|
|
// The order in which we sort common symbols.
|
|
enum Sort_commons_order
|
|
{
|
|
SORT_COMMONS_BY_SIZE_DESCENDING,
|
|
SORT_COMMONS_BY_ALIGNMENT_DESCENDING,
|
|
SORT_COMMONS_BY_ALIGNMENT_ASCENDING
|
|
};
|
|
|
|
// COUNT is an estimate of how many symbols will be inserted in the
|
|
// symbol table. It's ok to put 0 if you don't know; a correct
|
|
// guess will just save some CPU by reducing hashtable resizes.
|
|
Symbol_table(unsigned int count, const Version_script_info& version_script);
|
|
|
|
~Symbol_table();
|
|
|
|
void
|
|
set_icf(Icf* icf)
|
|
{ this->icf_ = icf;}
|
|
|
|
Icf*
|
|
icf() const
|
|
{ return this->icf_; }
|
|
|
|
// Returns true if ICF determined that this is a duplicate section.
|
|
bool
|
|
is_section_folded(Relobj* obj, unsigned int shndx) const;
|
|
|
|
void
|
|
set_gc(Garbage_collection* gc)
|
|
{ this->gc_ = gc; }
|
|
|
|
Garbage_collection*
|
|
gc() const
|
|
{ return this->gc_; }
|
|
|
|
// During garbage collection, this keeps undefined symbols.
|
|
void
|
|
gc_mark_undef_symbols(Layout*);
|
|
|
|
// This tells garbage collection that this symbol is referenced.
|
|
void
|
|
gc_mark_symbol(Symbol* sym);
|
|
|
|
// During garbage collection, this keeps sections that correspond to
|
|
// symbols seen in dynamic objects.
|
|
inline void
|
|
gc_mark_dyn_syms(Symbol* sym);
|
|
|
|
// Add COUNT external symbols from the relocatable object RELOBJ to
|
|
// the symbol table. SYMS is the symbols, SYMNDX_OFFSET is the
|
|
// offset in the symbol table of the first symbol, SYM_NAMES is
|
|
// their names, SYM_NAME_SIZE is the size of SYM_NAMES. This sets
|
|
// SYMPOINTERS to point to the symbols in the symbol table. It sets
|
|
// *DEFINED to the number of defined symbols.
|
|
template<int size, bool big_endian>
|
|
void
|
|
add_from_relobj(Sized_relobj_file<size, big_endian>* relobj,
|
|
const unsigned char* syms, size_t count,
|
|
size_t symndx_offset, const char* sym_names,
|
|
size_t sym_name_size,
|
|
typename Sized_relobj_file<size, big_endian>::Symbols*,
|
|
size_t* defined);
|
|
|
|
// Add one external symbol from the plugin object OBJ to the symbol table.
|
|
// Returns a pointer to the resolved symbol in the symbol table.
|
|
template<int size, bool big_endian>
|
|
Symbol*
|
|
add_from_pluginobj(Sized_pluginobj<size, big_endian>* obj,
|
|
const char* name, const char* ver,
|
|
elfcpp::Sym<size, big_endian>* sym);
|
|
|
|
// Add COUNT dynamic symbols from the dynamic object DYNOBJ to the
|
|
// symbol table. SYMS is the symbols. SYM_NAMES is their names.
|
|
// SYM_NAME_SIZE is the size of SYM_NAMES. The other parameters are
|
|
// symbol version data.
|
|
template<int size, bool big_endian>
|
|
void
|
|
add_from_dynobj(Sized_dynobj<size, big_endian>* dynobj,
|
|
const unsigned char* syms, size_t count,
|
|
const char* sym_names, size_t sym_name_size,
|
|
const unsigned char* versym, size_t versym_size,
|
|
const std::vector<const char*>*,
|
|
typename Sized_relobj_file<size, big_endian>::Symbols*,
|
|
size_t* defined);
|
|
|
|
// Add one external symbol from the incremental object OBJ to the symbol
|
|
// table. Returns a pointer to the resolved symbol in the symbol table.
|
|
template<int size, bool big_endian>
|
|
Sized_symbol<size>*
|
|
add_from_incrobj(Object* obj, const char* name,
|
|
const char* ver, elfcpp::Sym<size, big_endian>* sym);
|
|
|
|
// Define a special symbol based on an Output_data. It is a
|
|
// multiple definition error if this symbol is already defined.
|
|
Symbol*
|
|
define_in_output_data(const char* name, const char* version, Defined,
|
|
Output_data*, uint64_t value, uint64_t symsize,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis,
|
|
bool offset_is_from_end, bool only_if_ref);
|
|
|
|
// Define a special symbol based on an Output_segment. It is a
|
|
// multiple definition error if this symbol is already defined.
|
|
Symbol*
|
|
define_in_output_segment(const char* name, const char* version, Defined,
|
|
Output_segment*, uint64_t value, uint64_t symsize,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis,
|
|
Symbol::Segment_offset_base, bool only_if_ref);
|
|
|
|
// Define a special symbol with a constant value. It is a multiple
|
|
// definition error if this symbol is already defined.
|
|
Symbol*
|
|
define_as_constant(const char* name, const char* version, Defined,
|
|
uint64_t value, uint64_t symsize, elfcpp::STT type,
|
|
elfcpp::STB binding, elfcpp::STV visibility,
|
|
unsigned char nonvis, bool only_if_ref,
|
|
bool force_override);
|
|
|
|
// Define a set of symbols in output sections. If ONLY_IF_REF is
|
|
// true, only define them if they are referenced.
|
|
void
|
|
define_symbols(const Layout*, int count, const Define_symbol_in_section*,
|
|
bool only_if_ref);
|
|
|
|
// Define a set of symbols in output segments. If ONLY_IF_REF is
|
|
// true, only defined them if they are referenced.
|
|
void
|
|
define_symbols(const Layout*, int count, const Define_symbol_in_segment*,
|
|
bool only_if_ref);
|
|
|
|
// Define SYM using a COPY reloc. POSD is the Output_data where the
|
|
// symbol should be defined--typically a .dyn.bss section. VALUE is
|
|
// the offset within POSD.
|
|
template<int size>
|
|
void
|
|
define_with_copy_reloc(Sized_symbol<size>* sym, Output_data* posd,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr);
|
|
|
|
// Look up a symbol.
|
|
Symbol*
|
|
lookup(const char*, const char* version = NULL) const;
|
|
|
|
// Return the real symbol associated with the forwarder symbol FROM.
|
|
Symbol*
|
|
resolve_forwards(const Symbol* from) const;
|
|
|
|
// Return the sized version of a symbol in this table.
|
|
template<int size>
|
|
Sized_symbol<size>*
|
|
get_sized_symbol(Symbol*) const;
|
|
|
|
template<int size>
|
|
const Sized_symbol<size>*
|
|
get_sized_symbol(const Symbol*) const;
|
|
|
|
// Return the count of undefined symbols seen.
|
|
size_t
|
|
saw_undefined() const
|
|
{ return this->saw_undefined_; }
|
|
|
|
// Allocate the common symbols
|
|
void
|
|
allocate_commons(Layout*, Mapfile*);
|
|
|
|
// Add a warning for symbol NAME in object OBJ. WARNING is the text
|
|
// of the warning.
|
|
void
|
|
add_warning(const char* name, Object* obj, const std::string& warning)
|
|
{ this->warnings_.add_warning(this, name, obj, warning); }
|
|
|
|
// Canonicalize a symbol name for use in the hash table.
|
|
const char*
|
|
canonicalize_name(const char* name)
|
|
{ return this->namepool_.add(name, true, NULL); }
|
|
|
|
// Possibly issue a warning for a reference to SYM at LOCATION which
|
|
// is in OBJ.
|
|
template<int size, bool big_endian>
|
|
void
|
|
issue_warning(const Symbol* sym,
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
size_t relnum, off_t reloffset) const
|
|
{ this->warnings_.issue_warning(sym, relinfo, relnum, reloffset); }
|
|
|
|
// Check candidate_odr_violations_ to find symbols with the same name
|
|
// but apparently different definitions (different source-file/line-no).
|
|
void
|
|
detect_odr_violations(const Task*, const char* output_file_name) const;
|
|
|
|
// Add any undefined symbols named on the command line to the symbol
|
|
// table.
|
|
void
|
|
add_undefined_symbols_from_command_line(Layout*);
|
|
|
|
// SYM is defined using a COPY reloc. Return the dynamic object
|
|
// where the original definition was found.
|
|
Dynobj*
|
|
get_copy_source(const Symbol* sym) const;
|
|
|
|
// Set the dynamic symbol indexes. INDEX is the index of the first
|
|
// global dynamic symbol. Pointers to the symbols are stored into
|
|
// the vector. The names are stored into the Stringpool. This
|
|
// returns an updated dynamic symbol index.
|
|
unsigned int
|
|
set_dynsym_indexes(unsigned int index, std::vector<Symbol*>*,
|
|
Stringpool*, Versions*);
|
|
|
|
// Finalize the symbol table after we have set the final addresses
|
|
// of all the input sections. This sets the final symbol indexes,
|
|
// values and adds the names to *POOL. *PLOCAL_SYMCOUNT is the
|
|
// index of the first global symbol. OFF is the file offset of the
|
|
// global symbol table, DYNOFF is the offset of the globals in the
|
|
// dynamic symbol table, DYN_GLOBAL_INDEX is the index of the first
|
|
// global dynamic symbol, and DYNCOUNT is the number of global
|
|
// dynamic symbols. This records the parameters, and returns the
|
|
// new file offset. It updates *PLOCAL_SYMCOUNT if it created any
|
|
// local symbols.
|
|
off_t
|
|
finalize(off_t off, off_t dynoff, size_t dyn_global_index, size_t dyncount,
|
|
Stringpool* pool, unsigned int* plocal_symcount);
|
|
|
|
// Set the final file offset of the symbol table.
|
|
void
|
|
set_file_offset(off_t off)
|
|
{ this->offset_ = off; }
|
|
|
|
// Status code of Symbol_table::compute_final_value.
|
|
enum Compute_final_value_status
|
|
{
|
|
// No error.
|
|
CFVS_OK,
|
|
// Unsupported symbol section.
|
|
CFVS_UNSUPPORTED_SYMBOL_SECTION,
|
|
// No output section.
|
|
CFVS_NO_OUTPUT_SECTION
|
|
};
|
|
|
|
// Compute the final value of SYM and store status in location PSTATUS.
|
|
// During relaxation, this may be called multiple times for a symbol to
|
|
// compute its would-be final value in each relaxation pass.
|
|
|
|
template<int size>
|
|
typename Sized_symbol<size>::Value_type
|
|
compute_final_value(const Sized_symbol<size>* sym,
|
|
Compute_final_value_status* pstatus) const;
|
|
|
|
// Return the index of the first global symbol.
|
|
unsigned int
|
|
first_global_index() const
|
|
{ return this->first_global_index_; }
|
|
|
|
// Return the total number of symbols in the symbol table.
|
|
unsigned int
|
|
output_count() const
|
|
{ return this->output_count_; }
|
|
|
|
// Write out the global symbols.
|
|
void
|
|
write_globals(const Stringpool*, const Stringpool*,
|
|
Output_symtab_xindex*, Output_symtab_xindex*,
|
|
Output_file*) const;
|
|
|
|
// Write out a section symbol. Return the updated offset.
|
|
void
|
|
write_section_symbol(const Output_section*, Output_symtab_xindex*,
|
|
Output_file*, off_t) const;
|
|
|
|
// Loop over all symbols, applying the function F to each.
|
|
template<int size, typename F>
|
|
void
|
|
for_all_symbols(F f) const
|
|
{
|
|
for (Symbol_table_type::const_iterator p = this->table_.begin();
|
|
p != this->table_.end();
|
|
++p)
|
|
{
|
|
Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second);
|
|
f(sym);
|
|
}
|
|
}
|
|
|
|
// Dump statistical information to stderr.
|
|
void
|
|
print_stats() const;
|
|
|
|
// Return the version script information.
|
|
const Version_script_info&
|
|
version_script() const
|
|
{ return version_script_; }
|
|
|
|
private:
|
|
Symbol_table(const Symbol_table&);
|
|
Symbol_table& operator=(const Symbol_table&);
|
|
|
|
// The type of the list of common symbols.
|
|
typedef std::vector<Symbol*> Commons_type;
|
|
|
|
// The type of the symbol hash table.
|
|
|
|
typedef std::pair<Stringpool::Key, Stringpool::Key> Symbol_table_key;
|
|
|
|
// The hash function. The key values are Stringpool keys.
|
|
struct Symbol_table_hash
|
|
{
|
|
inline size_t
|
|
operator()(const Symbol_table_key& key) const
|
|
{
|
|
return key.first ^ key.second;
|
|
}
|
|
};
|
|
|
|
struct Symbol_table_eq
|
|
{
|
|
bool
|
|
operator()(const Symbol_table_key&, const Symbol_table_key&) const;
|
|
};
|
|
|
|
typedef Unordered_map<Symbol_table_key, Symbol*, Symbol_table_hash,
|
|
Symbol_table_eq> Symbol_table_type;
|
|
|
|
// A map from symbol name (as a pointer into the namepool) to all
|
|
// the locations the symbols is (weakly) defined (and certain other
|
|
// conditions are met). This map will be used later to detect
|
|
// possible One Definition Rule (ODR) violations.
|
|
struct Symbol_location_hash
|
|
{
|
|
size_t operator()(const Symbol_location& loc) const
|
|
{ return reinterpret_cast<uintptr_t>(loc.object) ^ loc.offset ^ loc.shndx; }
|
|
};
|
|
|
|
typedef Unordered_map<const char*,
|
|
Unordered_set<Symbol_location, Symbol_location_hash> >
|
|
Odr_map;
|
|
|
|
// Make FROM a forwarder symbol to TO.
|
|
void
|
|
make_forwarder(Symbol* from, Symbol* to);
|
|
|
|
// Add a symbol.
|
|
template<int size, bool big_endian>
|
|
Sized_symbol<size>*
|
|
add_from_object(Object*, const char* name, Stringpool::Key name_key,
|
|
const char* version, Stringpool::Key version_key,
|
|
bool def, const elfcpp::Sym<size, big_endian>& sym,
|
|
unsigned int st_shndx, bool is_ordinary,
|
|
unsigned int orig_st_shndx);
|
|
|
|
// Define a default symbol.
|
|
template<int size, bool big_endian>
|
|
void
|
|
define_default_version(Sized_symbol<size>*, bool,
|
|
Symbol_table_type::iterator);
|
|
|
|
// Resolve symbols.
|
|
template<int size, bool big_endian>
|
|
void
|
|
resolve(Sized_symbol<size>* to,
|
|
const elfcpp::Sym<size, big_endian>& sym,
|
|
unsigned int st_shndx, bool is_ordinary,
|
|
unsigned int orig_st_shndx,
|
|
Object*, const char* version,
|
|
bool is_default_version);
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
resolve(Sized_symbol<size>* to, const Sized_symbol<size>* from);
|
|
|
|
// Record that a symbol is forced to be local by a version script or
|
|
// by visibility.
|
|
void
|
|
force_local(Symbol*);
|
|
|
|
// Adjust NAME and *NAME_KEY for wrapping.
|
|
const char*
|
|
wrap_symbol(const char* name, Stringpool::Key* name_key);
|
|
|
|
// Whether we should override a symbol, based on flags in
|
|
// resolve.cc.
|
|
static bool
|
|
should_override(const Symbol*, unsigned int, elfcpp::STT, Defined,
|
|
Object*, bool*, bool*, bool);
|
|
|
|
// Report a problem in symbol resolution.
|
|
static void
|
|
report_resolve_problem(bool is_error, const char* msg, const Symbol* to,
|
|
Defined, Object* object);
|
|
|
|
// Override a symbol.
|
|
template<int size, bool big_endian>
|
|
void
|
|
override(Sized_symbol<size>* tosym,
|
|
const elfcpp::Sym<size, big_endian>& fromsym,
|
|
unsigned int st_shndx, bool is_ordinary,
|
|
Object* object, const char* version);
|
|
|
|
// Whether we should override a symbol with a special symbol which
|
|
// is automatically defined by the linker.
|
|
static bool
|
|
should_override_with_special(const Symbol*, elfcpp::STT, Defined);
|
|
|
|
// Override a symbol with a special symbol.
|
|
template<int size>
|
|
void
|
|
override_with_special(Sized_symbol<size>* tosym,
|
|
const Sized_symbol<size>* fromsym);
|
|
|
|
// Record all weak alias sets for a dynamic object.
|
|
template<int size>
|
|
void
|
|
record_weak_aliases(std::vector<Sized_symbol<size>*>*);
|
|
|
|
// Define a special symbol.
|
|
template<int size, bool big_endian>
|
|
Sized_symbol<size>*
|
|
define_special_symbol(const char** pname, const char** pversion,
|
|
bool only_if_ref, Sized_symbol<size>** poldsym,
|
|
bool* resolve_oldsym);
|
|
|
|
// Define a symbol in an Output_data, sized version.
|
|
template<int size>
|
|
Sized_symbol<size>*
|
|
do_define_in_output_data(const char* name, const char* version, Defined,
|
|
Output_data*,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_WXword ssize,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis,
|
|
bool offset_is_from_end, bool only_if_ref);
|
|
|
|
// Define a symbol in an Output_segment, sized version.
|
|
template<int size>
|
|
Sized_symbol<size>*
|
|
do_define_in_output_segment(
|
|
const char* name, const char* version, Defined, Output_segment* os,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_WXword ssize,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis,
|
|
Symbol::Segment_offset_base offset_base, bool only_if_ref);
|
|
|
|
// Define a symbol as a constant, sized version.
|
|
template<int size>
|
|
Sized_symbol<size>*
|
|
do_define_as_constant(
|
|
const char* name, const char* version, Defined,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_WXword ssize,
|
|
elfcpp::STT type, elfcpp::STB binding,
|
|
elfcpp::STV visibility, unsigned char nonvis,
|
|
bool only_if_ref, bool force_override);
|
|
|
|
// Add any undefined symbols named on the command line to the symbol
|
|
// table, sized version.
|
|
template<int size>
|
|
void
|
|
do_add_undefined_symbols_from_command_line(Layout*);
|
|
|
|
// Add one undefined symbol.
|
|
template<int size>
|
|
void
|
|
add_undefined_symbol_from_command_line(const char* name);
|
|
|
|
// Types of common symbols.
|
|
|
|
enum Commons_section_type
|
|
{
|
|
COMMONS_NORMAL,
|
|
COMMONS_TLS,
|
|
COMMONS_SMALL,
|
|
COMMONS_LARGE
|
|
};
|
|
|
|
// Allocate the common symbols, sized version.
|
|
template<int size>
|
|
void
|
|
do_allocate_commons(Layout*, Mapfile*, Sort_commons_order);
|
|
|
|
// Allocate the common symbols from one list.
|
|
template<int size>
|
|
void
|
|
do_allocate_commons_list(Layout*, Commons_section_type, Commons_type*,
|
|
Mapfile*, Sort_commons_order);
|
|
|
|
// Returns all of the lines attached to LOC, not just the one the
|
|
// instruction actually came from. This helps the ODR checker avoid
|
|
// false positives.
|
|
static std::vector<std::string>
|
|
linenos_from_loc(const Task* task, const Symbol_location& loc);
|
|
|
|
// Implement detect_odr_violations.
|
|
template<int size, bool big_endian>
|
|
void
|
|
sized_detect_odr_violations() const;
|
|
|
|
// Finalize symbols specialized for size.
|
|
template<int size>
|
|
off_t
|
|
sized_finalize(off_t, Stringpool*, unsigned int*);
|
|
|
|
// Finalize a symbol. Return whether it should be added to the
|
|
// symbol table.
|
|
template<int size>
|
|
bool
|
|
sized_finalize_symbol(Symbol*);
|
|
|
|
// Add a symbol the final symtab by setting its index.
|
|
template<int size>
|
|
void
|
|
add_to_final_symtab(Symbol*, Stringpool*, unsigned int* pindex, off_t* poff);
|
|
|
|
// Write globals specialized for size and endianness.
|
|
template<int size, bool big_endian>
|
|
void
|
|
sized_write_globals(const Stringpool*, const Stringpool*,
|
|
Output_symtab_xindex*, Output_symtab_xindex*,
|
|
Output_file*) const;
|
|
|
|
// Write out a symbol to P.
|
|
template<int size, bool big_endian>
|
|
void
|
|
sized_write_symbol(Sized_symbol<size>*,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
unsigned int shndx, elfcpp::STB,
|
|
const Stringpool*, unsigned char* p) const;
|
|
|
|
// Possibly warn about an undefined symbol from a dynamic object.
|
|
void
|
|
warn_about_undefined_dynobj_symbol(Symbol*) const;
|
|
|
|
// Write out a section symbol, specialized for size and endianness.
|
|
template<int size, bool big_endian>
|
|
void
|
|
sized_write_section_symbol(const Output_section*, Output_symtab_xindex*,
|
|
Output_file*, off_t) const;
|
|
|
|
// The type of the list of symbols which have been forced local.
|
|
typedef std::vector<Symbol*> Forced_locals;
|
|
|
|
// A map from symbols with COPY relocs to the dynamic objects where
|
|
// they are defined.
|
|
typedef Unordered_map<const Symbol*, Dynobj*> Copied_symbol_dynobjs;
|
|
|
|
// We increment this every time we see a new undefined symbol, for
|
|
// use in archive groups.
|
|
size_t saw_undefined_;
|
|
// The index of the first global symbol in the output file.
|
|
unsigned int first_global_index_;
|
|
// The file offset within the output symtab section where we should
|
|
// write the table.
|
|
off_t offset_;
|
|
// The number of global symbols we want to write out.
|
|
unsigned int output_count_;
|
|
// The file offset of the global dynamic symbols, or 0 if none.
|
|
off_t dynamic_offset_;
|
|
// The index of the first global dynamic symbol.
|
|
unsigned int first_dynamic_global_index_;
|
|
// The number of global dynamic symbols, or 0 if none.
|
|
unsigned int dynamic_count_;
|
|
// The symbol hash table.
|
|
Symbol_table_type table_;
|
|
// A pool of symbol names. This is used for all global symbols.
|
|
// Entries in the hash table point into this pool.
|
|
Stringpool namepool_;
|
|
// Forwarding symbols.
|
|
Unordered_map<const Symbol*, Symbol*> forwarders_;
|
|
// Weak aliases. A symbol in this list points to the next alias.
|
|
// The aliases point to each other in a circular list.
|
|
Unordered_map<Symbol*, Symbol*> weak_aliases_;
|
|
// We don't expect there to be very many common symbols, so we keep
|
|
// a list of them. When we find a common symbol we add it to this
|
|
// list. It is possible that by the time we process the list the
|
|
// symbol is no longer a common symbol. It may also have become a
|
|
// forwarder.
|
|
Commons_type commons_;
|
|
// This is like the commons_ field, except that it holds TLS common
|
|
// symbols.
|
|
Commons_type tls_commons_;
|
|
// This is for small common symbols.
|
|
Commons_type small_commons_;
|
|
// This is for large common symbols.
|
|
Commons_type large_commons_;
|
|
// A list of symbols which have been forced to be local. We don't
|
|
// expect there to be very many of them, so we keep a list of them
|
|
// rather than walking the whole table to find them.
|
|
Forced_locals forced_locals_;
|
|
// Manage symbol warnings.
|
|
Warnings warnings_;
|
|
// Manage potential One Definition Rule (ODR) violations.
|
|
Odr_map candidate_odr_violations_;
|
|
|
|
// When we emit a COPY reloc for a symbol, we define it in an
|
|
// Output_data. When it's time to emit version information for it,
|
|
// we need to know the dynamic object in which we found the original
|
|
// definition. This maps symbols with COPY relocs to the dynamic
|
|
// object where they were defined.
|
|
Copied_symbol_dynobjs copied_symbol_dynobjs_;
|
|
// Information parsed from the version script, if any.
|
|
const Version_script_info& version_script_;
|
|
Garbage_collection* gc_;
|
|
Icf* icf_;
|
|
};
|
|
|
|
// We inline get_sized_symbol for efficiency.
|
|
|
|
template<int size>
|
|
Sized_symbol<size>*
|
|
Symbol_table::get_sized_symbol(Symbol* sym) const
|
|
{
|
|
gold_assert(size == parameters->target().get_size());
|
|
return static_cast<Sized_symbol<size>*>(sym);
|
|
}
|
|
|
|
template<int size>
|
|
const Sized_symbol<size>*
|
|
Symbol_table::get_sized_symbol(const Symbol* sym) const
|
|
{
|
|
gold_assert(size == parameters->target().get_size());
|
|
return static_cast<const Sized_symbol<size>*>(sym);
|
|
}
|
|
|
|
} // End namespace gold.
|
|
|
|
#endif // !defined(GOLD_SYMTAB_H)
|