binutils-gdb/gold/dwp.cc
Cary Coutant 0d5bbdb0e1 Support compressed debug sections in dynamic object files.
This patch adds support for reading compressed debug info in
shared objects. It actually simplifies things, by moving the
support for compressed sections all the way up to the top-level
Object class, eliminating the need for several virtual methods.

gold/
	* dwp.cc (Sized_relobj_dwo::do_section_contents): Delete.
	(Sized_relobj_dwo::setup): Build compressed section map.
	(Sized_relobj_dwo::do_decompressed_section_contents): Delete.
	* dynobj.cc (Sized_dynobj::base_read_symbols): Build compressed
	section map.
	* object.cc (Sized_relobj_file::Sized_relobj_file): Remove
	compressed_sections_ field.
	(build_compressed_section_map): Take Object instead of
	Sized_relobj_file parameter; add decompress_if_needed parameter.
	(Sized_relobj_file::do_find_special_sections): Store compressed
	section map in parent Object.
	(Sized_relobj_file::do_decompressed_section_contents): Move
	implementation to Object::decompressed_section_contents.
	(Sized_relobj_file::do_discard_decompressed_sections): Move
	implementation to Object::discard_decompressed_sections.
	* object.h (build_compressed_section_map): Declare.
	(Object::Object): Add compressed_sections_ field.
	(Object::section_is_compressed): Move implementation here.
	(Object::decompressed_section_contents): De-virtualize.
	(Object::discard_decompressed_sections): De-virtualize.
	(Object::do_section_is_compressed): Delete.
	(Object::do_decompressed_section_contents): Delete.
	(Object::set_compressed_sections): New method.
	(Object::compressed_sections): New method.
	(Object::compressed_sections_): New data member.
	(Compressed_section_info, Compressed_section_map): Move to top of file.
	(Sized_relobj_file::do_section_is_compressed): Delete.
	(Sized_relobj_file::do_decompressed_section_contents): Delete.
	(Sized_relobj_file::do_discard_decompressed_sections): Delete.
	(Sized_relobj_file::compressed_sections_): Move to Object class.
2015-03-21 18:50:11 -07:00

2452 lines
72 KiB
C++

