// dwp.cc -- DWARF packaging utility
-// Copyright 2012 Free Software Foundation, Inc.
+// Copyright (C) 2012-2016 Free Software Foundation, Inc.
// Written by Cary Coutant <ccoutant@google.com>.
// This file is part of dwp, the DWARF packaging utility.
class Sized_relobj_dwo;
// List of .dwo files to process.
-typedef std::vector<std::string> File_list;
+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.
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>
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);
// Get the name of a section.
std::string
- do_section_name(unsigned int shndx)
+ do_section_name(unsigned int shndx) const
{ return this->elf_file_.section_name(shndx); }
// Get the size of a section.
const unsigned char*
do_section_contents(unsigned int, section_size_type*, bool);
- // Return a view of the uncompressed contents of a section. Set *PLEN
- // to the size. Set *IS_NEW to true if the contents need to be deleted
- // by the caller.
- const unsigned char*
- do_decompressed_section_contents(unsigned int shndx,
- section_size_type* plen,
- bool* is_new);
-
// The following virtual functions are abstract in the base classes,
// but are not used here.
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.
return this->get_view(loc.file_offset, *plen, true, cache);
}
-// Return a view of the uncompressed contents of a section. Set *PLEN
-// to the size. Set *IS_NEW to true if the contents need to be deleted
-// by the caller.
-
-template <int size, bool big_endian>
-const unsigned char*
-Sized_relobj_dwo<size, big_endian>::do_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);
-
- std::string sect_name = this->do_section_name(shndx);
- if (!is_prefix_of(".zdebug_", sect_name.c_str()))
- {
- *plen = buffer_size;
- *is_new = false;
- return buffer;
- }
-
- section_size_type uncompressed_size = get_uncompressed_size(buffer,
- buffer_size);
- unsigned char* uncompressed_data = new unsigned char[uncompressed_size];
- if (!decompress_input_section(buffer,
- buffer_size,
- uncompressed_data,
- uncompressed_size))
- this->error(_("could not decompress section %s"),
- this->section_name(shndx).c_str());
- *plen = uncompressed_size;
- *is_new = true;
- return uncompressed_data;
-}
-
// Class Dwo_file.
Dwo_file::~Dwo_file()
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 exactly one "
+ if (debug_types.size() > 1)
+ gold_fatal(_("%s: .dwp file must have no more than one "
".debug_types.dwo section"), this->name_);
- debug_shndx[elfcpp::DW_SECT_TYPES] = debug_types[0];
+ 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;
}
}
+// 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.
: elfcpp::DW_SECT_INFO);
unsigned int info_shndx = debug_shndx[info_sect];
- gold_assert(shndx > 0 && info_shndx > 0);
+ gold_assert(shndx > 0);
section_size_type index_len;
bool index_is_new;
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));
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
{
const char* dwo_name = die->string_attribute(elfcpp::DW_AT_GNU_dwo_name);
if (dwo_name != NULL)
- this->files_->push_back(dwo_name);
+ {
+ 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.
// 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 }
};
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"));
+ " (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.
{
// This output is intended to follow the GNU standards.
printf("GNU dwp %s\n", BFD_VERSION_STRING);
- printf(_("Copyright 2012 Free Software Foundation, Inc.\n"));
+ printf(_("Copyright (C) 2016 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\
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)
{
case 'v':
verbose = true;
break;
+ case VERIFY_ONLY:
+ verify_only = true;
+ break;
case 'V':
print_version();
case '?':
output_filename.append(".dwp");
}
- Dwp_output_file output_file(output_filename.c_str());
-
// Get list of .dwo files from the executable.
if (exe_filename != NULL)
{
// Add any additional files listed on command line.
for (int i = optind; i < argc; ++i)
- files.push_back(argv[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->c_str());
- Dwo_file dwo_file(f->c_str());
+ 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;