/// @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)
+file_is_kernel_package(const string& file_name, file_type file_type)
{
bool result = false;
string package_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)
+file_is_kernel_debuginfo_package(const string& file_name, file_type file_type)
{
bool result = false;
string package_name;
return found_vmlinux;
}
+/// Find a vmlinux binary in a given directory tree.
+///
+/// @param from the directory tree to start looking from.
+///
+/// @param vmlinux_path output parameter
+///
+/// return true iff the vmlinux binary was found
+static bool
+find_vmlinux_path(const string& from,
+ string &vmlinux_path)
+{
+ 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;
+ break;
+ }
+ }
+
+ 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 debug_info_root_path the path to the directory under which
+/// debug info is going to be found for binaries under @p dist_root.
+///
/// @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,
+ const string& debug_info_root_path,
string& vmlinux_path,
- vector<string>& module_paths,
- string& debug_info_root_path)
+ vector<string>& module_paths)
{
if (!dir_exists(dist_root))
return false;
//
// 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.
+ // under the 'debug_info_root_path' directory and its content is
+ // accessible from <debug_info_root_path>/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";
+ debug_info_root = debug_info_root_path.empty()
+ ? dist_root
+ : debug_info_root_path;
+ debug_info_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;
+ string from = 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 path of the vmlinux binary under the given directory, that
+/// must have been generated either from extracting a package.
+///
+/// @param from the directory under which to look for.
+///
+/// @param vmlinux_path output parameter. The path of the vmlinux
+/// binary that was found.
+///
+/// @return true if the path to the vmlinux binary was found.
+bool
+get_vmlinux_path_from_kernel_dist(const string& from,
+ string& vmlinux_path)
+{
+ if (!dir_exists(from))
+ 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 dist_root = from;
+ if (dir_exists(dist_root + "/lib/modules"))
+ dist_root + "/lib/modules";
+
+ bool found = false;
+ if (find_vmlinux_path(dist_root, vmlinux_path))
+ found = true;
return found;
}
{
string debug_info_root_path;
return get_binary_paths_from_kernel_dist(dist_root,
+ debug_info_root_path,
vmlinux_path,
- module_paths,
- debug_info_root_path);
+ module_paths);
}
/// Walk a given directory and build an instance of @ref corpus_group
/// somewhere under that directory, but if it's not in there, its path
/// can be set to the @p vmlinux_path parameter.
///
+/// @param debug_info_root the directory under which debug info is to
+/// be found for binaries under director @p root.
+///
/// @param vmlinux_path the path to the vmlinux binary, if that binary
/// is not under the @p root directory. If this is empty, then it
/// means the vmlinux binary is to be found under the @p root
/// @param env the environment to create the corpus_group in.
corpus_group_sptr
build_corpus_group_from_kernel_dist_under(const string& root,
+ const string debug_info_root,
const string& vmlinux_path,
vector<string>& suppr_paths,
vector<string>& kabi_wl_paths,
string vmlinux = vmlinux_path;
corpus_group_sptr result;
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);
+ get_binary_paths_from_kernel_dist(root, debug_info_root, vmlinux, modules);
if (verbose)
std::cout << "DONE\n";
if (got_binary_paths)
{
shared_ptr<char> di_root =
- make_path_absolute(debug_info_root_path.c_str());
+ make_path_absolute(debug_info_root.c_str());
char *di_root_ptr = di_root.get();
abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_OK;
corpus_group_sptr group;
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::get_vmlinux_path_from_kernel_dist;
+using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
using abigail::tools_utils::load_default_system_suppressions;
using abigail::tools_utils::load_default_user_suppressions;
using abigail::tools_utils::abidiff_status;
using abigail::ir::corpus_sptr;
+using abigail::ir::corpus_group_sptr;
using abigail::comparison::diff_context;
using abigail::comparison::diff_context_sptr;
using abigail::comparison::compute_diff;
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);
+ {
+ // For a linux kernel package, no analysis is done. It'll be
+ // done later at comparison time by
+ // compare_prepared_linux_kernel_packages
+ is_ok = true;
+ if (opts.verbose)
+ emit_prefix("abipkgdiff", cerr)
+ << " Analysis of " << package.path() << " DONE\n";
+ return is_ok;
+ }
is_ok &= get_interesting_files_under_dir(package.extracted_dir_path(),
/*file_name_to_look_for=*/"",
erase_created_temporary_directories_parent(opts);
}
-/// Compare the ABI of two packages
+/// Compare the ABI of two prepared packages that contain userspace
+/// binaries.
+///
+/// A prepared package is a package which content has been extracted
+/// and mapped.
///
/// @param first_package the first package to consider.
///
///
/// @return the status of the comparison.
static abidiff_status
-compare(package& first_package, package& second_package,
- abi_diff& diff, options& opts)
+compare_prepared_userspace_packages(package& first_package,
+ package& second_package,
+ abi_diff& diff, options& opts)
{
- // Prepare (extract and analyze the contents) the packages and their
- // ancillary packages.
- //
- // Note that the package preparations happens in parallel.
- if (!prepare_packages(first_package, second_package, opts))
- {
- maybe_erase_temp_dirs(first_package, second_package, opts);
- return abigail::tools_utils::ABIDIFF_ERROR;
- }
-
+ abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
+ abigail::workers::queue::tasks_type compare_tasks;
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/";
relative_debug_path;
}
- abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
-
- 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();
&& (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_RELOCATABLE))
+ || iter->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
{
- if ((((iter->second->type
- == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
- && is_linux_kernel_package)
- || (iter->second->type
- != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)))
+ if (iter->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
{
compare_args_sptr args
(new compare_args(*it->second,
debug_dir2,
second_package.private_types_suppressions(),
opts));
-
compare_task_sptr t(new compare_task(args));
compare_tasks.push_back(t);
}
return status;
}
+/// Compare the ABI of two prepared packages that contain linux kernel
+/// binaries.
+///
+/// A prepared package is a package which content has been extracted
+/// and mapped.
+///
+/// @param first_package the first package to consider.
+///
+/// @param second_package the second package to consider.
+///
+/// @param options the options the current program has been called
+/// with.
+///
+/// @param diff out parameter. If this function returns true, then
+/// this parameter is set to the result of the comparison.
+///
+/// @param opts the options of the current program.
+///
+/// @return the status of the comparison.
+static abidiff_status
+compare_prepared_linux_kernel_packages(package& first_package,
+ package& second_package,
+ options& opts)
+{
+ abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
+ string pkg_name = first_package.base_name();
+
+ // Setting debug-info path of binaries
+ string debug_dir1, debug_dir2, relative_debug_path = "/usr/lib/debug/";
+ if (first_package.debug_info_package()
+ && second_package.debug_info_package())
+ {
+ debug_dir1 =
+ first_package.debug_info_package()->extracted_dir_path() +
+ relative_debug_path;
+ if (second_package.debug_info_package())
+ debug_dir2 =
+ second_package.debug_info_package()->extracted_dir_path() +
+ relative_debug_path;
+ }
+
+ string vmlinux_path1, vmlinux_path2;
+
+ if (!get_vmlinux_path_from_kernel_dist(debug_dir1, vmlinux_path1))
+ return abigail::tools_utils::ABIDIFF_ERROR;
+
+ if (!get_vmlinux_path_from_kernel_dist(debug_dir2, vmlinux_path2))
+ return abigail::tools_utils::ABIDIFF_ERROR;
+
+ string dist_root1 = first_package.extracted_dir_path();
+ string dist_root2 = second_package.extracted_dir_path();
+
+ abigail::ir::environment_sptr env(new abigail::ir::environment);
+ suppressions_type supprs;
+ corpus_group_sptr corpus1, corpus2;
+ corpus1 = build_corpus_group_from_kernel_dist_under(dist_root1,
+ debug_dir1,
+ vmlinux_path1,
+ opts.suppression_paths,
+ opts.kabi_whitelist_paths,
+ supprs,
+ opts.verbose,
+ env);
+
+ if (!corpus1)
+ return abigail::tools_utils::ABIDIFF_ERROR;
+
+ corpus2 = build_corpus_group_from_kernel_dist_under(dist_root2,
+ debug_dir2,
+ vmlinux_path2,
+ opts.suppression_paths,
+ opts.kabi_whitelist_paths,
+ supprs,
+ opts.verbose,
+ env);
+
+ if (!corpus2)
+ return abigail::tools_utils::ABIDIFF_ERROR;
+
+ diff_context_sptr diff_ctxt(new diff_context);
+ set_diff_context_from_opts(diff_ctxt, opts);
+
+ corpus_diff_sptr diff = compute_diff(corpus1, corpus2, diff_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 (status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
+ {
+ cout << "== Kernel ABI changes between packages '"
+ << first_package.path() << "' and '"
+ << second_package.path() << "' are: ===\n";
+ diff->report(cout);
+ cout << "== End of kernel ABI changes between packages '"
+ << first_package.path()
+ << "' and '"
+ << second_package.path() << "' ===\n\n";
+ }
+
+ return status;
+}
+
+/// Compare the ABI of two prepared packages.
+///
+/// A prepared package is a package which content has been extracted
+/// and mapped.
+///
+/// @param first_package the first package to consider.
+///
+/// @param second_package the second package to consider.
+///
+/// @param options the options the current program has been called
+/// with.
+///
+/// @param diff out parameter. If this function returns true, then
+/// this parameter is set to the result of the comparison.
+///
+/// @param opts the options of the current program.
+///
+/// @return the status of the comparison.
+static abidiff_status
+compare_prepared_package(package& first_package, package& second_package,
+ abi_diff& diff, options& opts)
+{
+ abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
+
+ if (abigail::tools_utils::file_is_kernel_package(first_package.base_name(),
+ first_package.type()))
+ {
+ opts.show_symbols_not_referenced_by_debug_info = false;
+ status = compare_prepared_linux_kernel_packages(first_package,
+ second_package,
+ opts);
+ }
+ else
+ status = compare_prepared_userspace_packages(first_package,
+ second_package,
+ diff, opts);
+
+ return status;
+}
+
+/// Compare the ABI of two packages
+///
+/// @param first_package the first package to consider.
+///
+/// @param second_package the second package to consider.
+///
+/// @param options the options the current program has been called
+/// with.
+///
+/// @param diff out parameter. If this function returns true, then
+/// this parameter is set to the result of the comparison.
+///
+/// @param opts the options of the current program.
+///
+/// @return the status of the comparison.
+static abidiff_status
+compare(package& first_package, package& second_package,
+ abi_diff& diff, options& opts)
+{
+ // Prepare (extract and analyze the contents) the packages and their
+ // ancillary packages.
+ //
+ // Note that the package preparations happens in parallel.
+ if (!prepare_packages(first_package, second_package, opts))
+ {
+ maybe_erase_temp_dirs(first_package, second_package, opts);
+ return abigail::tools_utils::ABIDIFF_ERROR;
+ }
+
+ return compare_prepared_package(first_package, second_package, diff, opts);
+}
+
/// Compare the ABI of two packages.
///
/// @param first_package the first package to consider.
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
+
+ if (file_is_kernel_package(first_package->base_name(),
+ abigail::tools_utils::FILE_TYPE_RPM)
+ || file_is_kernel_package(second_package->base_name(),
+ abigail::tools_utils::FILE_TYPE_RPM))
+ {
+ if (file_is_kernel_package(first_package->base_name(),
+ abigail::tools_utils::FILE_TYPE_RPM)
+ != file_is_kernel_package(second_package->base_name(),
+ abigail::tools_utils::FILE_TYPE_RPM))
+ {
+ emit_prefix("abipkgdiff", cerr)
+ << "a Linux kernel package can only be compared to another "
+ "Linux kernel package\n";
+ return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+ | abigail::tools_utils::ABIDIFF_ERROR);
+ }
+
+ if (!first_package->debug_info_package()
+ || !second_package->debug_info_package())
+ {
+ emit_prefix("abipkgdiff", cerr)
+ << "a Linux Kernel package must be accompanied with its "
+ "debug info package\n";
+ return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+ | abigail::tools_utils::ABIDIFF_ERROR);
+ }
+ }
+
break;
case abigail::tools_utils::FILE_TYPE_DEB: