mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-26 19:44:11 +08:00
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.
3558 lines
106 KiB
C++
3558 lines
106 KiB
C++
// object.cc -- support for an object file for linking in gold
|
|
|
|
// 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.
|
|
|
|
#include "gold.h"
|
|
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <cstdarg>
|
|
#include "demangle.h"
|
|
#include "libiberty.h"
|
|
|
|
#include "gc.h"
|
|
#include "target-select.h"
|
|
#include "dwarf_reader.h"
|
|
#include "layout.h"
|
|
#include "output.h"
|
|
#include "symtab.h"
|
|
#include "cref.h"
|
|
#include "reloc.h"
|
|
#include "object.h"
|
|
#include "dynobj.h"
|
|
#include "plugin.h"
|
|
#include "compressed_output.h"
|
|
#include "incremental.h"
|
|
#include "merge.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// Struct Read_symbols_data.
|
|
|
|
// Destroy any remaining File_view objects and buffers of decompressed
|
|
// sections.
|
|
|
|
Read_symbols_data::~Read_symbols_data()
|
|
{
|
|
if (this->section_headers != NULL)
|
|
delete this->section_headers;
|
|
if (this->section_names != NULL)
|
|
delete this->section_names;
|
|
if (this->symbols != NULL)
|
|
delete this->symbols;
|
|
if (this->symbol_names != NULL)
|
|
delete this->symbol_names;
|
|
if (this->versym != NULL)
|
|
delete this->versym;
|
|
if (this->verdef != NULL)
|
|
delete this->verdef;
|
|
if (this->verneed != NULL)
|
|
delete this->verneed;
|
|
}
|
|
|
|
// Class Xindex.
|
|
|
|
// Initialize the symtab_xindex_ array. Find the SHT_SYMTAB_SHNDX
|
|
// section and read it in. SYMTAB_SHNDX is the index of the symbol
|
|
// table we care about.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Xindex::initialize_symtab_xindex(Object* object, unsigned int symtab_shndx)
|
|
{
|
|
if (!this->symtab_xindex_.empty())
|
|
return;
|
|
|
|
gold_assert(symtab_shndx != 0);
|
|
|
|
// Look through the sections in reverse order, on the theory that it
|
|
// is more likely to be near the end than the beginning.
|
|
unsigned int i = object->shnum();
|
|
while (i > 0)
|
|
{
|
|
--i;
|
|
if (object->section_type(i) == elfcpp::SHT_SYMTAB_SHNDX
|
|
&& this->adjust_shndx(object->section_link(i)) == symtab_shndx)
|
|
{
|
|
this->read_symtab_xindex<size, big_endian>(object, i, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
object->error(_("missing SHT_SYMTAB_SHNDX section"));
|
|
}
|
|
|
|
// Read in the symtab_xindex_ array, given the section index of the
|
|
// SHT_SYMTAB_SHNDX section. If PSHDRS is not NULL, it points at the
|
|
// section headers.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Xindex::read_symtab_xindex(Object* object, unsigned int xindex_shndx,
|
|
const unsigned char* pshdrs)
|
|
{
|
|
section_size_type bytecount;
|
|
const unsigned char* contents;
|
|
if (pshdrs == NULL)
|
|
contents = object->section_contents(xindex_shndx, &bytecount, false);
|
|
else
|
|
{
|
|
const unsigned char* p = (pshdrs
|
|
+ (xindex_shndx
|
|
* elfcpp::Elf_sizes<size>::shdr_size));
|
|
typename elfcpp::Shdr<size, big_endian> shdr(p);
|
|
bytecount = convert_to_section_size_type(shdr.get_sh_size());
|
|
contents = object->get_view(shdr.get_sh_offset(), bytecount, true, false);
|
|
}
|
|
|
|
gold_assert(this->symtab_xindex_.empty());
|
|
this->symtab_xindex_.reserve(bytecount / 4);
|
|
for (section_size_type i = 0; i < bytecount; i += 4)
|
|
{
|
|
unsigned int shndx = elfcpp::Swap<32, big_endian>::readval(contents + i);
|
|
// We preadjust the section indexes we save.
|
|
this->symtab_xindex_.push_back(this->adjust_shndx(shndx));
|
|
}
|
|
}
|
|
|
|
// Symbol symndx has a section of SHN_XINDEX; return the real section
|
|
// index.
|
|
|
|
unsigned int
|
|
Xindex::sym_xindex_to_shndx(Object* object, unsigned int symndx)
|
|
{
|
|
if (symndx >= this->symtab_xindex_.size())
|
|
{
|
|
object->error(_("symbol %u out of range for SHT_SYMTAB_SHNDX section"),
|
|
symndx);
|
|
return elfcpp::SHN_UNDEF;
|
|
}
|
|
unsigned int shndx = this->symtab_xindex_[symndx];
|
|
if (shndx < elfcpp::SHN_LORESERVE || shndx >= object->shnum())
|
|
{
|
|
object->error(_("extended index for symbol %u out of range: %u"),
|
|
symndx, shndx);
|
|
return elfcpp::SHN_UNDEF;
|
|
}
|
|
return shndx;
|
|
}
|
|
|
|
// Class Object.
|
|
|
|
// Report an error for this object file. This is used by the
|
|
// elfcpp::Elf_file interface, and also called by the Object code
|
|
// itself.
|
|
|
|
void
|
|
Object::error(const char* format, ...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
char* buf = NULL;
|
|
if (vasprintf(&buf, format, args) < 0)
|
|
gold_nomem();
|
|
va_end(args);
|
|
gold_error(_("%s: %s"), this->name().c_str(), buf);
|
|
free(buf);
|
|
}
|
|
|
|
// Return a view of the contents of a section.
|
|
|
|
const unsigned char*
|
|
Object::section_contents(unsigned int shndx, section_size_type* plen,
|
|
bool cache)
|
|
{ return this->do_section_contents(shndx, plen, cache); }
|
|
|
|
// Read the section data into SD. This is code common to Sized_relobj_file
|
|
// and Sized_dynobj, so we put it into Object.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Object::read_section_data(elfcpp::Elf_file<size, big_endian, Object>* elf_file,
|
|
Read_symbols_data* sd)
|
|
{
|
|
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
|
|
|
// Read the section headers.
|
|
const off_t shoff = elf_file->shoff();
|
|
const unsigned int shnum = this->shnum();
|
|
sd->section_headers = this->get_lasting_view(shoff, shnum * shdr_size,
|
|
true, true);
|
|
|
|
// Read the section names.
|
|
const unsigned char* pshdrs = sd->section_headers->data();
|
|
const unsigned char* pshdrnames = pshdrs + elf_file->shstrndx() * shdr_size;
|
|
typename elfcpp::Shdr<size, big_endian> shdrnames(pshdrnames);
|
|
|
|
if (shdrnames.get_sh_type() != elfcpp::SHT_STRTAB)
|
|
this->error(_("section name section has wrong type: %u"),
|
|
static_cast<unsigned int>(shdrnames.get_sh_type()));
|
|
|
|
sd->section_names_size =
|
|
convert_to_section_size_type(shdrnames.get_sh_size());
|
|
sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(),
|
|
sd->section_names_size, false,
|
|
false);
|
|
}
|
|
|
|
// If NAME is the name of a special .gnu.warning section, arrange for
|
|
// the warning to be issued. SHNDX is the section index. Return
|
|
// whether it is a warning section.
|
|
|
|
bool
|
|
Object::handle_gnu_warning_section(const char* name, unsigned int shndx,
|
|
Symbol_table* symtab)
|
|
{
|
|
const char warn_prefix[] = ".gnu.warning.";
|
|
const int warn_prefix_len = sizeof warn_prefix - 1;
|
|
if (strncmp(name, warn_prefix, warn_prefix_len) == 0)
|
|
{
|
|
// Read the section contents to get the warning text. It would
|
|
// be nicer if we only did this if we have to actually issue a
|
|
// warning. Unfortunately, warnings are issued as we relocate
|
|
// sections. That means that we can not lock the object then,
|
|
// as we might try to issue the same warning multiple times
|
|
// simultaneously.
|
|
section_size_type len;
|
|
const unsigned char* contents = this->section_contents(shndx, &len,
|
|
false);
|
|
if (len == 0)
|
|
{
|
|
const char* warning = name + warn_prefix_len;
|
|
contents = reinterpret_cast<const unsigned char*>(warning);
|
|
len = strlen(warning);
|
|
}
|
|
std::string warning(reinterpret_cast<const char*>(contents), len);
|
|
symtab->add_warning(name + warn_prefix_len, this, warning);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// If NAME is the name of the special section which indicates that
|
|
// this object was compiled with -fsplit-stack, mark it accordingly.
|
|
|
|
bool
|
|
Object::handle_split_stack_section(const char* name)
|
|
{
|
|
if (strcmp(name, ".note.GNU-split-stack") == 0)
|
|
{
|
|
this->uses_split_stack_ = true;
|
|
return true;
|
|
}
|
|
if (strcmp(name, ".note.GNU-no-split-stack") == 0)
|
|
{
|
|
this->has_no_split_stack_ = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Class Relobj
|
|
|
|
template<int size>
|
|
void
|
|
Relobj::initialize_input_to_output_map(unsigned int shndx,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr starting_address,
|
|
Unordered_map<section_offset_type,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr>* output_addresses) const {
|
|
Object_merge_map *map = this->object_merge_map_;
|
|
map->initialize_input_to_output_map<size>(shndx, starting_address,
|
|
output_addresses);
|
|
}
|
|
|
|
void
|
|
Relobj::add_merge_mapping(Output_section_data *output_data,
|
|
unsigned int shndx, section_offset_type offset,
|
|
section_size_type length,
|
|
section_offset_type output_offset) {
|
|
Object_merge_map* object_merge_map = this->get_or_create_merge_map();
|
|
object_merge_map->add_mapping(output_data, shndx, offset, length, output_offset);
|
|
}
|
|
|
|
bool
|
|
Relobj::merge_output_offset(unsigned int shndx, section_offset_type offset,
|
|
section_offset_type *poutput) const {
|
|
Object_merge_map* object_merge_map = this->object_merge_map_;
|
|
if (object_merge_map == NULL)
|
|
return false;
|
|
return object_merge_map->get_output_offset(shndx, offset, poutput);
|
|
}
|
|
|
|
const Output_section_data*
|
|
Relobj::find_merge_section(unsigned int shndx) const {
|
|
Object_merge_map* object_merge_map = this->object_merge_map_;
|
|
if (object_merge_map == NULL)
|
|
return NULL;
|
|
return object_merge_map->find_merge_section(shndx);
|
|
}
|
|
|
|
// To copy the symbols data read from the file to a local data structure.
|
|
// This function is called from do_layout only while doing garbage
|
|
// collection.
|
|
|
|
void
|
|
Relobj::copy_symbols_data(Symbols_data* gc_sd, Read_symbols_data* sd,
|
|
unsigned int section_header_size)
|
|
{
|
|
gc_sd->section_headers_data =
|
|
new unsigned char[(section_header_size)];
|
|
memcpy(gc_sd->section_headers_data, sd->section_headers->data(),
|
|
section_header_size);
|
|
gc_sd->section_names_data =
|
|
new unsigned char[sd->section_names_size];
|
|
memcpy(gc_sd->section_names_data, sd->section_names->data(),
|
|
sd->section_names_size);
|
|
gc_sd->section_names_size = sd->section_names_size;
|
|
if (sd->symbols != NULL)
|
|
{
|
|
gc_sd->symbols_data =
|
|
new unsigned char[sd->symbols_size];
|
|
memcpy(gc_sd->symbols_data, sd->symbols->data(),
|
|
sd->symbols_size);
|
|
}
|
|
else
|
|
{
|
|
gc_sd->symbols_data = NULL;
|
|
}
|
|
gc_sd->symbols_size = sd->symbols_size;
|
|
gc_sd->external_symbols_offset = sd->external_symbols_offset;
|
|
if (sd->symbol_names != NULL)
|
|
{
|
|
gc_sd->symbol_names_data =
|
|
new unsigned char[sd->symbol_names_size];
|
|
memcpy(gc_sd->symbol_names_data, sd->symbol_names->data(),
|
|
sd->symbol_names_size);
|
|
}
|
|
else
|
|
{
|
|
gc_sd->symbol_names_data = NULL;
|
|
}
|
|
gc_sd->symbol_names_size = sd->symbol_names_size;
|
|
}
|
|
|
|
// This function determines if a particular section name must be included
|
|
// in the link. This is used during garbage collection to determine the
|
|
// roots of the worklist.
|
|
|
|
bool
|
|
Relobj::is_section_name_included(const char* name)
|
|
{
|
|
if (is_prefix_of(".ctors", name)
|
|
|| is_prefix_of(".dtors", name)
|
|
|| is_prefix_of(".note", name)
|
|
|| is_prefix_of(".init", name)
|
|
|| is_prefix_of(".fini", name)
|
|
|| is_prefix_of(".gcc_except_table", name)
|
|
|| is_prefix_of(".jcr", name)
|
|
|| is_prefix_of(".preinit_array", name)
|
|
|| (is_prefix_of(".text", name)
|
|
&& strstr(name, "personality"))
|
|
|| (is_prefix_of(".data", name)
|
|
&& strstr(name, "personality"))
|
|
|| (is_prefix_of(".sdata", name)
|
|
&& strstr(name, "personality"))
|
|
|| (is_prefix_of(".gnu.linkonce.d", name)
|
|
&& strstr(name, "personality"))
|
|
|| (is_prefix_of(".rodata", name)
|
|
&& strstr(name, "nptl_version")))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Finalize the incremental relocation information. Allocates a block
|
|
// of relocation entries for each symbol, and sets the reloc_bases_
|
|
// array to point to the first entry in each block. If CLEAR_COUNTS
|
|
// is TRUE, also clear the per-symbol relocation counters.
|
|
|
|
void
|
|
Relobj::finalize_incremental_relocs(Layout* layout, bool clear_counts)
|
|
{
|
|
unsigned int nsyms = this->get_global_symbols()->size();
|
|
this->reloc_bases_ = new unsigned int[nsyms];
|
|
|
|
gold_assert(this->reloc_bases_ != NULL);
|
|
gold_assert(layout->incremental_inputs() != NULL);
|
|
|
|
unsigned int rindex = layout->incremental_inputs()->get_reloc_count();
|
|
for (unsigned int i = 0; i < nsyms; ++i)
|
|
{
|
|
this->reloc_bases_[i] = rindex;
|
|
rindex += this->reloc_counts_[i];
|
|
if (clear_counts)
|
|
this->reloc_counts_[i] = 0;
|
|
}
|
|
layout->incremental_inputs()->set_reloc_count(rindex);
|
|
}
|
|
|
|
Object_merge_map*
|
|
Relobj::get_or_create_merge_map()
|
|
{
|
|
if (!this->object_merge_map_)
|
|
this->object_merge_map_ = new Object_merge_map();
|
|
return this->object_merge_map_;
|
|
}
|
|
|
|
// Class Sized_relobj.
|
|
|
|
// Iterate over local symbols, calling a visitor class V for each GOT offset
|
|
// associated with a local symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj<size, big_endian>::do_for_all_local_got_entries(
|
|
Got_offset_list::Visitor* v) const
|
|
{
|
|
unsigned int nsyms = this->local_symbol_count();
|
|
for (unsigned int i = 0; i < nsyms; i++)
|
|
{
|
|
Local_got_entry_key key(i, 0);
|
|
Local_got_offsets::const_iterator p = this->local_got_offsets_.find(key);
|
|
if (p != this->local_got_offsets_.end())
|
|
{
|
|
const Got_offset_list* got_offsets = p->second;
|
|
got_offsets->for_all_got_offsets(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the address of an output section.
|
|
|
|
template<int size, bool big_endian>
|
|
uint64_t
|
|
Sized_relobj<size, big_endian>::do_output_section_address(
|
|
unsigned int shndx)
|
|
{
|
|
// If the input file is linked as --just-symbols, the output
|
|
// section address is the input section address.
|
|
if (this->just_symbols())
|
|
return this->section_address(shndx);
|
|
|
|
const Output_section* os = this->do_output_section(shndx);
|
|
gold_assert(os != NULL);
|
|
return os->address();
|
|
}
|
|
|
|
// Class Sized_relobj_file.
|
|
|
|
template<int size, bool big_endian>
|
|
Sized_relobj_file<size, big_endian>::Sized_relobj_file(
|
|
const std::string& name,
|
|
Input_file* input_file,
|
|
off_t offset,
|
|
const elfcpp::Ehdr<size, big_endian>& ehdr)
|
|
: Sized_relobj<size, big_endian>(name, input_file, offset),
|
|
elf_file_(this, ehdr),
|
|
symtab_shndx_(-1U),
|
|
local_symbol_count_(0),
|
|
output_local_symbol_count_(0),
|
|
output_local_dynsym_count_(0),
|
|
symbols_(),
|
|
defined_count_(0),
|
|
local_symbol_offset_(0),
|
|
local_dynsym_offset_(0),
|
|
local_values_(),
|
|
local_plt_offsets_(),
|
|
kept_comdat_sections_(),
|
|
has_eh_frame_(false),
|
|
is_deferred_layout_(false),
|
|
deferred_layout_(),
|
|
deferred_layout_relocs_(),
|
|
output_views_(NULL)
|
|
{
|
|
this->e_type_ = ehdr.get_e_type();
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
Sized_relobj_file<size, big_endian>::~Sized_relobj_file()
|
|
{
|
|
}
|
|
|
|
// Set up an object file based on the file header. This sets up the
|
|
// section information.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_setup()
|
|
{
|
|
const unsigned int shnum = this->elf_file_.shnum();
|
|
this->set_shnum(shnum);
|
|
}
|
|
|
|
// Find the SHT_SYMTAB section, given the section headers. The ELF
|
|
// standard says that maybe in the future there can be more than one
|
|
// SHT_SYMTAB section. Until somebody figures out how that could
|
|
// work, we assume there is only one.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::find_symtab(const unsigned char* pshdrs)
|
|
{
|
|
const unsigned int shnum = this->shnum();
|
|
this->symtab_shndx_ = 0;
|
|
if (shnum > 0)
|
|
{
|
|
// Look through the sections in reverse order, since gas tends
|
|
// to put the symbol table at the end.
|
|
const unsigned char* p = pshdrs + shnum * This::shdr_size;
|
|
unsigned int i = shnum;
|
|
unsigned int xindex_shndx = 0;
|
|
unsigned int xindex_link = 0;
|
|
while (i > 0)
|
|
{
|
|
--i;
|
|
p -= This::shdr_size;
|
|
typename This::Shdr shdr(p);
|
|
if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
|
|
{
|
|
this->symtab_shndx_ = i;
|
|
if (xindex_shndx > 0 && xindex_link == i)
|
|
{
|
|
Xindex* xindex =
|
|
new Xindex(this->elf_file_.large_shndx_offset());
|
|
xindex->read_symtab_xindex<size, big_endian>(this,
|
|
xindex_shndx,
|
|
pshdrs);
|
|
this->set_xindex(xindex);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Try to pick up the SHT_SYMTAB_SHNDX section, if there is
|
|
// one. This will work if it follows the SHT_SYMTAB
|
|
// section.
|
|
if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB_SHNDX)
|
|
{
|
|
xindex_shndx = i;
|
|
xindex_link = this->adjust_shndx(shdr.get_sh_link());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the Xindex structure to use for object with lots of
|
|
// sections.
|
|
|
|
template<int size, bool big_endian>
|
|
Xindex*
|
|
Sized_relobj_file<size, big_endian>::do_initialize_xindex()
|
|
{
|
|
gold_assert(this->symtab_shndx_ != -1U);
|
|
Xindex* xindex = new Xindex(this->elf_file_.large_shndx_offset());
|
|
xindex->initialize_symtab_xindex<size, big_endian>(this, this->symtab_shndx_);
|
|
return xindex;
|
|
}
|
|
|
|
// Return whether SHDR has the right type and flags to be a GNU
|
|
// .eh_frame section.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::check_eh_frame_flags(
|
|
const elfcpp::Shdr<size, big_endian>* shdr) const
|
|
{
|
|
elfcpp::Elf_Word sh_type = shdr->get_sh_type();
|
|
return ((sh_type == elfcpp::SHT_PROGBITS
|
|
|| sh_type == parameters->target().unwind_section_type())
|
|
&& (shdr->get_sh_flags() & elfcpp::SHF_ALLOC) != 0);
|
|
}
|
|
|
|
// Find the section header with the given name.
|
|
|
|
template<int size, bool big_endian>
|
|
const unsigned char*
|
|
Object::find_shdr(
|
|
const unsigned char* pshdrs,
|
|
const char* name,
|
|
const char* names,
|
|
section_size_type names_size,
|
|
const unsigned char* hdr) const
|
|
{
|
|
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
|
const unsigned int shnum = this->shnum();
|
|
const unsigned char* hdr_end = pshdrs + shdr_size * shnum;
|
|
size_t sh_name = 0;
|
|
|
|
while (1)
|
|
{
|
|
if (hdr)
|
|
{
|
|
// We found HDR last time we were called, continue looking.
|
|
typename elfcpp::Shdr<size, big_endian> shdr(hdr);
|
|
sh_name = shdr.get_sh_name();
|
|
}
|
|
else
|
|
{
|
|
// Look for the next occurrence of NAME in NAMES.
|
|
// The fact that .shstrtab produced by current GNU tools is
|
|
// string merged means we shouldn't have both .not.foo and
|
|
// .foo in .shstrtab, and multiple .foo sections should all
|
|
// have the same sh_name. However, this is not guaranteed
|
|
// by the ELF spec and not all ELF object file producers may
|
|
// be so clever.
|
|
size_t len = strlen(name) + 1;
|
|
const char *p = sh_name ? names + sh_name + len : names;
|
|
p = reinterpret_cast<const char*>(memmem(p, names_size - (p - names),
|
|
name, len));
|
|
if (p == NULL)
|
|
return NULL;
|
|
sh_name = p - names;
|
|
hdr = pshdrs;
|
|
if (sh_name == 0)
|
|
return hdr;
|
|
}
|
|
|
|
hdr += shdr_size;
|
|
while (hdr < hdr_end)
|
|
{
|
|
typename elfcpp::Shdr<size, big_endian> shdr(hdr);
|
|
if (shdr.get_sh_name() == sh_name)
|
|
return hdr;
|
|
hdr += shdr_size;
|
|
}
|
|
hdr = NULL;
|
|
if (sh_name == 0)
|
|
return hdr;
|
|
}
|
|
}
|
|
|
|
// Return whether there is a GNU .eh_frame section, given the section
|
|
// headers and the section names.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::find_eh_frame(
|
|
const unsigned char* pshdrs,
|
|
const char* names,
|
|
section_size_type names_size) const
|
|
{
|
|
const unsigned char* s = NULL;
|
|
|
|
while (1)
|
|
{
|
|
s = this->template find_shdr<size, big_endian>(pshdrs, ".eh_frame",
|
|
names, names_size, s);
|
|
if (s == NULL)
|
|
return false;
|
|
|
|
typename This::Shdr shdr(s);
|
|
if (this->check_eh_frame_flags(&shdr))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Return TRUE if this is a section whose contents will be needed in the
|
|
// Add_symbols task. This function is only called for sections that have
|
|
// already passed the test in is_compressed_debug_section() and the debug
|
|
// section name prefix, ".debug"/".zdebug", has been skipped.
|
|
|
|
static bool
|
|
need_decompressed_section(const char* name)
|
|
{
|
|
if (*name++ != '_')
|
|
return false;
|
|
|
|
#ifdef ENABLE_THREADS
|
|
// Decompressing these sections now will help only if we're
|
|
// multithreaded.
|
|
if (parameters->options().threads())
|
|
{
|
|
// We will need .zdebug_str if this is not an incremental link
|
|
// (i.e., we are processing string merge sections) or if we need
|
|
// to build a gdb index.
|
|
if ((!parameters->incremental() || parameters->options().gdb_index())
|
|
&& strcmp(name, "str") == 0)
|
|
return true;
|
|
|
|
// We will need these other sections when building a gdb index.
|
|
if (parameters->options().gdb_index()
|
|
&& (strcmp(name, "info") == 0
|
|
|| strcmp(name, "types") == 0
|
|
|| strcmp(name, "pubnames") == 0
|
|
|| strcmp(name, "pubtypes") == 0
|
|
|| strcmp(name, "ranges") == 0
|
|
|| strcmp(name, "abbrev") == 0))
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Even when single-threaded, we will need .zdebug_str if this is
|
|
// not an incremental link and we are building a gdb index.
|
|
// Otherwise, we would decompress the section twice: once for
|
|
// string merge processing, and once for building the gdb index.
|
|
if (!parameters->incremental()
|
|
&& parameters->options().gdb_index()
|
|
&& strcmp(name, "str") == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Build a table for any compressed debug sections, mapping each section index
|
|
// to the uncompressed size and (if needed) the decompressed contents.
|
|
|
|
template<int size, bool big_endian>
|
|
Compressed_section_map*
|
|
build_compressed_section_map(
|
|
const unsigned char* pshdrs,
|
|
unsigned int shnum,
|
|
const char* names,
|
|
section_size_type names_size,
|
|
Object* obj,
|
|
bool decompress_if_needed)
|
|
{
|
|
Compressed_section_map* uncompressed_map = new Compressed_section_map();
|
|
const unsigned int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
|
const unsigned char* p = pshdrs + shdr_size;
|
|
|
|
for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
|
|
{
|
|
typename elfcpp::Shdr<size, big_endian> shdr(p);
|
|
if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
|
|
&& (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
|
|
{
|
|
if (shdr.get_sh_name() >= names_size)
|
|
{
|
|
obj->error(_("bad section name offset for section %u: %lu"),
|
|
i, static_cast<unsigned long>(shdr.get_sh_name()));
|
|
continue;
|
|
}
|
|
|
|
const char* name = names + shdr.get_sh_name();
|
|
bool is_compressed = ((shdr.get_sh_flags()
|
|
& elfcpp::SHF_COMPRESSED) != 0);
|
|
bool is_zcompressed = (!is_compressed
|
|
&& is_compressed_debug_section(name));
|
|
|
|
if (is_zcompressed || is_compressed)
|
|
{
|
|
section_size_type len;
|
|
const unsigned char* contents =
|
|
obj->section_contents(i, &len, false);
|
|
uint64_t uncompressed_size;
|
|
if (is_zcompressed)
|
|
{
|
|
// Skip over the ".zdebug" prefix.
|
|
name += 7;
|
|
uncompressed_size = get_uncompressed_size(contents, len);
|
|
}
|
|
else
|
|
{
|
|
// Skip over the ".debug" prefix.
|
|
name += 6;
|
|
elfcpp::Chdr<size, big_endian> chdr(contents);
|
|
uncompressed_size = chdr.get_ch_size();
|
|
}
|
|
Compressed_section_info info;
|
|
info.size = convert_to_section_size_type(uncompressed_size);
|
|
info.flag = shdr.get_sh_flags();
|
|
info.contents = NULL;
|
|
if (uncompressed_size != -1ULL)
|
|
{
|
|
unsigned char* uncompressed_data = NULL;
|
|
if (decompress_if_needed && need_decompressed_section(name))
|
|
{
|
|
uncompressed_data = new unsigned char[uncompressed_size];
|
|
if (decompress_input_section(contents, len,
|
|
uncompressed_data,
|
|
uncompressed_size,
|
|
size, big_endian,
|
|
shdr.get_sh_flags()))
|
|
info.contents = uncompressed_data;
|
|
else
|
|
delete[] uncompressed_data;
|
|
}
|
|
(*uncompressed_map)[i] = info;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return uncompressed_map;
|
|
}
|
|
|
|
// Stash away info for a number of special sections.
|
|
// Return true if any of the sections found require local symbols to be read.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::do_find_special_sections(
|
|
Read_symbols_data* sd)
|
|
{
|
|
const unsigned char* const pshdrs = sd->section_headers->data();
|
|
const unsigned char* namesu = sd->section_names->data();
|
|
const char* names = reinterpret_cast<const char*>(namesu);
|
|
|
|
if (this->find_eh_frame(pshdrs, names, sd->section_names_size))
|
|
this->has_eh_frame_ = true;
|
|
|
|
Compressed_section_map* compressed_sections =
|
|
build_compressed_section_map<size, big_endian>(
|
|
pshdrs, this->shnum(), names, sd->section_names_size, this, true);
|
|
if (compressed_sections != NULL)
|
|
this->set_compressed_sections(compressed_sections);
|
|
|
|
return (this->has_eh_frame_
|
|
|| (!parameters->options().relocatable()
|
|
&& parameters->options().gdb_index()
|
|
&& (memmem(names, sd->section_names_size, "debug_info", 11) != NULL
|
|
|| memmem(names, sd->section_names_size,
|
|
"debug_types", 12) != NULL)));
|
|
}
|
|
|
|
// Read the sections and symbols from an object file.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
|
{
|
|
this->base_read_symbols(sd);
|
|
}
|
|
|
|
// Read the sections and symbols from an object file. This is common
|
|
// code for all target-specific overrides of do_read_symbols().
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::base_read_symbols(Read_symbols_data* sd)
|
|
{
|
|
this->read_section_data(&this->elf_file_, sd);
|
|
|
|
const unsigned char* const pshdrs = sd->section_headers->data();
|
|
|
|
this->find_symtab(pshdrs);
|
|
|
|
bool need_local_symbols = this->do_find_special_sections(sd);
|
|
|
|
sd->symbols = NULL;
|
|
sd->symbols_size = 0;
|
|
sd->external_symbols_offset = 0;
|
|
sd->symbol_names = NULL;
|
|
sd->symbol_names_size = 0;
|
|
|
|
if (this->symtab_shndx_ == 0)
|
|
{
|
|
// No symbol table. Weird but legal.
|
|
return;
|
|
}
|
|
|
|
// Get the symbol table section header.
|
|
typename This::Shdr symtabshdr(pshdrs
|
|
+ this->symtab_shndx_ * This::shdr_size);
|
|
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
|
|
|
// If this object has a .eh_frame section, or if building a .gdb_index
|
|
// section and there is debug info, we need all the symbols.
|
|
// Otherwise we only need the external symbols. While it would be
|
|
// simpler to just always read all the symbols, I've seen object
|
|
// files with well over 2000 local symbols, which for a 64-bit
|
|
// object file format is over 5 pages that we don't need to read
|
|
// now.
|
|
|
|
const int sym_size = This::sym_size;
|
|
const unsigned int loccount = symtabshdr.get_sh_info();
|
|
this->local_symbol_count_ = loccount;
|
|
this->local_values_.resize(loccount);
|
|
section_offset_type locsize = loccount * sym_size;
|
|
off_t dataoff = symtabshdr.get_sh_offset();
|
|
section_size_type datasize =
|
|
convert_to_section_size_type(symtabshdr.get_sh_size());
|
|
off_t extoff = dataoff + locsize;
|
|
section_size_type extsize = datasize - locsize;
|
|
|
|
off_t readoff = need_local_symbols ? dataoff : extoff;
|
|
section_size_type readsize = need_local_symbols ? datasize : extsize;
|
|
|
|
if (readsize == 0)
|
|
{
|
|
// No external symbols. Also weird but also legal.
|
|
return;
|
|
}
|
|
|
|
File_view* fvsymtab = this->get_lasting_view(readoff, readsize, true, false);
|
|
|
|
// Read the section header for the symbol names.
|
|
unsigned int strtab_shndx = this->adjust_shndx(symtabshdr.get_sh_link());
|
|
if (strtab_shndx >= this->shnum())
|
|
{
|
|
this->error(_("invalid symbol table name index: %u"), strtab_shndx);
|
|
return;
|
|
}
|
|
typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size);
|
|
if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
|
|
{
|
|
this->error(_("symbol table name section has wrong type: %u"),
|
|
static_cast<unsigned int>(strtabshdr.get_sh_type()));
|
|
return;
|
|
}
|
|
|
|
// Read the symbol names.
|
|
File_view* fvstrtab = this->get_lasting_view(strtabshdr.get_sh_offset(),
|
|
strtabshdr.get_sh_size(),
|
|
false, true);
|
|
|
|
sd->symbols = fvsymtab;
|
|
sd->symbols_size = readsize;
|
|
sd->external_symbols_offset = need_local_symbols ? locsize : 0;
|
|
sd->symbol_names = fvstrtab;
|
|
sd->symbol_names_size =
|
|
convert_to_section_size_type(strtabshdr.get_sh_size());
|
|
}
|
|
|
|
// Return the section index of symbol SYM. Set *VALUE to its value in
|
|
// the object file. Set *IS_ORDINARY if this is an ordinary section
|
|
// index, not a special code between SHN_LORESERVE and SHN_HIRESERVE.
|
|
// Note that for a symbol which is not defined in this object file,
|
|
// this will set *VALUE to 0 and return SHN_UNDEF; it will not return
|
|
// the final value of the symbol in the link.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Sized_relobj_file<size, big_endian>::symbol_section_and_value(unsigned int sym,
|
|
Address* value,
|
|
bool* is_ordinary)
|
|
{
|
|
section_size_type symbols_size;
|
|
const unsigned char* symbols = this->section_contents(this->symtab_shndx_,
|
|
&symbols_size,
|
|
false);
|
|
|
|
const size_t count = symbols_size / This::sym_size;
|
|
gold_assert(sym < count);
|
|
|
|
elfcpp::Sym<size, big_endian> elfsym(symbols + sym * This::sym_size);
|
|
*value = elfsym.get_st_value();
|
|
|
|
return this->adjust_sym_shndx(sym, elfsym.get_st_shndx(), is_ordinary);
|
|
}
|
|
|
|
// Return whether to include a section group in the link. LAYOUT is
|
|
// used to keep track of which section groups we have already seen.
|
|
// INDEX is the index of the section group and SHDR is the section
|
|
// header. If we do not want to include this group, we set bits in
|
|
// OMIT for each section which should be discarded.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::include_section_group(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
unsigned int index,
|
|
const char* name,
|
|
const unsigned char* shdrs,
|
|
const char* section_names,
|
|
section_size_type section_names_size,
|
|
std::vector<bool>* omit)
|
|
{
|
|
// Read the section contents.
|
|
typename This::Shdr shdr(shdrs + index * This::shdr_size);
|
|
const unsigned char* pcon = this->get_view(shdr.get_sh_offset(),
|
|
shdr.get_sh_size(), true, false);
|
|
const elfcpp::Elf_Word* pword =
|
|
reinterpret_cast<const elfcpp::Elf_Word*>(pcon);
|
|
|
|
// The first word contains flags. We only care about COMDAT section
|
|
// groups. Other section groups are always included in the link
|
|
// just like ordinary sections.
|
|
elfcpp::Elf_Word flags = elfcpp::Swap<32, big_endian>::readval(pword);
|
|
|
|
// Look up the group signature, which is the name of a symbol. ELF
|
|
// uses a symbol name because some group signatures are long, and
|
|
// the name is generally already in the symbol table, so it makes
|
|
// sense to put the long string just once in .strtab rather than in
|
|
// both .strtab and .shstrtab.
|
|
|
|
// Get the appropriate symbol table header (this will normally be
|
|
// the single SHT_SYMTAB section, but in principle it need not be).
|
|
const unsigned int link = this->adjust_shndx(shdr.get_sh_link());
|
|
typename This::Shdr symshdr(this, this->elf_file_.section_header(link));
|
|
|
|
// Read the symbol table entry.
|
|
unsigned int symndx = shdr.get_sh_info();
|
|
if (symndx >= symshdr.get_sh_size() / This::sym_size)
|
|
{
|
|
this->error(_("section group %u info %u out of range"),
|
|
index, symndx);
|
|
return false;
|
|
}
|
|
off_t symoff = symshdr.get_sh_offset() + symndx * This::sym_size;
|
|
const unsigned char* psym = this->get_view(symoff, This::sym_size, true,
|
|
false);
|
|
elfcpp::Sym<size, big_endian> sym(psym);
|
|
|
|
// Read the symbol table names.
|
|
section_size_type symnamelen;
|
|
const unsigned char* psymnamesu;
|
|
psymnamesu = this->section_contents(this->adjust_shndx(symshdr.get_sh_link()),
|
|
&symnamelen, true);
|
|
const char* psymnames = reinterpret_cast<const char*>(psymnamesu);
|
|
|
|
// Get the section group signature.
|
|
if (sym.get_st_name() >= symnamelen)
|
|
{
|
|
this->error(_("symbol %u name offset %u out of range"),
|
|
symndx, sym.get_st_name());
|
|
return false;
|
|
}
|
|
|
|
std::string signature(psymnames + sym.get_st_name());
|
|
|
|
// It seems that some versions of gas will create a section group
|
|
// associated with a section symbol, and then fail to give a name to
|
|
// the section symbol. In such a case, use the name of the section.
|
|
if (signature[0] == '\0' && sym.get_st_type() == elfcpp::STT_SECTION)
|
|
{
|
|
bool is_ordinary;
|
|
unsigned int sym_shndx = this->adjust_sym_shndx(symndx,
|
|
sym.get_st_shndx(),
|
|
&is_ordinary);
|
|
if (!is_ordinary || sym_shndx >= this->shnum())
|
|
{
|
|
this->error(_("symbol %u invalid section index %u"),
|
|
symndx, sym_shndx);
|
|
return false;
|
|
}
|
|
typename This::Shdr member_shdr(shdrs + sym_shndx * This::shdr_size);
|
|
if (member_shdr.get_sh_name() < section_names_size)
|
|
signature = section_names + member_shdr.get_sh_name();
|
|
}
|
|
|
|
// Record this section group in the layout, and see whether we've already
|
|
// seen one with the same signature.
|
|
bool include_group;
|
|
bool is_comdat;
|
|
Kept_section* kept_section = NULL;
|
|
|
|
if ((flags & elfcpp::GRP_COMDAT) == 0)
|
|
{
|
|
include_group = true;
|
|
is_comdat = false;
|
|
}
|
|
else
|
|
{
|
|
include_group = layout->find_or_add_kept_section(signature,
|
|
this, index, true,
|
|
true, &kept_section);
|
|
is_comdat = true;
|
|
}
|
|
|
|
if (is_comdat && include_group)
|
|
{
|
|
Incremental_inputs* incremental_inputs = layout->incremental_inputs();
|
|
if (incremental_inputs != NULL)
|
|
incremental_inputs->report_comdat_group(this, signature.c_str());
|
|
}
|
|
|
|
size_t count = shdr.get_sh_size() / sizeof(elfcpp::Elf_Word);
|
|
|
|
std::vector<unsigned int> shndxes;
|
|
bool relocate_group = include_group && parameters->options().relocatable();
|
|
if (relocate_group)
|
|
shndxes.reserve(count - 1);
|
|
|
|
for (size_t i = 1; i < count; ++i)
|
|
{
|
|
elfcpp::Elf_Word shndx =
|
|
this->adjust_shndx(elfcpp::Swap<32, big_endian>::readval(pword + i));
|
|
|
|
if (relocate_group)
|
|
shndxes.push_back(shndx);
|
|
|
|
if (shndx >= this->shnum())
|
|
{
|
|
this->error(_("section %u in section group %u out of range"),
|
|
shndx, index);
|
|
continue;
|
|
}
|
|
|
|
// Check for an earlier section number, since we're going to get
|
|
// it wrong--we may have already decided to include the section.
|
|
if (shndx < index)
|
|
this->error(_("invalid section group %u refers to earlier section %u"),
|
|
index, shndx);
|
|
|
|
// Get the name of the member section.
|
|
typename This::Shdr member_shdr(shdrs + shndx * This::shdr_size);
|
|
if (member_shdr.get_sh_name() >= section_names_size)
|
|
{
|
|
// This is an error, but it will be diagnosed eventually
|
|
// in do_layout, so we don't need to do anything here but
|
|
// ignore it.
|
|
continue;
|
|
}
|
|
std::string mname(section_names + member_shdr.get_sh_name());
|
|
|
|
if (include_group)
|
|
{
|
|
if (is_comdat)
|
|
kept_section->add_comdat_section(mname, shndx,
|
|
member_shdr.get_sh_size());
|
|
}
|
|
else
|
|
{
|
|
(*omit)[shndx] = true;
|
|
|
|
// Store a mapping from this section to the Kept_section
|
|
// information for the group. This mapping is used for
|
|
// relocation processing and diagnostics.
|
|
// If the kept section is a linkonce section, we don't
|
|
// bother with it unless the comdat group contains just
|
|
// a single section, making it easy to match up.
|
|
if (is_comdat
|
|
&& (kept_section->is_comdat() || count == 2))
|
|
this->set_kept_comdat_section(shndx, true, symndx,
|
|
member_shdr.get_sh_size(),
|
|
kept_section);
|
|
}
|
|
}
|
|
|
|
if (relocate_group)
|
|
layout->layout_group(symtab, this, index, name, signature.c_str(),
|
|
shdr, flags, &shndxes);
|
|
|
|
return include_group;
|
|
}
|
|
|
|
// Whether to include a linkonce section in the link. NAME is the
|
|
// name of the section and SHDR is the section header.
|
|
|
|
// Linkonce sections are a GNU extension implemented in the original
|
|
// GNU linker before section groups were defined. The semantics are
|
|
// that we only include one linkonce section with a given name. The
|
|
// name of a linkonce section is normally .gnu.linkonce.T.SYMNAME,
|
|
// where T is the type of section and SYMNAME is the name of a symbol.
|
|
// In an attempt to make linkonce sections interact well with section
|
|
// groups, we try to identify SYMNAME and use it like a section group
|
|
// signature. We want to block section groups with that signature,
|
|
// but not other linkonce sections with that signature. We also use
|
|
// the full name of the linkonce section as a normal section group
|
|
// signature.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::include_linkonce_section(
|
|
Layout* layout,
|
|
unsigned int index,
|
|
const char* name,
|
|
const elfcpp::Shdr<size, big_endian>& shdr)
|
|
{
|
|
typename elfcpp::Elf_types<size>::Elf_WXword sh_size = shdr.get_sh_size();
|
|
// In general the symbol name we want will be the string following
|
|
// the last '.'. However, we have to handle the case of
|
|
// .gnu.linkonce.t.__i686.get_pc_thunk.bx, which was generated by
|
|
// some versions of gcc. So we use a heuristic: if the name starts
|
|
// with ".gnu.linkonce.t.", we use everything after that. Otherwise
|
|
// we look for the last '.'. We can't always simply skip
|
|
// ".gnu.linkonce.X", because we have to deal with cases like
|
|
// ".gnu.linkonce.d.rel.ro.local".
|
|
const char* const linkonce_t = ".gnu.linkonce.t.";
|
|
const char* symname;
|
|
if (strncmp(name, linkonce_t, strlen(linkonce_t)) == 0)
|
|
symname = name + strlen(linkonce_t);
|
|
else
|
|
symname = strrchr(name, '.') + 1;
|
|
std::string sig1(symname);
|
|
std::string sig2(name);
|
|
Kept_section* kept1;
|
|
Kept_section* kept2;
|
|
bool include1 = layout->find_or_add_kept_section(sig1, this, index, false,
|
|
false, &kept1);
|
|
bool include2 = layout->find_or_add_kept_section(sig2, this, index, false,
|
|
true, &kept2);
|
|
|
|
if (!include2)
|
|
{
|
|
// We are not including this section because we already saw the
|
|
// name of the section as a signature. This normally implies
|
|
// that the kept section is another linkonce section. If it is
|
|
// the same size, record it as the section which corresponds to
|
|
// this one.
|
|
if (kept2->object() != NULL && !kept2->is_comdat())
|
|
this->set_kept_comdat_section(index, false, 0, sh_size, kept2);
|
|
}
|
|
else if (!include1)
|
|
{
|
|
// The section is being discarded on the basis of its symbol
|
|
// name. This means that the corresponding kept section was
|
|
// part of a comdat group, and it will be difficult to identify
|
|
// the specific section within that group that corresponds to
|
|
// this linkonce section. We'll handle the simple case where
|
|
// the group has only one member section. Otherwise, it's not
|
|
// worth the effort.
|
|
if (kept1->object() != NULL && kept1->is_comdat())
|
|
this->set_kept_comdat_section(index, false, 0, sh_size, kept1);
|
|
}
|
|
else
|
|
{
|
|
kept1->set_linkonce_size(sh_size);
|
|
kept2->set_linkonce_size(sh_size);
|
|
}
|
|
|
|
return include1 && include2;
|
|
}
|
|
|
|
// Layout an input section.
|
|
|
|
template<int size, bool big_endian>
|
|
inline void
|
|
Sized_relobj_file<size, big_endian>::layout_section(
|
|
Layout* layout,
|
|
unsigned int shndx,
|
|
const char* name,
|
|
const typename This::Shdr& shdr,
|
|
unsigned int sh_type,
|
|
unsigned int reloc_shndx,
|
|
unsigned int reloc_type)
|
|
{
|
|
off_t offset;
|
|
Output_section* os = layout->layout(this, shndx, name, shdr, sh_type,
|
|
reloc_shndx, reloc_type, &offset);
|
|
|
|
this->output_sections()[shndx] = os;
|
|
if (offset == -1)
|
|
this->section_offsets()[shndx] = invalid_address;
|
|
else
|
|
this->section_offsets()[shndx] = convert_types<Address, off_t>(offset);
|
|
|
|
// If this section requires special handling, and if there are
|
|
// relocs that apply to it, then we must do the special handling
|
|
// before we apply the relocs.
|
|
if (offset == -1 && reloc_shndx != 0)
|
|
this->set_relocs_must_follow_section_writes();
|
|
}
|
|
|
|
// Layout an input .eh_frame section.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::layout_eh_frame_section(
|
|
Layout* layout,
|
|
const unsigned char* symbols_data,
|
|
section_size_type symbols_size,
|
|
const unsigned char* symbol_names_data,
|
|
section_size_type symbol_names_size,
|
|
unsigned int shndx,
|
|
const typename This::Shdr& shdr,
|
|
unsigned int reloc_shndx,
|
|
unsigned int reloc_type)
|
|
{
|
|
gold_assert(this->has_eh_frame_);
|
|
|
|
off_t offset;
|
|
Output_section* os = layout->layout_eh_frame(this,
|
|
symbols_data,
|
|
symbols_size,
|
|
symbol_names_data,
|
|
symbol_names_size,
|
|
shndx,
|
|
shdr,
|
|
reloc_shndx,
|
|
reloc_type,
|
|
&offset);
|
|
this->output_sections()[shndx] = os;
|
|
if (os == NULL || offset == -1)
|
|
this->section_offsets()[shndx] = invalid_address;
|
|
else
|
|
this->section_offsets()[shndx] = convert_types<Address, off_t>(offset);
|
|
|
|
// If this section requires special handling, and if there are
|
|
// relocs that aply to it, then we must do the special handling
|
|
// before we apply the relocs.
|
|
if (os != NULL && offset == -1 && reloc_shndx != 0)
|
|
this->set_relocs_must_follow_section_writes();
|
|
}
|
|
|
|
// Lay out the input sections. We walk through the sections and check
|
|
// whether they should be included in the link. If they should, we
|
|
// pass them to the Layout object, which will return an output section
|
|
// and an offset.
|
|
// This function is called twice sometimes, two passes, when mapping
|
|
// of input sections to output sections must be delayed.
|
|
// This is true for the following :
|
|
// * Garbage collection (--gc-sections): Some input sections will be
|
|
// discarded and hence the assignment must wait until the second pass.
|
|
// In the first pass, it is for setting up some sections as roots to
|
|
// a work-list for --gc-sections and to do comdat processing.
|
|
// * Identical Code Folding (--icf=<safe,all>): Some input sections
|
|
// will be folded and hence the assignment must wait.
|
|
// * Using plugins to map some sections to unique segments: Mapping
|
|
// some sections to unique segments requires mapping them to unique
|
|
// output sections too. This can be done via plugins now and this
|
|
// information is not available in the first pass.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_layout(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_symbols_data* sd)
|
|
{
|
|
const unsigned int unwind_section_type =
|
|
parameters->target().unwind_section_type();
|
|
const unsigned int shnum = this->shnum();
|
|
|
|
/* Should this function be called twice? */
|
|
bool is_two_pass = (parameters->options().gc_sections()
|
|
|| parameters->options().icf_enabled()
|
|
|| layout->is_unique_segment_for_sections_specified());
|
|
|
|
/* Only one of is_pass_one and is_pass_two is true. Both are false when
|
|
a two-pass approach is not needed. */
|
|
bool is_pass_one = false;
|
|
bool is_pass_two = false;
|
|
|
|
Symbols_data* gc_sd = NULL;
|
|
|
|
/* Check if do_layout needs to be two-pass. If so, find out which pass
|
|
should happen. In the first pass, the data in sd is saved to be used
|
|
later in the second pass. */
|
|
if (is_two_pass)
|
|
{
|
|
gc_sd = this->get_symbols_data();
|
|
if (gc_sd == NULL)
|
|
{
|
|
gold_assert(sd != NULL);
|
|
is_pass_one = true;
|
|
}
|
|
else
|
|
{
|
|
if (parameters->options().gc_sections())
|
|
gold_assert(symtab->gc()->is_worklist_ready());
|
|
if (parameters->options().icf_enabled())
|
|
gold_assert(symtab->icf()->is_icf_ready());
|
|
is_pass_two = true;
|
|
}
|
|
}
|
|
|
|
if (shnum == 0)
|
|
return;
|
|
|
|
if (is_pass_one)
|
|
{
|
|
// During garbage collection save the symbols data to use it when
|
|
// re-entering this function.
|
|
gc_sd = new Symbols_data;
|
|
this->copy_symbols_data(gc_sd, sd, This::shdr_size * shnum);
|
|
this->set_symbols_data(gc_sd);
|
|
}
|
|
|
|
const unsigned char* section_headers_data = NULL;
|
|
section_size_type section_names_size;
|
|
const unsigned char* symbols_data = NULL;
|
|
section_size_type symbols_size;
|
|
const unsigned char* symbol_names_data = NULL;
|
|
section_size_type symbol_names_size;
|
|
|
|
if (is_two_pass)
|
|
{
|
|
section_headers_data = gc_sd->section_headers_data;
|
|
section_names_size = gc_sd->section_names_size;
|
|
symbols_data = gc_sd->symbols_data;
|
|
symbols_size = gc_sd->symbols_size;
|
|
symbol_names_data = gc_sd->symbol_names_data;
|
|
symbol_names_size = gc_sd->symbol_names_size;
|
|
}
|
|
else
|
|
{
|
|
section_headers_data = sd->section_headers->data();
|
|
section_names_size = sd->section_names_size;
|
|
if (sd->symbols != NULL)
|
|
symbols_data = sd->symbols->data();
|
|
symbols_size = sd->symbols_size;
|
|
if (sd->symbol_names != NULL)
|
|
symbol_names_data = sd->symbol_names->data();
|
|
symbol_names_size = sd->symbol_names_size;
|
|
}
|
|
|
|
// Get the section headers.
|
|
const unsigned char* shdrs = section_headers_data;
|
|
const unsigned char* pshdrs;
|
|
|
|
// Get the section names.
|
|
const unsigned char* pnamesu = (is_two_pass
|
|
? gc_sd->section_names_data
|
|
: sd->section_names->data());
|
|
|
|
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
|
|
|
// If any input files have been claimed by plugins, we need to defer
|
|
// actual layout until the replacement files have arrived.
|
|
const bool should_defer_layout =
|
|
(parameters->options().has_plugins()
|
|
&& parameters->options().plugins()->should_defer_layout());
|
|
unsigned int num_sections_to_defer = 0;
|
|
|
|
// For each section, record the index of the reloc section if any.
|
|
// Use 0 to mean that there is no reloc section, -1U to mean that
|
|
// there is more than one.
|
|
std::vector<unsigned int> reloc_shndx(shnum, 0);
|
|
std::vector<unsigned int> reloc_type(shnum, elfcpp::SHT_NULL);
|
|
// Skip the first, dummy, section.
|
|
pshdrs = shdrs + This::shdr_size;
|
|
for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
|
|
{
|
|
typename This::Shdr shdr(pshdrs);
|
|
|
|
// Count the number of sections whose layout will be deferred.
|
|
if (should_defer_layout && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
|
|
++num_sections_to_defer;
|
|
|
|
unsigned int sh_type = shdr.get_sh_type();
|
|
if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA)
|
|
{
|
|
unsigned int target_shndx = this->adjust_shndx(shdr.get_sh_info());
|
|
if (target_shndx == 0 || target_shndx >= shnum)
|
|
{
|
|
this->error(_("relocation section %u has bad info %u"),
|
|
i, target_shndx);
|
|
continue;
|
|
}
|
|
|
|
if (reloc_shndx[target_shndx] != 0)
|
|
reloc_shndx[target_shndx] = -1U;
|
|
else
|
|
{
|
|
reloc_shndx[target_shndx] = i;
|
|
reloc_type[target_shndx] = sh_type;
|
|
}
|
|
}
|
|
}
|
|
|
|
Output_sections& out_sections(this->output_sections());
|
|
std::vector<Address>& out_section_offsets(this->section_offsets());
|
|
|
|
if (!is_pass_two)
|
|
{
|
|
out_sections.resize(shnum);
|
|
out_section_offsets.resize(shnum);
|
|
}
|
|
|
|
// If we are only linking for symbols, then there is nothing else to
|
|
// do here.
|
|
if (this->input_file()->just_symbols())
|
|
{
|
|
if (!is_pass_two)
|
|
{
|
|
delete sd->section_headers;
|
|
sd->section_headers = NULL;
|
|
delete sd->section_names;
|
|
sd->section_names = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (num_sections_to_defer > 0)
|
|
{
|
|
parameters->options().plugins()->add_deferred_layout_object(this);
|
|
this->deferred_layout_.reserve(num_sections_to_defer);
|
|
this->is_deferred_layout_ = true;
|
|
}
|
|
|
|
// Whether we've seen a .note.GNU-stack section.
|
|
bool seen_gnu_stack = false;
|
|
// The flags of a .note.GNU-stack section.
|
|
uint64_t gnu_stack_flags = 0;
|
|
|
|
// Keep track of which sections to omit.
|
|
std::vector<bool> omit(shnum, false);
|
|
|
|
// Keep track of reloc sections when emitting relocations.
|
|
const bool relocatable = parameters->options().relocatable();
|
|
const bool emit_relocs = (relocatable
|
|
|| parameters->options().emit_relocs());
|
|
std::vector<unsigned int> reloc_sections;
|
|
|
|
// Keep track of .eh_frame sections.
|
|
std::vector<unsigned int> eh_frame_sections;
|
|
|
|
// Keep track of .debug_info and .debug_types sections.
|
|
std::vector<unsigned int> debug_info_sections;
|
|
std::vector<unsigned int> debug_types_sections;
|
|
|
|
// Skip the first, dummy, section.
|
|
pshdrs = shdrs + This::shdr_size;
|
|
for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
|
|
{
|
|
typename This::Shdr shdr(pshdrs);
|
|
const unsigned int sh_name = shdr.get_sh_name();
|
|
unsigned int sh_type = shdr.get_sh_type();
|
|
|
|
if (sh_name >= section_names_size)
|
|
{
|
|
this->error(_("bad section name offset for section %u: %lu"),
|
|
i, static_cast<unsigned long>(sh_name));
|
|
return;
|
|
}
|
|
|
|
const char* name = pnames + sh_name;
|
|
|
|
if (!is_pass_two)
|
|
{
|
|
if (this->handle_gnu_warning_section(name, i, symtab))
|
|
{
|
|
if (!relocatable && !parameters->options().shared())
|
|
omit[i] = true;
|
|
}
|
|
|
|
// The .note.GNU-stack section is special. It gives the
|
|
// protection flags that this object file requires for the stack
|
|
// in memory.
|
|
if (strcmp(name, ".note.GNU-stack") == 0)
|
|
{
|
|
seen_gnu_stack = true;
|
|
gnu_stack_flags |= shdr.get_sh_flags();
|
|
omit[i] = true;
|
|
}
|
|
|
|
// The .note.GNU-split-stack section is also special. It
|
|
// indicates that the object was compiled with
|
|
// -fsplit-stack.
|
|
if (this->handle_split_stack_section(name))
|
|
{
|
|
if (!relocatable && !parameters->options().shared())
|
|
omit[i] = true;
|
|
}
|
|
|
|
// Skip attributes section.
|
|
if (parameters->target().is_attributes_section(name))
|
|
{
|
|
omit[i] = true;
|
|
}
|
|
|
|
bool discard = omit[i];
|
|
if (!discard)
|
|
{
|
|
if (sh_type == elfcpp::SHT_GROUP)
|
|
{
|
|
if (!this->include_section_group(symtab, layout, i, name,
|
|
shdrs, pnames,
|
|
section_names_size,
|
|
&omit))
|
|
discard = true;
|
|
}
|
|
else if ((shdr.get_sh_flags() & elfcpp::SHF_GROUP) == 0
|
|
&& Layout::is_linkonce(name))
|
|
{
|
|
if (!this->include_linkonce_section(layout, i, name, shdr))
|
|
discard = true;
|
|
}
|
|
}
|
|
|
|
// Add the section to the incremental inputs layout.
|
|
Incremental_inputs* incremental_inputs = layout->incremental_inputs();
|
|
if (incremental_inputs != NULL
|
|
&& !discard
|
|
&& can_incremental_update(sh_type))
|
|
{
|
|
off_t sh_size = shdr.get_sh_size();
|
|
section_size_type uncompressed_size;
|
|
if (this->section_is_compressed(i, &uncompressed_size))
|
|
sh_size = uncompressed_size;
|
|
incremental_inputs->report_input_section(this, i, name, sh_size);
|
|
}
|
|
|
|
if (discard)
|
|
{
|
|
// Do not include this section in the link.
|
|
out_sections[i] = NULL;
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (is_pass_one && parameters->options().gc_sections())
|
|
{
|
|
if (this->is_section_name_included(name)
|
|
|| layout->keep_input_section (this, name)
|
|
|| sh_type == elfcpp::SHT_INIT_ARRAY
|
|
|| sh_type == elfcpp::SHT_FINI_ARRAY)
|
|
{
|
|
symtab->gc()->worklist().push_back(Section_id(this, i));
|
|
}
|
|
// If the section name XXX can be represented as a C identifier
|
|
// it cannot be discarded if there are references to
|
|
// __start_XXX and __stop_XXX symbols. These need to be
|
|
// specially handled.
|
|
if (is_cident(name))
|
|
{
|
|
symtab->gc()->add_cident_section(name, Section_id(this, i));
|
|
}
|
|
}
|
|
|
|
// When doing a relocatable link we are going to copy input
|
|
// reloc sections into the output. We only want to copy the
|
|
// ones associated with sections which are not being discarded.
|
|
// However, we don't know that yet for all sections. So save
|
|
// reloc sections and process them later. Garbage collection is
|
|
// not triggered when relocatable code is desired.
|
|
if (emit_relocs
|
|
&& (sh_type == elfcpp::SHT_REL
|
|
|| sh_type == elfcpp::SHT_RELA))
|
|
{
|
|
reloc_sections.push_back(i);
|
|
continue;
|
|
}
|
|
|
|
if (relocatable && sh_type == elfcpp::SHT_GROUP)
|
|
continue;
|
|
|
|
// The .eh_frame section is special. It holds exception frame
|
|
// information that we need to read in order to generate the
|
|
// exception frame header. We process these after all the other
|
|
// sections so that the exception frame reader can reliably
|
|
// determine which sections are being discarded, and discard the
|
|
// corresponding information.
|
|
if (this->check_eh_frame_flags(&shdr)
|
|
&& strcmp(name, ".eh_frame") == 0)
|
|
{
|
|
// If the target has a special unwind section type, let's
|
|
// canonicalize it here.
|
|
sh_type = unwind_section_type;
|
|
if (!relocatable)
|
|
{
|
|
if (is_pass_one)
|
|
{
|
|
if (this->is_deferred_layout())
|
|
out_sections[i] = reinterpret_cast<Output_section*>(2);
|
|
else
|
|
out_sections[i] = reinterpret_cast<Output_section*>(1);
|
|
out_section_offsets[i] = invalid_address;
|
|
}
|
|
else if (this->is_deferred_layout())
|
|
this->deferred_layout_.push_back(
|
|
Deferred_layout(i, name, sh_type, pshdrs,
|
|
reloc_shndx[i], reloc_type[i]));
|
|
else
|
|
eh_frame_sections.push_back(i);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (is_pass_two && parameters->options().gc_sections())
|
|
{
|
|
// This is executed during the second pass of garbage
|
|
// collection. do_layout has been called before and some
|
|
// sections have been already discarded. Simply ignore
|
|
// such sections this time around.
|
|
if (out_sections[i] == NULL)
|
|
{
|
|
gold_assert(out_section_offsets[i] == invalid_address);
|
|
continue;
|
|
}
|
|
if (((shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
|
|
&& symtab->gc()->is_section_garbage(this, i))
|
|
{
|
|
if (parameters->options().print_gc_sections())
|
|
gold_info(_("%s: removing unused section from '%s'"
|
|
" in file '%s'"),
|
|
program_name, this->section_name(i).c_str(),
|
|
this->name().c_str());
|
|
out_sections[i] = NULL;
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (is_pass_two && parameters->options().icf_enabled())
|
|
{
|
|
if (out_sections[i] == NULL)
|
|
{
|
|
gold_assert(out_section_offsets[i] == invalid_address);
|
|
continue;
|
|
}
|
|
if (((shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
|
|
&& symtab->icf()->is_section_folded(this, i))
|
|
{
|
|
if (parameters->options().print_icf_sections())
|
|
{
|
|
Section_id folded =
|
|
symtab->icf()->get_folded_section(this, i);
|
|
Relobj* folded_obj =
|
|
reinterpret_cast<Relobj*>(folded.first);
|
|
gold_info(_("%s: ICF folding section '%s' in file '%s' "
|
|
"into '%s' in file '%s'"),
|
|
program_name, this->section_name(i).c_str(),
|
|
this->name().c_str(),
|
|
folded_obj->section_name(folded.second).c_str(),
|
|
folded_obj->name().c_str());
|
|
}
|
|
out_sections[i] = NULL;
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Defer layout here if input files are claimed by plugins. When gc
|
|
// is turned on this function is called twice; we only want to do this
|
|
// on the first pass.
|
|
if (!is_pass_two
|
|
&& this->is_deferred_layout()
|
|
&& (shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
|
|
{
|
|
this->deferred_layout_.push_back(Deferred_layout(i, name, sh_type,
|
|
pshdrs,
|
|
reloc_shndx[i],
|
|
reloc_type[i]));
|
|
// Put dummy values here; real values will be supplied by
|
|
// do_layout_deferred_sections.
|
|
out_sections[i] = reinterpret_cast<Output_section*>(2);
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
|
|
// During gc_pass_two if a section that was previously deferred is
|
|
// found, do not layout the section as layout_deferred_sections will
|
|
// do it later from gold.cc.
|
|
if (is_pass_two
|
|
&& (out_sections[i] == reinterpret_cast<Output_section*>(2)))
|
|
continue;
|
|
|
|
if (is_pass_one)
|
|
{
|
|
// This is during garbage collection. The out_sections are
|
|
// assigned in the second call to this function.
|
|
out_sections[i] = reinterpret_cast<Output_section*>(1);
|
|
out_section_offsets[i] = invalid_address;
|
|
}
|
|
else
|
|
{
|
|
// When garbage collection is switched on the actual layout
|
|
// only happens in the second call.
|
|
this->layout_section(layout, i, name, shdr, sh_type, reloc_shndx[i],
|
|
reloc_type[i]);
|
|
|
|
// When generating a .gdb_index section, we do additional
|
|
// processing of .debug_info and .debug_types sections after all
|
|
// the other sections for the same reason as above.
|
|
if (!relocatable
|
|
&& parameters->options().gdb_index()
|
|
&& !(shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
|
|
{
|
|
if (strcmp(name, ".debug_info") == 0
|
|
|| strcmp(name, ".zdebug_info") == 0)
|
|
debug_info_sections.push_back(i);
|
|
else if (strcmp(name, ".debug_types") == 0
|
|
|| strcmp(name, ".zdebug_types") == 0)
|
|
debug_types_sections.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_pass_two)
|
|
layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags, this);
|
|
|
|
// Handle the .eh_frame sections after the other sections.
|
|
gold_assert(!is_pass_one || eh_frame_sections.empty());
|
|
for (std::vector<unsigned int>::const_iterator p = eh_frame_sections.begin();
|
|
p != eh_frame_sections.end();
|
|
++p)
|
|
{
|
|
unsigned int i = *p;
|
|
const unsigned char* pshdr;
|
|
pshdr = section_headers_data + i * This::shdr_size;
|
|
typename This::Shdr shdr(pshdr);
|
|
|
|
this->layout_eh_frame_section(layout,
|
|
symbols_data,
|
|
symbols_size,
|
|
symbol_names_data,
|
|
symbol_names_size,
|
|
i,
|
|
shdr,
|
|
reloc_shndx[i],
|
|
reloc_type[i]);
|
|
}
|
|
|
|
// When doing a relocatable link handle the reloc sections at the
|
|
// end. Garbage collection and Identical Code Folding is not
|
|
// turned on for relocatable code.
|
|
if (emit_relocs)
|
|
this->size_relocatable_relocs();
|
|
|
|
gold_assert(!is_two_pass || reloc_sections.empty());
|
|
|
|
for (std::vector<unsigned int>::const_iterator p = reloc_sections.begin();
|
|
p != reloc_sections.end();
|
|
++p)
|
|
{
|
|
unsigned int i = *p;
|
|
const unsigned char* pshdr;
|
|
pshdr = section_headers_data + i * This::shdr_size;
|
|
typename This::Shdr shdr(pshdr);
|
|
|
|
unsigned int data_shndx = this->adjust_shndx(shdr.get_sh_info());
|
|
if (data_shndx >= shnum)
|
|
{
|
|
// We already warned about this above.
|
|
continue;
|
|
}
|
|
|
|
Output_section* data_section = out_sections[data_shndx];
|
|
if (data_section == reinterpret_cast<Output_section*>(2))
|
|
{
|
|
if (is_pass_two)
|
|
continue;
|
|
// The layout for the data section was deferred, so we need
|
|
// to defer the relocation section, too.
|
|
const char* name = pnames + shdr.get_sh_name();
|
|
this->deferred_layout_relocs_.push_back(
|
|
Deferred_layout(i, name, shdr.get_sh_type(), pshdr, 0,
|
|
elfcpp::SHT_NULL));
|
|
out_sections[i] = reinterpret_cast<Output_section*>(2);
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
if (data_section == NULL)
|
|
{
|
|
out_sections[i] = NULL;
|
|
out_section_offsets[i] = invalid_address;
|
|
continue;
|
|
}
|
|
|
|
Relocatable_relocs* rr = new Relocatable_relocs();
|
|
this->set_relocatable_relocs(i, rr);
|
|
|
|
Output_section* os = layout->layout_reloc(this, i, shdr, data_section,
|
|
rr);
|
|
out_sections[i] = os;
|
|
out_section_offsets[i] = invalid_address;
|
|
}
|
|
|
|
// When building a .gdb_index section, scan the .debug_info and
|
|
// .debug_types sections.
|
|
gold_assert(!is_pass_one
|
|
|| (debug_info_sections.empty() && debug_types_sections.empty()));
|
|
for (std::vector<unsigned int>::const_iterator p
|
|
= debug_info_sections.begin();
|
|
p != debug_info_sections.end();
|
|
++p)
|
|
{
|
|
unsigned int i = *p;
|
|
layout->add_to_gdb_index(false, this, symbols_data, symbols_size,
|
|
i, reloc_shndx[i], reloc_type[i]);
|
|
}
|
|
for (std::vector<unsigned int>::const_iterator p
|
|
= debug_types_sections.begin();
|
|
p != debug_types_sections.end();
|
|
++p)
|
|
{
|
|
unsigned int i = *p;
|
|
layout->add_to_gdb_index(true, this, symbols_data, symbols_size,
|
|
i, reloc_shndx[i], reloc_type[i]);
|
|
}
|
|
|
|
if (is_pass_two)
|
|
{
|
|
delete[] gc_sd->section_headers_data;
|
|
delete[] gc_sd->section_names_data;
|
|
delete[] gc_sd->symbols_data;
|
|
delete[] gc_sd->symbol_names_data;
|
|
this->set_symbols_data(NULL);
|
|
}
|
|
else
|
|
{
|
|
delete sd->section_headers;
|
|
sd->section_headers = NULL;
|
|
delete sd->section_names;
|
|
sd->section_names = NULL;
|
|
}
|
|
}
|
|
|
|
// Layout sections whose layout was deferred while waiting for
|
|
// input files from a plugin.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_layout_deferred_sections(Layout* layout)
|
|
{
|
|
typename std::vector<Deferred_layout>::iterator deferred;
|
|
|
|
for (deferred = this->deferred_layout_.begin();
|
|
deferred != this->deferred_layout_.end();
|
|
++deferred)
|
|
{
|
|
typename This::Shdr shdr(deferred->shdr_data_);
|
|
|
|
if (!parameters->options().relocatable()
|
|
&& deferred->name_ == ".eh_frame"
|
|
&& this->check_eh_frame_flags(&shdr))
|
|
{
|
|
// Checking is_section_included is not reliable for
|
|
// .eh_frame sections, because they do not have an output
|
|
// section. This is not a problem normally because we call
|
|
// layout_eh_frame_section unconditionally, but when
|
|
// deferring sections that is not true. We don't want to
|
|
// keep all .eh_frame sections because that will cause us to
|
|
// keep all sections that they refer to, which is the wrong
|
|
// way around. Instead, the eh_frame code will discard
|
|
// .eh_frame sections that refer to discarded sections.
|
|
|
|
// Reading the symbols again here may be slow.
|
|
Read_symbols_data sd;
|
|
this->base_read_symbols(&sd);
|
|
this->layout_eh_frame_section(layout,
|
|
sd.symbols->data(),
|
|
sd.symbols_size,
|
|
sd.symbol_names->data(),
|
|
sd.symbol_names_size,
|
|
deferred->shndx_,
|
|
shdr,
|
|
deferred->reloc_shndx_,
|
|
deferred->reloc_type_);
|
|
continue;
|
|
}
|
|
|
|
// If the section is not included, it is because the garbage collector
|
|
// decided it is not needed. Avoid reverting that decision.
|
|
if (!this->is_section_included(deferred->shndx_))
|
|
continue;
|
|
|
|
this->layout_section(layout, deferred->shndx_, deferred->name_.c_str(),
|
|
shdr, shdr.get_sh_type(), deferred->reloc_shndx_,
|
|
deferred->reloc_type_);
|
|
}
|
|
|
|
this->deferred_layout_.clear();
|
|
|
|
// Now handle the deferred relocation sections.
|
|
|
|
Output_sections& out_sections(this->output_sections());
|
|
std::vector<Address>& out_section_offsets(this->section_offsets());
|
|
|
|
for (deferred = this->deferred_layout_relocs_.begin();
|
|
deferred != this->deferred_layout_relocs_.end();
|
|
++deferred)
|
|
{
|
|
unsigned int shndx = deferred->shndx_;
|
|
typename This::Shdr shdr(deferred->shdr_data_);
|
|
unsigned int data_shndx = this->adjust_shndx(shdr.get_sh_info());
|
|
|
|
Output_section* data_section = out_sections[data_shndx];
|
|
if (data_section == NULL)
|
|
{
|
|
out_sections[shndx] = NULL;
|
|
out_section_offsets[shndx] = invalid_address;
|
|
continue;
|
|
}
|
|
|
|
Relocatable_relocs* rr = new Relocatable_relocs();
|
|
this->set_relocatable_relocs(shndx, rr);
|
|
|
|
Output_section* os = layout->layout_reloc(this, shndx, shdr,
|
|
data_section, rr);
|
|
out_sections[shndx] = os;
|
|
out_section_offsets[shndx] = invalid_address;
|
|
}
|
|
}
|
|
|
|
// Add the symbols to the symbol table.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_add_symbols(Symbol_table* symtab,
|
|
Read_symbols_data* sd,
|
|
Layout*)
|
|
{
|
|
if (sd->symbols == NULL)
|
|
{
|
|
gold_assert(sd->symbol_names == NULL);
|
|
return;
|
|
}
|
|
|
|
const int sym_size = This::sym_size;
|
|
size_t symcount = ((sd->symbols_size - sd->external_symbols_offset)
|
|
/ sym_size);
|
|
if (symcount * sym_size != sd->symbols_size - sd->external_symbols_offset)
|
|
{
|
|
this->error(_("size of symbols is not multiple of symbol size"));
|
|
return;
|
|
}
|
|
|
|
this->symbols_.resize(symcount);
|
|
|
|
const char* sym_names =
|
|
reinterpret_cast<const char*>(sd->symbol_names->data());
|
|
symtab->add_from_relobj(this,
|
|
sd->symbols->data() + sd->external_symbols_offset,
|
|
symcount, this->local_symbol_count_,
|
|
sym_names, sd->symbol_names_size,
|
|
&this->symbols_,
|
|
&this->defined_count_);
|
|
|
|
delete sd->symbols;
|
|
sd->symbols = NULL;
|
|
delete sd->symbol_names;
|
|
sd->symbol_names = NULL;
|
|
}
|
|
|
|
// Find out if this object, that is a member of a lib group, should be included
|
|
// in the link. We check every symbol defined by this object. If the symbol
|
|
// table has a strong undefined reference to that symbol, we have to include
|
|
// the object.
|
|
|
|
template<int size, bool big_endian>
|
|
Archive::Should_include
|
|
Sized_relobj_file<size, big_endian>::do_should_include_member(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
Read_symbols_data* sd,
|
|
std::string* why)
|
|
{
|
|
char* tmpbuf = NULL;
|
|
size_t tmpbuflen = 0;
|
|
const char* sym_names =
|
|
reinterpret_cast<const char*>(sd->symbol_names->data());
|
|
const unsigned char* syms =
|
|
sd->symbols->data() + sd->external_symbols_offset;
|
|
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
|
|
size_t symcount = ((sd->symbols_size - sd->external_symbols_offset)
|
|
/ sym_size);
|
|
|
|
const unsigned char* p = syms;
|
|
|
|
for (size_t i = 0; i < symcount; ++i, p += sym_size)
|
|
{
|
|
elfcpp::Sym<size, big_endian> sym(p);
|
|
unsigned int st_shndx = sym.get_st_shndx();
|
|
if (st_shndx == elfcpp::SHN_UNDEF)
|
|
continue;
|
|
|
|
unsigned int st_name = sym.get_st_name();
|
|
const char* name = sym_names + st_name;
|
|
Symbol* symbol;
|
|
Archive::Should_include t = Archive::should_include_member(symtab,
|
|
layout,
|
|
name,
|
|
&symbol, why,
|
|
&tmpbuf,
|
|
&tmpbuflen);
|
|
if (t == Archive::SHOULD_INCLUDE_YES)
|
|
{
|
|
if (tmpbuf != NULL)
|
|
free(tmpbuf);
|
|
return t;
|
|
}
|
|
}
|
|
if (tmpbuf != NULL)
|
|
free(tmpbuf);
|
|
return Archive::SHOULD_INCLUDE_UNKNOWN;
|
|
}
|
|
|
|
// Iterate over global defined symbols, calling a visitor class V for each.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_for_all_global_symbols(
|
|
Read_symbols_data* sd,
|
|
Library_base::Symbol_visitor_base* v)
|
|
{
|
|
const char* sym_names =
|
|
reinterpret_cast<const char*>(sd->symbol_names->data());
|
|
const unsigned char* syms =
|
|
sd->symbols->data() + sd->external_symbols_offset;
|
|
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
|
|
size_t symcount = ((sd->symbols_size - sd->external_symbols_offset)
|
|
/ sym_size);
|
|
const unsigned char* p = syms;
|
|
|
|
for (size_t i = 0; i < symcount; ++i, p += sym_size)
|
|
{
|
|
elfcpp::Sym<size, big_endian> sym(p);
|
|
if (sym.get_st_shndx() != elfcpp::SHN_UNDEF)
|
|
v->visit(sym_names + sym.get_st_name());
|
|
}
|
|
}
|
|
|
|
// Return whether the local symbol SYMNDX has a PLT offset.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::local_has_plt_offset(
|
|
unsigned int symndx) const
|
|
{
|
|
typename Local_plt_offsets::const_iterator p =
|
|
this->local_plt_offsets_.find(symndx);
|
|
return p != this->local_plt_offsets_.end();
|
|
}
|
|
|
|
// Get the PLT offset of a local symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Sized_relobj_file<size, big_endian>::do_local_plt_offset(
|
|
unsigned int symndx) const
|
|
{
|
|
typename Local_plt_offsets::const_iterator p =
|
|
this->local_plt_offsets_.find(symndx);
|
|
gold_assert(p != this->local_plt_offsets_.end());
|
|
return p->second;
|
|
}
|
|
|
|
// Set the PLT offset of a local symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::set_local_plt_offset(
|
|
unsigned int symndx, unsigned int plt_offset)
|
|
{
|
|
std::pair<typename Local_plt_offsets::iterator, bool> ins =
|
|
this->local_plt_offsets_.insert(std::make_pair(symndx, plt_offset));
|
|
gold_assert(ins.second);
|
|
}
|
|
|
|
// First pass over the local symbols. Here we add their names to
|
|
// *POOL and *DYNPOOL, and we store the symbol value in
|
|
// THIS->LOCAL_VALUES_. This function is always called from a
|
|
// singleton thread. This is followed by a call to
|
|
// finalize_local_symbols.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_count_local_symbols(Stringpool* pool,
|
|
Stringpool* dynpool)
|
|
{
|
|
gold_assert(this->symtab_shndx_ != -1U);
|
|
if (this->symtab_shndx_ == 0)
|
|
{
|
|
// This object has no symbols. Weird but legal.
|
|
return;
|
|
}
|
|
|
|
// Read the symbol table section header.
|
|
const unsigned int symtab_shndx = this->symtab_shndx_;
|
|
typename This::Shdr symtabshdr(this,
|
|
this->elf_file_.section_header(symtab_shndx));
|
|
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
|
|
|
// Read the local symbols.
|
|
const int sym_size = This::sym_size;
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
gold_assert(loccount == symtabshdr.get_sh_info());
|
|
off_t locsize = loccount * sym_size;
|
|
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
|
|
locsize, true, true);
|
|
|
|
// Read the symbol names.
|
|
const unsigned int strtab_shndx =
|
|
this->adjust_shndx(symtabshdr.get_sh_link());
|
|
section_size_type strtab_size;
|
|
const unsigned char* pnamesu = this->section_contents(strtab_shndx,
|
|
&strtab_size,
|
|
true);
|
|
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
|
|
|
// Loop over the local symbols.
|
|
|
|
const Output_sections& out_sections(this->output_sections());
|
|
std::vector<Address>& out_section_offsets(this->section_offsets());
|
|
unsigned int shnum = this->shnum();
|
|
unsigned int count = 0;
|
|
unsigned int dyncount = 0;
|
|
// Skip the first, dummy, symbol.
|
|
psyms += sym_size;
|
|
bool strip_all = parameters->options().strip_all();
|
|
bool discard_all = parameters->options().discard_all();
|
|
bool discard_locals = parameters->options().discard_locals();
|
|
bool discard_sec_merge = parameters->options().discard_sec_merge();
|
|
for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
|
|
{
|
|
elfcpp::Sym<size, big_endian> sym(psyms);
|
|
|
|
Symbol_value<size>& lv(this->local_values_[i]);
|
|
|
|
bool is_ordinary;
|
|
unsigned int shndx = this->adjust_sym_shndx(i, sym.get_st_shndx(),
|
|
&is_ordinary);
|
|
lv.set_input_shndx(shndx, is_ordinary);
|
|
|
|
if (sym.get_st_type() == elfcpp::STT_SECTION)
|
|
lv.set_is_section_symbol();
|
|
else if (sym.get_st_type() == elfcpp::STT_TLS)
|
|
lv.set_is_tls_symbol();
|
|
else if (sym.get_st_type() == elfcpp::STT_GNU_IFUNC)
|
|
lv.set_is_ifunc_symbol();
|
|
|
|
// Save the input symbol value for use in do_finalize_local_symbols().
|
|
lv.set_input_value(sym.get_st_value());
|
|
|
|
// Decide whether this symbol should go into the output file.
|
|
|
|
if (is_ordinary
|
|
&& shndx < shnum
|
|
&& (out_sections[shndx] == NULL
|
|
|| (out_sections[shndx]->order() == ORDER_EHFRAME
|
|
&& out_section_offsets[shndx] == invalid_address)))
|
|
{
|
|
// This is either a discarded section or an optimized .eh_frame
|
|
// section.
|
|
lv.set_no_output_symtab_entry();
|
|
gold_assert(!lv.needs_output_dynsym_entry());
|
|
continue;
|
|
}
|
|
|
|
if (sym.get_st_type() == elfcpp::STT_SECTION
|
|
|| !this->adjust_local_symbol(&lv))
|
|
{
|
|
lv.set_no_output_symtab_entry();
|
|
gold_assert(!lv.needs_output_dynsym_entry());
|
|
continue;
|
|
}
|
|
|
|
if (sym.get_st_name() >= strtab_size)
|
|
{
|
|
this->error(_("local symbol %u section name out of range: %u >= %u"),
|
|
i, sym.get_st_name(),
|
|
static_cast<unsigned int>(strtab_size));
|
|
lv.set_no_output_symtab_entry();
|
|
continue;
|
|
}
|
|
|
|
const char* name = pnames + sym.get_st_name();
|
|
|
|
// If needed, add the symbol to the dynamic symbol table string pool.
|
|
if (lv.needs_output_dynsym_entry())
|
|
{
|
|
dynpool->add(name, true, NULL);
|
|
++dyncount;
|
|
}
|
|
|
|
if (strip_all
|
|
|| (discard_all && lv.may_be_discarded_from_output_symtab()))
|
|
{
|
|
lv.set_no_output_symtab_entry();
|
|
continue;
|
|
}
|
|
|
|
// By default, discard temporary local symbols in merge sections.
|
|
// If --discard-locals option is used, discard all temporary local
|
|
// symbols. These symbols start with system-specific local label
|
|
// prefixes, typically .L for ELF system. We want to be compatible
|
|
// with GNU ld so here we essentially use the same check in
|
|
// bfd_is_local_label(). The code is different because we already
|
|
// know that:
|
|
//
|
|
// - the symbol is local and thus cannot have global or weak binding.
|
|
// - the symbol is not a section symbol.
|
|
// - the symbol has a name.
|
|
//
|
|
// We do not discard a symbol if it needs a dynamic symbol entry.
|
|
if ((discard_locals
|
|
|| (discard_sec_merge
|
|
&& is_ordinary
|
|
&& out_section_offsets[shndx] == invalid_address))
|
|
&& sym.get_st_type() != elfcpp::STT_FILE
|
|
&& !lv.needs_output_dynsym_entry()
|
|
&& lv.may_be_discarded_from_output_symtab()
|
|
&& parameters->target().is_local_label_name(name))
|
|
{
|
|
lv.set_no_output_symtab_entry();
|
|
continue;
|
|
}
|
|
|
|
// Discard the local symbol if -retain_symbols_file is specified
|
|
// and the local symbol is not in that file.
|
|
if (!parameters->options().should_retain_symbol(name))
|
|
{
|
|
lv.set_no_output_symtab_entry();
|
|
continue;
|
|
}
|
|
|
|
// Add the symbol to the symbol table string pool.
|
|
pool->add(name, true, NULL);
|
|
++count;
|
|
}
|
|
|
|
this->output_local_symbol_count_ = count;
|
|
this->output_local_dynsym_count_ = dyncount;
|
|
}
|
|
|
|
// Compute the final value of a local symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
typename Sized_relobj_file<size, big_endian>::Compute_final_local_value_status
|
|
Sized_relobj_file<size, big_endian>::compute_final_local_value_internal(
|
|
unsigned int r_sym,
|
|
const Symbol_value<size>* lv_in,
|
|
Symbol_value<size>* lv_out,
|
|
bool relocatable,
|
|
const Output_sections& out_sections,
|
|
const std::vector<Address>& out_offsets,
|
|
const Symbol_table* symtab)
|
|
{
|
|
// We are going to overwrite *LV_OUT, if it has a merged symbol value,
|
|
// we may have a memory leak.
|
|
gold_assert(lv_out->has_output_value());
|
|
|
|
bool is_ordinary;
|
|
unsigned int shndx = lv_in->input_shndx(&is_ordinary);
|
|
|
|
// Set the output symbol value.
|
|
|
|
if (!is_ordinary)
|
|
{
|
|
if (shndx == elfcpp::SHN_ABS || Symbol::is_common_shndx(shndx))
|
|
lv_out->set_output_value(lv_in->input_value());
|
|
else
|
|
{
|
|
this->error(_("unknown section index %u for local symbol %u"),
|
|
shndx, r_sym);
|
|
lv_out->set_output_value(0);
|
|
return This::CFLV_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (shndx >= this->shnum())
|
|
{
|
|
this->error(_("local symbol %u section index %u out of range"),
|
|
r_sym, shndx);
|
|
lv_out->set_output_value(0);
|
|
return This::CFLV_ERROR;
|
|
}
|
|
|
|
Output_section* os = out_sections[shndx];
|
|
Address secoffset = out_offsets[shndx];
|
|
if (symtab->is_section_folded(this, shndx))
|
|
{
|
|
gold_assert(os == NULL && secoffset == invalid_address);
|
|
// Get the os of the section it is folded onto.
|
|
Section_id folded = symtab->icf()->get_folded_section(this,
|
|
shndx);
|
|
gold_assert(folded.first != NULL);
|
|
Sized_relobj_file<size, big_endian>* folded_obj = reinterpret_cast
|
|
<Sized_relobj_file<size, big_endian>*>(folded.first);
|
|
os = folded_obj->output_section(folded.second);
|
|
gold_assert(os != NULL);
|
|
secoffset = folded_obj->get_output_section_offset(folded.second);
|
|
|
|
// This could be a relaxed input section.
|
|
if (secoffset == invalid_address)
|
|
{
|
|
const Output_relaxed_input_section* relaxed_section =
|
|
os->find_relaxed_input_section(folded_obj, folded.second);
|
|
gold_assert(relaxed_section != NULL);
|
|
secoffset = relaxed_section->address() - os->address();
|
|
}
|
|
}
|
|
|
|
if (os == NULL)
|
|
{
|
|
// This local symbol belongs to a section we are discarding.
|
|
// In some cases when applying relocations later, we will
|
|
// attempt to match it to the corresponding kept section,
|
|
// so we leave the input value unchanged here.
|
|
return This::CFLV_DISCARDED;
|
|
}
|
|
else if (secoffset == invalid_address)
|
|
{
|
|
uint64_t start;
|
|
|
|
// This is a SHF_MERGE section or one which otherwise
|
|
// requires special handling.
|
|
if (os->order() == ORDER_EHFRAME)
|
|
{
|
|
// This local symbol belongs to a discarded or optimized
|
|
// .eh_frame section. Just treat it like the case in which
|
|
// os == NULL above.
|
|
gold_assert(this->has_eh_frame_);
|
|
return This::CFLV_DISCARDED;
|
|
}
|
|
else if (!lv_in->is_section_symbol())
|
|
{
|
|
// This is not a section symbol. We can determine
|
|
// the final value now.
|
|
uint64_t value =
|
|
os->output_address(this, shndx, lv_in->input_value());
|
|
if (relocatable)
|
|
value -= os->address();
|
|
lv_out->set_output_value(value);
|
|
}
|
|
else if (!os->find_starting_output_address(this, shndx, &start))
|
|
{
|
|
// This is a section symbol, but apparently not one in a
|
|
// merged section. First check to see if this is a relaxed
|
|
// input section. If so, use its address. Otherwise just
|
|
// use the start of the output section. This happens with
|
|
// relocatable links when the input object has section
|
|
// symbols for arbitrary non-merge sections.
|
|
const Output_section_data* posd =
|
|
os->find_relaxed_input_section(this, shndx);
|
|
if (posd != NULL)
|
|
{
|
|
uint64_t value = posd->address();
|
|
if (relocatable)
|
|
value -= os->address();
|
|
lv_out->set_output_value(value);
|
|
}
|
|
else
|
|
lv_out->set_output_value(os->address());
|
|
}
|
|
else
|
|
{
|
|
// We have to consider the addend to determine the
|
|
// value to use in a relocation. START is the start
|
|
// of this input section. If we are doing a relocatable
|
|
// link, use offset from start output section instead of
|
|
// address.
|
|
Address adjusted_start =
|
|
relocatable ? start - os->address() : start;
|
|
Merged_symbol_value<size>* msv =
|
|
new Merged_symbol_value<size>(lv_in->input_value(),
|
|
adjusted_start);
|
|
lv_out->set_merged_symbol_value(msv);
|
|
}
|
|
}
|
|
else if (lv_in->is_tls_symbol()
|
|
|| (lv_in->is_section_symbol()
|
|
&& (os->flags() & elfcpp::SHF_TLS)))
|
|
lv_out->set_output_value(os->tls_offset()
|
|
+ secoffset
|
|
+ lv_in->input_value());
|
|
else
|
|
lv_out->set_output_value((relocatable ? 0 : os->address())
|
|
+ secoffset
|
|
+ lv_in->input_value());
|
|
}
|
|
return This::CFLV_OK;
|
|
}
|
|
|
|
// Compute final local symbol value. R_SYM is the index of a local
|
|
// symbol in symbol table. LV points to a symbol value, which is
|
|
// expected to hold the input value and to be over-written by the
|
|
// final value. SYMTAB points to a symbol table. Some targets may want
|
|
// to know would-be-finalized local symbol values in relaxation.
|
|
// Hence we provide this method. Since this method updates *LV, a
|
|
// callee should make a copy of the original local symbol value and
|
|
// use the copy instead of modifying an object's local symbols before
|
|
// everything is finalized. The caller should also free up any allocated
|
|
// memory in the return value in *LV.
|
|
template<int size, bool big_endian>
|
|
typename Sized_relobj_file<size, big_endian>::Compute_final_local_value_status
|
|
Sized_relobj_file<size, big_endian>::compute_final_local_value(
|
|
unsigned int r_sym,
|
|
const Symbol_value<size>* lv_in,
|
|
Symbol_value<size>* lv_out,
|
|
const Symbol_table* symtab)
|
|
{
|
|
// This is just a wrapper of compute_final_local_value_internal.
|
|
const bool relocatable = parameters->options().relocatable();
|
|
const Output_sections& out_sections(this->output_sections());
|
|
const std::vector<Address>& out_offsets(this->section_offsets());
|
|
return this->compute_final_local_value_internal(r_sym, lv_in, lv_out,
|
|
relocatable, out_sections,
|
|
out_offsets, symtab);
|
|
}
|
|
|
|
// Finalize the local symbols. Here we set the final value in
|
|
// THIS->LOCAL_VALUES_ and set their output symbol table indexes.
|
|
// This function is always called from a singleton thread. The actual
|
|
// output of the local symbols will occur in a separate task.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Sized_relobj_file<size, big_endian>::do_finalize_local_symbols(
|
|
unsigned int index,
|
|
off_t off,
|
|
Symbol_table* symtab)
|
|
{
|
|
gold_assert(off == static_cast<off_t>(align_address(off, size >> 3)));
|
|
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
this->local_symbol_offset_ = off;
|
|
|
|
const bool relocatable = parameters->options().relocatable();
|
|
const Output_sections& out_sections(this->output_sections());
|
|
const std::vector<Address>& out_offsets(this->section_offsets());
|
|
|
|
for (unsigned int i = 1; i < loccount; ++i)
|
|
{
|
|
Symbol_value<size>* lv = &this->local_values_[i];
|
|
|
|
Compute_final_local_value_status cflv_status =
|
|
this->compute_final_local_value_internal(i, lv, lv, relocatable,
|
|
out_sections, out_offsets,
|
|
symtab);
|
|
switch (cflv_status)
|
|
{
|
|
case CFLV_OK:
|
|
if (!lv->is_output_symtab_index_set())
|
|
{
|
|
lv->set_output_symtab_index(index);
|
|
++index;
|
|
}
|
|
break;
|
|
case CFLV_DISCARDED:
|
|
case CFLV_ERROR:
|
|
// Do nothing.
|
|
break;
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Set the output dynamic symbol table indexes for the local variables.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Sized_relobj_file<size, big_endian>::do_set_local_dynsym_indexes(
|
|
unsigned int index)
|
|
{
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
for (unsigned int i = 1; i < loccount; ++i)
|
|
{
|
|
Symbol_value<size>& lv(this->local_values_[i]);
|
|
if (lv.needs_output_dynsym_entry())
|
|
{
|
|
lv.set_output_dynsym_index(index);
|
|
++index;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Set the offset where local dynamic symbol information will be stored.
|
|
// Returns the count of local symbols contributed to the symbol table by
|
|
// this object.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Sized_relobj_file<size, big_endian>::do_set_local_dynsym_offset(off_t off)
|
|
{
|
|
gold_assert(off == static_cast<off_t>(align_address(off, size >> 3)));
|
|
this->local_dynsym_offset_ = off;
|
|
return this->output_local_dynsym_count_;
|
|
}
|
|
|
|
// If Symbols_data is not NULL get the section flags from here otherwise
|
|
// get it from the file.
|
|
|
|
template<int size, bool big_endian>
|
|
uint64_t
|
|
Sized_relobj_file<size, big_endian>::do_section_flags(unsigned int shndx)
|
|
{
|
|
Symbols_data* sd = this->get_symbols_data();
|
|
if (sd != NULL)
|
|
{
|
|
const unsigned char* pshdrs = sd->section_headers_data
|
|
+ This::shdr_size * shndx;
|
|
typename This::Shdr shdr(pshdrs);
|
|
return shdr.get_sh_flags();
|
|
}
|
|
// If sd is NULL, read the section header from the file.
|
|
return this->elf_file_.section_flags(shndx);
|
|
}
|
|
|
|
// Get the section's ent size from Symbols_data. Called by get_section_contents
|
|
// in icf.cc
|
|
|
|
template<int size, bool big_endian>
|
|
uint64_t
|
|
Sized_relobj_file<size, big_endian>::do_section_entsize(unsigned int shndx)
|
|
{
|
|
Symbols_data* sd = this->get_symbols_data();
|
|
gold_assert(sd != NULL);
|
|
|
|
const unsigned char* pshdrs = sd->section_headers_data
|
|
+ This::shdr_size * shndx;
|
|
typename This::Shdr shdr(pshdrs);
|
|
return shdr.get_sh_entsize();
|
|
}
|
|
|
|
// Write out the local symbols.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::write_local_symbols(
|
|
Output_file* of,
|
|
const Stringpool* sympool,
|
|
const Stringpool* dynpool,
|
|
Output_symtab_xindex* symtab_xindex,
|
|
Output_symtab_xindex* dynsym_xindex,
|
|
off_t symtab_off)
|
|
{
|
|
const bool strip_all = parameters->options().strip_all();
|
|
if (strip_all)
|
|
{
|
|
if (this->output_local_dynsym_count_ == 0)
|
|
return;
|
|
this->output_local_symbol_count_ = 0;
|
|
}
|
|
|
|
gold_assert(this->symtab_shndx_ != -1U);
|
|
if (this->symtab_shndx_ == 0)
|
|
{
|
|
// This object has no symbols. Weird but legal.
|
|
return;
|
|
}
|
|
|
|
// Read the symbol table section header.
|
|
const unsigned int symtab_shndx = this->symtab_shndx_;
|
|
typename This::Shdr symtabshdr(this,
|
|
this->elf_file_.section_header(symtab_shndx));
|
|
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
|
const unsigned int loccount = this->local_symbol_count_;
|
|
gold_assert(loccount == symtabshdr.get_sh_info());
|
|
|
|
// Read the local symbols.
|
|
const int sym_size = This::sym_size;
|
|
off_t locsize = loccount * sym_size;
|
|
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
|
|
locsize, true, false);
|
|
|
|
// Read the symbol names.
|
|
const unsigned int strtab_shndx =
|
|
this->adjust_shndx(symtabshdr.get_sh_link());
|
|
section_size_type strtab_size;
|
|
const unsigned char* pnamesu = this->section_contents(strtab_shndx,
|
|
&strtab_size,
|
|
false);
|
|
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
|
|
|
// Get views into the output file for the portions of the symbol table
|
|
// and the dynamic symbol table that we will be writing.
|
|
off_t output_size = this->output_local_symbol_count_ * sym_size;
|
|
unsigned char* oview = NULL;
|
|
if (output_size > 0)
|
|
oview = of->get_output_view(symtab_off + this->local_symbol_offset_,
|
|
output_size);
|
|
|
|
off_t dyn_output_size = this->output_local_dynsym_count_ * sym_size;
|
|
unsigned char* dyn_oview = NULL;
|
|
if (dyn_output_size > 0)
|
|
dyn_oview = of->get_output_view(this->local_dynsym_offset_,
|
|
dyn_output_size);
|
|
|
|
const Output_sections& out_sections(this->output_sections());
|
|
|
|
gold_assert(this->local_values_.size() == loccount);
|
|
|
|
unsigned char* ov = oview;
|
|
unsigned char* dyn_ov = dyn_oview;
|
|
psyms += sym_size;
|
|
for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
|
|
{
|
|
elfcpp::Sym<size, big_endian> isym(psyms);
|
|
|
|
Symbol_value<size>& lv(this->local_values_[i]);
|
|
|
|
bool is_ordinary;
|
|
unsigned int st_shndx = this->adjust_sym_shndx(i, isym.get_st_shndx(),
|
|
&is_ordinary);
|
|
if (is_ordinary)
|
|
{
|
|
gold_assert(st_shndx < out_sections.size());
|
|
if (out_sections[st_shndx] == NULL)
|
|
continue;
|
|
st_shndx = out_sections[st_shndx]->out_shndx();
|
|
if (st_shndx >= elfcpp::SHN_LORESERVE)
|
|
{
|
|
if (lv.has_output_symtab_entry())
|
|
symtab_xindex->add(lv.output_symtab_index(), st_shndx);
|
|
if (lv.has_output_dynsym_entry())
|
|
dynsym_xindex->add(lv.output_dynsym_index(), st_shndx);
|
|
st_shndx = elfcpp::SHN_XINDEX;
|
|
}
|
|
}
|
|
|
|
// Write the symbol to the output symbol table.
|
|
if (lv.has_output_symtab_entry())
|
|
{
|
|
elfcpp::Sym_write<size, big_endian> osym(ov);
|
|
|
|
gold_assert(isym.get_st_name() < strtab_size);
|
|
const char* name = pnames + isym.get_st_name();
|
|
osym.put_st_name(sympool->get_offset(name));
|
|
osym.put_st_value(lv.value(this, 0));
|
|
osym.put_st_size(isym.get_st_size());
|
|
osym.put_st_info(isym.get_st_info());
|
|
osym.put_st_other(isym.get_st_other());
|
|
osym.put_st_shndx(st_shndx);
|
|
|
|
ov += sym_size;
|
|
}
|
|
|
|
// Write the symbol to the output dynamic symbol table.
|
|
if (lv.has_output_dynsym_entry())
|
|
{
|
|
gold_assert(dyn_ov < dyn_oview + dyn_output_size);
|
|
elfcpp::Sym_write<size, big_endian> osym(dyn_ov);
|
|
|
|
gold_assert(isym.get_st_name() < strtab_size);
|
|
const char* name = pnames + isym.get_st_name();
|
|
osym.put_st_name(dynpool->get_offset(name));
|
|
osym.put_st_value(lv.value(this, 0));
|
|
osym.put_st_size(isym.get_st_size());
|
|
osym.put_st_info(isym.get_st_info());
|
|
osym.put_st_other(isym.get_st_other());
|
|
osym.put_st_shndx(st_shndx);
|
|
|
|
dyn_ov += sym_size;
|
|
}
|
|
}
|
|
|
|
|
|
if (output_size > 0)
|
|
{
|
|
gold_assert(ov - oview == output_size);
|
|
of->write_output_view(symtab_off + this->local_symbol_offset_,
|
|
output_size, oview);
|
|
}
|
|
|
|
if (dyn_output_size > 0)
|
|
{
|
|
gold_assert(dyn_ov - dyn_oview == dyn_output_size);
|
|
of->write_output_view(this->local_dynsym_offset_, dyn_output_size,
|
|
dyn_oview);
|
|
}
|
|
}
|
|
|
|
// Set *INFO to symbolic information about the offset OFFSET in the
|
|
// section SHNDX. Return true if we found something, false if we
|
|
// found nothing.
|
|
|
|
template<int size, bool big_endian>
|
|
bool
|
|
Sized_relobj_file<size, big_endian>::get_symbol_location_info(
|
|
unsigned int shndx,
|
|
off_t offset,
|
|
Symbol_location_info* info)
|
|
{
|
|
if (this->symtab_shndx_ == 0)
|
|
return false;
|
|
|
|
section_size_type symbols_size;
|
|
const unsigned char* symbols = this->section_contents(this->symtab_shndx_,
|
|
&symbols_size,
|
|
false);
|
|
|
|
unsigned int symbol_names_shndx =
|
|
this->adjust_shndx(this->section_link(this->symtab_shndx_));
|
|
section_size_type names_size;
|
|
const unsigned char* symbol_names_u =
|
|
this->section_contents(symbol_names_shndx, &names_size, false);
|
|
const char* symbol_names = reinterpret_cast<const char*>(symbol_names_u);
|
|
|
|
const int sym_size = This::sym_size;
|
|
const size_t count = symbols_size / sym_size;
|
|
|
|
const unsigned char* p = symbols;
|
|
for (size_t i = 0; i < count; ++i, p += sym_size)
|
|
{
|
|
elfcpp::Sym<size, big_endian> sym(p);
|
|
|
|
if (sym.get_st_type() == elfcpp::STT_FILE)
|
|
{
|
|
if (sym.get_st_name() >= names_size)
|
|
info->source_file = "(invalid)";
|
|
else
|
|
info->source_file = symbol_names + sym.get_st_name();
|
|
continue;
|
|
}
|
|
|
|
bool is_ordinary;
|
|
unsigned int st_shndx = this->adjust_sym_shndx(i, sym.get_st_shndx(),
|
|
&is_ordinary);
|
|
if (is_ordinary
|
|
&& st_shndx == shndx
|
|
&& static_cast<off_t>(sym.get_st_value()) <= offset
|
|
&& (static_cast<off_t>(sym.get_st_value() + sym.get_st_size())
|
|
> offset))
|
|
{
|
|
info->enclosing_symbol_type = sym.get_st_type();
|
|
if (sym.get_st_name() > names_size)
|
|
info->enclosing_symbol_name = "(invalid)";
|
|
else
|
|
{
|
|
info->enclosing_symbol_name = symbol_names + sym.get_st_name();
|
|
if (parameters->options().do_demangle())
|
|
{
|
|
char* demangled_name = cplus_demangle(
|
|
info->enclosing_symbol_name.c_str(),
|
|
DMGL_ANSI | DMGL_PARAMS);
|
|
if (demangled_name != NULL)
|
|
{
|
|
info->enclosing_symbol_name.assign(demangled_name);
|
|
free(demangled_name);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Look for a kept section corresponding to the given discarded section,
|
|
// and return its output address. This is used only for relocations in
|
|
// debugging sections. If we can't find the kept section, return 0.
|
|
|
|
template<int size, bool big_endian>
|
|
typename Sized_relobj_file<size, big_endian>::Address
|
|
Sized_relobj_file<size, big_endian>::map_to_kept_section(
|
|
unsigned int shndx,
|
|
std::string& section_name,
|
|
bool* pfound) const
|
|
{
|
|
Kept_section* kept_section;
|
|
bool is_comdat;
|
|
uint64_t sh_size;
|
|
unsigned int symndx;
|
|
bool found = false;
|
|
|
|
if (this->get_kept_comdat_section(shndx, &is_comdat, &symndx, &sh_size,
|
|
&kept_section))
|
|
{
|
|
Relobj* kept_object = kept_section->object();
|
|
unsigned int kept_shndx = 0;
|
|
if (!kept_section->is_comdat())
|
|
{
|
|
// The kept section is a linkonce section.
|
|
if (sh_size == kept_section->linkonce_size())
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
if (is_comdat)
|
|
{
|
|
// Find the corresponding kept section.
|
|
// Since we're using this mapping for relocation processing,
|
|
// we don't want to match sections unless they have the same
|
|
// size.
|
|
uint64_t kept_size;
|
|
if (kept_section->find_comdat_section(section_name, &kept_shndx,
|
|
&kept_size))
|
|
{
|
|
if (sh_size == kept_size)
|
|
found = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint64_t kept_size;
|
|
if (kept_section->find_single_comdat_section(&kept_shndx,
|
|
&kept_size)
|
|
&& sh_size == kept_size)
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
Sized_relobj_file<size, big_endian>* kept_relobj =
|
|
static_cast<Sized_relobj_file<size, big_endian>*>(kept_object);
|
|
Output_section* os = kept_relobj->output_section(kept_shndx);
|
|
Address offset = kept_relobj->get_output_section_offset(kept_shndx);
|
|
if (os != NULL && offset != invalid_address)
|
|
{
|
|
*pfound = true;
|
|
return os->address() + offset;
|
|
}
|
|
}
|
|
}
|
|
*pfound = false;
|
|
return 0;
|
|
}
|
|
|
|
// Look for a kept section corresponding to the given discarded section,
|
|
// and return its object file.
|
|
|
|
template<int size, bool big_endian>
|
|
Relobj*
|
|
Sized_relobj_file<size, big_endian>::find_kept_section_object(
|
|
unsigned int shndx, unsigned int *symndx_p) const
|
|
{
|
|
Kept_section* kept_section;
|
|
bool is_comdat;
|
|
uint64_t sh_size;
|
|
if (this->get_kept_comdat_section(shndx, &is_comdat, symndx_p, &sh_size,
|
|
&kept_section))
|
|
return kept_section->object();
|
|
return NULL;
|
|
}
|
|
|
|
// Return the name of symbol SYMNDX.
|
|
|
|
template<int size, bool big_endian>
|
|
const char*
|
|
Sized_relobj_file<size, big_endian>::get_symbol_name(unsigned int symndx)
|
|
{
|
|
if (this->symtab_shndx_ == 0)
|
|
return NULL;
|
|
|
|
section_size_type symbols_size;
|
|
const unsigned char* symbols = this->section_contents(this->symtab_shndx_,
|
|
&symbols_size,
|
|
false);
|
|
|
|
unsigned int symbol_names_shndx =
|
|
this->adjust_shndx(this->section_link(this->symtab_shndx_));
|
|
section_size_type names_size;
|
|
const unsigned char* symbol_names_u =
|
|
this->section_contents(symbol_names_shndx, &names_size, false);
|
|
const char* symbol_names = reinterpret_cast<const char*>(symbol_names_u);
|
|
|
|
const unsigned char* p = symbols + symndx * This::sym_size;
|
|
|
|
if (p >= symbols + symbols_size)
|
|
return NULL;
|
|
|
|
elfcpp::Sym<size, big_endian> sym(p);
|
|
|
|
return symbol_names + sym.get_st_name();
|
|
}
|
|
|
|
// Get symbol counts.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Sized_relobj_file<size, big_endian>::do_get_global_symbol_counts(
|
|
const Symbol_table*,
|
|
size_t* defined,
|
|
size_t* used) const
|
|
{
|
|
*defined = this->defined_count_;
|
|
size_t count = 0;
|
|
for (typename Symbols::const_iterator p = this->symbols_.begin();
|
|
p != this->symbols_.end();
|
|
++p)
|
|
if (*p != NULL
|
|
&& (*p)->source() == Symbol::FROM_OBJECT
|
|
&& (*p)->object() == this
|
|
&& (*p)->is_defined())
|
|
++count;
|
|
*used = count;
|
|
}
|
|
|
|
// Return a view of the decompressed contents of a section. Set *PLEN
|
|
// to the size. Set *IS_NEW to true if the contents need to be freed
|
|
// by the caller.
|
|
|
|
const unsigned char*
|
|
Object::decompressed_section_contents(
|
|
unsigned int shndx,
|
|
section_size_type* plen,
|
|
bool* is_new)
|
|
{
|
|
section_size_type buffer_size;
|
|
const unsigned char* buffer = this->do_section_contents(shndx, &buffer_size,
|
|
false);
|
|
|
|
if (this->compressed_sections_ == NULL)
|
|
{
|
|
*plen = buffer_size;
|
|
*is_new = false;
|
|
return buffer;
|
|
}
|
|
|
|
Compressed_section_map::const_iterator p =
|
|
this->compressed_sections_->find(shndx);
|
|
if (p == this->compressed_sections_->end())
|
|
{
|
|
*plen = buffer_size;
|
|
*is_new = false;
|
|
return buffer;
|
|
}
|
|
|
|
section_size_type uncompressed_size = p->second.size;
|
|
if (p->second.contents != NULL)
|
|
{
|
|
*plen = uncompressed_size;
|
|
*is_new = false;
|
|
return p->second.contents;
|
|
}
|
|
|
|
unsigned char* uncompressed_data = new unsigned char[uncompressed_size];
|
|
if (!decompress_input_section(buffer,
|
|
buffer_size,
|
|
uncompressed_data,
|
|
uncompressed_size,
|
|
elfsize(),
|
|
is_big_endian(),
|
|
p->second.flag))
|
|
this->error(_("could not decompress section %s"),
|
|
this->do_section_name(shndx).c_str());
|
|
|
|
// We could cache the results in p->second.contents and store
|
|
// false in *IS_NEW, but build_compressed_section_map() would
|
|
// have done so if it had expected it to be profitable. If
|
|
// we reach this point, we expect to need the contents only
|
|
// once in this pass.
|
|
*plen = uncompressed_size;
|
|
*is_new = true;
|
|
return uncompressed_data;
|
|
}
|
|
|
|
// Discard any buffers of uncompressed sections. This is done
|
|
// at the end of the Add_symbols task.
|
|
|
|
void
|
|
Object::discard_decompressed_sections()
|
|
{
|
|
if (this->compressed_sections_ == NULL)
|
|
return;
|
|
|
|
for (Compressed_section_map::iterator p = this->compressed_sections_->begin();
|
|
p != this->compressed_sections_->end();
|
|
++p)
|
|
{
|
|
if (p->second.contents != NULL)
|
|
{
|
|
delete[] p->second.contents;
|
|
p->second.contents = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Input_objects methods.
|
|
|
|
// Add a regular relocatable object to the list. Return false if this
|
|
// object should be ignored.
|
|
|
|
bool
|
|
Input_objects::add_object(Object* obj)
|
|
{
|
|
// Print the filename if the -t/--trace option is selected.
|
|
if (parameters->options().trace())
|
|
gold_info("%s", obj->name().c_str());
|
|
|
|
if (!obj->is_dynamic())
|
|
this->relobj_list_.push_back(static_cast<Relobj*>(obj));
|
|
else
|
|
{
|
|
// See if this is a duplicate SONAME.
|
|
Dynobj* dynobj = static_cast<Dynobj*>(obj);
|
|
const char* soname = dynobj->soname();
|
|
|
|
Unordered_map<std::string, Object*>::value_type val(soname, obj);
|
|
std::pair<Unordered_map<std::string, Object*>::iterator, bool> ins =
|
|
this->sonames_.insert(val);
|
|
if (!ins.second)
|
|
{
|
|
// We have already seen a dynamic object with this soname.
|
|
// If any instances of this object on the command line have
|
|
// the --no-as-needed flag, make sure the one we keep is
|
|
// marked so.
|
|
if (!obj->as_needed())
|
|
{
|
|
gold_assert(ins.first->second != NULL);
|
|
ins.first->second->clear_as_needed();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this->dynobj_list_.push_back(dynobj);
|
|
}
|
|
|
|
// Add this object to the cross-referencer if requested.
|
|
if (parameters->options().user_set_print_symbol_counts()
|
|
|| parameters->options().cref())
|
|
{
|
|
if (this->cref_ == NULL)
|
|
this->cref_ = new Cref();
|
|
this->cref_->add_object(obj);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// For each dynamic object, record whether we've seen all of its
|
|
// explicit dependencies.
|
|
|
|
void
|
|
Input_objects::check_dynamic_dependencies() const
|
|
{
|
|
bool issued_copy_dt_needed_error = false;
|
|
for (Dynobj_list::const_iterator p = this->dynobj_list_.begin();
|
|
p != this->dynobj_list_.end();
|
|
++p)
|
|
{
|
|
const Dynobj::Needed& needed((*p)->needed());
|
|
bool found_all = true;
|
|
Dynobj::Needed::const_iterator pneeded;
|
|
for (pneeded = needed.begin(); pneeded != needed.end(); ++pneeded)
|
|
{
|
|
if (this->sonames_.find(*pneeded) == this->sonames_.end())
|
|
{
|
|
found_all = false;
|
|
break;
|
|
}
|
|
}
|
|
(*p)->set_has_unknown_needed_entries(!found_all);
|
|
|
|
// --copy-dt-needed-entries aka --add-needed is a GNU ld option
|
|
// that gold does not support. However, they cause no trouble
|
|
// unless there is a DT_NEEDED entry that we don't know about;
|
|
// warn only in that case.
|
|
if (!found_all
|
|
&& !issued_copy_dt_needed_error
|
|
&& (parameters->options().copy_dt_needed_entries()
|
|
|| parameters->options().add_needed()))
|
|
{
|
|
const char* optname;
|
|
if (parameters->options().copy_dt_needed_entries())
|
|
optname = "--copy-dt-needed-entries";
|
|
else
|
|
optname = "--add-needed";
|
|
gold_error(_("%s is not supported but is required for %s in %s"),
|
|
optname, (*pneeded).c_str(), (*p)->name().c_str());
|
|
issued_copy_dt_needed_error = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start processing an archive.
|
|
|
|
void
|
|
Input_objects::archive_start(Archive* archive)
|
|
{
|
|
if (parameters->options().user_set_print_symbol_counts()
|
|
|| parameters->options().cref())
|
|
{
|
|
if (this->cref_ == NULL)
|
|
this->cref_ = new Cref();
|
|
this->cref_->add_archive_start(archive);
|
|
}
|
|
}
|
|
|
|
// Stop processing an archive.
|
|
|
|
void
|
|
Input_objects::archive_stop(Archive* archive)
|
|
{
|
|
if (parameters->options().user_set_print_symbol_counts()
|
|
|| parameters->options().cref())
|
|
this->cref_->add_archive_stop(archive);
|
|
}
|
|
|
|
// Print symbol counts
|
|
|
|
void
|
|
Input_objects::print_symbol_counts(const Symbol_table* symtab) const
|
|
{
|
|
if (parameters->options().user_set_print_symbol_counts()
|
|
&& this->cref_ != NULL)
|
|
this->cref_->print_symbol_counts(symtab);
|
|
}
|
|
|
|
// Print a cross reference table.
|
|
|
|
void
|
|
Input_objects::print_cref(const Symbol_table* symtab, FILE* f) const
|
|
{
|
|
if (parameters->options().cref() && this->cref_ != NULL)
|
|
this->cref_->print_cref(symtab, f);
|
|
}
|
|
|
|
// Relocate_info methods.
|
|
|
|
// Return a string describing the location of a relocation when file
|
|
// and lineno information is not available. This is only used in
|
|
// error messages.
|
|
|
|
template<int size, bool big_endian>
|
|
std::string
|
|
Relocate_info<size, big_endian>::location(size_t, off_t offset) const
|
|
{
|
|
Sized_dwarf_line_info<size, big_endian> line_info(this->object);
|
|
std::string ret = line_info.addr2line(this->data_shndx, offset, NULL);
|
|
if (!ret.empty())
|
|
return ret;
|
|
|
|
ret = this->object->name();
|
|
|
|
Symbol_location_info info;
|
|
if (this->object->get_symbol_location_info(this->data_shndx, offset, &info))
|
|
{
|
|
if (!info.source_file.empty())
|
|
{
|
|
ret += ":";
|
|
ret += info.source_file;
|
|
}
|
|
ret += ":";
|
|
if (info.enclosing_symbol_type == elfcpp::STT_FUNC)
|
|
ret += _("function ");
|
|
ret += info.enclosing_symbol_name;
|
|
return ret;
|
|
}
|
|
|
|
ret += "(";
|
|
ret += this->object->section_name(this->data_shndx);
|
|
char buf[100];
|
|
snprintf(buf, sizeof buf, "+0x%lx)", static_cast<long>(offset));
|
|
ret += buf;
|
|
return ret;
|
|
}
|
|
|
|
} // End namespace gold.
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace gold;
|
|
|
|
// Read an ELF file with the header and return the appropriate
|
|
// instance of Object.
|
|
|
|
template<int size, bool big_endian>
|
|
Object*
|
|
make_elf_sized_object(const std::string& name, Input_file* input_file,
|
|
off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr,
|
|
bool* punconfigured)
|
|
{
|
|
Target* target = select_target(input_file, offset,
|
|
ehdr.get_e_machine(), size, big_endian,
|
|
ehdr.get_e_ident()[elfcpp::EI_OSABI],
|
|
ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
|
|
if (target == NULL)
|
|
gold_fatal(_("%s: unsupported ELF machine number %d"),
|
|
name.c_str(), ehdr.get_e_machine());
|
|
|
|
if (!parameters->target_valid())
|
|
set_parameters_target(target);
|
|
else if (target != ¶meters->target())
|
|
{
|
|
if (punconfigured != NULL)
|
|
*punconfigured = true;
|
|
else
|
|
gold_error(_("%s: incompatible target"), name.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
return target->make_elf_object<size, big_endian>(name, input_file, offset,
|
|
ehdr);
|
|
}
|
|
|
|
} // End anonymous namespace.
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// Return whether INPUT_FILE is an ELF object.
|
|
|
|
bool
|
|
is_elf_object(Input_file* input_file, off_t offset,
|
|
const unsigned char** start, int* read_size)
|
|
{
|
|
off_t filesize = input_file->file().filesize();
|
|
int want = elfcpp::Elf_recognizer::max_header_size;
|
|
if (filesize - offset < want)
|
|
want = filesize - offset;
|
|
|
|
const unsigned char* p = input_file->file().get_view(offset, 0, want,
|
|
true, false);
|
|
*start = p;
|
|
*read_size = want;
|
|
|
|
return elfcpp::Elf_recognizer::is_elf_file(p, want);
|
|
}
|
|
|
|
// Read an ELF file and return the appropriate instance of Object.
|
|
|
|
Object*
|
|
make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
|
|
const unsigned char* p, section_offset_type bytes,
|
|
bool* punconfigured)
|
|
{
|
|
if (punconfigured != NULL)
|
|
*punconfigured = false;
|
|
|
|
std::string error;
|
|
bool big_endian = false;
|
|
int size = 0;
|
|
if (!elfcpp::Elf_recognizer::is_valid_header(p, bytes, &size,
|
|
&big_endian, &error))
|
|
{
|
|
gold_error(_("%s: %s"), name.c_str(), error.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
if (size == 32)
|
|
{
|
|
if (big_endian)
|
|
{
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
elfcpp::Ehdr<32, true> ehdr(p);
|
|
return make_elf_sized_object<32, true>(name, input_file,
|
|
offset, ehdr, punconfigured);
|
|
#else
|
|
if (punconfigured != NULL)
|
|
*punconfigured = true;
|
|
else
|
|
gold_error(_("%s: not configured to support "
|
|
"32-bit big-endian object"),
|
|
name.c_str());
|
|
return NULL;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
elfcpp::Ehdr<32, false> ehdr(p);
|
|
return make_elf_sized_object<32, false>(name, input_file,
|
|
offset, ehdr, punconfigured);
|
|
#else
|
|
if (punconfigured != NULL)
|
|
*punconfigured = true;
|
|
else
|
|
gold_error(_("%s: not configured to support "
|
|
"32-bit little-endian object"),
|
|
name.c_str());
|
|
return NULL;
|
|
#endif
|
|
}
|
|
}
|
|
else if (size == 64)
|
|
{
|
|
if (big_endian)
|
|
{
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
elfcpp::Ehdr<64, true> ehdr(p);
|
|
return make_elf_sized_object<64, true>(name, input_file,
|
|
offset, ehdr, punconfigured);
|
|
#else
|
|
if (punconfigured != NULL)
|
|
*punconfigured = true;
|
|
else
|
|
gold_error(_("%s: not configured to support "
|
|
"64-bit big-endian object"),
|
|
name.c_str());
|
|
return NULL;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
elfcpp::Ehdr<64, false> ehdr(p);
|
|
return make_elf_sized_object<64, false>(name, input_file,
|
|
offset, ehdr, punconfigured);
|
|
#else
|
|
if (punconfigured != NULL)
|
|
*punconfigured = true;
|
|
else
|
|
gold_error(_("%s: not configured to support "
|
|
"64-bit little-endian object"),
|
|
name.c_str());
|
|
return NULL;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Instantiate the templates we need.
|
|
|
|
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
|
|
template
|
|
void
|
|
Relobj::initialize_input_to_output_map<64>(unsigned int shndx,
|
|
elfcpp::Elf_types<64>::Elf_Addr starting_address,
|
|
Unordered_map<section_offset_type,
|
|
elfcpp::Elf_types<64>::Elf_Addr>* output_addresses) const;
|
|
#endif
|
|
|
|
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
|
|
template
|
|
void
|
|
Relobj::initialize_input_to_output_map<32>(unsigned int shndx,
|
|
elfcpp::Elf_types<32>::Elf_Addr starting_address,
|
|
Unordered_map<section_offset_type,
|
|
elfcpp::Elf_types<32>::Elf_Addr>* output_addresses) const;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Object::read_section_data<32, false>(elfcpp::Elf_file<32, false, Object>*,
|
|
Read_symbols_data*);
|
|
template
|
|
const unsigned char*
|
|
Object::find_shdr<32,false>(const unsigned char*, const char*, const char*,
|
|
section_size_type, const unsigned char*) const;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Object::read_section_data<32, true>(elfcpp::Elf_file<32, true, Object>*,
|
|
Read_symbols_data*);
|
|
template
|
|
const unsigned char*
|
|
Object::find_shdr<32,true>(const unsigned char*, const char*, const char*,
|
|
section_size_type, const unsigned char*) const;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Object::read_section_data<64, false>(elfcpp::Elf_file<64, false, Object>*,
|
|
Read_symbols_data*);
|
|
template
|
|
const unsigned char*
|
|
Object::find_shdr<64,false>(const unsigned char*, const char*, const char*,
|
|
section_size_type, const unsigned char*) const;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Object::read_section_data<64, true>(elfcpp::Elf_file<64, true, Object>*,
|
|
Read_symbols_data*);
|
|
template
|
|
const unsigned char*
|
|
Object::find_shdr<64,true>(const unsigned char*, const char*, const char*,
|
|
section_size_type, const unsigned char*) const;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Sized_relobj<32, false>;
|
|
|
|
template
|
|
class Sized_relobj_file<32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Sized_relobj<32, true>;
|
|
|
|
template
|
|
class Sized_relobj_file<32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Sized_relobj<64, false>;
|
|
|
|
template
|
|
class Sized_relobj_file<64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Sized_relobj<64, true>;
|
|
|
|
template
|
|
class Sized_relobj_file<64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
struct Relocate_info<32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
struct Relocate_info<32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
struct Relocate_info<64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
struct Relocate_info<64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
void
|
|
Xindex::initialize_symtab_xindex<32, false>(Object*, unsigned int);
|
|
|
|
template
|
|
void
|
|
Xindex::read_symtab_xindex<32, false>(Object*, unsigned int,
|
|
const unsigned char*);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
void
|
|
Xindex::initialize_symtab_xindex<32, true>(Object*, unsigned int);
|
|
|
|
template
|
|
void
|
|
Xindex::read_symtab_xindex<32, true>(Object*, unsigned int,
|
|
const unsigned char*);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
void
|
|
Xindex::initialize_symtab_xindex<64, false>(Object*, unsigned int);
|
|
|
|
template
|
|
void
|
|
Xindex::read_symtab_xindex<64, false>(Object*, unsigned int,
|
|
const unsigned char*);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
void
|
|
Xindex::initialize_symtab_xindex<64, true>(Object*, unsigned int);
|
|
|
|
template
|
|
void
|
|
Xindex::read_symtab_xindex<64, true>(Object*, unsigned int,
|
|
const unsigned char*);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
Compressed_section_map*
|
|
build_compressed_section_map<32, false>(const unsigned char*, unsigned int,
|
|
const char*, section_size_type,
|
|
Object*, bool);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
Compressed_section_map*
|
|
build_compressed_section_map<32, true>(const unsigned char*, unsigned int,
|
|
const char*, section_size_type,
|
|
Object*, bool);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
Compressed_section_map*
|
|
build_compressed_section_map<64, false>(const unsigned char*, unsigned int,
|
|
const char*, section_size_type,
|
|
Object*, bool);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
Compressed_section_map*
|
|
build_compressed_section_map<64, true>(const unsigned char*, unsigned int,
|
|
const char*, section_size_type,
|
|
Object*, bool);
|
|
#endif
|
|
|
|
} // End namespace gold.
|