// dwp.cc -- DWARF packaging utility
// Copyright (C) 2012-2015 Free Software Foundation, Inc.
// Written by Cary Coutant <ccoutant@google.com>.
// This file is part of dwp, the DWARF packaging utility.
// 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 "dwp.h"
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <vector>
#include <algorithm>
#include "getopt.h"
#include "libiberty.h"
#include "../bfd/bfdver.h"
#include "elfcpp.h"
#include "elfcpp_file.h"
#include "dwarf.h"
#include "dirsearch.h"
#include "fileread.h"
#include "object.h"
#include "compressed_output.h"
#include "stringpool.h"
#include "dwarf_reader.h"
static void
usage(FILE* fd, int) ATTRIBUTE_NORETURN;
static void
print_version() ATTRIBUTE_NORETURN;
namespace gold {
class Dwp_output_file;
template <int size, bool big_endian>
class Sized_relobj_dwo;
// List of .dwo files to process.
struct Dwo_file_entry
{
Dwo_file_entry(uint64_t id, std::string name)
: dwo_id(id), dwo_name(name)
{ }
uint64_t dwo_id;
std::string dwo_name;
};
typedef std::vector<Dwo_file_entry> File_list;
// Type to hold the offset and length of an input section
// within an output section.
struct Section_bounds
{
section_offset_type offset;
section_size_type size;
Section_bounds()
: offset(0), size(0)
{ }
Section_bounds(section_offset_type o, section_size_type s)
: offset(o), size(s)
{ }
};
// A set of sections for a compilation unit or type unit.
struct Unit_set
{
uint64_t signature;
Section_bounds sections[elfcpp::DW_SECT_MAX + 1];
Unit_set()
: signature(0), sections()
{ }
};
// An input file.
// This class may represent a .dwo file, a .dwp file
// produced by an earlier run, or an executable file whose
// debug section identifies a set of .dwo files to read.
class Dwo_file
{
public:
Dwo_file(const char* name)
: name_(name), obj_(NULL), input_file_(NULL), is_compressed_(),
sect_offsets_(), str_offset_map_()
{ }
~Dwo_file();
// Read the input executable file and extract the list of .dwo files
// that it references.
void
read_executable(File_list* files);
// Read the input file and send its contents to OUTPUT_FILE.
void
read(Dwp_output_file* output_file);
// Verify a .dwp file given a list of .dwo files referenced by the
// corresponding executable file. Returns true if no problems
// were found.
bool
verify(const File_list& files);
private:
// Types for mapping input string offsets to output string offsets.
typedef std::pair<section_offset_type, section_offset_type>
Str_offset_map_entry;
typedef std::vector<Str_offset_map_entry> Str_offset_map;
// A less-than comparison routine for Str_offset_map.
struct Offset_compare
{
bool
operator()(const Str_offset_map_entry& i1,
const Str_offset_map_entry& i2) const
{ return i1.first < i2.first; }
};
// Create a Sized_relobj_dwo of the given size and endianness,
// and record the target info. P is a pointer to the ELF header
// in memory.
Relobj*
make_object(Dwp_output_file* output_file);
template <int size, bool big_endian>
Relobj*
sized_make_object(const unsigned char* p, Input_file* input_file,
Dwp_output_file* output_file);
// Return the number of sections in the input object file.
unsigned int
shnum() const
{ return this->obj_->shnum(); }
// Return section type.
unsigned int
section_type(unsigned int shndx)
{ return this->obj_->section_type(shndx); }
// Get the name of a section.
std::string
section_name(unsigned int shndx)
{ return this->obj_->section_name(shndx); }
// Return a view of the contents of a section, decompressed if necessary.
// Set *PLEN to the size. Set *IS_NEW to true if the contents need to be
// deleted by the caller.
const unsigned char*
section_contents(unsigned int shndx, section_size_type* plen, bool* is_new)
{ return this->obj_->decompressed_section_contents(shndx, plen, is_new); }
// Read the .debug_cu_index or .debug_tu_index section of a .dwp file,
// and process the CU or TU sets.
void
read_unit_index(unsigned int, unsigned int *, Dwp_output_file*,
bool is_tu_index);
template <bool big_endian>
void
sized_read_unit_index(unsigned int, unsigned int *, Dwp_output_file*,
bool is_tu_index);
// Verify the .debug_cu_index section of a .dwp file, comparing it
// against the list of .dwo files referenced by the corresponding
// executable file.
bool
verify_dwo_list(unsigned int, const File_list& files);
template <bool big_endian>
bool
sized_verify_dwo_list(unsigned int, const File_list& files);
// Merge the input string table section into the output file.
void
add_strings(Dwp_output_file*, unsigned int);
// Copy a section from the input file to the output file.
Section_bounds
copy_section(Dwp_output_file* output_file, unsigned int shndx,
elfcpp::DW_SECT section_id);
// Remap the string offsets in the .debug_str_offsets.dwo section.
const unsigned char*
remap_str_offsets(const unsigned char* contents, section_size_type len);
template <bool big_endian>
const unsigned char*
sized_remap_str_offsets(const unsigned char* contents, section_size_type len);
// Remap a single string offsets from an offset in the input string table
// to an offset in the output string table.
unsigned int
remap_str_offset(section_offset_type val);
// Add a set of .debug_info.dwo or .debug_types.dwo and related sections
// to OUTPUT_FILE.
void
add_unit_set(Dwp_output_file* output_file, unsigned int *debug_shndx,
bool is_debug_types);
// The filename.
const char* name_;
// The ELF file, represented as a gold Relobj instance.
Relobj* obj_;
// The Input_file object.
Input_file* input_file_;
// Flags indicating which sections are compressed.
std::vector<bool> is_compressed_;
// Map input section index onto output section offset and size.
std::vector<Section_bounds> sect_offsets_;
// Map input string offsets to output string offsets.
Str_offset_map str_offset_map_;
};
// An ELF input file.
// We derive from Sized_relobj so that we can use interfaces
// in libgold to access the file.
template <int size, bool big_endian>
class Sized_relobj_dwo : public Sized_relobj<size, big_endian>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef typename Sized_relobj<size, big_endian>::Symbols Symbols;
Sized_relobj_dwo(const char* name, Input_file* input_file,
const elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj<size, big_endian>(name, input_file),
elf_file_(this, ehdr)
{ }
~Sized_relobj_dwo()
{ }
// Setup the section information.
void
setup();
protected:
// Return section type.
unsigned int
do_section_type(unsigned int shndx)
{ return this->elf_file_.section_type(shndx); }
// Get the name of a section.
std::string
do_section_name(unsigned int shndx) const
{ return this->elf_file_.section_name(shndx); }
// Get the size of a section.
uint64_t
do_section_size(unsigned int shndx)
{ return this->elf_file_.section_size(shndx); }
// Return a view of the contents of a section.
const unsigned char*
do_section_contents(unsigned int, section_size_type*, bool);
// The following virtual functions are abstract in the base classes,
// but are not used here.
// Read the symbols.
void
do_read_symbols(Read_symbols_data*)
{ gold_unreachable(); }
// Lay out the input sections.
void
do_layout(Symbol_table*, Layout*, Read_symbols_data*)
{ gold_unreachable(); }
// Layout sections whose layout was deferred while waiting for
// input files from a plugin.
void
do_layout_deferred_sections(Layout*)
{ gold_unreachable(); }
// Add the symbols to the symbol table.
void
do_add_symbols(Symbol_table*, Read_symbols_data*, Layout*)
{ gold_unreachable(); }
Archive::Should_include
do_should_include_member(Symbol_table*, Layout*, Read_symbols_data*,
std::string*)
{ gold_unreachable(); }
// Iterate over global symbols, calling a visitor class V for each.
void
do_for_all_global_symbols(Read_symbols_data*,
Library_base::Symbol_visitor_base*)
{ gold_unreachable(); }
// Return section flags.
uint64_t
do_section_flags(unsigned int)
{ gold_unreachable(); }
// Return section entsize.
uint64_t
do_section_entsize(unsigned int)
{ gold_unreachable(); }
// Return section address.
uint64_t
do_section_address(unsigned int)
{ gold_unreachable(); }
// Return the section link field.
unsigned int
do_section_link(unsigned int)
{ gold_unreachable(); }
// Return the section link field.
unsigned int
do_section_info(unsigned int)
{ gold_unreachable(); }
// Return the section alignment.
uint64_t
do_section_addralign(unsigned int)
{ gold_unreachable(); }
// Return the Xindex structure to use.
Xindex*
do_initialize_xindex()
{ gold_unreachable(); }
// Get symbol counts.
void
do_get_global_symbol_counts(const Symbol_table*, size_t*, size_t*) const
{ gold_unreachable(); }
// Get global symbols.
const Symbols*
do_get_global_symbols() const
{ return NULL; }
// Return the value of a local symbol.
uint64_t
do_local_symbol_value(unsigned int, uint64_t) const
{ gold_unreachable(); }
unsigned int
do_local_plt_offset(unsigned int) const
{ gold_unreachable(); }
// Return whether local symbol SYMNDX is a TLS symbol.
bool
do_local_is_tls(unsigned int) const
{ gold_unreachable(); }
// Return the number of local symbols.
unsigned int
do_local_symbol_count() const
{ gold_unreachable(); }
// Return the number of local symbols in the output symbol table.
unsigned int
do_output_local_symbol_count() const
{ gold_unreachable(); }
// Return the file offset for local symbols in the output symbol table.
off_t
do_local_symbol_offset() const
{ gold_unreachable(); }
// Read the relocs.
void
do_read_relocs(Read_relocs_data*)
{ gold_unreachable(); }
// Process the relocs to find list of referenced sections. Used only
// during garbage collection.
void
do_gc_process_relocs(Symbol_table*, Layout*, Read_relocs_data*)
{ gold_unreachable(); }
// Scan the relocs and adjust the symbol table.
void
do_scan_relocs(Symbol_table*, Layout*, Read_relocs_data*)
{ gold_unreachable(); }
// Count the local symbols.
void
do_count_local_symbols(Stringpool_template<char>*,
Stringpool_template<char>*)
{ gold_unreachable(); }
// Finalize the local symbols.
unsigned int
do_finalize_local_symbols(unsigned int, off_t, Symbol_table*)
{ gold_unreachable(); }
// Set the offset where local dynamic symbol information will be stored.
unsigned int
do_set_local_dynsym_indexes(unsigned int)
{ gold_unreachable(); }
// Set the offset where local dynamic symbol information will be stored.
unsigned int
do_set_local_dynsym_offset(off_t)
{ gold_unreachable(); }
// Relocate the input sections and write out the local symbols.
void
do_relocate(const Symbol_table*, const Layout*, Output_file*)
{ gold_unreachable(); }
private:
// General access to the ELF file.
elfcpp::Elf_file<size, big_endian, Object> elf_file_;
};
// The output file.
// This class is responsible for collecting the debug index information
// and writing the .dwp file in ELF format.
class Dwp_output_file
{
public:
Dwp_output_file(const char* name)
: name_(name), machine_(0), size_(0), big_endian_(false), osabi_(0),
abiversion_(0), fd_(NULL), next_file_offset_(0), shnum_(1), sections_(),
section_id_map_(), shoff_(0), shstrndx_(0), have_strings_(false),
stringpool_(), shstrtab_(), cu_index_(), tu_index_(), last_type_sig_(0),
last_tu_slot_(0)
{
this->section_id_map_.resize(elfcpp::DW_SECT_MAX + 1);
this->stringpool_.set_no_zero_null();
}
// Record the target info from an input file.
void
record_target_info(const char* name, int machine, int size, bool big_endian,
int osabi, int abiversion);
// Add a string to the debug strings section.
section_offset_type
add_string(const char* str, size_t len);
// Add a section to the output file, and return the new section offset.
section_offset_type
add_contribution(elfcpp::DW_SECT section_id, const unsigned char* contents,
section_size_type len, int align);
// Add a set of .debug_info and related sections to the output file.
void
add_cu_set(Unit_set* cu_set);
// Lookup a type signature and return TRUE if we have already seen it.
bool
lookup_tu(uint64_t type_sig);
// Add a set of .debug_types and related sections to the output file.
void
add_tu_set(Unit_set* tu_set);
// Finalize the file, write the string tables and index sections,
// and close the file.
void
finalize();
private:
// Contributions to output sections.
struct Contribution
{
section_offset_type output_offset;
section_size_type size;
const unsigned char* contents;
};
// Sections in the output file.
struct Section
{
const char* name;
off_t offset;
section_size_type size;
int align;
std::vector<Contribution> contributions;
Section(const char* n, int a)
: name(n), offset(0), size(0), align(a), contributions()
{ }
};
// The index sections defined by the DWARF Package File Format spec.
class Dwp_index
{
public:
// Vector for the section table.
typedef std::vector<const Unit_set*> Section_table;
Dwp_index()
: capacity_(0), used_(0), hash_table_(NULL), section_table_(),
section_mask_(0)
{ }
~Dwp_index()
{ }
// Find a slot in the hash table for SIGNATURE. Return TRUE
// if the entry already exists.
bool
find_or_add(uint64_t signature, unsigned int* slotp);
// Enter a CU or TU set at the given SLOT in the hash table.
void
enter_set(unsigned int slot, const Unit_set* set);
// Return the contents of the given SLOT in the hash table of signatures.
uint64_t
hash_table(unsigned int slot) const
{ return this->hash_table_[slot]; }
// Return the contents of the given SLOT in the parallel table of
// shndx pool indexes.
uint32_t
index_table(unsigned int slot) const
{ return this->index_table_[slot]; }
// Return the total number of slots in the hash table.
unsigned int
hash_table_total_slots() const
{ return this->capacity_; }
// Return the number of used slots in the hash table.
unsigned int
hash_table_used_slots() const
{ return this->used_; }
// Return an iterator into the shndx pool.
Section_table::const_iterator
section_table() const
{ return this->section_table_.begin(); }
Section_table::const_iterator
section_table_end() const
{ return this->section_table_.end(); }
// Return the number of rows in the section table.
unsigned int
section_table_rows() const
{ return this->section_table_.size(); }
// Return the mask indicating which columns will be used
// in the section table.
int
section_table_cols() const
{ return this->section_mask_; }
private:
// Initialize the hash table.
void
initialize();
// Grow the hash table when we reach 2/3 capacity.
void
grow();
// The number of slots in the table, a power of 2 such that
// capacity > 3 * size / 2.
unsigned int capacity_;
// The current number of used slots in the hash table.
unsigned int used_;
// The storage for the hash table of signatures.
uint64_t* hash_table_;
// The storage for the parallel table of shndx pool indexes.
uint32_t* index_table_;
// The table of section offsets and sizes.
Section_table section_table_;
// Bit mask to indicate which debug sections are present in the file.
int section_mask_;
}; // End class Dwp_output_file::Dwp_index.
// Add a new output section and return the section index.
unsigned int
add_output_section(const char* section_name, int align);
// Write a new section to the output file.
void
write_new_section(const char* section_name, const unsigned char* contents,
section_size_type len, int align);
// Write the ELF header.
void
write_ehdr();
template<unsigned int size, bool big_endian>
void
sized_write_ehdr();
// Write a section header.
void
write_shdr(const char* name, unsigned int type, unsigned int flags,
uint64_t addr, off_t offset, section_size_type sect_size,
unsigned int link, unsigned int info,
unsigned int align, unsigned int ent_size);
template<unsigned int size, bool big_endian>
void
sized_write_shdr(const char* name, unsigned int type, unsigned int flags,
uint64_t addr, off_t offset, section_size_type sect_size,
unsigned int link, unsigned int info,
unsigned int align, unsigned int ent_size);
// Write the contributions to an output section.
void
write_contributions(const Section& sect);
// Write a CU or TU index section.
template<bool big_endian>
void
write_index(const char* sect_name, const Dwp_index& index);
// The output filename.
const char* name_;
// ELF header parameters.
int machine_;
int size_;
int big_endian_;
int osabi_;
int abiversion_;
// The output file descriptor.
FILE* fd_;
// Next available file offset.
off_t next_file_offset_;
// The number of sections.
unsigned int shnum_;
// Section table. The first entry is shndx 1.
std::vector<Section> sections_;
// Section id map. This maps a DW_SECT enum to an shndx.
std::vector<unsigned int> section_id_map_;
// File offset of the section header table.
off_t shoff_;
// Section index of the section string table.
unsigned int shstrndx_;
// TRUE if we have added any strings to the string pool.
bool have_strings_;
// String pool for the output .debug_str.dwo section.
Stringpool stringpool_;
// String pool for the .shstrtab section.
Stringpool shstrtab_;
// The compilation unit index.
Dwp_index cu_index_;
// The type unit index.
Dwp_index tu_index_;
// Cache of the last type signature looked up.
uint64_t last_type_sig_;
// Cache of the slot index for the last type signature.
unsigned int last_tu_slot_;
};
// A specialization of Dwarf_info_reader, for reading dwo_names from
// DWARF CUs.
class Dwo_name_info_reader : public Dwarf_info_reader
{
public:
Dwo_name_info_reader(Relobj* object, unsigned int shndx)
: Dwarf_info_reader(false, object, NULL, 0, shndx, 0, 0),
files_(NULL)
{ }
~Dwo_name_info_reader()
{ }
// Get the dwo_names from the DWARF compilation unit DIEs.
void
get_dwo_names(File_list* files)
{
this->files_ = files;
this->parse();
}
protected:
// Visit a compilation unit.
virtual void
visit_compilation_unit(off_t cu_offset, off_t cu_length, Dwarf_die*);
private:
// The list of files to populate.
File_list* files_;
};
// A specialization of Dwarf_info_reader, for reading DWARF CUs and TUs
// and adding them to the output file.
class Unit_reader : public Dwarf_info_reader
{
public:
Unit_reader(bool is_type_unit, Relobj* object, unsigned int shndx)
: Dwarf_info_reader(is_type_unit, object, NULL, 0, shndx, 0, 0),
output_file_(NULL), sections_(NULL)
{ }
~Unit_reader()
{ }
// Read the CUs or TUs and add them to the output file.
void
add_units(Dwp_output_file*, unsigned int debug_abbrev, Section_bounds*);
protected:
// Visit a compilation unit.
virtual void
visit_compilation_unit(off_t cu_offset, off_t cu_length, Dwarf_die*);
// Visit a type unit.
virtual void
visit_type_unit(off_t tu_offset, off_t tu_length, off_t type_offset,
uint64_t signature, Dwarf_die*);
private:
Dwp_output_file* output_file_;
Section_bounds* sections_;
};
// Return the name of a DWARF .dwo section.
static const char*
get_dwarf_section_name(elfcpp::DW_SECT section_id)
{
static const char* dwarf_section_names[] = {
NULL, // unused
".debug_info.dwo", // DW_SECT_INFO = 1
".debug_types.dwo", // DW_SECT_TYPES = 2
".debug_abbrev.dwo", // DW_SECT_ABBREV = 3
".debug_line.dwo", // DW_SECT_LINE = 4
".debug_loc.dwo", // DW_SECT_LOC = 5
".debug_str_offsets.dwo", // DW_SECT_STR_OFFSETS = 6
".debug_macinfo.dwo", // DW_SECT_MACINFO = 7
".debug_macro.dwo", // DW_SECT_MACRO = 8
};
gold_assert(section_id > 0 && section_id <= elfcpp::DW_SECT_MAX);
return dwarf_section_names[section_id];
}
// Class Sized_relobj_dwo.
// Setup the section information.
template <int size, bool big_endian>
void
Sized_relobj_dwo<size, big_endian>::setup()
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
const off_t shoff = this->elf_file_.shoff();
const unsigned int shnum = this->elf_file_.shnum();
this->set_shnum(shnum);
this->section_offsets().resize(shnum);
// Read the section headers.
const unsigned char* const pshdrs = this->get_view(shoff, shnum * shdr_size,
true, false);
// Read the section names.
const unsigned char* pshdrnames =
pshdrs + this->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()));
section_size_type section_names_size =
convert_to_section_size_type(shdrnames.get_sh_size());
const unsigned char* namesu = this->get_view(shdrnames.get_sh_offset(),
section_names_size, false,
false);
const char* names = reinterpret_cast<const char*>(namesu);
Compressed_section_map* compressed_sections =
build_compressed_section_map<size, big_endian>(
pshdrs, this->shnum(), names, section_names_size, this, true);
if (compressed_sections != NULL && !compressed_sections->empty())
this->set_compressed_sections(compressed_sections);
}
// Return a view of the contents of a section.
template <int size, bool big_endian>
const unsigned char*
Sized_relobj_dwo<size, big_endian>::do_section_contents(
unsigned int shndx,
section_size_type* plen,
bool cache)
{
Object::Location loc(this->elf_file_.section_contents(shndx));
*plen = convert_to_section_size_type(loc.data_size);
if (*plen == 0)
{
static const unsigned char empty[1] = { '\0' };
return empty;
}
return this->get_view(loc.file_offset, *plen, true, cache);
}
// Class Dwo_file.
Dwo_file::~Dwo_file()
{
if (this->obj_ != NULL)
delete this->obj_;
if (this->input_file_ != NULL)
delete this->input_file_;
}
// Read the input executable file and extract the list of .dwo files
// that it references.
void
Dwo_file::read_executable(File_list* files)
{
this->obj_ = this->make_object(NULL);
unsigned int shnum = this->shnum();
this->is_compressed_.resize(shnum);
this->sect_offsets_.resize(shnum);
unsigned int debug_info = 0;
unsigned int debug_abbrev = 0;
// Scan the section table and collect the debug sections we need.
// (Section index 0 is a dummy section; skip it.)
for (unsigned int i = 1; i < shnum; i++)
{
if (this->section_type(i) != elfcpp::SHT_PROGBITS)
continue;
std::string sect_name = this->section_name(i);
const char* suffix = sect_name.c_str();
if (is_prefix_of(".debug_", suffix))
suffix += 7;
else if (is_prefix_of(".zdebug_", suffix))
{
this->is_compressed_[i] = true;
suffix += 8;
}
else
continue;
if (strcmp(suffix, "info") == 0)
debug_info = i;
else if (strcmp(suffix, "abbrev") == 0)
debug_abbrev = i;
}
if (debug_info > 0)
{
Dwo_name_info_reader dwarf_reader(this->obj_, debug_info);
dwarf_reader.set_abbrev_shndx(debug_abbrev);
dwarf_reader.get_dwo_names(files);
}
}
// Read the input file and send its contents to OUTPUT_FILE.
void
Dwo_file::read(Dwp_output_file* output_file)
{
this->obj_ = this->make_object(output_file);
unsigned int shnum = this->shnum();
this->is_compressed_.resize(shnum);
this->sect_offsets_.resize(shnum);
typedef std::vector<unsigned int> Types_list;
Types_list debug_types;
unsigned int debug_shndx[elfcpp::DW_SECT_MAX + 1];
for (unsigned int i = 0; i <= elfcpp::DW_SECT_MAX; i++)
debug_shndx[i] = 0;
unsigned int debug_str = 0;
unsigned int debug_cu_index = 0;
unsigned int debug_tu_index = 0;
// Scan the section table and collect debug sections.
// (Section index 0 is a dummy section; skip it.)
for (unsigned int i = 1; i < shnum; i++)
{
if (this->section_type(i) != elfcpp::SHT_PROGBITS)
continue;
std::string sect_name = this->section_name(i);
const char* suffix = sect_name.c_str();
if (is_prefix_of(".debug_", suffix))
suffix += 7;
else if (is_prefix_of(".zdebug_", suffix))
{
this->is_compressed_[i] = true;
suffix += 8;
}
else
continue;
if (strcmp(suffix, "info.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_INFO] = i;
else if (strcmp(suffix, "types.dwo") == 0)
debug_types.push_back(i);
else if (strcmp(suffix, "abbrev.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_ABBREV] = i;
else if (strcmp(suffix, "line.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_LINE] = i;
else if (strcmp(suffix, "loc.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_LOC] = i;
else if (strcmp(suffix, "str.dwo") == 0)
debug_str = i;
else if (strcmp(suffix, "str_offsets.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_STR_OFFSETS] = i;
else if (strcmp(suffix, "macinfo.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_MACINFO] = i;
else if (strcmp(suffix, "macro.dwo") == 0)
debug_shndx[elfcpp::DW_SECT_MACRO] = i;
else if (strcmp(suffix, "cu_index") == 0)
debug_cu_index = i;
else if (strcmp(suffix, "tu_index") == 0)
debug_tu_index = i;
}
// Merge the input string table into the output string table.
this->add_strings(output_file, debug_str);
// If we found any .dwp index sections, read those and add the section
// sets to the output file.
if (debug_cu_index > 0 || debug_tu_index > 0)
{
if (debug_cu_index > 0)
this->read_unit_index(debug_cu_index, debug_shndx, output_file, false);
if (debug_tu_index > 0)
{
if (debug_types.size() > 1)
gold_fatal(_("%s: .dwp file must have no more than one "
".debug_types.dwo section"), this->name_);
if (debug_types.size() == 1)
debug_shndx[elfcpp::DW_SECT_TYPES] = debug_types[0];
else
debug_shndx[elfcpp::DW_SECT_TYPES] = 0;
this->read_unit_index(debug_tu_index, debug_shndx, output_file, true);
}
return;
}
// If we found no index sections, this is a .dwo file.
if (debug_shndx[elfcpp::DW_SECT_INFO] > 0)
this->add_unit_set(output_file, debug_shndx, false);
debug_shndx[elfcpp::DW_SECT_INFO] = 0;
for (Types_list::const_iterator tp = debug_types.begin();
tp != debug_types.end();
++tp)
{
debug_shndx[elfcpp::DW_SECT_TYPES] = *tp;
this->add_unit_set(output_file, debug_shndx, true);
}
}
// Verify a .dwp file given a list of .dwo files referenced by the
// corresponding executable file. Returns true if no problems
// were found.
bool
Dwo_file::verify(const File_list& files)
{
this->obj_ = this->make_object(NULL);
unsigned int shnum = this->shnum();
this->is_compressed_.resize(shnum);
this->sect_offsets_.resize(shnum);
unsigned int debug_cu_index = 0;
// Scan the section table and collect debug sections.
// (Section index 0 is a dummy section; skip it.)
for (unsigned int i = 1; i < shnum; i++)
{
if (this->section_type(i) != elfcpp::SHT_PROGBITS)
continue;
std::string sect_name = this->section_name(i);
const char* suffix = sect_name.c_str();
if (is_prefix_of(".debug_", suffix))
suffix += 7;
else if (is_prefix_of(".zdebug_", suffix))
{
this->is_compressed_[i] = true;
suffix += 8;
}
else
continue;
if (strcmp(suffix, "cu_index") == 0)
debug_cu_index = i;
}
if (debug_cu_index == 0)
gold_fatal(_("%s: no .debug_cu_index section found"), this->name_);
return this->verify_dwo_list(debug_cu_index, files);
}
// Create a Sized_relobj_dwo of the given size and endianness,
// and record the target info.
Relobj*
Dwo_file::make_object(Dwp_output_file* output_file)
{
// Open the input file.
Input_file* input_file = new Input_file(this->name_);
this->input_file_ = input_file;
Dirsearch dirpath;
int index;
if (!input_file->open(dirpath, NULL, &index))
gold_fatal(_("%s: can't open"), this->name_);
// Check that it's an ELF file.
off_t filesize = input_file->file().filesize();
int hdrsize = elfcpp::Elf_recognizer::max_header_size;
if (filesize < hdrsize)
hdrsize = filesize;
const unsigned char* elf_header =
input_file->file().get_view(0, 0, hdrsize, true, false);
if (!elfcpp::Elf_recognizer::is_elf_file(elf_header, hdrsize))
gold_fatal(_("%s: not an ELF object file"), this->name_);
// Get the size, endianness, machine, etc. info from the header,
// make an appropriately-sized Relobj, and pass the target info
// to the output object.
int size;
bool big_endian;
std::string error;
if (!elfcpp::Elf_recognizer::is_valid_header(elf_header, hdrsize, &size,
&big_endian, &error))
gold_fatal(_("%s: %s"), this->name_, error.c_str());
if (size == 32)
{
if (big_endian)
#ifdef HAVE_TARGET_32_BIG
return this->sized_make_object<32, true>(elf_header, input_file,
output_file);
#else
gold_unreachable();
#endif
else
#ifdef HAVE_TARGET_32_LITTLE
return this->sized_make_object<32, false>(elf_header, input_file,
output_file);
#else
gold_unreachable();
#endif
}
else if (size == 64)
{
if (big_endian)
#ifdef HAVE_TARGET_64_BIG
return this->sized_make_object<64, true>(elf_header, input_file,
output_file);
#else
gold_unreachable();
#endif
else
#ifdef HAVE_TARGET_64_LITTLE
return this->sized_make_object<64, false>(elf_header, input_file,
output_file);
#else
gold_unreachable();
#endif
}
else
gold_unreachable();
}
// Function template to create a Sized_relobj_dwo and record the target info.
// P is a pointer to the ELF header in memory.
template <int size, bool big_endian>
Relobj*
Dwo_file::sized_make_object(const unsigned char* p, Input_file* input_file,
Dwp_output_file* output_file)
{
elfcpp::Ehdr<size, big_endian> ehdr(p);
Sized_relobj_dwo<size, big_endian>* obj =
new Sized_relobj_dwo<size, big_endian>(this->name_, input_file, ehdr);
obj->setup();
if (output_file != NULL)
output_file->record_target_info(
this->name_, ehdr.get_e_machine(), size, big_endian,
ehdr.get_e_ident()[elfcpp::EI_OSABI],
ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
return obj;
}
// Read the .debug_cu_index or .debug_tu_index section of a .dwp file,
// and process the CU or TU sets.
void
Dwo_file::read_unit_index(unsigned int shndx, unsigned int *debug_shndx,
Dwp_output_file* output_file, bool is_tu_index)
{
if (this->obj_->is_big_endian())
this->sized_read_unit_index<true>(shndx, debug_shndx, output_file,
is_tu_index);
else
this->sized_read_unit_index<false>(shndx, debug_shndx, output_file,
is_tu_index);
}
template <bool big_endian>
void
Dwo_file::sized_read_unit_index(unsigned int shndx,
unsigned int *debug_shndx,
Dwp_output_file* output_file,
bool is_tu_index)
{
elfcpp::DW_SECT info_sect = (is_tu_index
? elfcpp::DW_SECT_TYPES
: elfcpp::DW_SECT_INFO);
unsigned int info_shndx = debug_shndx[info_sect];
gold_assert(shndx > 0);
section_size_type index_len;
bool index_is_new;
const unsigned char* contents =
this->section_contents(shndx, &index_len, &index_is_new);
unsigned int version =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents);
// We don't support version 1 anymore because it was experimental
// and because in normal use, dwp is not expected to read .dwp files
// produced by an earlier version of the tool.
if (version != 2)
gold_fatal(_("%s: section %s has unsupported version number %d"),
this->name_, this->section_name(shndx).c_str(), version);
unsigned int ncols =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ sizeof(uint32_t));
unsigned int nused =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ 2 * sizeof(uint32_t));
if (ncols == 0 || nused == 0)
return;
gold_assert(info_shndx > 0);
unsigned int nslots =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ 3 * sizeof(uint32_t));
const unsigned char* phash = contents + 4 * sizeof(uint32_t);
const unsigned char* pindex = phash + nslots * sizeof(uint64_t);
const unsigned char* pcolhdrs = pindex + nslots * sizeof(uint32_t);
const unsigned char* poffsets = pcolhdrs + ncols * sizeof(uint32_t);
const unsigned char* psizes = poffsets + nused * ncols * sizeof(uint32_t);
const unsigned char* pend = psizes + nused * ncols * sizeof(uint32_t);
if (pend > contents + index_len)
gold_fatal(_("%s: section %s is corrupt"), this->name_,
this->section_name(shndx).c_str());
// Copy the related sections and track the section offsets and sizes.
Section_bounds sections[elfcpp::DW_SECT_MAX + 1];
for (int i = elfcpp::DW_SECT_ABBREV; i <= elfcpp::DW_SECT_MAX; ++i)
{
if (debug_shndx[i] > 0)
sections[i] = this->copy_section(output_file, debug_shndx[i],
static_cast<elfcpp::DW_SECT>(i));
}
// Get the contents of the .debug_info.dwo or .debug_types.dwo section.
section_size_type info_len;
bool info_is_new;
const unsigned char* info_contents =
this->section_contents(info_shndx, &info_len, &info_is_new);
// Loop over the slots of the hash table.
for (unsigned int i = 0; i < nslots; ++i)
{
uint64_t signature =
elfcpp::Swap_unaligned<64, big_endian>::readval(phash);
unsigned int index =
elfcpp::Swap_unaligned<32, big_endian>::readval(pindex);
if (index != 0 && (!is_tu_index || !output_file->lookup_tu(signature)))
{
Unit_set* unit_set = new Unit_set();
unit_set->signature = signature;
const unsigned char* pch = pcolhdrs;
const unsigned char* porow =
poffsets + (index - 1) * ncols * sizeof(uint32_t);
const unsigned char* psrow =
psizes + (index - 1) * ncols * sizeof(uint32_t);
// Adjust the offset of each contribution within the input section
// by the offset of the input section within the output section.
for (unsigned int j = 0; j <= ncols; j++)
{
unsigned int dw_sect =
elfcpp::Swap_unaligned<64, big_endian>::readval(pch);
unsigned int offset =
elfcpp::Swap_unaligned<64, big_endian>::readval(porow);
unsigned int size =
elfcpp::Swap_unaligned<64, big_endian>::readval(psrow);
unit_set->sections[dw_sect].offset = (sections[dw_sect].offset
+ offset);
unit_set->sections[dw_sect].size = size;
pch += sizeof(uint32_t);
porow += sizeof(uint32_t);
psrow += sizeof(uint32_t);
}
const unsigned char* unit_start =
info_contents + unit_set->sections[info_sect].offset;
section_size_type unit_length = unit_set->sections[info_sect].size;
// Dwp_output_file::add_contribution writes the .debug_info.dwo
// section directly to the output file, so we only need to
// duplicate contributions for .debug_types.dwo section.
if (is_tu_index)
{
unsigned char *copy = new unsigned char[unit_length];
memcpy(copy, unit_start, unit_length);
unit_start = copy;
}
section_offset_type off =
output_file->add_contribution(info_sect, unit_start,
unit_length, 1);
unit_set->sections[info_sect].offset = off;
if (is_tu_index)
output_file->add_tu_set(unit_set);
else
output_file->add_cu_set(unit_set);
}
phash += sizeof(uint64_t);
pindex += sizeof(uint32_t);
}
if (index_is_new)
delete[] contents;
if (info_is_new)
delete[] info_contents;
}
// Verify the .debug_cu_index section of a .dwp file, comparing it
// against the list of .dwo files referenced by the corresponding
// executable file.
bool
Dwo_file::verify_dwo_list(unsigned int shndx, const File_list& files)
{
if (this->obj_->is_big_endian())
return this->sized_verify_dwo_list<true>(shndx, files);
else
return this->sized_verify_dwo_list<false>(shndx, files);
}
template <bool big_endian>
bool
Dwo_file::sized_verify_dwo_list(unsigned int shndx, const File_list& files)
{
gold_assert(shndx > 0);
section_size_type index_len;
bool index_is_new;
const unsigned char* contents =
this->section_contents(shndx, &index_len, &index_is_new);
unsigned int version =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents);
// We don't support version 1 anymore because it was experimental
// and because in normal use, dwp is not expected to read .dwp files
// produced by an earlier version of the tool.
if (version != 2)
gold_fatal(_("%s: section %s has unsupported version number %d"),
this->name_, this->section_name(shndx).c_str(), version);
unsigned int ncols =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ sizeof(uint32_t));
unsigned int nused =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ 2 * sizeof(uint32_t));
if (ncols == 0 || nused == 0)
return true;
unsigned int nslots =
elfcpp::Swap_unaligned<32, big_endian>::readval(contents
+ 3 * sizeof(uint32_t));
const unsigned char* phash = contents + 4 * sizeof(uint32_t);
const unsigned char* pindex = phash + nslots * sizeof(uint64_t);
const unsigned char* pcolhdrs = pindex + nslots * sizeof(uint32_t);
const unsigned char* poffsets = pcolhdrs + ncols * sizeof(uint32_t);
const unsigned char* psizes = poffsets + nused * ncols * sizeof(uint32_t);
const unsigned char* pend = psizes + nused * ncols * sizeof(uint32_t);
if (pend > contents + index_len)
gold_fatal(_("%s: section %s is corrupt"), this->name_,
this->section_name(shndx).c_str());
int nmissing = 0;
for (File_list::const_iterator f = files.begin(); f != files.end(); ++f)
{
uint64_t dwo_id = f->dwo_id;
unsigned int slot = static_cast<unsigned int>(dwo_id) & (nslots - 1);
const unsigned char* ph = phash + slot * sizeof(uint64_t);
const unsigned char* pi = pindex + slot * sizeof(uint32_t);
uint64_t probe = elfcpp::Swap_unaligned<64, big_endian>::readval(ph);
uint32_t row_index = elfcpp::Swap_unaligned<32, big_endian>::readval(pi);
if (row_index != 0 && probe != dwo_id)
{
unsigned int h2 = ((static_cast<unsigned int>(dwo_id >> 32)
& (nslots - 1)) | 1);
do
{
slot = (slot + h2) & (nslots - 1);
ph = phash + slot * sizeof(uint64_t);
pi = pindex + slot * sizeof(uint32_t);
probe = elfcpp::Swap_unaligned<64, big_endian>::readval(ph);
row_index = elfcpp::Swap_unaligned<32, big_endian>::readval(pi);
} while (row_index != 0 && probe != dwo_id);
}
if (row_index == 0)
{
printf(_("missing .dwo file: %016llx %s\n"),
static_cast<long long>(dwo_id), f->dwo_name.c_str());
++nmissing;
}
}
gold_info(_("Found %d missing .dwo files"), nmissing);
if (index_is_new)
delete[] contents;
return nmissing == 0;
}
// Merge the input string table section into the output file.
void
Dwo_file::add_strings(Dwp_output_file* output_file, unsigned int debug_str)
{
section_size_type len;
bool is_new;
const unsigned char* pdata = this->section_contents(debug_str, &len, &is_new);
const char* p = reinterpret_cast<const char*>(pdata);
const char* pend = p + len;
// Check that the last string is null terminated.
if (pend[-1] != '\0')
gold_fatal(_("%s: last entry in string section '%s' "
"is not null terminated"),
this->name_,
this->section_name(debug_str).c_str());
// Count the number of strings in the section, and size the map.
size_t count = 0;
for (const char* pt = p; pt < pend; pt += strlen(pt) + 1)
++count;
this->str_offset_map_.reserve(count + 1);
// Add the strings to the output string table, and record the new offsets
// in the map.
section_offset_type i = 0;
section_offset_type new_offset;
while (p < pend)
{
size_t len = strlen(p);
new_offset = output_file->add_string(p, len);
this->str_offset_map_.push_back(std::make_pair(i, new_offset));
p += len + 1;
i += len + 1;
}
new_offset = 0;
this->str_offset_map_.push_back(std::make_pair(i, new_offset));
if (is_new)
delete[] pdata;
}
// Copy a section from the input file to the output file.
// Return the offset and length of this input section's contribution
// in the output section. If copying .debug_str_offsets.dwo, remap
// the string offsets for the output string table.
Section_bounds
Dwo_file::copy_section(Dwp_output_file* output_file, unsigned int shndx,
elfcpp::DW_SECT section_id)
{
// Some sections may be referenced from more than one set.
// Don't copy a section more than once.
if (this->sect_offsets_[shndx].size > 0)
return this->sect_offsets_[shndx];
// Get the section contents. Upon return, if IS_NEW is true, the memory
// has been allocated via new; if false, the memory is part of the mapped
// input file, and we will need to duplicate it so that it will persist
// after we close the input file.
section_size_type len;
bool is_new;
const unsigned char* contents = this->section_contents(shndx, &len, &is_new);
if (section_id == elfcpp::DW_SECT_STR_OFFSETS)
{
const unsigned char* remapped = this->remap_str_offsets(contents, len);
if (is_new)
delete[] contents;
contents = remapped;
}
else if (!is_new)
{
unsigned char* copy = new unsigned char[len];
memcpy(copy, contents, len);
contents = copy;
}
// Add the contents of the input section to the output section.
// The output file takes ownership of the memory pointed to by CONTENTS.
section_offset_type off = output_file->add_contribution(section_id, contents,
len, 1);
// Store the output section bounds.
Section_bounds bounds(off, len);
this->sect_offsets_[shndx] = bounds;
return bounds;
}
// Remap the
const unsigned char*
Dwo_file::remap_str_offsets(const unsigned char* contents,
section_size_type len)
{
if ((len & 3) != 0)
gold_fatal(_("%s: .debug_str_offsets.dwo section size not a multiple of 4"),
this->name_);
if (this->obj_->is_big_endian())
return this->sized_remap_str_offsets<true>(contents, len);
else
return this->sized_remap_str_offsets<false>(contents, len);
}
template <bool big_endian>
const unsigned char*
Dwo_file::sized_remap_str_offsets(const unsigned char* contents,
section_size_type len)
{
unsigned char* remapped = new unsigned char[len];
const unsigned char* p = contents;
unsigned char* q = remapped;
while (len > 0)
{
unsigned int val = elfcpp::Swap_unaligned<32, big_endian>::readval(p);
val = this->remap_str_offset(val);
elfcpp::Swap_unaligned<32, big_endian>::writeval(q, val);
len -= 4;
p += 4;
q += 4;
}
return remapped;
}
unsigned int
Dwo_file::remap_str_offset(section_offset_type val)
{
Str_offset_map_entry entry;
entry.first = val;
Str_offset_map::const_iterator p =
std::lower_bound(this->str_offset_map_.begin(),
this->str_offset_map_.end(),
entry, Offset_compare());
if (p == this->str_offset_map_.end() || p->first > val)
{
if (p == this->str_offset_map_.begin())
return 0;
--p;
gold_assert(p->first <= val);
}
return p->second + (val - p->first);
}
// Add a set of .debug_info.dwo or .debug_types.dwo and related sections
// to OUTPUT_FILE.
void
Dwo_file::add_unit_set(Dwp_output_file* output_file, unsigned int *debug_shndx,
bool is_debug_types)
{
unsigned int shndx = (is_debug_types
? debug_shndx[elfcpp::DW_SECT_TYPES]
: debug_shndx[elfcpp::DW_SECT_INFO]);
gold_assert(shndx != 0);
if (debug_shndx[elfcpp::DW_SECT_ABBREV] == 0)
gold_fatal(_("%s: no .debug_abbrev.dwo section found"), this->name_);
// Copy the related sections and track the section offsets and sizes.
Section_bounds sections[elfcpp::DW_SECT_MAX + 1];
for (int i = elfcpp::DW_SECT_ABBREV; i <= elfcpp::DW_SECT_MAX; ++i)
{
if (debug_shndx[i] > 0)
sections[i] = this->copy_section(output_file, debug_shndx[i],
static_cast<elfcpp::DW_SECT>(i));
}
// Parse the .debug_info or .debug_types section and add each compilation
// or type unit to the output file, along with the contributions to the
// related sections.
Unit_reader reader(is_debug_types, this->obj_, shndx);
reader.add_units(output_file, debug_shndx[elfcpp::DW_SECT_ABBREV], sections);
}
// Class Dwp_output_file.
// Record the target info from an input file. On first call, we
// set the ELF header values for the output file. On subsequent
// calls, we just verify that the values match.
void
Dwp_output_file::record_target_info(const char*, int machine,
int size, bool big_endian,
int osabi, int abiversion)
{
// TODO: Check the values on subsequent calls.
if (this->size_ > 0)
return;
this->machine_ = machine;
this->size_ = size;
this->big_endian_ = big_endian;
this->osabi_ = osabi;
this->abiversion_ = abiversion;
if (size == 32)
this->next_file_offset_ = elfcpp::Elf_sizes<32>::ehdr_size;
else if (size == 64)
this->next_file_offset_ = elfcpp::Elf_sizes<64>::ehdr_size;
else
gold_unreachable();
this->fd_ = ::fopen(this->name_, "wb");
if (this->fd_ == NULL)
gold_fatal(_("%s: %s"), this->name_, strerror(errno));
// Write zeroes for the ELF header initially. We'll write
// the actual header during finalize().
static const char buf[elfcpp::Elf_sizes<64>::ehdr_size] = { 0 };
if (::fwrite(buf, 1, this->next_file_offset_, this->fd_)
< (size_t) this->next_file_offset_)
gold_fatal(_("%s: %s"), this->name_, strerror(errno));
}
// Add a string to the debug strings section.
section_offset_type
Dwp_output_file::add_string(const char* str, size_t len)
{
Stringpool::Key key;
this->stringpool_.add_with_length(str, len, true, &key);
this->have_strings_ = true;
// We aren't supposed to call get_offset() until after
// calling set_string_offsets(), but the offsets will
// not change unless optimizing the string pool.
return this->stringpool_.get_offset_from_key(key);
}
// Align the file offset to the given boundary.
static inline off_t
align_offset(off_t off, int align)
{
return (off + align - 1) & ~(align - 1);
}
// Add a new output section and return the section index.
unsigned int
Dwp_output_file::add_output_section(const char* section_name, int align)
{
Section sect(section_name, align);
this->sections_.push_back(sect);
return this->shnum_++;
}
// Add a contribution to a section in the output file, and return the offset
// of the contribution within the output section. The .debug_info.dwo section
// is expected to be the largest one, so we will write the contents of this
// section directly to the output file as we receive contributions, allowing
// us to free that memory as soon as possible. We will save the remaining
// contributions until we finalize the layout of the output file.
section_offset_type
Dwp_output_file::add_contribution(elfcpp::DW_SECT section_id,
const unsigned char* contents,
section_size_type len,
int align)
{
const char* section_name = get_dwarf_section_name(section_id);
gold_assert(static_cast<size_t>(section_id) < this->section_id_map_.size());
unsigned int shndx = this->section_id_map_[section_id];
// Create the section if necessary.
if (shndx == 0)
{
section_name = this->shstrtab_.add_with_length(section_name,
strlen(section_name),
false, NULL);
shndx = this->add_output_section(section_name, align);
this->section_id_map_[section_id] = shndx;
}
Section& section = this->sections_[shndx - 1];
section_offset_type section_offset;
if (section_id == elfcpp::DW_SECT_INFO)
{
// Write the .debug_info.dwo section directly.
// We do not need to free the memory in this case.
off_t file_offset = this->next_file_offset_;
gold_assert(this->size_ > 0 && file_offset > 0);
file_offset = align_offset(file_offset, align);
if (section.offset == 0)
section.offset = file_offset;
if (align > section.align)
{
// Since we've already committed to the layout for this
// section, an unexpected large alignment boundary may
// be impossible to honor.
if (align_offset(section.offset, align) != section.offset)
gold_fatal(_("%s: alignment (%d) for section '%s' "
"cannot be honored"),
this->name_, align, section_name);
section.align = align;
}
section_offset = file_offset - section.offset;
section.size = file_offset + len - section.offset;
::fseek(this->fd_, file_offset, SEEK_SET);
if (::fwrite(contents, 1, len, this->fd_) < len)
gold_fatal(_("%s: error writing section '%s'"), this->name_,
section_name);
this->next_file_offset_ = file_offset + len;
}
else
{
// Collect the contributions and keep track of the total size.
if (align > section.align)
section.align = align;
section_offset = align_offset(section.size, align);
section.size = section_offset + len;
Contribution contrib = { section_offset, len, contents };
section.contributions.push_back(contrib);
}
return section_offset;
}
// Add a set of .debug_info and related sections to the output file.
void
Dwp_output_file::add_cu_set(Unit_set* cu_set)
{
uint64_t dwo_id = cu_set->signature;
unsigned int slot;
if (!this->cu_index_.find_or_add(dwo_id, &slot))
this->cu_index_.enter_set(slot, cu_set);
else
gold_warning(_("%s: duplicate entry for CU (dwo_id 0x%llx)"),
this->name_, (unsigned long long)dwo_id);
}
// Lookup a type signature and return TRUE if we have already seen it.
bool
Dwp_output_file::lookup_tu(uint64_t type_sig)
{
this->last_type_sig_ = type_sig;
return this->tu_index_.find_or_add(type_sig, &this->last_tu_slot_);
}
// Add a set of .debug_types and related sections to the output file.
void
Dwp_output_file::add_tu_set(Unit_set* tu_set)
{
uint64_t type_sig = tu_set->signature;
unsigned int slot;
if (type_sig == this->last_type_sig_)
slot = this->last_tu_slot_;
else
this->tu_index_.find_or_add(type_sig, &slot);
this->tu_index_.enter_set(slot, tu_set);
}
// Find a slot in the hash table for SIGNATURE. Return TRUE
// if the entry already exists.
bool
Dwp_output_file::Dwp_index::find_or_add(uint64_t signature,
unsigned int* slotp)
{
if (this->capacity_ == 0)
this->initialize();
unsigned int slot =
static_cast<unsigned int>(signature) & (this->capacity_ - 1);
unsigned int secondary_hash;
uint64_t probe = this->hash_table_[slot];
uint32_t row_index = this->index_table_[slot];
if (row_index != 0 && probe != signature)
{
secondary_hash = (static_cast<unsigned int>(signature >> 32)
& (this->capacity_ - 1)) | 1;
do
{
slot = (slot + secondary_hash) & (this->capacity_ - 1);
probe = this->hash_table_[slot];
row_index = this->index_table_[slot];
} while (row_index != 0 && probe != signature);
}
*slotp = slot;
return (row_index != 0);
}
// Enter a CU or TU set at the given SLOT in the hash table.
void
Dwp_output_file::Dwp_index::enter_set(unsigned int slot,
const Unit_set* set)
{
gold_assert(slot < this->capacity_);
// Add a row to the offsets and sizes tables.
this->section_table_.push_back(set);
uint32_t row_index = this->section_table_rows();
// Mark the sections used in this set.
for (unsigned int i = 1; i <= elfcpp::DW_SECT_MAX; i++)
if (set->sections[i].size > 0)
this->section_mask_ |= 1 << i;
// Enter the signature and pool index into the hash table.
gold_assert(this->hash_table_[slot] == 0);
this->hash_table_[slot] = set->signature;
this->index_table_[slot] = row_index;
++this->used_;
// Grow the hash table when we exceed 2/3 capacity.
if (this->used_ * 3 > this->capacity_ * 2)
this->grow();
}
// Initialize the hash table.
void
Dwp_output_file::Dwp_index::initialize()
{
this->capacity_ = 16;
this->hash_table_ = new uint64_t[this->capacity_];
memset(this->hash_table_, 0, this->capacity_ * sizeof(uint64_t));
this->index_table_ = new uint32_t[this->capacity_];
memset(this->index_table_, 0, this->capacity_ * sizeof(uint32_t));
}
// Grow the hash table when we reach 2/3 capacity.
void
Dwp_output_file::Dwp_index::grow()
{
unsigned int old_capacity = this->capacity_;
uint64_t* old_hash_table = this->hash_table_;
uint32_t* old_index_table = this->index_table_;
unsigned int old_used = this->used_;
this->capacity_ = old_capacity * 2;
this->hash_table_ = new uint64_t[this->capacity_];
memset(this->hash_table_, 0, this->capacity_ * sizeof(uint64_t));
this->index_table_ = new uint32_t[this->capacity_];
memset(this->index_table_, 0, this->capacity_ * sizeof(uint32_t));
this->used_ = 0;
for (unsigned int i = 0; i < old_capacity; ++i)
{
uint64_t signature = old_hash_table[i];
uint32_t row_index = old_index_table[i];
if (row_index != 0)
{
unsigned int slot;
bool found = this->find_or_add(signature, &slot);
gold_assert(!found);
this->hash_table_[slot] = signature;
this->index_table_[slot] = row_index;
++this->used_;
}
}
gold_assert(this->used_ == old_used);
delete[] old_hash_table;
delete[] old_index_table;
}
// Finalize the file, write the string tables and index sections,
// and close the file.
void
Dwp_output_file::finalize()
{
unsigned char* buf;
// Write the accumulated output sections.
for (unsigned int i = 0; i < this->sections_.size(); i++)
{
Section& sect = this->sections_[i];
// If the offset has already been assigned, the section has been written.
if (sect.offset > 0 || sect.size == 0)
continue;
off_t file_offset = this->next_file_offset_;
file_offset = align_offset(file_offset, sect.align);
sect.offset = file_offset;
this->write_contributions(sect);
this->next_file_offset_ = file_offset + sect.size;
}
// Write the debug string table.
if (this->have_strings_)
{
this->stringpool_.set_string_offsets();
section_size_type len = this->stringpool_.get_strtab_size();
buf = new unsigned char[len];
this->stringpool_.write_to_buffer(buf, len);
this->write_new_section(".debug_str.dwo", buf, len, 1);
delete[] buf;
}
// Write the CU and TU indexes.
if (this->big_endian_)
{
this->write_index<true>(".debug_cu_index", this->cu_index_);
this->write_index<true>(".debug_tu_index", this->tu_index_);
}
else
{
this->write_index<false>(".debug_cu_index", this->cu_index_);
this->write_index<false>(".debug_tu_index", this->tu_index_);
}
off_t file_offset = this->next_file_offset_;
// Write the section string table.
this->shstrndx_ = this->shnum_++;
const char* shstrtab_name =
this->shstrtab_.add_with_length(".shstrtab", sizeof(".shstrtab") - 1,
false, NULL);
this->shstrtab_.set_string_offsets();
section_size_type shstrtab_len = this->shstrtab_.get_strtab_size();
buf = new unsigned char[shstrtab_len];
this->shstrtab_.write_to_buffer(buf, shstrtab_len);
off_t shstrtab_off = file_offset;
::fseek(this->fd_, file_offset, 0);
if (::fwrite(buf, 1, shstrtab_len, this->fd_) < shstrtab_len)
gold_fatal(_("%s: error writing section '.shstrtab'"), this->name_);
delete[] buf;
file_offset += shstrtab_len;
// Write the section header table. The first entry is a NULL entry.
// This is followed by the debug sections, and finally we write the
// .shstrtab section header.
file_offset = align_offset(file_offset, this->size_ == 32 ? 4 : 8);
this->shoff_ = file_offset;
::fseek(this->fd_, file_offset, 0);
section_size_type sh0_size = 0;
unsigned int sh0_link = 0;
if (this->shnum_ >= elfcpp::SHN_LORESERVE)
sh0_size = this->shnum_;
if (this->shstrndx_ >= elfcpp::SHN_LORESERVE)
sh0_link = this->shstrndx_;
this->write_shdr(NULL, 0, 0, 0, 0, sh0_size, sh0_link, 0, 0, 0);
for (unsigned int i = 0; i < this->sections_.size(); ++i)
{
Section& sect = this->sections_[i];
this->write_shdr(sect.name, elfcpp::SHT_PROGBITS, 0, 0, sect.offset,
sect.size, 0, 0, sect.align, 0);
}
this->write_shdr(shstrtab_name, elfcpp::SHT_STRTAB, 0, 0,
shstrtab_off, shstrtab_len, 0, 0, 1, 0);
// Write the ELF header.
this->write_ehdr();
// Close the file.
if (this->fd_ != NULL)
{
if (::fclose(this->fd_) != 0)
gold_fatal(_("%s: %s"), this->name_, strerror(errno));
}
this->fd_ = NULL;
}
// Write the contributions to an output section.
void
Dwp_output_file::write_contributions(const Section& sect)
{
for (unsigned int i = 0; i < sect.contributions.size(); ++i)
{
const Contribution& c = sect.contributions[i];
::fseek(this->fd_, sect.offset + c.output_offset, SEEK_SET);
if (::fwrite(c.contents, 1, c.size, this->fd_) < c.size)
gold_fatal(_("%s: error writing section '%s'"), this->name_, sect.name);
delete[] c.contents;
}
}
// Write a new section to the output file.
void
Dwp_output_file::write_new_section(const char* section_name,
const unsigned char* contents,
section_size_type len, int align)
{
section_name = this->shstrtab_.add_with_length(section_name,
strlen(section_name),
false, NULL);
unsigned int shndx = this->add_output_section(section_name, align);
Section& section = this->sections_[shndx - 1];
off_t file_offset = this->next_file_offset_;
file_offset = align_offset(file_offset, align);
section.offset = file_offset;
section.size = len;
::fseek(this->fd_, file_offset, SEEK_SET);
if (::fwrite(contents, 1, len, this->fd_) < len)
gold_fatal(_("%s: error writing section '%s'"), this->name_, section_name);
this->next_file_offset_ = file_offset + len;
}
// Write a CU or TU index section.
template<bool big_endian>
void
Dwp_output_file::write_index(const char* sect_name, const Dwp_index& index)
{
const unsigned int nslots = index.hash_table_total_slots();
const unsigned int nused = index.hash_table_used_slots();
const unsigned int nrows = index.section_table_rows();
int column_mask = index.section_table_cols();
unsigned int ncols = 0;
for (unsigned int c = 1; c <= elfcpp::DW_SECT_MAX; ++c)
if (column_mask & (1 << c))
ncols++;
const unsigned int ntable = (nrows * 2 + 1) * ncols;
const section_size_type index_size = (4 * sizeof(uint32_t)
+ nslots * sizeof(uint64_t)
+ nslots * sizeof(uint32_t)
+ ntable * sizeof(uint32_t));
// Allocate a buffer for the section contents.
unsigned char* buf = new unsigned char[index_size];
unsigned char* p = buf;
// Write the section header: version number, padding,
// number of used slots and total number of slots.
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, 2);
p += sizeof(uint32_t);
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, ncols);
p += sizeof(uint32_t);
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, nused);
p += sizeof(uint32_t);
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, nslots);
p += sizeof(uint32_t);
// Write the hash table.
for (unsigned int i = 0; i < nslots; ++i)
{
elfcpp::Swap_unaligned<64, big_endian>::writeval(p, index.hash_table(i));
p += sizeof(uint64_t);
}
// Write the parallel index table.
for (unsigned int i = 0; i < nslots; ++i)
{
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, index.index_table(i));
p += sizeof(uint32_t);
}
// Write the first row of the table of section offsets.
for (unsigned int c = 1; c <= elfcpp::DW_SECT_MAX; ++c)
{
if (column_mask & (1 << c))
{
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, c);
p += sizeof(uint32_t);
}
}
// Write the table of section offsets.
Dwp_index::Section_table::const_iterator tbl = index.section_table();
for (unsigned int r = 0; r < nrows; ++r)
{
gold_assert(tbl != index.section_table_end());
const Section_bounds* sects = (*tbl)->sections;
for (unsigned int c = 1; c <= elfcpp::DW_SECT_MAX; ++c)
{
if (column_mask & (1 << c))
{
section_offset_type offset = sects[c].offset;
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, offset);
p += sizeof(uint32_t);
}
else
gold_assert(sects[c].size == 0);
}
++tbl;
}
// Write the table of section sizes.
tbl = index.section_table();
for (unsigned int r = 0; r < nrows; ++r)
{
gold_assert(tbl != index.section_table_end());
const Section_bounds* sects = (*tbl)->sections;
for (unsigned int c = 1; c <= elfcpp::DW_SECT_MAX; ++c)
{
if (column_mask & (1 << c))
{
section_size_type size = sects[c].size;
elfcpp::Swap_unaligned<32, big_endian>::writeval(p, size);
p += sizeof(uint32_t);
}
else
gold_assert(sects[c].size == 0);
}
++tbl;
}
gold_assert(p == buf + index_size);
this->write_new_section(sect_name, buf, index_size, sizeof(uint64_t));
delete[] buf;
}
// Write the ELF header.
void
Dwp_output_file::write_ehdr()
{
if (this->size_ == 32)
{
if (this->big_endian_)
return this->sized_write_ehdr<32, true>();
else
return this->sized_write_ehdr<32, false>();
}
else if (this->size_ == 64)
{
if (this->big_endian_)
return this->sized_write_ehdr<64, true>();
else
return this->sized_write_ehdr<64, false>();
}
else
gold_unreachable();
}
template<unsigned int size, bool big_endian>
void
Dwp_output_file::sized_write_ehdr()
{
const unsigned int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size;
unsigned char buf[ehdr_size];
elfcpp::Ehdr_write<size, big_endian> ehdr(buf);
unsigned char e_ident[elfcpp::EI_NIDENT];
memset(e_ident, 0, elfcpp::EI_NIDENT);
e_ident[elfcpp::EI_MAG0] = elfcpp::ELFMAG0;
e_ident[elfcpp::EI_MAG1] = elfcpp::ELFMAG1;
e_ident[elfcpp::EI_MAG2] = elfcpp::ELFMAG2;
e_ident[elfcpp::EI_MAG3] = elfcpp::ELFMAG3;
if (size == 32)
e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS32;
else if (size == 64)
e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS64;
else
gold_unreachable();
e_ident[elfcpp::EI_DATA] = (big_endian
? elfcpp::ELFDATA2MSB
: elfcpp::ELFDATA2LSB);
e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT;
ehdr.put_e_ident(e_ident);
ehdr.put_e_type(elfcpp::ET_REL);
ehdr.put_e_machine(this->machine_);
ehdr.put_e_version(elfcpp::EV_CURRENT);
ehdr.put_e_entry(0);
ehdr.put_e_phoff(0);
ehdr.put_e_shoff(this->shoff_);
ehdr.put_e_flags(0);
ehdr.put_e_ehsize(elfcpp::Elf_sizes<size>::ehdr_size);
ehdr.put_e_phentsize(0);
ehdr.put_e_phnum(0);
ehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size);
ehdr.put_e_shnum(this->shnum_ < elfcpp::SHN_LORESERVE ? this->shnum_ : 0);
ehdr.put_e_shstrndx(this->shstrndx_ < elfcpp::SHN_LORESERVE
? this->shstrndx_
: static_cast<unsigned int>(elfcpp::SHN_XINDEX));
::fseek(this->fd_, 0, 0);
if (::fwrite(buf, 1, ehdr_size, this->fd_) < ehdr_size)
gold_fatal(_("%s: error writing ELF header"), this->name_);
}
// Write a section header.
void
Dwp_output_file::write_shdr(const char* name, unsigned int type,
unsigned int flags, uint64_t addr, off_t offset,
section_size_type sect_size, unsigned int link,
unsigned int info, unsigned int align,
unsigned int ent_size)
{
if (this->size_ == 32)
{
if (this->big_endian_)
return this->sized_write_shdr<32, true>(name, type, flags, addr,
offset, sect_size, link, info,
align, ent_size);
else
return this->sized_write_shdr<32, false>(name, type, flags, addr,
offset, sect_size, link, info,
align, ent_size);
}
else if (this->size_ == 64)
{
if (this->big_endian_)
return this->sized_write_shdr<64, true>(name, type, flags, addr,
offset, sect_size, link, info,
align, ent_size);
else
return this->sized_write_shdr<64, false>(name, type, flags, addr,
offset, sect_size, link, info,
align, ent_size);
}
else
gold_unreachable();
}
template<unsigned int size, bool big_endian>
void
Dwp_output_file::sized_write_shdr(const char* name, unsigned int type,
unsigned int flags, uint64_t addr,
off_t offset, section_size_type sect_size,
unsigned int link, unsigned int info,
unsigned int align, unsigned int ent_size)
{
const unsigned int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
unsigned char buf[shdr_size];
elfcpp::Shdr_write<size, big_endian> shdr(buf);
shdr.put_sh_name(name == NULL ? 0 : this->shstrtab_.get_offset(name));
shdr.put_sh_type(type);
shdr.put_sh_flags(flags);
shdr.put_sh_addr(addr);
shdr.put_sh_offset(offset);
shdr.put_sh_size(sect_size);
shdr.put_sh_link(link);
shdr.put_sh_info(info);
shdr.put_sh_addralign(align);
shdr.put_sh_entsize(ent_size);
if (::fwrite(buf, 1, shdr_size, this->fd_) < shdr_size)
gold_fatal(_("%s: error writing section header table"), this->name_);
}
// Class Dwo_name_info_reader.
// Visit a compilation unit.
void
Dwo_name_info_reader::visit_compilation_unit(off_t, off_t, Dwarf_die* die)
{
const char* dwo_name = die->string_attribute(elfcpp::DW_AT_GNU_dwo_name);
if (dwo_name != NULL)
{
uint64_t dwo_id = die->uint_attribute(elfcpp::DW_AT_GNU_dwo_id);
this->files_->push_back(Dwo_file_entry(dwo_id, dwo_name));
}
}
// Class Unit_reader.
// Read the CUs or TUs and add them to the output file.
void
Unit_reader::add_units(Dwp_output_file* output_file,
unsigned int debug_abbrev,
Section_bounds* sections)
{
this->output_file_ = output_file;
this->sections_ = sections;
this->set_abbrev_shndx(debug_abbrev);
this->parse();
}
// Visit a compilation unit.
void
Unit_reader::visit_compilation_unit(off_t, off_t cu_length, Dwarf_die* die)
{
if (cu_length == 0)
return;
Unit_set* unit_set = new Unit_set();
unit_set->signature = die->uint_attribute(elfcpp::DW_AT_GNU_dwo_id);
for (unsigned int i = elfcpp::DW_SECT_ABBREV; i <= elfcpp::DW_SECT_MAX; ++i)
unit_set->sections[i] = this->sections_[i];
// Dwp_output_file::add_contribution writes the .debug_info.dwo section
// directly to the output file, so we do not need to duplicate the
// section contents, and add_contribution does not need to free the memory.
section_offset_type off =
this->output_file_->add_contribution(elfcpp::DW_SECT_INFO,
this->buffer_at_offset(0),
cu_length, 1);
Section_bounds bounds(off, cu_length);
unit_set->sections[elfcpp::DW_SECT_INFO] = bounds;
this->output_file_->add_cu_set(unit_set);
}
// Visit a type unit.
void
Unit_reader::visit_type_unit(off_t, off_t tu_length, off_t,
uint64_t signature, Dwarf_die*)
{
if (tu_length == 0)
return;
if (this->output_file_->lookup_tu(signature))
return;
Unit_set* unit_set = new Unit_set();
unit_set->signature = signature;
for (unsigned int i = elfcpp::DW_SECT_ABBREV; i <= elfcpp::DW_SECT_MAX; ++i)
unit_set->sections[i] = this->sections_[i];
unsigned char* contents = new unsigned char[tu_length];
memcpy(contents, this->buffer_at_offset(0), tu_length);
section_offset_type off =
this->output_file_->add_contribution(elfcpp::DW_SECT_TYPES, contents,
tu_length, 1);
Section_bounds bounds(off, tu_length);
unit_set->sections[elfcpp::DW_SECT_TYPES] = bounds;
this->output_file_->add_tu_set(unit_set);
}
}; // End namespace gold
using namespace gold;
// Options.
enum Dwp_options {
VERIFY_ONLY = 0x101,
};
struct option dwp_options[] =
{
{ "exec", required_argument, NULL, 'e' },
{ "help", no_argument, NULL, 'h' },
{ "output", required_argument, NULL, 'o' },
{ "verbose", no_argument, NULL, 'v' },
{ "verify-only", no_argument, NULL, VERIFY_ONLY },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
// Print usage message and exit.
static void
usage(FILE* fd, int exit_status)
{
fprintf(fd, _("Usage: %s [options] [file...]\n"), program_name);
fprintf(fd, _(" -h, --help Print this help message\n"));
fprintf(fd, _(" -e EXE, --exec EXE Get list of dwo files from EXE"
" (defaults output to EXE.dwp)\n"));
fprintf(fd, _(" -o FILE, --output FILE Set output dwp file name\n"));
fprintf(fd, _(" -v, --verbose Verbose output\n"));
fprintf(fd, _(" --verify-only Verify output file against"
" exec file\n"));
fprintf(fd, _(" -V, --version Print version number\n"));
// REPORT_BUGS_TO is defined in bfd/bfdver.h.
const char* report = REPORT_BUGS_TO;
if (*report != '\0')
fprintf(fd, _("\nReport bugs to %s\n"), report);
exit(exit_status);
}
// Report version information.
static void
print_version()
{
// This output is intended to follow the GNU standards.
printf("GNU dwp %s\n", BFD_VERSION_STRING);
printf(_("Copyright (C) 2015 Free Software Foundation, Inc.\n"));
printf(_("\
This program is free software; you may redistribute it under the terms of\n\
the GNU General Public License version 3 or (at your option) any later version.\n\
This program has absolutely no warranty.\n"));
exit(EXIT_SUCCESS);
}
// Main program.
int
main(int argc, char** argv)
{
#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
setlocale(LC_MESSAGES, "");
#endif
#if defined (HAVE_SETLOCALE)
setlocale(LC_CTYPE, "");
#endif
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
program_name = argv[0];
// Initialize the global parameters, to let random code get to the
// errors object.
Errors errors(program_name);
set_parameters_errors(&errors);
// Initialize gold's global options. We don't use these in
// this program, but they need to be initialized so that
// functions we call from libgold work properly.
General_options options;
set_parameters_options(&options);
// In libiberty; expands @filename to the args in "filename".
expandargv(&argc, &argv);
// Collect file names and options.
File_list files;
std::string output_filename;
const char* exe_filename = NULL;
bool verbose = false;
bool verify_only = false;
int c;
while ((c = getopt_long(argc, argv, "e:ho:vV", dwp_options, NULL)) != -1)
{
switch (c)
{
case 'h':
usage(stdout, EXIT_SUCCESS);
case 'e':
exe_filename = optarg;
break;
case 'o':
output_filename.assign(optarg);
break;
case 'v':
verbose = true;
break;
case VERIFY_ONLY:
verify_only = true;
break;
case 'V':
print_version();
case '?':
default:
usage(stderr, EXIT_FAILURE);
}
}
if (output_filename.empty())
{
if (exe_filename == NULL)
gold_fatal(_("no output file specified"));
output_filename.assign(exe_filename);
output_filename.append(".dwp");
}
// Get list of .dwo files from the executable.
if (exe_filename != NULL)
{
Dwo_file exe_file(exe_filename);
exe_file.read_executable(&files);
}
// Add any additional files listed on command line.
for (int i = optind; i < argc; ++i)
files.push_back(Dwo_file_entry(0, argv[i]));
if (exe_filename == NULL && files.empty())
gold_fatal(_("no input files and no executable specified"));
if (verify_only)
{
// Get list of DWO files in the DWP file and compare with
// references found in the EXE file.
Dwo_file dwp_file(output_filename.c_str());
bool ok = dwp_file.verify(files);
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
// Process each file, adding its contents to the output file.
Dwp_output_file output_file(output_filename.c_str());
for (File_list::const_iterator f = files.begin(); f != files.end(); ++f)
{
if (verbose)
fprintf(stderr, "%s\n", f->dwo_name.c_str());
Dwo_file dwo_file(f->dwo_name.c_str());
dwo_file.read(&output_file);
}
output_file.finalize();
return EXIT_SUCCESS;
}