#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <dirent.h>
#include <time.h>
#include <fts.h>
#include <cstdlib>
return get_stat(path, &st);
}
+/// Test that a given directory exists.
+///
+/// @param path the path of the directory to consider.
+///
+/// @return true iff a directory exists with the name @p path
+bool
+dir_exists(const string &path)
+{return file_exists(path) && is_dir(path);}
+
+/// Test if a given directory exists and is empty.
+///
+/// @param path the path of the directory to consider
+bool
+dir_is_empty(const string &path)
+{
+ if (!dir_exists(path))
+ return false;
+
+ DIR* dir = opendir(path.c_str());
+ if (!dir)
+ return false;
+
+ dirent entry, *result = 0;
+ if (readdir_r(dir, &entry, &result))
+ return false;
+
+ closedir(dir);
+
+ return result == 0;
+}
+
/// Test if path is a path to a regular file or a symbolic link to a
/// regular file.
///
return str.compare(str_len - suffix_len, suffix_len, suffix) == 0;
}
+/// Test if a given string begins with a particular prefix.
+///
+/// @param str the string consider.
+///
+/// @param prefix the prefix to look for.
+///
+/// @return true iff string @p str begins with prefix @p prefix.
+bool
+string_begins_with(const string& str, const string& prefix)
+{
+ if (str.empty())
+ return false;
+
+ if (prefix.empty())
+ return true;
+
+ string::size_type prefix_len = prefix.length();
+ if (prefix_len > str.length())
+ return false;
+
+ return str.compare(0, prefix.length(), prefix) == 0;
+}
+
/// Test if a string is made of ascii characters.
///
/// @param str the string to consider.
return r;
}
+/// Get the package name of a .deb package.
+///
+/// @param str the string containing the .deb NVR.
+///
+/// @param name output parameter. This is set with the package name
+/// of the .deb package iff the function returns true.
+///
+/// @return true iff the function successfully finds the .deb package
+/// name.
+bool
+get_deb_name(const string& str, string& name)
+{
+ if (str.empty() || str[0] == '_')
+ return false;
+
+ string::size_type str_len = str.length(), i = 0 ;
+
+ for (; i < str_len; ++i)
+ {
+ if (str[i] == '_')
+ break;
+ }
+
+ if (i == str_len)
+ return false;
+
+ name = str.substr(0, i);
+ return true;
+}
+
+/// Get the package name of an rpm package.
+///
+/// @param str the string containing the NVR of the rpm.
+///
+/// @param name output parameter. This is set with the package name
+/// of the rpm package iff the function returns true.
+///
+/// @return true iff the function successfully finds the rpm package
+/// name.
+bool
+get_rpm_name(const string& str, string& name)
+{
+ if (str.empty() || str[0] == '-')
+ return false;
+
+ string::size_type str_len = str.length(), i = 0;
+ string::value_type c;
+
+ for (; i < str_len; ++i)
+ {
+ c = str[i];
+ string::size_type next_index = i + 1;
+ if ((next_index < str_len) && c == '-' && isdigit(str[next_index]))
+ break;
+ }
+
+ if (i == str_len)
+ return false;
+
+ name = str.substr(0, i);
+
+ return true;
+}
+
+/// Get the architecture string from the NVR of an rpm.
+///
+/// @param str the NVR to consider.
+///
+/// @param arch output parameter. Is set to the resulting
+/// archirecture string iff the function returns true.
+///
+/// @return true iff the function could find the architecture string
+/// from the NVR.
+bool
+get_rpm_arch(const string& str, string& arch)
+{
+ if (str.empty())
+ return false;
+
+ if (!string_ends_with(str, ".rpm"))
+ return false;
+
+ string::size_type str_len = str.length(), i = 0;
+ string::value_type c;
+ string::size_type last_dot_index = 0, dot_before_last_index = 0;
+
+ for (i = str_len - 1; i > 0; --i)
+ {
+ c = str[i];
+ if (c == '.')
+ {
+ last_dot_index = i;
+ break;
+ }
+ }
+
+ if (i == 0)
+ return false;
+
+ for(--i; i > 0; --i)
+ {
+ c = str[i];
+ if (c == '.')
+ {
+ dot_before_last_index = i;
+ break;
+ }
+ }
+
+ if (i == 0)
+ return false;
+
+ arch = str.substr(dot_before_last_index + 1,
+ last_dot_index - dot_before_last_index - 1);
+
+ return true;
+}
+
+/// Tests if a given file name designates a kernel package.
+///
+/// @param file_name the file name to consider.
+///
+/// @param file_type the type of the file @p file_name.
+///
+/// @return true iff @p file_name of kind @p file_type designates a
+/// kernel package.
+bool
+file_is_kernel_package(string& file_name, file_type file_type)
+{
+ bool result = false;
+ string package_name;
+
+ if (file_type == FILE_TYPE_RPM)
+ {
+ if (!get_rpm_name(file_name, package_name))
+ return false;
+ result = (package_name == "kernel");
+ }
+ else if (file_type == FILE_TYPE_DEB)
+ {
+ if (!get_deb_name(file_name, package_name))
+ return false;
+ result = (string_begins_with(package_name, "linux-image"));
+ }
+
+ return result;
+}
+
+/// Tests if a given file name designates a kernel debuginfo package.
+///
+/// @param file_name the file name to consider.
+///
+/// @param file_type the type of the file @p file_name.
+///
+/// @return true iff @p file_name of kind @p file_type designates a
+/// kernel debuginfo package.
+bool
+file_is_kernel_debuginfo_package(string& file_name, file_type file_type)
+{
+ bool result = false;
+ string package_name;
+
+ if (file_type == FILE_TYPE_RPM)
+ {
+ if (!get_rpm_name(file_name, package_name))
+ return false;
+ result = (package_name == "kernel-debuginfo");
+ }
+ else if (file_type == FILE_TYPE_DEB)
+ {
+ if (!get_deb_name(file_name, package_name))
+ return false;
+ result = (string_begins_with(package_name, "linux-image")
+ && (string_ends_with(package_name, "-dbg")
+ || string_ends_with(package_name, "-dbgsyms")));
+ }
+
+ return result;
+}
+
+/// The delete functor of a char buffer that has been created using
+/// malloc.
struct malloced_char_star_deleter
{
void
/// with the sub-string "whitelist". For instance
/// RHEL7_x86_64_whitelist.
///
-/// Then the content of the section is a set of function names, one
-/// name per line. Each function names is the name of a function
-/// whose changes are to be keept.
+/// Then the content of the section is a set of function or variable
+/// names, one name per line. Each function or variable name is the
+/// name of a function or a variable whose changes are to be keept.
///
-/// This function reads the white list and generate
-/// function_suppression_sptr (or several, if there are more than one
-/// section) that is added to a vector of suppressions.
+/// This function reads the white list and generates a
+/// function_suppression_sptr or variable_suppression_sptr (or
+/// several, if there are more than one section) that is added to a
+/// vector of suppressions.
///
/// @param abi_whitelist_path the path to the Kernel ABI whitelist.
///
if (!string_ends_with(section_name, "whitelist"))
continue;
- function_suppression_sptr suppr;
- string function_names_regexp;
+ function_suppression_sptr fn_suppr;
+ variable_suppression_sptr var_suppr;
+ string function_names_regexp, variable_names_regexp;
for (config::properties_type::const_iterator p =
(*s)->get_properties().begin();
p != (*s)->get_properties().end();
if (simple_property_sptr prop = is_simple_property(*p))
if (prop->has_empty_value())
{
- const string &function_name = prop->get_name();
- if (!function_name.empty())
+ const string &name = prop->get_name();
+ if (!name.empty())
{
if (!function_names_regexp.empty())
function_names_regexp += "|";
- function_names_regexp += "^" + function_name + "$";
+ function_names_regexp += "^" + name + "$";
+
+ if (!variable_names_regexp.empty())
+ variable_names_regexp += "|";
+ variable_names_regexp += "^" + name + "$";
}
}
}
if (!function_names_regexp.empty())
{
- suppr.reset(new function_suppression);
- suppr->set_label(section_name);
- suppr->set_name_not_regex_str(function_names_regexp);
- suppr->set_drops_artifact_from_ir(true);
- supprs.push_back(suppr);
+ fn_suppr.reset(new function_suppression);
+ fn_suppr->set_label(section_name);
+ fn_suppr->set_name_not_regex_str(function_names_regexp);
+ fn_suppr->set_drops_artifact_from_ir(true);
+ supprs.push_back(fn_suppr);
+ created_a_suppr = true;
+ }
+
+ if (!variable_names_regexp.empty())
+ {
+ var_suppr.reset(new variable_suppression);
+ var_suppr->set_label(section_name);
+ var_suppr->set_name_not_regex_str(variable_names_regexp);
+ var_suppr->set_drops_artifact_from_ir(true);
+ supprs.push_back(var_suppr);
created_a_suppr = true;
}
}
using abigail::tools_utils::file_type;
using abigail::tools_utils::make_path_absolute;
using abigail::tools_utils::base_name;
+using abigail::tools_utils::get_rpm_arch;
+using abigail::tools_utils::file_is_kernel_package;
using abigail::tools_utils::gen_suppr_spec_from_headers;
+using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelist;
using abigail::tools_utils::get_default_system_suppression_file_path;
using abigail::tools_utils::get_default_user_suppression_file_path;
using abigail::tools_utils::load_default_system_suppressions;
string debug_package2;
string devel_package1;
string devel_package2;
+ string kabi_whitelist_package;
size_t num_workers;
bool verbose;
bool drop_private_types;
bool show_harmless_changes;
bool show_locs;
bool show_added_syms;
+ bool show_symbols_not_referenced_by_debug_info;
bool show_added_binaries;
bool fail_if_no_debug_info;
bool show_identical_binaries;
vector<string> suppression_paths;
+ vector<string> kabi_whitelist_paths;
+ suppressions_type kabi_suppressions;
options(const string& program_name)
: prog_name(program_name),
show_harmless_changes(),
show_locs(true),
show_added_syms(true),
+ show_symbols_not_referenced_by_debug_info(true),
show_added_binaries(true),
fail_if_no_debug_info(),
show_identical_binaries()
/// Debug info package. Contains the debug info for the binaries
/// int he main packge.
KIND_DEBUG_INFO,
+ /// Contains kernel ABI whitelists
+ KIND_KABI_WHITELISTS,
/// Source package. Contains the source of the binaries in the
/// main package.
KIND_SRC
map<string, elf_file_sptr> path_elf_file_sptr_map_;
package_sptr debug_info_package_;
package_sptr devel_package_;
+ package_sptr kabi_whitelist_package_;
suppressions_type private_types_suppressions_;
public:
path(const string& s)
{path_ = s;}
+ /// Getter of the base name of the package.
+ ///
+ /// @return the base name of the package.
+ string
+ base_name() const
+ {
+ string name;
+ abigail::tools_utils::base_name(path(), name);
+ return name;
+ }
+
/// Getter for the path to the root dir where the packages are
/// extracted.
///
devel_package(const package_sptr& p)
{devel_package_ = p;}
+ /// Getter of the associated kernel abi whitelist package, if any.
+ ///
+ /// @return the associated kernel abi whitelist package.
+ const package_sptr
+ kabi_whitelist_package() const
+ {return kabi_whitelist_package_;}
+
+ /// Setter of the associated kernel abi whitelist package.
+ ///
+ /// @param p the new kernel abi whitelist package.
+ void
+ kabi_whitelist_package(const package_sptr& p)
+ {kabi_whitelist_package_ = p;}
+
/// Getter of the specifications to suppress change reports about
/// private types.
///
/// Arguments passed to the comparison tasks.
struct compare_args
{
- const elf_file elf1;
- const string& debug_dir1;
+ const elf_file elf1;
+ const string& debug_dir1;
const suppressions_type& private_types_suppr1;
- const elf_file elf2;
- const string& debug_dir2;
+ const elf_file elf2;
+ const string& debug_dir2;
const suppressions_type& private_types_suppr2;
- const options& opts;
+ const options& opts;
/// Constructor for compare_args, which is used to pass
/// information to the comparison threads.
<< " --no-default-suppression don't load any default "
"suppression specifications\n"
<< " --suppressions|--suppr <path> specify supression specification path\n"
+ << " --linux-kernel-abi-whitelist|--lkaw path to a "
+ "linux kernel abi whitelist\n"
+ << " --lkaw-pkg <path> path to a linux kernel abi whitelist package\n"
<< " --keep-tmp-files don't erase created temporary files\n"
<< " --dso-only compare shared libraries only\n"
<< " --no-linkage-name do not display linkage names of "
<< " --no-show-relative-offset-changes do not show relative"
" offset changes\n"
<< " --no-added-syms do not display added functions or variables\n"
+ << " --no-unreferenced-symbols do not display changes "
+ "about symbols not referenced by debug info\n"
<< " --no-added-binaries do not display added binaries\n"
<< " --no-abignore do not look for *.abignore files\n"
<< " --no-parallel do not execute in parallel\n"
if (!check_file(*i, cerr, opts.prog_name))
return false;
+ for (vector<string>::const_iterator i =
+ opts.kabi_whitelist_paths.begin();
+ i != opts.kabi_whitelist_paths.end();
+ ++i)
+ if (!check_file(*i, cerr, "abidiff"))
+ return false;
+
return true;
}
ctxt->show_added_vars(opts.show_added_syms);
ctxt->show_added_symbols_unreferenced_by_debug_info
(opts.show_added_syms);
+ ctxt->show_symbols_unreferenced_by_debug_info
+ (opts.show_symbols_not_referenced_by_debug_info);
if (!opts.show_harmless_changes)
ctxt->switch_categories_off
read_context_sptr c = create_read_context(elf1.path, &di_dir1, env.get(),
/*load_all_types=*/false);
add_read_context_suppressions(*c, priv_types_supprs1);
+ if (!opts.kabi_suppressions.empty())
+ add_read_context_suppressions(*c, opts.kabi_suppressions);
+
corpus1 = read_corpus_from_elf(*c, c1_status);
if (!(c1_status & abigail::dwarf_reader::STATUS_OK))
read_context_sptr c = create_read_context(elf2.path, &di_dir2, env.get(),
/*load_all_types=*/false);
add_read_context_suppressions(*c, priv_types_supprs2);
+
+ if (!opts.kabi_suppressions.empty())
+ add_read_context_suppressions(*c, opts.kabi_suppressions);
+
corpus2 = read_corpus_from_elf(*c, c2_status);
if (!(c2_status & abigail::dwarf_reader::STATUS_OK))
return suppr;
}
+/// While walking a file directory, check if a directory entry is a
+/// kabi whitelist of a particular architecture.
+///
+/// If it is, then save its file path in a vector of whitelists.
+///
+/// @param entry the directory entry to consider.
+///
+/// @param arch the architecture to consider.
+///
+/// @param whitelists out parameter. If @p entry is the whitelist we
+/// are looking for, add its path to this output parameter.
+static void
+maybe_collect_kabi_whitelists(const FTSENT *entry,
+ const string arch,
+ vector<string> &whitelists)
+{
+ if (entry == NULL
+ || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
+ || entry->fts_info == FTS_ERR
+ || entry->fts_info == FTS_NS)
+ return;
+
+ string path = entry->fts_path;
+ maybe_get_symlink_target_file_path(path, path);
+
+ string kabi_whitelist_name = "kabi_whitelist_" + arch;
+
+ if (string_ends_with(path, kabi_whitelist_name))
+ whitelists.push_back(path);
+}
+
+/// Get the kabi whitelist for a particular architecture under a given
+/// directory.
+///
+/// @param dir the directory to look at.
+///
+/// @param arch the architecture to consider.
+///
+/// @param whitelist_paths the vector where to add the whitelists
+/// found. Note that a whitelist is added to this parameter iff the
+/// function returns true.
+///
+/// @return true iff the function found a whitelist at least.
+static bool
+get_kabi_whitelists_from_arch_under_dir(const string& dir,
+ const string& arch,
+ vector<string>& whitelist_paths)
+{
+ bool is_ok = false;
+ char* paths[] = {const_cast<char*>(dir.c_str()), 0};
+
+ FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
+ if (!file_hierarchy)
+ return is_ok;
+
+ FTSENT *entry;
+ while ((entry = fts_read(file_hierarchy)))
+ maybe_collect_kabi_whitelists(entry, arch, whitelist_paths);
+
+ fts_close(file_hierarchy);
+
+ return true;
+}
+
+/// Find a kabi whitelist in a linux kernel RPM package.
+///
+/// Note that the linux kernel RPM package must have been extracted
+/// somewhere already.
+///
+/// This function then looks for the whitelist under the /lib/modules
+/// directory inside the extracted content of the package. If it
+/// finds it and saves its file path in the
+/// options::kabi_whitelist_paths data member.
+///
+/// @param pkg the linux kernel package to consider.
+///
+/// @param opts the options the program was invoked with.
+static bool
+maybe_handle_kabi_whitelist_pkg(const package& pkg, options &opts)
+{
+ if (opts.kabi_whitelist_package.empty()
+ || !opts.kabi_whitelist_paths.empty()
+ || !pkg.kabi_whitelist_package())
+ return false;
+
+ if (pkg.type() != abigail::tools_utils::FILE_TYPE_RPM)
+ return false;
+
+ string pkg_name = pkg.base_name();
+ bool is_linux_kernel_package = file_is_kernel_package(pkg_name, pkg.type());
+
+ if (!is_linux_kernel_package)
+ return false;
+
+ package_sptr kabi_wl_pkg = pkg.kabi_whitelist_package();
+ assert(kabi_wl_pkg);
+
+ if (!file_exists(kabi_wl_pkg->extracted_dir_path())
+ || !is_dir(kabi_wl_pkg->extracted_dir_path()))
+ return false;
+
+ string rpm_arch;
+ if (!get_rpm_arch(pkg_name, rpm_arch))
+ return false;
+
+ string kabi_wl_path = kabi_wl_pkg->extracted_dir_path();
+ kabi_wl_path += "/lib/modules";
+ vector<string> whitelist_paths;
+
+ get_kabi_whitelists_from_arch_under_dir(kabi_wl_path, rpm_arch,
+ whitelist_paths);
+
+ if (!whitelist_paths.empty())
+ {
+ std::sort(whitelist_paths.begin(), whitelist_paths.end());
+ opts.kabi_whitelist_paths.push_back(whitelist_paths.back());
+ }
+
+ return true;
+}
+
/// The task that performs the extraction of the content of a package
/// into a temporary directory.
///
/// Convenience typedef for a shared_ptr of @ref compare_task.
typedef shared_ptr<compare_task> compare_task_sptr;
-/// This function is sub-routine of create_maps_of_package_content.
+/// This function is a sub-routine of create_maps_of_package_content.
///
/// It's called during the walking of the directory tree containing
/// the extracted content of package. It's called with an entry of
///
/// @param opts the options of the current program.
///
+/// @param file_name_to_look_for if this parameter is set, the
+/// function only looks for a file name which name is the same as the
+/// value of this parameter.
+///
/// @param paths out parameter. This is the set of meaningful paths
/// of the current directory tree being analyzed. These paths are
/// those that are going to be involved in ABI comparison.
static void
maybe_update_vector_of_package_content(const FTSENT *entry,
options &opts,
+ const string& file_name_to_look_for,
vector<string>& paths)
{
if (entry == NULL
string path = entry->fts_path;
maybe_get_symlink_target_file_path(path, path);
+ if (!file_name_to_look_for.empty())
+ {
+ string name;
+ abigail::tools_utils::base_name(path, name);
+ if (name == file_name_to_look_for)
+ paths.push_back(path);
+ return;
+ }
+
if (guess_file_type(path) == abigail::tools_utils::FILE_TYPE_ELF)
paths.push_back(path);
else if (opts.abignore && string_ends_with(path, ".abignore"))
opts.suppression_paths.push_back(path);
}
+/// Walk a given directory to collect files that are "interesting" to
+/// analyze. By default, "interesting" means interesting from a
+/// kernel package analysis point of view.
+///
+/// @param dir the directory to walk.
+///
+/// @param file_name_to_look_for if this parameter is set, only a file
+/// with this name is going to be collected.
+///
+/// @param interesting_files out parameter. This parameter is
+/// populated with the interesting files found by the function iff the
+/// function returns true.
+///
+/// @return true iff the function completed successfully.
+static bool
+get_interesting_files_under_dir(const string dir,
+ const string& file_name_to_look_for,
+ options& opts,
+ vector<string>& interesting_files)
+{
+ bool is_ok = false;
+ char* paths[] = {const_cast<char*>(dir.c_str()), 0};
+
+ FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
+ if (!file_hierarchy)
+ return is_ok;
+
+ FTSENT *entry;
+ while ((entry = fts_read(file_hierarchy)))
+ maybe_update_vector_of_package_content(entry, opts,
+ file_name_to_look_for,
+ interesting_files);
+
+ fts_close(file_hierarchy);
+
+ is_ok = true;
+
+ return is_ok;
+}
+
/// Create maps of the content of a given package.
///
/// The maps contain relevant metadata about the content of the
<< package.extracted_dir_path()
<< " ...\n";
- bool is_ok = false;
- char* paths[] = {const_cast<char*>(package.extracted_dir_path().c_str()), 0};
-
- FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
- if (!file_hierarchy)
- return is_ok;
-
+ bool is_ok = true;
vector<string> elf_file_paths;
- FTSENT *entry;
- while ((entry = fts_read(file_hierarchy)))
- maybe_update_vector_of_package_content(entry, opts, elf_file_paths);
+
+ // if package is linux kernel package and its associated debug
+ // info package looks like a kernel debuginfo package, then try to
+ // go find the vmlinux file in that debug info file.
+ string pkg_name = package.base_name();
+ bool is_linux_kernel_package = file_is_kernel_package(pkg_name,
+ package.type());
+ if (is_linux_kernel_package)
+ if (package_sptr debuginfo_pkg = package.debug_info_package())
+ is_ok =
+ get_interesting_files_under_dir(debuginfo_pkg->extracted_dir_path(),
+ "vmlinux", opts, elf_file_paths);
+
+ is_ok &= get_interesting_files_under_dir(package.extracted_dir_path(),
+ /*file_name_to_look_for=*/"",
+ opts, elf_file_paths);
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
&& e->type != abigail::dwarf_reader::ELF_TYPE_EXEC
&& e->type != abigail::dwarf_reader::ELF_TYPE_PI_EXEC)
{
- if (opts.verbose)
- emit_prefix("abipkgdiff", cerr)
- << "skipping non-DSO non-executable file " << e->path << "\n";
- continue;
+ if (is_linux_kernel_package)
+ {
+ if (e->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
+ {
+ // This is a Linux Kernel module.
+ ;
+ }
+ }
+ else if (opts.verbose)
+ {
+ emit_prefix("abipkgdiff", cerr)
+ << "skipping non-DSO non-executable file "
+ << e->path
+ << "\n";
+ continue;
+ }
}
}
pkg_extraction_task_sptr main_pkg_extraction;
pkg_extraction_task_sptr dbg_extraction;
pkg_extraction_task_sptr devel_extraction;
+ pkg_extraction_task_sptr kabi_whitelist_extraction;
- size_t NUM_EXTRACTIONS = 3;
+ size_t NUM_EXTRACTIONS = 1;
main_pkg_extraction.reset(new pkg_extraction_task(pkg, opts));
if (package_sptr dbg_pkg = pkg.debug_info_package())
- dbg_extraction.reset(new pkg_extraction_task(*dbg_pkg, opts));
+ {
+ dbg_extraction.reset(new pkg_extraction_task(*dbg_pkg, opts));
+ ++NUM_EXTRACTIONS;
+ }
if (package_sptr devel_pkg = pkg.devel_package())
- devel_extraction.reset(new pkg_extraction_task(*devel_pkg, opts));
+ {
+ devel_extraction.reset(new pkg_extraction_task(*devel_pkg, opts));
+ ++NUM_EXTRACTIONS;
+ }
+
+ if (package_sptr kabi_wl_pkg = pkg.kabi_whitelist_package())
+ {
+ kabi_whitelist_extraction.reset(new pkg_extraction_task(*kabi_wl_pkg,
+ opts));
+ ++NUM_EXTRACTIONS;
+ }
size_t num_workers = (opts.parallel
? std::min(opts.num_workers, NUM_EXTRACTIONS)
: 1);
abigail::workers::queue extraction_queue(num_workers);
- // Perform the extraction of the 3 packages in parallel.
+ // Perform the extraction of the NUM_WORKERS packages in parallel.
extraction_queue.schedule_task(dbg_extraction);
extraction_queue.schedule_task(main_pkg_extraction);
extraction_queue.schedule_task(devel_extraction);
+ extraction_queue.schedule_task(kabi_whitelist_extraction);
// Wait for the extraction to be done.
extraction_queue.wait_for_workers_to_complete();
is_ok = create_maps_of_package_content(pkg, opts);
if (is_ok)
- maybe_create_private_types_suppressions(pkg, opts);
+ {
+ maybe_create_private_types_suppressions(pkg, opts);
+ maybe_handle_kabi_whitelist_pkg(pkg, opts);
+ }
return is_ok;
}
return abigail::tools_utils::ABIDIFF_ERROR;
}
+ string pkg_name = first_package.base_name();
+ bool is_linux_kernel_package =
+ abigail::tools_utils::file_is_kernel_package(pkg_name,
+ first_package.type());
+
// Setting debug-info path of libraries
string debug_dir1, debug_dir2, relative_debug_path = "/usr/lib/debug/";
if (first_package.debug_info_package()
abigail::workers::queue::tasks_type compare_tasks;
+ if (is_linux_kernel_package)
+ {
+ opts.show_symbols_not_referenced_by_debug_info = false;
+ 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.kabi_suppressions);
+ }
+
for (map<string, elf_file_sptr>::iterator it =
first_package.path_elf_file_sptr_map().begin();
it != first_package.path_elf_file_sptr_map().end();
if (iter != second_package.path_elf_file_sptr_map().end()
&& (iter->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
|| iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
- || iter->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC))
+ || iter->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC
+ || iter->second->type
+ == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
{
- compare_args_sptr args
- (new compare_args(*it->second,
- debug_dir1,
- first_package.private_types_suppressions(),
- *iter->second,
- debug_dir2,
- second_package.private_types_suppressions(),
- opts));
-
- compare_task_sptr t(new compare_task(args));
- compare_tasks.push_back(t);
-
+ if ((((iter->second->type
+ == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
+ && is_linux_kernel_package)
+ || (iter->second->type
+ != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)))
+ {
+ compare_args_sptr args
+ (new compare_args(*it->second,
+ debug_dir1,
+ first_package.private_types_suppressions(),
+ *iter->second,
+ debug_dir2,
+ second_package.private_types_suppressions(),
+ opts));
+
+ compare_task_sptr t(new compare_task(args));
+ compare_tasks.push_back(t);
+ }
second_package.path_elf_file_sptr_map().erase(iter);
}
- else
+ else if (iter == second_package.path_elf_file_sptr_map().end())
{
diff.removed_binaries.push_back(it->second);
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
opts.show_relative_offset_changes = false;
else if (!strcmp(argv[i], "--no-added-syms"))
opts.show_added_syms = false;
+ else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
+ opts.show_symbols_not_referenced_by_debug_info = false;
else if (!strcmp(argv[i], "--no-added-binaries"))
opts.show_added_binaries = false;
else if (!strcmp(argv[i], "--fail-no-dbg"))
opts.suppression_paths.push_back(argv[j]);
++i;
}
+ else if (!strcmp(argv[i], "--linux-kernel-abi-whitelist")
+ || !strcmp(argv[i], "--lkaw"))
+ {
+ int j = i + 1;
+ if (j >= argc)
+ {
+ opts.missing_operand = true;
+ opts.wrong_option = argv[i];
+ return true;
+ }
+ opts.kabi_whitelist_paths.push_back(argv[j]);
+ ++i;
+ }
+ else if (!strcmp(argv[i], "--lkaw-pkg"))
+ {
+ int j = i + 1;
+ if (j >= argc)
+ {
+ opts.missing_operand = true;
+ opts.wrong_option = argv[i];
+ return true;
+ }
+ opts.kabi_whitelist_package = argv[j];
+ ++i;
+ }
else if (!strcmp(argv[i], "--help")
|| !strcmp(argv[i], "-h"))
{
"devel_package2",
/*pkg_kind=*/package::KIND_DEVEL)));
+ if (!opts.kabi_whitelist_package.empty())
+ first_package->kabi_whitelist_package
+ (package_sptr(new package
+ (opts.kabi_whitelist_package,
+ "kabi_whitelist_package",
+ /*pkg_kind=*/package::KIND_KABI_WHITELISTS)));
+
string package_name;
switch (first_package->type())
{