libraries in `ELF`_ format. It emits a meaningful report describing the
differences between the two ABIs.
+This tool can also compare the textual representations of the ABI of
+two ELF binaries (as emitted by ``abidw``) or an ELF binary against a
+textual representation of another ELF binary.
+
For a comprehensive ABI change report that includes changes about
function and variable sub-types, the two input shared libraries must
be accompanied with their debug information in `DWARF`_ format.
#define CHAR_STR(xml_char_str) \
reinterpret_cast<char*>(xml_char_str.get())
+xmlNodePtr
+advance_to_next_sibling_element(xmlNodePtr node);
+
void
escape_xml_string(const std::string& str,
std::string& escaped);
corpus_sptr
read_corpus_from_input(read_context& ctxt);
+corpus_group_sptr
+read_corpus_group_from_input(read_context& ctxt);
+
+corpus_group_sptr
+read_corpus_group_from_native_xml(std::istream* in,
+ environment* env);
+
+corpus_group_sptr
+read_corpus_group_from_native_xml_file(const string& path,
+ environment* env);
+
void
add_read_context_suppressions(read_context& ctxt,
const suppr::suppressions_type& supprs);
FILE_TYPE_ELF,
/// An archive (AR) file.
FILE_TYPE_AR,
- // A native xml file format representing a corpus of one or several
- // translation units.
+ // A native abixml file format representing a corpus of one or
+ // several translation units.
FILE_TYPE_XML_CORPUS,
+ // A native abixml file format representing a corpus group of one or
+ // several corpora.
+ FILE_TYPE_XML_CORPUS_GROUP,
// A zip file, possibly containing a corpus of one of several
// translation units.
FILE_TYPE_ZIP_CORPUS,
return result;
}
+/// Maybe get the next sibling element node of an XML node, or stay to the sam
+///
+/// If there is no next sibling xml element node, the function returns
+/// the initial node.
+///
+/// @param node the initial node to consider.
+///
+/// @return the next sibling node or the initial node @p node.
+static xmlNodePtr
+go_to_next_sibling_element_or_stay(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ for (n = node; n; n = n->next)
+ {
+ if (n->type == XML_ELEMENT_NODE)
+ break;
+ }
+ return n ? n : node;
+}
+
+/// Get the next sibling element node of an XML node.
+///
+/// If there is no next sibling xml element node, the function returns nil.
+///
+/// @param node the XML node to consider.
+///
+/// @return the next sibling element node or nil.
+xmlNodePtr
+advance_to_next_sibling_element(xmlNodePtr node)
+{
+ xmlNodePtr n = go_to_next_sibling_element_or_stay(node->next);
+ if (n == 0 || n->type != XML_ELEMENT_NODE)
+ return 0;
+ return n;
+}
+
}//end namespace xml
}//end namespace abigail
xmlNodePtr m_corp_node;
deque<shared_ptr<decl_base> > m_decls_stack;
corpus_sptr m_corpus;
+ corpus_group_sptr m_corpus_group;
corpus::exported_decls_builder* m_exported_decls_builder;
suppr::suppressions_type m_supprs;
set_corpus(corpus_sptr c)
{m_corpus = c;}
+ /// Getter of the current corpus group.
+ ///
+ /// @return the current corpus group.n
+ const corpus_group_sptr&
+ get_corpus_group() const
+ {return m_corpus_group;}
+
+ /// Getter of the current corpus group.
+ ///
+ /// @return the current corpus group.
+ corpus_group_sptr&
+ get_corpus_group()
+ {return m_corpus_group;}
+
+ /// Setter of the corpus_group
+ ///
+ /// @param group the new corpus group.
+ void
+ set_corpus_group(const corpus_group_sptr& group)
+ {m_corpus_group = group;}
+
/// Getter for the object that determines if a given declaration
/// ought to be put in the set of exported decls of the current
/// corpus.
{
translation_unit_sptr tu, nil;
- xmlNodePtr node = 0;
- if (!ctxt.get_corpus_node())
+ xmlNodePtr node = ctxt.get_corpus_node();
+ if (!node)
{
xml::reader_sptr reader = ctxt.get_reader();
if (!reader)
if (!reader)
return nil;
- // The document must start with the abi-corpus node.
- int status = 1;
- while (status == 1
- && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
- status = advance_cursor (ctxt);
+ bool call_reader_next = false;
- if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
- BAD_CAST("abi-corpus")))
- return nil;
-
- if (!ctxt.get_corpus())
+ xmlNodePtr node = ctxt.get_corpus_node();
+ if (!node)
{
- corpus_sptr c(new corpus(ctxt.get_environment(), ""));
- ctxt.set_corpus(c);
+ // The document must start with the abi-corpus node.
+ int status = 1;
+ while (status == 1
+ && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
+ status = advance_cursor (ctxt);
+
+ if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+ BAD_CAST("abi-corpus")))
+ return nil;
+
+ if (!ctxt.get_corpus())
+ {
+ corpus_sptr c(new corpus(ctxt.get_environment(), ""));
+ ctxt.set_corpus(c);
+ }
+
+ if (!ctxt.get_corpus_group())
+ ctxt.clear_per_corpus_data();
+
+ corpus& corp = *ctxt.get_corpus();
+ ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get());
+
+ xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path");
+ if (path_str)
+ corp.set_path(reinterpret_cast<char*>(path_str.get()));
+
+ xml::xml_char_sptr architecture_str =
+ XML_READER_GET_ATTRIBUTE(reader, "architecture");
+ if (architecture_str)
+ corp.set_architecture_name
+ (reinterpret_cast<char*>(architecture_str.get()));
+
+ xml::xml_char_sptr soname_str =
+ XML_READER_GET_ATTRIBUTE(reader, "soname");
+ if (soname_str)
+ corp.set_soname(reinterpret_cast<char*>(soname_str.get()));
+
+ node = xmlTextReaderExpand(reader.get());
+ if (!node)
+ return nil;
+
+ call_reader_next = true;
+
+ ctxt.set_corpus_node(node->children);
}
+ else
+ {
+ if (!ctxt.get_corpus())
+ {
+ corpus_sptr c(new corpus(ctxt.get_environment(), ""));
+ ctxt.set_corpus(c);
+ }
- ctxt.clear_per_corpus_data();
+ if (!ctxt.get_corpus_group())
+ ctxt.clear_per_corpus_data();
- corpus& corp = *ctxt.get_corpus();
- ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get());
+ corpus& corp = *ctxt.get_corpus();
+ ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get());
- xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path");
- if (path_str)
- corp.set_path(reinterpret_cast<char*>(path_str.get()));
+ xml::xml_char_sptr path_str = XML_NODE_GET_ATTRIBUTE(node, "path");
+ if (path_str)
+ corp.set_path(reinterpret_cast<char*>(path_str.get()));
- xml::xml_char_sptr architecture_str =
- XML_READER_GET_ATTRIBUTE(reader, "architecture");
- if (architecture_str)
- corp.set_architecture_name(reinterpret_cast<char*>(architecture_str.get()));
+ xml::xml_char_sptr architecture_str =
+ XML_NODE_GET_ATTRIBUTE(node, "architecture");
+ if (architecture_str)
+ corp.set_architecture_name
+ (reinterpret_cast<char*>(architecture_str.get()));
- xml::xml_char_sptr soname_str = XML_READER_GET_ATTRIBUTE(reader, "soname");
- if (soname_str)
- corp.set_soname(reinterpret_cast<char*>(soname_str.get()));
+ xml::xml_char_sptr soname_str =
+ XML_NODE_GET_ATTRIBUTE(node, "soname");
+ if (soname_str)
+ corp.set_soname(reinterpret_cast<char*>(soname_str.get()));
- xmlNodePtr node = xmlTextReaderExpand(reader.get());
- if (!node)
- return nil;
+ ctxt.set_corpus_node(node->children);
+ }
+
+ corpus& corp = *ctxt.get_corpus();
- ctxt.set_corpus_node(node->children);
walk_xml_node_to_map_type_ids(ctxt, node);
// Read the needed element
corp.set_origin(corpus::NATIVE_XML_ORIGIN);
- // This is the necessary counter-part of the xmlTextReaderExpand()
- // call at the beginning of the function.
- xmlTextReaderNext(reader.get());
+ if (call_reader_next)
+ {
+ // This is the necessary counter-part of the xmlTextReaderExpand()
+ // call at the beginning of the function.
+ xmlTextReaderNext(reader.get());
+ }
+ else
+ {
+ node = ctxt.get_corpus_node();
+ node = xml::advance_to_next_sibling_element(node);
+ if (!node)
+ {
+ node = ctxt.get_corpus_node();
+ node = xml::advance_to_next_sibling_element(node->parent);
+ }
+ ctxt.set_corpus_node(node);
+ }
return ctxt.get_corpus();;
}
+/// Parse the input XML document containing an ABI corpus group,
+/// represented by an 'abi-corpus-group' element node, associated to
+/// the current context.
+///
+/// @param ctxt the current input context.
+///
+/// @return the corpus group resulting from the parsing
+corpus_group_sptr
+read_corpus_group_from_input(read_context& ctxt)
+{
+ corpus_group_sptr nil;
+
+ xml::reader_sptr reader = ctxt.get_reader();
+ if (!reader)
+ return nil;
+
+ // The document must start with the abi-corpus-group node.
+ int status = 1;
+ while (status == 1
+ && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
+ status = advance_cursor (ctxt);
+
+ if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
+ BAD_CAST("abi-corpus-group")))
+ return nil;
+
+ if (!ctxt.get_corpus_group())
+ {
+ corpus_group_sptr g(new corpus_group(ctxt.get_environment(),
+ ctxt.get_path()));
+ ctxt.set_corpus_group(g);
+ }
+
+ corpus_group_sptr group = ctxt.get_corpus_group();
+ xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path");
+ if (path_str)
+ group->set_path(reinterpret_cast<char*>(path_str.get()));
+
+ xmlNodePtr node = xmlTextReaderExpand(reader.get());
+ if (!node)
+ return nil;
+
+ //node = xml::get_first_element_sibling_if_text(node->children);
+ node = xml::advance_to_next_sibling_element(node->children);
+ ctxt.set_corpus_node(node);
+
+ corpus_sptr corp;
+ while (corp = read_corpus_from_input(ctxt))
+ ctxt.get_corpus_group()->add_corpus(corp);
+
+ xmlTextReaderNext(reader.get());
+
+ return ctxt.get_corpus_group();
+}
+
+/// De-serialize an ABI corpus group from an input XML document which
+/// root node is 'abi-corpus-group'.
+///
+/// @param in the input stream to read the XML document from.
+///
+/// @param env the environment to use. Note that the life time of
+/// this environment must be greater than the lifetime of the
+/// resulting corpus as the corpus uses resources that are allocated
+/// in the environment.
+///
+/// @return the resulting corpus group de-serialized from the parsing.
+/// This is non-null iff the parsing resulted in a valid corpus group.
+corpus_group_sptr
+read_corpus_group_from_native_xml(std::istream* in,
+ environment* env)
+{
+ read_context_sptr read_ctxt = create_native_xml_read_context(in, env);
+ return read_corpus_group_from_input(*read_ctxt);
+}
+
+/// De-serialize an ABI corpus group from an XML document file which
+/// root node is 'abi-corpus-group'.
+///
+/// @param path the path to the input file to read the XML document
+/// from.
+///
+/// @param env the environment to use. Note that the life time of
+/// this environment must be greater than the lifetime of the
+/// resulting corpus as the corpus uses resources that are allocated
+/// in the environment.
+///
+/// @return the resulting corpus group de-serialized from the parsing.
+/// This is non-null if the parsing successfully resulted in a corpus
+/// group.
+corpus_group_sptr
+read_corpus_group_from_native_xml_file(const string& path,
+ environment* env)
+{
+ read_context_sptr read_ctxt = create_native_xml_read_context(path, env);
+ corpus_group_sptr group = read_corpus_group_from_input(*read_ctxt);
+ return group;
+}
+
/// Parse an ABI instrumentation file (in XML format) at a given path.
///
/// @param input_file a path to the file containing the xml document
return result;
}
-/// Read an ABI corpus from a given reading context.
-///
-/// @return the resulting ABI corpus.
-corpus_sptr
-read_corpus_from_native_xml(read_context& ctxt)
-{return read_corpus_from_input(ctxt);}
-
/// De-serialize an ABI corpus from an input XML document which root
/// node is 'abi-corpus'.
///
case FILE_TYPE_XML_CORPUS:
repr = "native XML corpus file type";
break;
+ case FILE_TYPE_XML_CORPUS_GROUP:
+ repr = "native XML corpus group file type";
+ break;
case FILE_TYPE_ZIP_CORPUS:
repr = "native ZIP corpus file type";
break;
&& buf[10] == ' ')
return FILE_TYPE_NATIVE_BI;
+ if (buf[0] == '<'
+ && buf[1] == 'a'
+ && buf[2] == 'b'
+ && buf[3] == 'i'
+ && buf[4] == '-'
+ && buf[5] == 'c'
+ && buf[6] == 'o'
+ && buf[7] == 'r'
+ && buf[8] == 'p'
+ && buf[9] == 'u'
+ && buf[10] == 's'
+ && buf[11] == '-'
+ && buf[12] == 'g'
+ && buf[13] == 'r'
+ && buf[14] == 'o'
+ && buf[15] == 'u'
+ && buf[16] == 'p'
+ && buf[17] == ' ')
+ return FILE_TYPE_XML_CORPUS_GROUP;
+
if (buf[0] == '<'
&& buf[1] == 'a'
&& buf[2] == 'b'
using abigail::translation_unit;
using abigail::translation_unit_sptr;
using abigail::corpus_sptr;
+using abigail::corpus_group_sptr;
using abigail::comparison::translation_unit_diff_sptr;
using abigail::comparison::corpus_diff;
using abigail::comparison::corpus_diff_sptr;
}
}
+/// This function sets diff context options that are specific to
+/// kernel module interface comparison.
+///
+/// @param ctxt the diff context to consider.
+static void
+adjust_diff_context_for_kmidiff(diff_context_sptr &ctxt)
+{
+ ctxt->show_linkage_names(false);
+ ctxt->show_added_fns(false);
+ ctxt->show_added_vars(false);
+ ctxt->show_added_symbols_unreferenced_by_debug_info
+ (false);
+}
+
int
main(int argc, char* argv[])
{
abigail::dwarf_reader::STATUS_OK,
c2_status = abigail::dwarf_reader::STATUS_OK;
corpus_sptr c1, c2;
+ corpus_group_sptr g1, g2;
char *di_dir1 = 0, *di_dir2 = 0;
bool files_suppressed = false;
c1 = abigail::xml_reader::read_corpus_from_input(*ctxt);
}
break;
+ case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
+ {
+ abigail::xml_reader::read_context_sptr ctxt =
+ abigail::xml_reader::create_native_xml_read_context(opts.file1,
+ env.get());
+ assert(ctxt);
+ set_suppressions(*ctxt, opts);
+ g1 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
+ }
+ break;
case abigail::tools_utils::FILE_TYPE_ZIP_CORPUS:
#ifdef WITH_ZIP_ARCHIVE
c1 = abigail::xml_reader::read_corpus_from_file(opts.file1);
#endif //WITH_ZIP_ARCHIVE
- break;
- case abigail::tools_utils::FILE_TYPE_RPM:
- case abigail::tools_utils::FILE_TYPE_SRPM:
- case abigail::tools_utils::FILE_TYPE_DEB:
- case abigail::tools_utils::FILE_TYPE_DIR:
- case abigail::tools_utils::FILE_TYPE_TAR:
- break;
- }
+ break;
+ case abigail::tools_utils::FILE_TYPE_RPM:
+ case abigail::tools_utils::FILE_TYPE_SRPM:
+ case abigail::tools_utils::FILE_TYPE_DEB:
+ case abigail::tools_utils::FILE_TYPE_DIR:
+ case abigail::tools_utils::FILE_TYPE_TAR:
+ break;
+ }
switch (t2_type)
{
c2 = abigail::xml_reader::read_corpus_from_input(*ctxt);
}
break;
+ case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
+ {
+ abigail::xml_reader::read_context_sptr ctxt =
+ abigail::xml_reader::create_native_xml_read_context(opts.file2,
+ env.get());
+ assert(ctxt);
+ set_suppressions(*ctxt, opts);
+ g2 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
+ }
+ break;
case abigail::tools_utils::FILE_TYPE_ZIP_CORPUS:
#ifdef WITH_ZIP_ARCHIVE
c2 = abigail::xml_reader::read_corpus_from_file(opts.file2);
#endif //WITH_ZIP_ARCHIVE
- break;
- case abigail::tools_utils::FILE_TYPE_RPM:
- case abigail::tools_utils::FILE_TYPE_SRPM:
- case abigail::tools_utils::FILE_TYPE_DEB:
- case abigail::tools_utils::FILE_TYPE_DIR:
- case abigail::tools_utils::FILE_TYPE_TAR:
- break;
- }
-
- if (!t1 && !c1)
+ break;
+ case abigail::tools_utils::FILE_TYPE_RPM:
+ case abigail::tools_utils::FILE_TYPE_SRPM:
+ case abigail::tools_utils::FILE_TYPE_DEB:
+ case abigail::tools_utils::FILE_TYPE_DIR:
+ case abigail::tools_utils::FILE_TYPE_TAR:
+ break;
+ }
+
+ if (!t1 && !c1 && !g1)
{
emit_prefix(argv[0], cerr)
<< "failed to read input file " << opts.file1 << "\n";
}
}
- if (!t2 && !c2)
+ if (!t2 && !c2 && !g2)
{
emit_prefix(argv[0], cerr)
<< "failed to read input file " << opts.file2 << "\n";
}
if (!!c1 != !!c2
- || !!t1 != !!t2)
+ || !!t1 != !!t2
+ || !!g1 != !!g2)
{
emit_prefix(argv[0], cerr)
<< "the two input should be of the same kind\n";
if (diff->has_changes())
diff->report(cout);
}
+ else if (g1)
+ {
+ if (opts.show_symtabs)
+ {
+ display_symtabs(c1, c2, cout);
+ return abigail::tools_utils::ABIDIFF_OK;
+ }
+
+ adjust_diff_context_for_kmidiff(ctxt);
+ corpus_diff_sptr diff = compute_diff(g1, g2, ctxt);
+
+ if (diff->has_net_changes())
+ status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
+
+ if (diff->has_incompatible_changes())
+ status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
+
+ if (diff->has_changes())
+ diff->report(cout);
+
+ }
else
status = abigail::tools_utils::ABIDIFF_ERROR;
}
#if WITH_ZIP_ARCHIVE
corp = read_corpus_from_file(opts.file_path);
#endif
- break;
- case abigail::tools_utils::FILE_TYPE_RPM:
- break;
- case abigail::tools_utils::FILE_TYPE_SRPM:
- break;
- case abigail::tools_utils::FILE_TYPE_DEB:
- break;
- case abigail::tools_utils::FILE_TYPE_DIR:
- break;
- case abigail::tools_utils::FILE_TYPE_TAR:
- break;
- }
+ break;
+ case abigail::tools_utils::FILE_TYPE_RPM:
+ break;
+ case abigail::tools_utils::FILE_TYPE_SRPM:
+ break;
+ case abigail::tools_utils::FILE_TYPE_DEB:
+ break;
+ case abigail::tools_utils::FILE_TYPE_DIR:
+ break;
+ case abigail::tools_utils::FILE_TYPE_TAR:
+ break;
+ case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
+ break;
+ }
if (!tu && !corp)
{
#include "abg-tools-utils.h"
#include "abg-corpus.h"
#include "abg-dwarf-reader.h"
+#include "abg-reader.h"
#include "abg-comparison.h"
using std::string;
using abigail::suppr::suppressions_type;
using abigail::suppr::read_suppressions;
using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelist;
+using abigail::tools_utils::guess_file_type;
+using abigail::tools_utils::file_type;
+using abigail::xml_reader::read_corpus_group_from_native_xml_file;
/// The options of this program.
struct options
corpus_group_sptr group1, group2;
if (!opts.kernel_dist_root1.empty())
{
- group1 =
- build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root1,
- 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);
+ file_type ftype = guess_file_type(opts.kernel_dist_root1);
+ if (ftype == FILE_TYPE_DIR)
+ {
+ group1 =
+ build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root1,
+ 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);
+ }
+ else if (ftype == FILE_TYPE_XML_CORPUS_GROUP)
+ group1 =
+ read_corpus_group_from_native_xml_file(opts.kernel_dist_root1,
+ env.get());
+
}
if (!opts.kernel_dist_root2.empty())
{
- group2 =
- build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root2,
- 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);
+ file_type ftype = guess_file_type(opts.kernel_dist_root2);
+ if (ftype == FILE_TYPE_DIR)
+ {
+ group2 =
+ build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root2,
+ 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);
+ }
+ else if (ftype == FILE_TYPE_XML_CORPUS_GROUP)
+ group2 =
+ read_corpus_group_from_native_xml_file(opts.kernel_dist_root2,
+ env.get());
}
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;