the file. The input shared library must contain associated debug
information in `DWARF`_ format.
+When given the ``--linux-tree`` option, this program can also handle a
+Linux kernel tree. That is, a directory tree that contains both the
+vmlinux binary and Linux kernel modules. It analyses those Linux
+kernel binaries and emits an XML representation of the interface
+between the kernel and its module, to standard output. In this case,
+we don't call it an ABI, but a KMI (Kernel Module Interface). The
+emitted KMI includes all the globally defined functions and variables,
+along with a complete representation of their types. The input
+binaries must contain associated debug information in `DWARF`_ format.
+
Invocation
==========
provided -- then the entire KMI, that is, all publicly defined and
exported functions and global variables by the Linux Kernel
binaries is emitted.
+
+ * ``--linux-tree | --lt``
+
+ Make ``abidw`` to consider the input path as a path to a directory
+ containing the vmlinux binary as several kernel modules binaries.
+ In that case, this program emits the representation of the Kernel
+ Module Interface (KMI) on the standard output.
+
+ Below is an example of usage of ``abidw`` on a Linux Kernel tree.
+
+ First, checkout a Linux kernel source tree and build it. Then
+ install the kernel modules in a directory somewhere. Copy the
+ vmlinux binary into that directory too. And then serialize the
+ KMI of that kernel to disk, using ``abidw``: ::
+
+ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+ $ cd linux && git checkout v4.5
+ $ make allyesconfig all
+ $ mkdir build-output
+ $ make INSTALL_MOD_PATH=./build-output modules_install
+ $ cp vmlinux build-output/modules/4.5.0
+ $ abidw --linux-tree build-output/modules/4.5.0 > build-output/linux-4.5.0.kmi
* ``--headers-dir | --hd`` <headers-directory-path-1>
bool ensure_parent_dir_created(const string&);
ostream& emit_prefix(const string& prog_name, ostream& out);
bool check_file(const string& path, ostream& out, const string& prog_name = "");
+bool check_dir(const string& path, ostream& out, const string& prog_name="");
bool string_ends_with(const string&, const string&);
bool string_begins_with(const string&, const string&);
bool string_is_ascii(const string&);
gen_suppr_spec_from_kernel_abi_whitelist(const string& abi_whitelist_path,
suppr::suppressions_type& s);
+bool
+get_binary_paths_from_kernel_dist(const string& dist_root,
+ string& vmlinux_path,
+ vector<string>& module_paths,
+ string& debug_info_root_path);
+
+bool
+get_binary_paths_from_kernel_dist(const string& dist_root,
+ string& vmlinux_path,
+ vector<string>& module_paths);
+
string
get_default_system_suppression_file_path();
std::tr1::shared_ptr<char>
make_path_absolute(const char*p);
+corpus_group_sptr
+build_corpus_group_from_kernel_dist_under(const string& root,
+ vector<string>& suppr_paths,
+ vector<string>& kabi_wl_paths,
+ suppr::suppressions_type& supprs,
+ bool verbose,
+ environment_sptr& env);
+
extern const char* PRIVATE_TYPES_SUPPR_SPEC_NAME;
}// end namespace tools_utils
write_corpus_to_archive(const corpus_sptr corp,
const bool annotate = false);
+bool
+write_corpus(const corpus_sptr corpus,
+ unsigned indent,
+ write_context& ctxt,
+ std::ostream& out,
+ const bool annotate = false);
+
bool
write_corpus(const corpus_sptr corpus,
unsigned indent,
const string& path,
const bool annotate = false);
+bool
+write_corpus_group(const corpus_group_sptr& group,
+ unsigned indent,
+ write_context& ctxt,
+ std::ostream& out,
+ const bool annotate = false);
+
+bool
+write_corpus_group(const corpus_group_sptr& group,
+ unsigned indent,
+ std::ostream& out,
+ const bool annotate = false);
+
+bool
+write_corpus_group(const corpus_group_sptr& group,
+ unsigned indent,
+ const string& path,
+ const bool annotate = false);
+
}// end namespace xml_writer
}// end namespace abigail
#include <iostream>
#include <sstream>
+#include "abg-dwarf-reader.h"
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS
return true;
}
+/// Check if a given path exists, is readable and is a directory.
+///
+/// @param path the path to consider.
+///
+/// @param out the out stream to report errors to.
+///
+/// @param prog_name the program name on behalf of which to report the
+/// error, if any.
+///
+/// @return true iff @p path exists and is for a directory.
+bool
+check_dir(const string& path, ostream& out, const string& prog_name)
+{
+ if (!file_exists(path))
+ {
+ emit_prefix(prog_name, out) << "path " << path << " does not exist\n";
+ return false;
+ }
+
+ if (!is_dir(path))
+ {
+ emit_prefix(prog_name, out) << path << " is not a directory\n";
+ return false;
+ }
+
+ return true;
+}
+
/// Test if a given string ends with a particular suffix.
///
/// @param str the string to consider.
read_suppressions(default_user_suppr_path, supprs);
}
+/// If we were given suppression specification files or kabi whitelist
+/// files, this function parses those, come up with suppression
+/// specifications as a result, and set them to the read context.
+///
+/// @param read_ctxt the read context to consider.
+///
+/// @param suppr_paths paths to suppression specification files that
+/// we were given. If empty, it means we were not given any
+/// suppression specification path.
+///
+/// @param kabi_whitelist_paths paths to kabi whitelist files that we
+/// were given. If empty, it means we were not given any kabi
+/// whitelist.
+///
+/// @param supprs the suppressions specifications resulting from
+/// parsing the suppression specification files at @p suppr_paths and
+/// the kabi whitelist at @p kabi_whitelist_paths.
+///
+/// @param opts the options to consider.
+static void
+load_generate_apply_suppressions(dwarf_reader::read_context &read_ctxt,
+ vector<string>& suppr_paths,
+ vector<string>& kabi_whitelist_paths,
+ suppressions_type& supprs)
+{
+ if (supprs.empty())
+ {
+ for (vector<string>::const_iterator i = suppr_paths.begin();
+ i != suppr_paths.end();
+ ++i)
+ read_suppressions(*i, supprs);
+
+ for (vector<string>::const_iterator i =
+ kabi_whitelist_paths.begin();
+ i != kabi_whitelist_paths.end();
+ ++i)
+ gen_suppr_spec_from_kernel_abi_whitelist(*i, supprs);
+ }
+
+ abigail::dwarf_reader::add_read_context_suppressions(read_ctxt, supprs);
+}
+
+/// Test if an FTSENT pointer (resulting from fts_read) represents the
+/// vmlinux binary.
+///
+/// @param entry the FTSENT to consider.
+///
+/// @return true iff @p entry is for a vmlinux binary.
+static bool
+is_vmlinux(const FTSENT *entry)
+{
+ if (entry == NULL
+ || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
+ || entry->fts_info == FTS_ERR
+ || entry->fts_info == FTS_NS)
+ return false;
+
+ string fname = entry->fts_name;
+
+ if (fname == "vmlinux")
+ {
+ string dirname;
+ dir_name(entry->fts_path, dirname);
+ if (string_ends_with(dirname, "compressed"))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Test if an FTSENT pointer (resulting from fts_read) represents a a
+/// linux kernel module binary.
+///
+/// @param entry the FTSENT to consider.
+///
+/// @return true iff @p entry is for a linux kernel module binary.
+static bool
+is_kernel_module(const FTSENT *entry)
+{
+ if (entry == NULL
+ || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
+ || entry->fts_info == FTS_ERR
+ || entry->fts_info == FTS_NS)
+ return false;
+
+ string fname = entry->fts_name;
+ if (string_ends_with(fname, ".ko")
+ || string_ends_with(fname, ".ko.xz")
+ || string_ends_with(fname, ".ko.gz"))
+ return true;
+
+ return false;
+}
+
+/// Find a vmlinux and its kernel modules in a given directory tree.
+///
+/// @param from the directory tree to start looking from.
+///
+/// @param vmlinux_path output parameter. This is set to the path
+/// where the vmlinux binary is found. This is set iff the returns
+/// true.
+///
+/// @param module_paths output parameter. This is set to the paths of
+/// the linux kernel module binaries.
+///
+/// @return true iff at least the vmlinux binary was found.
+static bool
+find_vmlinux_and_module_paths(const string& from,
+ string &vmlinux_path,
+ vector<string> &module_paths)
+{
+ char* path[] = {const_cast<char*>(from.c_str()), 0};
+
+ FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
+ if (!file_hierarchy)
+ return false;
+
+ bool found_vmlinux = false;
+ FTSENT *entry;
+ while ((entry = fts_read(file_hierarchy)))
+ {
+ // Skip descendents of symbolic links.
+ if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
+ {
+ fts_set(file_hierarchy, entry, FTS_SKIP);
+ continue;
+ }
+
+ if (!found_vmlinux && is_vmlinux(entry))
+ {
+ vmlinux_path = entry->fts_path;
+ found_vmlinux = true;
+ }
+ else if (is_kernel_module(entry))
+ module_paths.push_back(entry->fts_path);
+ }
+
+ fts_close(file_hierarchy);
+
+ return found_vmlinux;
+}
+
+/// Get the paths of the vmlinux and kernel module binaries under
+/// given directory.
+///
+/// @param dist_root the directory under which to look for.
+///
+/// @param vmlinux_path output parameter. The path of the vmlinux
+/// binary that was found.
+///
+/// @param module_paths output parameter. The paths of the kernel
+/// module binaries that were found.
+///
+/// @param debug_info_root_path output parameter. If a debug info
+/// sub-directory is found under @p dist_root, it's set to this
+/// parameter.
+///
+/// @return true if at least the path to the vmlinux binary was found.
+bool
+get_binary_paths_from_kernel_dist(const string& dist_root,
+ string& vmlinux_path,
+ vector<string>& module_paths,
+ string& debug_info_root_path)
+{
+ if (!dir_exists(dist_root))
+ return false;
+
+ // For now, we assume either an Enterprise Linux or a Fedora kernel
+ // distribution directory.
+ //
+ // We also take into account split debug info package for these. In
+ // this case, the content split debug info package is installed
+ // under the 'dist_root' directory as well, and its content is
+ // accessible from <dist_root>/usr/lib/debug directory.
+
+ string kernel_modules_root;
+ string debug_info_root;
+ if (dir_exists(dist_root + "/lib/modules"))
+ {
+ dist_root + "/lib/modules";
+ debug_info_root = dist_root + "/usr/lib/debug";
+ }
+
+ if (dir_is_empty(debug_info_root))
+ debug_info_root.clear();
+
+ bool found = false;
+ string from = !debug_info_root.empty() ? debug_info_root : dist_root;
+ if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
+ found = true;
+
+ debug_info_root_path = debug_info_root;
+
+ return found;
+}
+
+/// Get the paths of the vmlinux and kernel module binaries under
+/// given directory.
+///
+/// @param dist_root the directory under which to look for.
+///
+/// @param vmlinux_path output parameter. The path of the vmlinux
+/// binary that was found.
+///
+/// @param module_paths output parameter. The paths of the kernel
+/// module binaries that were found.
+///
+/// @return true if at least the path to the vmlinux binary was found.
+bool
+get_binary_paths_from_kernel_dist(const string& dist_root,
+ string& vmlinux_path,
+ vector<string>& module_paths)
+{
+ string debug_info_root_path;
+ return get_binary_paths_from_kernel_dist(dist_root,
+ vmlinux_path,
+ module_paths,
+ debug_info_root_path);
+}
+
+/// Walk a given directory and build an instance of @ref corpus_group
+/// from the vmlinux kernel binary and the linux kernel modules found
+/// under that directory and under its sub-directories, recursively.
+///
+/// The main corpus of the @ref corpus_group is made of the vmlinux
+/// binary. The other corpora are made of the linux kernel binaries.
+///
+/// @param root the path of the directory under which vmlinux and its
+/// kernel modules are to be found.
+///
+/// @param opts the options to use during the search.
+///
+/// @param env the environment to create the corpus_group in.
+corpus_group_sptr
+build_corpus_group_from_kernel_dist_under(const string& root,
+ vector<string>& suppr_paths,
+ vector<string>& kabi_wl_paths,
+ suppressions_type& supprs,
+ bool verbose,
+ environment_sptr& env)
+{
+ corpus_group_sptr result;
+ string vmlinux;
+ vector<string> modules;
+ string debug_info_root_path;
+
+ if (verbose)
+ std::cout << "Analysing kernel dist root '"
+ << root << "' ... " << std::flush;
+
+ bool got_binary_paths =
+ get_binary_paths_from_kernel_dist(root, vmlinux, modules,
+ debug_info_root_path);
+
+ if (verbose)
+ std::cout << "DONE\n";
+
+ if (got_binary_paths)
+ {
+ shared_ptr<char> di_root =
+ make_path_absolute(debug_info_root_path.c_str());
+ char *di_root_ptr = di_root.get();
+ abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_OK;
+ corpus_group_sptr group;
+ if (!vmlinux.empty())
+ {
+ dwarf_reader::read_context_sptr ctxt =
+ dwarf_reader::create_read_context(vmlinux, &di_root_ptr, env.get(),
+ /*read_all_types=*/false,
+ /*linux_kernel_mode=*/true);
+
+ load_generate_apply_suppressions(*ctxt, suppr_paths,
+ kabi_wl_paths, supprs);
+
+ // If we have been given a whitelist of functions and
+ // variable symbols to look at, then we can avoid loading
+ // and analyzing the ELF symbol table.
+ bool do_ignore_symbol_table = !kabi_wl_paths.empty();
+ set_ignore_symbol_table(*ctxt, do_ignore_symbol_table);
+
+ group.reset(new corpus_group(env.get(), root));
+
+ set_read_context_corpus_group(*ctxt, group);
+
+ if (verbose)
+ std::cout << "reading kernel binary '"
+ << vmlinux << "' ... " << std::flush;
+
+ // Read the vmlinux corpus and add it to the group.
+ read_and_add_corpus_to_group_from_elf(*ctxt, *group, status);
+
+ if (verbose)
+ std::cout << " DONE\n";
+ }
+
+ if (!group->is_empty())
+ {
+ // Now add the corpora of the modules to the corpus group.
+ int total_nb_modules = modules.size();
+ int cur_module_index = 1;
+ for (vector<string>::const_iterator m = modules.begin();
+ m != modules.end();
+ ++m, ++cur_module_index)
+ {
+ if (verbose)
+ std::cout << "reading module '"
+ << *m << "' ("
+ << cur_module_index
+ << "/" << total_nb_modules
+ << ") ... " << std::flush;
+
+ dwarf_reader::read_context_sptr module_ctxt =
+ dwarf_reader::create_read_context(*m, &di_root_ptr, env.get(),
+ /*read_all_types=*/false,
+ /*linux_kernel_mode=*/true);
+
+ // If we have been given a whitelist of functions and
+ // variable symbols to look at, then we can avoid loading
+ // and analyzing the ELF symbol table.
+ bool do_ignore_symbol_table = !kabi_wl_paths.empty();
+
+ set_ignore_symbol_table(*module_ctxt, do_ignore_symbol_table);
+
+ load_generate_apply_suppressions(*module_ctxt, suppr_paths,
+ kabi_wl_paths, supprs);
+
+ set_read_context_corpus_group(*module_ctxt, group);
+
+ read_and_add_corpus_to_group_from_elf(*module_ctxt,
+ *group, status);
+ if (verbose)
+ std::cout << " DONE\n";
+ }
+
+ result = group;
+ }
+ }
+
+ return result;
+}
+
}//end namespace tools_utils
using abigail::ir::function_decl;
get_annotate()
{return m_annotate;}
+ /// Setter of the annotation option.
+ ///
+ /// @param f the new value of the flag.
+ void
+ set_annotate(bool f)
+ {m_annotate = f;}
+
/// Getter of the @ref id_manager.
///
/// @return the @ref id_manager used by the current instance of @ref
///
/// @param indent the number of white space indentation to use.
///
+/// @param ctxt the write context to use.
+///
/// @param out the output stream to serialize the ABI corpus to.
///
/// @param annotate whether ABIXML output should be annotated.
bool
write_corpus(const corpus_sptr corpus,
unsigned indent,
+ write_context& ctxt,
std::ostream& out,
const bool annotate)
{
if (!corpus)
return false;
- write_context ctxt(corpus->get_environment(), out, annotate);
-
do_indent_to_level(ctxt, indent, 0);
out << "<abi-corpus";
if (!corpus->get_path().empty())
out << ">\n";
- // Write the list of needed corpora
+ // Write the list of needed corpora.
+
+ bool saved_annotate = ctxt.get_annotate();
+ ctxt.set_annotate(annotate);
+
if (!corpus->get_needed().empty())
{
do_indent_to_level(ctxt, indent, 1);
do_indent_to_level(ctxt, indent, 0);
out << "</abi-corpus>\n";
+ ctxt.set_annotate(saved_annotate);
+
+ return true;
+}
+
+/// Serialize an ABI corpus to a single native xml document. The root
+/// note of the resulting XML document is 'abi-corpus'.
+///
+/// @param corpus the corpus to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus(const corpus_sptr corpus,
+ unsigned indent,
+ std::ostream& out,
+ const bool annotate)
+{
+ if (!corpus)
+ return false;
+
+ write_context ctxt(corpus->get_environment(), out, annotate);
+
+ return write_corpus(corpus, indent, ctxt, out, annotate);
+}
+
+/// Serialize an ABI corpus group to a single native xml document.
+/// The root note of the resulting XML document is 'abi-corpus-group'.
+///
+/// @param group the corpus group to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param ctxt the write context to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus_group(const corpus_group_sptr& group,
+ unsigned indent,
+ write_context& ctxt,
+ std::ostream& out,
+ const bool annotate)
+
+{
+ if (!group)
+ return false;
+
+ do_indent_to_level(ctxt, indent, 0);
+ out << "<abi-corpus-group";
+
+ if (!group->get_path().empty())
+ out << " path='" << xml::escape_xml_string(group->get_path()) << "'";
+
+ if (!group->get_architecture_name().empty())
+ out << " architecture='" << group->get_architecture_name()<< "'";
+
+ if (group->is_empty())
+ {
+ out << "/>\n";
+ return true;
+ }
+
+ out << ">\n";
+
+ // Write the list of corpora
+ for (corpus_group::corpora_type::const_iterator c =
+ group->get_corpora().begin();
+ c != group->get_corpora().end();
+ ++c)
+ write_corpus(*c, get_indent_to_level(ctxt, indent, 1), ctxt, out, annotate);
+
+ do_indent_to_level(ctxt, indent, 0);
+ out << "</abi-corpus-group>\n";
+
return true;
}
+/// Serialize an ABI corpus group to a single native xml document.
+/// The root note of the resulting XML document is 'abi-corpus-group'.
+///
+/// @param group the corpus group to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus_group(const corpus_group_sptr& group,
+ unsigned indent,
+ std::ostream& out,
+ const bool annotate)
+
+{
+ if (!group)
+ return false;
+
+ write_context ctxt(group->get_environment(), out, annotate);
+
+ return write_corpus_group(group, indent, ctxt, out, annotate);
+}
+
/// Serialize an ABI corpus to a single native xml document. The root
/// note of the resulting XML document is 'abi-corpus'.
///
///
/// @param indent the number of white space indentation to use.
///
-/// @param out the output file to serialize the ABI corpus to.
+/// @param path the output file to serialize the ABI corpus to.
///
/// @param annotate whether ABIXML output should be annotated.
///
using abigail::xml_reader::read_translation_unit_from_file;
using abigail::xml_reader::read_corpus_from_native_xml_file;
using abigail::xml_writer::write_translation_unit;
-using abigail::xml_writer::write_corpus;
using abigail::workers::queue;
using abigail::workers::task;
using abigail::tools_utils::temp_file;
using abigail::tools_utils::temp_file_sptr;
using abigail::tools_utils::check_file;
+using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
using abigail::ir::environment_sptr;
using abigail::ir::environment;
using abigail::corpus;
bool write_corpus_path;
bool load_all_types;
bool linux_kernel_mode;
+ bool corpus_group_for_linux;
bool show_stats;
bool noout;
bool show_locs;
write_corpus_path(true),
load_all_types(),
linux_kernel_mode(true),
+ corpus_group_for_linux(false),
show_stats(),
noout(),
show_locs(true),
"a Linux Kernel binary\n"
<< " --kmi-whitelist|-w path to a linux kernel "
"abi whitelist\n"
+ << " --linux-tree|--lt emit the ABI for the union of a"
+ "vmlinux and its modules\n"
<< " --abidiff compare the loaded ABI against itself\n"
<< " --annotate annotate the ABI artifacts emitted in the output\n"
<< " --stats show statistics about various internal stuff\n"
opts.kabi_whitelist_paths.push_back(argv[j]);
++i;
}
+ else if (!strcmp(argv[i], "--linux-tree")
+ || !strcmp(argv[i], "--lt"))
+ opts.corpus_group_for_linux = true;
else if (!strcmp(argv[i], "--noout"))
opts.noout = true;
else if (!strcmp(argv[i], "--no-architecture"))
add_read_context_suppressions(read_ctxt, opts.kabi_whitelist_supprs);
}
-int
-main(int argc, char* argv[])
+/// Load an ABI @ref corpus (the internal representation of the ABI of
+/// a binary) and write it out as an abixml.
+///
+/// @param argv the arguments the program was called with.
+///
+/// @param env the environment the ABI artifacts are being created in.
+///
+/// @param context the context of the ELF reading.
+///
+/// @param opts the options of the program.
+///
+/// @return the exit code: 0 if everything went fine, non-zero
+/// otherwise.
+static int
+load_corpus_and_write_abixml(char* argv[],
+ environment_sptr& env,
+ read_context_sptr& context,
+ const options& opts)
{
- options opts;
-
- if (!parse_command_line(argc, argv, opts)
- || (opts.in_file_path.empty()
- && !opts.display_version))
- {
- if (!opts.wrong_option.empty())
- emit_prefix(argv[0], cerr)
- << "unrecognized option: " << opts.wrong_option << "\n";
- display_usage(argv[0], cerr);
- return 1;
- }
-
- if (opts.display_version)
- {
- string major, minor, revision;
- abigail::abigail_get_library_version(major, minor, revision);
- cout << major << "." << minor << "." << revision << "\n";
- return 0;
- }
+ int exit_code = 0;
- assert(!opts.in_file_path.empty());
- if (!abigail::tools_utils::check_file(opts.in_file_path, cerr, argv[0]))
- return 1;
-
- if (!maybe_check_suppression_files(opts))
- return 1;
-
- abigail::tools_utils::file_type type =
- abigail::tools_utils::guess_file_type(opts.in_file_path);
- if (type != abigail::tools_utils::FILE_TYPE_ELF
- && type != abigail::tools_utils::FILE_TYPE_AR)
- {
- emit_prefix(argv[0], cerr)
- << opts.in_file_path << " is not an ELF file\n";
- return 1;
- }
-
- char* p = opts.di_root_path.get();
- environment_sptr env(new environment);
+ read_context& ctxt = *context;
corpus_sptr corp;
- read_context_sptr c = create_read_context(opts.in_file_path,
- &p, env.get(),
- opts.load_all_types,
- opts.linux_kernel_mode);
- read_context& ctxt = *c;
-
- set_show_stats(ctxt, opts.show_stats);
- set_suppressions(ctxt, opts);
- abigail::dwarf_reader::set_do_log(ctxt, opts.do_log);
- if (!opts.kabi_whitelist_supprs.empty())
- set_ignore_symbol_table(ctxt, true);
-
- if (opts.check_alt_debug_info_path)
- {
- bool has_alt_di = false;
- string alt_di_path;
- abigail::dwarf_reader::status status =
- abigail::dwarf_reader::has_alt_debug_info(ctxt,
- has_alt_di,
- alt_di_path);
- if (status & abigail::dwarf_reader::STATUS_OK)
- {
- if (alt_di_path.empty())
- ;
- else
- {
- cout << "found the alternate debug info file";
- if (opts.show_base_name_alt_debug_info_path)
- {
- tools_utils::base_name(alt_di_path, alt_di_path);
- cout << " '" << alt_di_path << "'";
- }
- cout << "\n";
- }
- return 0;
- }
- else
- {
- emit_prefix(argv[0], cerr)
- << "could not find alternate debug info file\n";
- return 1;
- }
- }
-
dwarf_reader::status s = dwarf_reader::STATUS_UNKNOWN;
corp = read_corpus_from_elf(ctxt, s);
- c.reset();
+ context.reset();
if (!corp)
{
if (s == dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
{
- if (p == 0)
+ if (opts.di_root_path.get() == 0)
{
emit_prefix(argv[0], cerr)
<< "Could not read debug info from "
{
emit_prefix(argv[0], cerr)
<< "Could not read debug info for '" << opts.in_file_path
- << "' from debug info root directory '" << p
+ << "' from debug info root directory '"
+ << opts.di_root_path.get()
<< "'\n";
}
}
return 0;
}
else
- abigail::xml_writer::write_corpus(corp, 0, cout, opts.annotate);
+ exit_code = !abigail::xml_writer::write_corpus(corp, 0, cout,
+ opts.annotate);
+ }
+
+ return exit_code;
+}
+
+/// Load a corpus group representing the union of a Linux Kernel
+/// vmlinux binary and its modules, and emit an abixml representation
+/// for it.
+///
+/// @param argv the arguments this program was called with.
+///
+/// @param env the environment the ABI artifacts are created in.
+///
+/// @param opts the options this program was created with.
+///
+/// @return the exit code. Zero if everything went well, non-zero
+/// otherwise.
+static int
+load_kernel_corpus_group_and_write_abixml(char* argv[],
+ environment_sptr& env,
+ options& opts)
+{
+ if (!(tools_utils::is_dir(opts.in_file_path) && opts.corpus_group_for_linux))
+ return 0;
+
+ int exit_code = 0;
+
+ suppressions_type supprs;
+ corpus_group_sptr group =
+ build_corpus_group_from_kernel_dist_under(opts.in_file_path,
+ opts.suppression_paths,
+ opts.kabi_whitelist_paths,
+ supprs, opts.do_log, env);
+
+ if (!group)
+ return exit_code;
+
+ if (!opts.write_architecture)
+ group->set_architecture_name("");
+ if (!opts.write_corpus_path)
+ group->set_path("");
+
+ if (!opts.out_file_path.empty())
+ {
+ ofstream of(opts.out_file_path.c_str(), std::ios_base::trunc);
+ if (!of.is_open())
+ {
+ emit_prefix(argv[0], cerr)
+ << "could not open output file '"
+ << opts.out_file_path << "'\n";
+ return 1;
+ }
+ exit_code = !xml_writer::write_corpus_group(group, 0, of, opts.annotate);
+ }
+ else
+ exit_code = !xml_writer::write_corpus_group(group, 0, cout, opts.annotate);
+
+ return exit_code;
+}
+
+int
+main(int argc, char* argv[])
+{
+ options opts;
+
+ if (!parse_command_line(argc, argv, opts)
+ || (opts.in_file_path.empty()
+ && !opts.display_version))
+ {
+ if (!opts.wrong_option.empty())
+ emit_prefix(argv[0], cerr)
+ << "unrecognized option: " << opts.wrong_option << "\n";
+ display_usage(argv[0], cerr);
+ return 1;
}
- return 0;
+ if (opts.display_version)
+ {
+ string major, minor, revision;
+ abigail::abigail_get_library_version(major, minor, revision);
+ cout << major << "." << minor << "." << revision << "\n";
+ return 0;
+ }
+
+ assert(!opts.in_file_path.empty());
+ if (opts.corpus_group_for_linux)
+ {
+ if (!abigail::tools_utils::check_dir(opts.in_file_path, cerr, argv[0]))
+ return 1;
+ }
+ else
+ {
+ if (!abigail::tools_utils::check_file(opts.in_file_path, cerr, argv[0]))
+ return 1;
+ }
+
+ if (!maybe_check_suppression_files(opts))
+ return 1;
+
+ abigail::tools_utils::file_type type =
+ abigail::tools_utils::guess_file_type(opts.in_file_path);
+ if (type != abigail::tools_utils::FILE_TYPE_ELF
+ && type != abigail::tools_utils::FILE_TYPE_AR
+ && type != abigail::tools_utils::FILE_TYPE_DIR)
+ {
+ emit_prefix(argv[0], cerr)
+ << "files of the kind of "<< opts.in_file_path << " are not handled\n";
+ return 1;
+ }
+
+ char* p = opts.di_root_path.get();
+ environment_sptr env(new environment);
+ int exit_code = 0;
+
+ if (tools_utils::is_regular_file(opts.in_file_path))
+ {
+ read_context_sptr c = create_read_context(opts.in_file_path,
+ &p, env.get(),
+ opts.load_all_types,
+ opts.linux_kernel_mode);
+ read_context& ctxt = *c;
+ set_show_stats(ctxt, opts.show_stats);
+ set_suppressions(ctxt, opts);
+ abigail::dwarf_reader::set_do_log(ctxt, opts.do_log);
+ if (!opts.kabi_whitelist_supprs.empty())
+ set_ignore_symbol_table(ctxt, true);
+
+ if (opts.check_alt_debug_info_path)
+ {
+ bool has_alt_di = false;
+ string alt_di_path;
+ abigail::dwarf_reader::status status =
+ abigail::dwarf_reader::has_alt_debug_info(ctxt,
+ has_alt_di,
+ alt_di_path);
+ if (status & abigail::dwarf_reader::STATUS_OK)
+ {
+ if (alt_di_path.empty())
+ ;
+ else
+ {
+ cout << "found the alternate debug info file";
+ if (opts.show_base_name_alt_debug_info_path)
+ {
+ tools_utils::base_name(alt_di_path, alt_di_path);
+ cout << " '" << alt_di_path << "'";
+ }
+ cout << "\n";
+ }
+ return 0;
+ }
+ else
+ {
+ emit_prefix(argv[0], cerr)
+ << "could not find alternate debug info file\n";
+ return 1;
+ }
+ }
+ exit_code = load_corpus_and_write_abixml(argv, env, c, opts);
+ }
+ else
+ exit_code = load_kernel_corpus_group_and_write_abixml(argv, env, opts);
+
+ return exit_code;
}
return true;
}
-/// If the user specified suppression specification files, this
-/// function parses those, set them to the options of the program and
-/// set the read context with the suppression specification.
-///
-/// @param read_ctxt the read context to consider.
-///
-/// @param opts the options to consider.
-static void
-set_suppressions(read_context &read_ctxt, options& opts)
-{
- if (opts.read_time_supprs.empty())
- {
- for (vector<string>::const_iterator i = opts.suppression_paths.begin();
- i != opts.suppression_paths.end();
- ++i)
- read_suppressions(*i, opts.read_time_supprs);
-
- for (vector<string>::const_iterator i =
- opts.kabi_whitelist_paths.begin();
- i != opts.kabi_whitelist_paths.end();
- ++i)
- gen_suppr_spec_from_kernel_abi_whitelist(*i, opts.read_time_supprs);
- }
-
- abigail::dwarf_reader::add_read_context_suppressions(read_ctxt,
- opts.read_time_supprs);
-}
-
/// Setup the diff context from the program's options.
///
/// @param ctxt the diff context to consider.
ctxt->add_suppressions(opts.diff_time_supprs);
}
-/// Test if an FTSENT pointer (resulting from fts_read) represents the
-/// vmlinux binary.
-///
-/// @param entry the FTSENT to consider.
-///
-/// @return true iff @p entry is for a vmlinux binary.
-static bool
-is_vmlinux(const FTSENT *entry)
-{
- if (entry == NULL
- || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
- || entry->fts_info == FTS_ERR
- || entry->fts_info == FTS_NS)
- return false;
-
- string fname = entry->fts_name;
-
- if (fname == "vmlinux")
- {
- string dirname;
- dir_name(entry->fts_path, dirname);
- if (string_ends_with(dirname, "compressed"))
- return false;
-
- return true;
- }
-
- return false;
-}
-
-/// Test if an FTSENT pointer (resulting from fts_read) represents a a
-/// linux kernel module binary.
-///
-/// @param entry the FTSENT to consider.
-///
-/// @return true iff @p entry is for a linux kernel module binary.
-static bool
-is_kernel_module(const FTSENT *entry)
-{
- if (entry == NULL
- || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
- || entry->fts_info == FTS_ERR
- || entry->fts_info == FTS_NS)
- return false;
-
- string fname = entry->fts_name;
- if (string_ends_with(fname, ".ko")
- || string_ends_with(fname, ".ko.xz")
- || string_ends_with(fname, ".ko.gz"))
- return true;
-
- return false;
-}
-
-/// Find a vmlinux and its kernel modules in a given directory tree.
-///
-/// @param from the directory tree to start looking from.
-///
-/// @param vmlinux_path output parameter. This is set to the path
-/// where the vmlinux binary is found. This is set iff the returns
-/// true.
-///
-/// @param module_paths output parameter. This is set to the paths of
-/// the linux kernel module binaries.
-///
-/// @return true iff at least the vmlinux binary was found.
-static bool
-find_vmlinux_and_module_paths(const string& from,
- string &vmlinux_path,
- vector<string> &module_paths)
-{
- char* path[] = {const_cast<char*>(from.c_str()), 0};
-
- FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
- if (!file_hierarchy)
- return false;
-
- bool found_vmlinux = false;
- FTSENT *entry;
- while ((entry = fts_read(file_hierarchy)))
- {
- // Skip descendents of symbolic links.
- if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
- {
- fts_set(file_hierarchy, entry, FTS_SKIP);
- continue;
- }
-
- if (!found_vmlinux && is_vmlinux(entry))
- {
- vmlinux_path = entry->fts_path;
- found_vmlinux = true;
- }
- else if (is_kernel_module(entry))
- module_paths.push_back(entry->fts_path);
- }
-
- fts_close(file_hierarchy);
-
- return found_vmlinux;
-}
-
-/// Get the paths of the vmlinux and kernel module binaries under
-/// given directory.
-///
-/// @param dist_root the directory under which to look for.
-///
-/// @param vmlinux_path output parameter. The path of the vmlinux
-/// binary that was found.
-///
-/// @param module_paths output parameter. The paths of the kernel
-/// module binaries that were found.
-///
-/// @param debug_info_root_path output parameter. If a debug info
-/// sub-directory is found under @p dist_root, it's set to this
-/// parameter.
-///
-/// @return true if at least the path to the vmlinux binary was found.
-static bool
-get_binary_paths_from_kernel_dist(const string& dist_root,
- string& vmlinux_path,
- vector<string>& module_paths,
- string& debug_info_root_path)
-{
- if (!dir_exists(dist_root))
- return false;
-
- // For now, we assume either an Enterprise Linux or a Fedora kernel
- // distribution directory.
- //
- // We also take into account split debug info package for these. In
- // this case, the content split debug info package is installed
- // under the 'dist_root' directory as well, and its content is
- // accessible from <dist_root>/usr/lib/debug directory.
-
- string kernel_modules_root;
- string debug_info_root;
- if (dir_exists(dist_root + "/lib/modules"))
- {
- dist_root + "/lib/modules";
- debug_info_root = dist_root + "/usr/lib/debug";
- }
-
- if (dir_is_empty(debug_info_root))
- debug_info_root.clear();
-
- bool found = false;
- string from = !debug_info_root.empty() ? debug_info_root : dist_root;
- if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
- found = true;
-
- debug_info_root_path = debug_info_root;
-
- return found;
-}
-
-/// Get the paths of the vmlinux and kernel module binaries under
-/// given directory.
-///
-/// @param dist_root the directory under which to look for.
-///
-/// @param vmlinux_path output parameter. The path of the vmlinux
-/// binary that was found.
-///
-/// @param module_paths output parameter. The paths of the kernel
-/// module binaries that were found.
-///
-/// @return true if at least the path to the vmlinux binary was found.
-static bool
-get_binary_paths_from_kernel_dist(const string& dist_root,
- string& vmlinux_path,
- vector<string>& module_paths)
-{
- string debug_info_root_path;
- return get_binary_paths_from_kernel_dist(dist_root,
- vmlinux_path,
- module_paths,
- debug_info_root_path);
-}
-
-/// Walk a given directory and build an instance of @ref corpus_group
-/// from the vmlinux kernel binary and the linux kernel modules found
-/// under that directory and under its sub-directories, recursively.
-///
-/// The main corpus of the @ref corpus_group is made of the vmlinux
-/// binary. The other corpora are made of the linux kernel binaries.
-///
-/// @param root the path of the directory under which vmlinux and its
-/// kernel modules are to be found.
-///
-/// @param opts the options to use during the search.
-///
-/// @param env the environment to create the corpus_group in.
-static corpus_group_sptr
-build_corpus_group_from_kernel_dist_under(const string& root,
- options& opts,
- environment_sptr& env)
-{
- corpus_group_sptr result;
- string vmlinux;
- vector<string> modules;
- string debug_info_root_path;
-
- if (opts.verbose)
- cout << "Analysing kernel dist root '" << root << "' ... " << std::flush;
-
- bool got_binary_paths =
- get_binary_paths_from_kernel_dist(root, vmlinux, modules,
- debug_info_root_path);
-
- if (opts.verbose)
- cout << "DONE\n";
-
- if (got_binary_paths)
- {
- shared_ptr<char> di_root =
- make_path_absolute(debug_info_root_path.c_str());
- char *di_root_ptr = di_root.get();
- abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_OK;
- corpus_group_sptr group;
- if (!vmlinux.empty())
- {
- read_context_sptr ctxt =
- create_read_context(vmlinux, &di_root_ptr, env.get(),
- /*read_all_types=*/false,
- /*linux_kernel_mode=*/true);
-
- set_suppressions(*ctxt, opts);
-
- // If we have been given a whitelist of functions and
- // variable symbols to look at, then we can avoid loading
- // and analyzing the ELF symbol table.
- bool do_ignore_symbol_table = !opts.read_time_supprs.empty();
- set_ignore_symbol_table(*ctxt, do_ignore_symbol_table);
-
- group.reset(new corpus_group(env.get(), root));
-
- set_read_context_corpus_group(*ctxt, group);
-
- if (opts.verbose)
- cout << "reading kernel binary '"
- << vmlinux << "' ... " << std::flush;
-
- // Read the vmlinux corpus and add it to the group.
- read_and_add_corpus_to_group_from_elf(*ctxt, *group, status);
-
- if (opts.verbose)
- cout << " DONE\n";
- }
-
- if (!group->is_empty())
- {
- // Now add the corpora of the modules to the corpus group.
- int total_nb_modules = modules.size();
- int cur_module_index = 1;
- for (vector<string>::const_iterator m = modules.begin();
- m != modules.end();
- ++m, ++cur_module_index)
- {
- if (opts.verbose)
- cout << "reading module '"
- << *m << "' ("
- << cur_module_index
- << "/" << total_nb_modules
- << ") ... " << std::flush;
-
- read_context_sptr module_ctxt =
- create_read_context(*m, &di_root_ptr, env.get(),
- /*read_all_types=*/false,
- /*linux_kernel_mode=*/true);
-
- // If we have been given a whitelist of functions and
- // variable symbols to look at, then we can avoid loading
- // and analyzing the ELF symbol table.
- bool do_ignore_symbol_table = !opts.read_time_supprs.empty();
-
- set_ignore_symbol_table(*module_ctxt, do_ignore_symbol_table);
-
- set_suppressions(*module_ctxt, opts);
-
- set_read_context_corpus_group(*module_ctxt, group);
-
- read_and_add_corpus_to_group_from_elf(*module_ctxt,
- *group, status);
- if (opts.verbose)
- cout << " DONE\n";
- }
-
- result = group;
- }
- }
-
- return result;
-}
-
/// Print information about the kernel (and modules) binaries found
/// under a given directory.
///
{
group1 =
build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root1,
- opts, env);
+ opts.suppression_paths,
+ opts.kabi_whitelist_paths,
+ opts.read_time_supprs,
+ opts.verbose,
+ env);
print_kernel_dist_binary_paths_under(opts.kernel_dist_root1, opts);
}
{
group2 =
build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root2,
- opts, env);
+ opts.suppression_paths,
+ opts.kabi_whitelist_paths,
+ opts.read_time_supprs,
+ opts.verbose,
+ env);
print_kernel_dist_binary_paths_under(opts.kernel_dist_root2, opts);
}