libabigail/tools/biar.cc
Dodji Seketeli e2d450176b Add a symbol database to the ABI Corpus & support symbol aliases
* include/abg-corpus.h (corpus::{g,s}et_{fun,var}_symbol_map{_sptr}):
	Declare new accessors.
	(corpus::lookup_{variable,function}_symbol): Declare new member
	functions.
	* src/abg-corpus.cc (corpus::{g,s}et_{fun,var}_symbol_map{_sptr}):
	Define new accessors.
	(corpus::lookup_{variable,function}_symbol): Define new member
	functions.
	* include/abg-ir.h (string_elf_symbol_sptr_map_type)
	(string_elf_symbol_sptr_map_sptr, elf_symbols)
	(string_elf_symbols_map_type, string_elf_symbols_map_sptr): New
	convenience typedefs.
	(elf_symbol::{get_main_symbol, is_main_symbol, get_next_alias,
	has_aliases, add_alias, get_id_string,
	get_name_and_version_from_id, operator=}): Declare new member
	functions.
	* src/abg-ir.cc (elf_symbol::{get_main_symbol, is_main_symbol,
	get_next_alias, has_aliases, add_alias, get_id_string,
	get_name_and_version_from_id, operator=}): Define new member
	functions.
	* include/abg-reader.h (read_corpus_from_file): Take a shared
	pointer to corpus.
	* src/abg-reader.cc (read_context::{g,s}et_corpus): Define these.
	(build_elf_symbol_db, build_elf_symbol_from_reference)
	(read_symbol_db_from_input): Define new functions.
	(read_corpus_from_input): Adjust.  Make it read symbol databases.
	(build_elf_symbol): Harden this.
	(build_{var,function}_decl): Read the symbol reference.  Do not
	read the local symbol serialization anymore.
	(read_corpus_from_archive): Adjust.
	(read_corpus_from_file): Take a reference to a shared pointer to
	corpus, rather than a reference to the corpus.
	(read_corpus_from_native_xml): Only keep the overload that returns
	a corpus.  Set the current context with the corpus.
	* src/abg-dwarf-reader.cc (addr_elf_symbol_sptr_map_type)
	(addr_elf_symbol_sptr_map_sptr): New convenience typedefs.
	(read_context::{fun_sym_addr_sym_index_map_,
	var_sym_addr_sym_index_map_): Remove.
	(read_context::{fun,var}_addr_sym_map_): New.  Replace the above
	that got removed.
	(read_context::{var,fun}_syms_): New.
	(read_context::lookup_elf_{fn,var}_symbol_from_address): Adjust.
	(read_context::{fun,var}_addr_sym_map{_sptr}): New.
	(read_context::{fun,var}_syms{_sptr}): New.
	(read_context::load_symbol_maps): Replace
	read_context::load_symbol_addr_to_index_maps.  Adjust to load all
	the new maps.
	(read_context::maybe_load_symbol_maps): New.
	(read_debug_info_into_corpus): Renamed build_corpus into this.
	Update to load symbol maps and set it to the corpus.
	* src/abg-writer.cc (write_context::get_fun_symbol_map): New
	accessor.
	(write_elf_symbol_aliases, write_elf_symbol_reference)
	(write_elf_symbols_table): Define new static functions.
	(write_var_decl): Write the reference to the underlying symbol of
	the variable.  Do not write the full symbol here anymore.
	(write_function_decl):  Likewise, write the reference to the
	underlying symbol of the function.  Do not write the full symbol
	here anymore.
	(write_corpus_to_native_xml): Write the symbol databases at the
	beginning of the corpus document.
	* src/abg-comparison.cc
	(corpus_diff::priv::ensure_lookup_tables_populated): Now that the
	corpus has symbols, check if a the symbol of an allegedly deleted
	function (resp. variable) is deleted; if not, then do not report
	the function (resp. variable) as deleted.  Similarly, check if the
	symbol of an allegedly added function (resp. variable) is added.
	if not, the do not report the function (resp. variable) as added.
	* tests/test-write-read-archive.cc (main): Adjust.
	* tools/biar.cc (extract_tus_from_archive): Likewise.
	* tests/data/test-diff-filter/test9-report.txt: Adjust.
	* tests/data/test-read-dwarf/test0.abi: Likewise.
	* tests/data/test-read-dwarf/test1.abi: Likewise.
	* tests/data/test-read-dwarf/test2.so.abi: Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2014-05-28 16:33:35 +02:00

334 lines
8.7 KiB
C++

// -*- Mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library 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 Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
#include <sys/types.h>
#include <sys/stat.h>// The two above is for 'stat'
#include <cstring> // for 'strcmp'
#include <cstdlib> // for 'system'
#include <iostream>
#include <fstream>
#include <list>
#include "abg-corpus.h"
#include "abg-reader.h"
#include "abg-writer.h"
#include "abg-tools-utils.h"
using std::cerr;
using std::cout;
using std::string;
using std::list;
struct options
{
bool list_content;
string extract_dest;
string archive;
list<string> in_files;
string out_dir;
options()
: list_content(false)
{
}
};//end options
/// Display a message explaining the usage of the program on stdout.
static void
display_usage()
{
cout << "usage: biar [options] [archive-file-path]\n"
<< " where options are: \n"
<< "--help|-h display this usage message\n"
<< "--list|l <archive> list the archive content\n"
<< "--add|-a <files-to-add> <archive> add files to an archive\n"
<< "--extract|x [dest-dir] <archive> extract archive content\n"
;
}
/// Parse the command line arguments and populate an instance of \a
/// options as a result.
///
/// @param argc the number of words on the command line, including the
/// program name.
///
/// @param argv an array of words representing the command line. This
/// is the thing that is parsed.
///
/// @param opts the options that are set as a result of parsing the
/// command line. This output parameter is set iff the function
/// returs true.
///
/// @return true if the function could make sense of the command line
/// and parsed it correctly. Otherwise, return false. If this
/// function returns false, the caller would be well inspired to call
/// display_usage() to remind the user how to properly invoke this
/// program so that its command line makes sense.
static bool
parse_args(int argc, char* argv[], options& opts)
{
if (argc < 2)
return false;
for (int i = 1; i < argc; ++i)
{
char* arg = argv[i];
if (! strcmp(arg, "--help")
||! strcmp(arg, "-h"))
return false;
if (arg[0] != '-')
opts.archive = arg;
else if (! strcmp(arg, "--list")
||! strcmp(arg, "-l"))
opts.list_content = true;
else if (! strcmp(arg, "--add")
|| ! strcmp(arg,"-a"))
{
int arg_index0, arg_index1;
char *f = 0;
for (arg_index0 = i + 1; arg_index0 < argc; ++arg_index0)
{
// --add must be followed by at N words that don't start
// by '-' (N > 1). The first N-1 words are the
// arguments of --add (the files to add to the archive)
// and the last one is the name of the archive to add
// the files to.
f = argv[arg_index0];
if (f[0] == '-')
break;
arg_index1 = arg_index0 + 1;
if (arg_index1 >= argc
|| argv[arg_index1][0] == '-')
break;
opts.in_files.push_back(f);
}
if (opts.in_files.empty())
return false;
}
else if (! strcmp(arg, "--extract")
|| ! strcmp(arg, "-x"))
{
int arg_index = i + 1, arch_index = arg_index + 1;
if (arg_index < argc
&& argv[arg_index][0] != '-'
&& arch_index < argc
&& argv[arch_index][0] != '-')
opts.extract_dest = argv[arg_index];
else if (arg_index < argc
&& argv[arg_index][0] != '-')
// No destination directory argument was given for the
// --extract option, so consider it to be the current
// directory
opts.extract_dest = ".";
else
return false;
}
}
return true;
}
using abigail::corpus;
using abigail::corpus_sptr;
using abigail::translation_unit;
using abigail::translation_unit_sptr;
using abigail::translation_units;
using abigail::xml_reader::read_corpus_from_file;
using abigail::xml_writer::write_corpus_to_archive;
/// List the content of a given archive. The names of the files of
/// the archive are then displayed on stdout.
///
/// @param archive_path a path to the file containing the archive file
/// to list the content of.
///
/// @return true upon successful completion, false otherwise.
static bool
list_content(const string& archive_path)
{
if (archive_path.empty())
{
cerr << "Empty archive path\n";
return false;
}
corpus_sptr archive = read_corpus_from_file(archive_path);
if (!archive)
{
cerr << "Could not read archive at '" << archive_path << "'\n";
return false;
}
for (translation_units::const_iterator i =
archive->get_translation_units().begin();
i != archive->get_translation_units().end();
++i)
cout << (*i)->get_path() << "\n";
return true;
}
/// Add a translation unit to an archive -- or create one for that
/// matter.
///
/// @param tu_paths a list of paths to add to the archive.
///
/// @param archive_path the path of the archive to either open or
/// create. The specified in \tu_paths are then added to this
/// archive. Note that this function creates the entire directory
/// tree needed up to \a archive_path, if needed.
///
/// @return true upon successful completion, false otherwise.
static bool
add_tus_to_archive(const list<string>& tu_paths,
const string& archive_path)
{
translation_units tus;
corpus corp(archive_path);
bool added_some_tus = false;
for (list<string>::const_iterator i = tu_paths.begin();
i != tu_paths.end();
++i)
{
translation_unit_sptr tu =
abigail::xml_reader::read_translation_unit_from_file(*i);
if (!tu)
{
cerr << "could not read binary instrumentation file '"
<< *i
<< "'\n";
continue;
}
corp.add(tu);
added_some_tus = true;
}
if (added_some_tus)
{
if (!write_corpus_to_archive(corp))
{
cerr << "could not write archive file '"
<< corp.get_path()
<< "'\n";
return false;
}
}
return true;
}
/// Extract translation units from a given archive.
///
/// @param dest_path the path of the destination directory which the
/// elements of the archive are to be extracted under.
///
/// @param archive_path the path to the archive to extract. The
/// archive must exist and be accessible with read privileges.
///
/// @return true upon successful completion, false otherwise.
static bool
extract_tus_from_archive(const string& dest_path,
const string& archive_path)
{
if (dest_path.empty())
{
cerr << "empty file directory\n";
return false;
}
corpus_sptr archive(new corpus(archive_path));
if (read_corpus_from_file(archive) < 1)
{
cerr << "could not read archive at '"
<< archive_path
<< "'\n;";
return false;
}
string cmd = "mkdir -p " + dest_path;
if (system(cmd.c_str()))
{
cerr << "could not create file directory '"
<< dest_path
<< "'\n";
return false;
}
for (translation_units::const_iterator i =
archive->get_translation_units().begin();
i != archive->get_translation_units().end();
++i)
{
string dest = dest_path + "/" + (*i)->get_path();
if (!abigail::tools::ensure_parent_dir_created(dest))
{
cerr << "could not create parent director for '" << dest << "'\n";
return false;
}
if (!abigail::xml_writer::write_translation_unit(**i, /*indent=*/0, dest))
{
cerr << "could not write binary instrumentation file to '"
<< dest
<< "'\n";
return false;
}
}
return true;
}
/// Parse the command line and perform the archive-related operations
/// asked by the user, if the command line makes sense; otherwise,
/// display a usage help message and bail out.
int
main(int argc, char* argv[])
{
options opts;
if (!parse_args(argc, argv, opts)
|| opts.archive.empty())
{
display_usage();
return -1;
}
if (opts.list_content)
return !list_content(opts.archive);
else if (!opts.in_files.empty())
return !add_tus_to_archive(opts.in_files, opts.archive);
else if (!opts.extract_dest.empty())
return !extract_tus_from_archive(opts.extract_dest, opts.archive);
else
{
display_usage();
return -1;
}
return 0;
}