diff --git a/configure.ac b/configure.ac index a009fbc6..d2896020 100644 --- a/configure.ac +++ b/configure.ac @@ -201,6 +201,12 @@ AC_ARG_ENABLE(ctf, ENABLE_CTF=$enableval, ENABLE_CTF=auto) +dnl check if user has enabled BTF code +AC_ARG_ENABLE(btf, + AS_HELP_STRING([--enable-btf=yes|no], + [disable support of btf files)]), + ENABLE_BTF=$enableval, + ENABLE_BTF=auto) dnl ************************************************* dnl check for dependencies dnl ************************************************* @@ -339,6 +345,77 @@ if test x$ENABLE_CTF != xno; then fi fi +dnl configure BTF usage +BPF_LIBS= +if test x$ENABLE_BTF != xno; then + AC_CHECK_HEADER([bpf/btf.h], + [ENABLE_BTF=yes], + [AC_MSG_NOTICE([could not find bpf/btf.h])]) + if test x$ENABLE_BTF = xyes; then + AC_MSG_NOTICE([enable BTF support]) + ENABLE_BTF=yes + AC_DEFINE([WITH_BTF], 1, + [Defined if user enabled BTF usage]) + BPF_LIBS=-lbpf + else + AC_MSG_NOTICE([BTF support was disabled]) + ENABLE_BTF=no + fi + +dnl Test if various functions and structs are present. + + if test x$ENABLE_BTF = xyes; then + dnl Test if struct btf_enum64 is present. + AC_CHECK_TYPE([struct btf_enum64], + [HAVE_BTF_ENUM64=yes], + [HAVE_BTF_ENUM64=no], + [#include ]) + + if test x$HAVE_BTF_ENUM64 = xyes; then + AC_DEFINE([WITH_BTF_ENUM64], 1, [struct btf_enum64 is present]) + fi + + dnl Test if btf__get_nr_types is present + AC_CHECK_DECL([btf__get_nr_types], + [HAVE_BTF__GET_NR_TYPES=yes], + [HAVE_BTF__GET_NR_TYPES=no], + [#include ]) + + if test x$HAVE_BTF__GET_NR_TYPES = xyes; then + AC_DEFINE(WITH_BTF__GET_NR_TYPES, 1, [The function btf__get_nr_types is present]) + fi + + dnl Test if btf__type_cnt is present + AC_CHECK_DECL([btf__type_cnt], + [HAVE_BTF__TYPE_CNT=yes], + [HAVE_BTF__TYPE_CNT=no], + [#include ]) + if test x$HAVE_BTF__TYPE_CNT = xyes; then + AC_DEFINE(WITH_BTF__TYPE_CNT, 1, [The function btf__type_cnt is present]) + fi + + dnl Test if BTF_KIND_TYPE_TAG exists + AC_CHECK_DECL([int kind = BTF_KIND_TYPE_TAG], + [HAVE_BTF_KIND_TYPE_TAG=yes], + [HAVE_BTF_KIND_TYPE_TAG=no], + [#include ]) + if test x$HAVE_BTF_KIND_TYPE_TAG = xyes; then + AC_DEFINE([WITH_BTF_KIND_TYPE_TAG], 1, + [The BTF_KIND_TYPE_TAG enumerator is present]) + fi + + dnl Test if BTF_KIND_DECL_TAG exists + AC_CHECK_DECL([int kind = BTF_KIND_DECL_TAG], + [HAVE_BTF_KIND_DECL_TAG=yes], + [HAVE_BTF_KIND_DECL_TAG=no], + [#include ]) + if test x$HAVE_BTF_KIND_DECL_TAG = xyes; then + AC_DEFINE([WITH_BTF_KIND_DECL_TAG], 1, + [The BTF_KIND_DECL_TAG enumerator is present]) + fi + fi +fi + dnl Check for dependency: libxml LIBXML2_VERSION=2.6.22 PKG_CHECK_MODULES(XML, libxml-2.0 >= $LIBXML2_VERSION) @@ -741,7 +818,7 @@ AX_VALGRIND_CHECK dnl Set the list of libraries libabigail depends on -DEPS_LIBS="$XML_LIBS $ELF_LIBS $DW_LIBS $CTF_LIBS" +DEPS_LIBS="$XML_LIBS $ELF_LIBS $DW_LIBS $CTF_LIBS $BPF_LIBS" AC_SUBST(DEPS_LIBS) if test x$ABIGAIL_DEVEL != x; then @@ -782,6 +859,7 @@ fi dnl Set a few Automake conditionals AM_CONDITIONAL([CTF_READER],[test "x$ENABLE_CTF" = "xyes"]) +AM_CONDITIONAL([BTF_READER],[test "x$ENABLE_BTF" = "xyes"]) dnl Set the level of C++ standard we use. CXXFLAGS="$CXXFLAGS -std=$CXX_STANDARD" @@ -1092,6 +1170,7 @@ AC_MSG_NOTICE([ Enable fedabipkgdiff : ${ENABLE_FEDABIPKGDIFF} Enable python 3 : ${ENABLE_PYTHON3} Enable CTF front-end : ${ENABLE_CTF} + Enable BTF front-end : ${ENABLE_BTF} Enable running tests under Valgrind : ${enable_valgrind} Enable build with -fsanitize=address : ${ENABLE_ASAN} Enable build with -fsanitize=memory : ${ENABLE_MSAN} diff --git a/doc/manuals/abidiff.rst b/doc/manuals/abidiff.rst index dd357fba..2debc20d 100644 --- a/doc/manuals/abidiff.rst +++ b/doc/manuals/abidiff.rst @@ -16,8 +16,9 @@ For a comprehensive ABI change report between two input shared libraries that includes changes about function and variable sub-types, ``abidiff`` uses by default, debug information in `DWARF`_ format, if present, otherwise it compares interfaces using debug information in -`CTF`_ format, if present, finally, if neither is found, it uses only -`ELF`_ symbols to report which of them were added or removed. +`CTF`_ or `BTF`_ formats, if present. Finally, if no debug info in +these formats is found, it only considers `ELF`_ symbols and report +about their addition or removal. .. include:: tools-use-libabigail.txt @@ -605,6 +606,11 @@ Options When comparing binaries, extract ABI information from `CTF`_ debug information, if present. + * ``--btf`` + + When comparing binaries, extract ABI information from `BTF`_ debug + information, if present. + * ``--stats`` Emit statistics about various internal things. @@ -830,6 +836,7 @@ Usage examples .. _ELF: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format .. _DWARF: http://www.dwarfstd.org .. _CTF: https://raw.githubusercontent.com/wiki/oracle/binutils-gdb/files/ctf-spec.pdf +.. _BTF: https://docs.kernel.org/bpf/btf.html .. _ODR: https://en.wikipedia.org/wiki/One_Definition_Rule .. _One Definition Rule: https://en.wikipedia.org/wiki/One_Definition_Rule .. _DWZ: https://sourceware.org/dwz diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst index 87db1890..93eced71 100644 --- a/doc/manuals/abidw.rst +++ b/doc/manuals/abidw.rst @@ -21,10 +21,10 @@ functions and variables, along with a complete representation of their types. To generate either ABI or KMI representation, by default ``abidw`` -uses debug information in `DWARF`_ format, if present, otherwise it -looks for debug information in `CTF`_ format, if present, finally, if -neither is found, it uses only `ELF`_ symbols to report which of them -were added or removed. +uses debug information in the `DWARF`_ format, if present, otherwise +it looks for debug information in `CTF`_ or `BTF`_formats, if present. +Finally, if no debug info in these formats is found, it only considers +`ELF`_ symbols and report about their addition or removal. .. include:: tools-use-libabigail.txt @@ -389,6 +389,7 @@ standard `here .. _GNU: http://www.gnu.org .. _Linux Kernel: https://kernel.org/ .. _CTF: https://raw.githubusercontent.com/wiki/oracle/binutils-gdb/files/ctf-spec.pdf +.. _BTF: https://docs.kernel.org/bpf/btf.html .. _ODR: https://en.wikipedia.org/wiki/One_Definition_Rule .. _One Definition Rule: https://en.wikipedia.org/wiki/One_Definition_Rule .. _DWZ: https://sourceware.org/dwz diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst index 9d7a3973..d448ad3e 100644 --- a/doc/manuals/abipkgdiff.rst +++ b/doc/manuals/abipkgdiff.rst @@ -13,17 +13,17 @@ binaries. For a comprehensive ABI change report that includes changes about function and variable sub-types, the two input packages must be accompanied with their debug information packages that contain debug -information either in `DWARF`_ or in `CTF`_ formats. Please note -however that some packages contain binaries that embed the debug +information either in `DWARF`_, `CTF`_ or in `BTF`_ formats. Please +note however that some packages contain binaries that embed the debug information directly in a section of said binaries. In those cases, obviously, no separate debug information package is needed as the tool will find the debug information inside the binaries. By default, ``abipkgdiff`` uses debug information in `DWARF`_ format, if present, otherwise it compares binaries interfaces using debug -information in `CTF`_ format, if present, finally, if neither is -found, it uses only `ELF`_ symbols to report which of them were added -or removed. +information in `CTF`_ or in `BTF`_ formats, if present. Finally, if no +debug info in these formats is found, it only considers `ELF`_ symbols +and report about their addition or removal. .. include:: tools-use-libabigail.txt @@ -554,6 +554,11 @@ Options This is used to compare packages with `CTF`_ debug information, if present. + * ``--btf`` + + This is used to compare packages with `BTF`_ debug information, + if present. + .. _abipkgdiff_return_value_label: Return value @@ -573,6 +578,7 @@ In the later case, the value of the exit code is the same as for the .. _tar: https://en.wikipedia.org/wiki/Tar_%28computing%29 .. _DWARF: http://www.dwarfstd.org .. _CTF: https://raw.githubusercontent.com/wiki/oracle/binutils-gdb/files/ctf-spec.pdf +.. _BTF: https://docs.kernel.org/bpf/btf.html .. _Development Package: https://fedoraproject.org/wiki/Packaging:Guidelines?rd=Packaging/Guidelines#Devel_Packages .. _ODR: https://en.wikipedia.org/wiki/One_Definition_Rule .. _One Definition Rule: https://en.wikipedia.org/wiki/One_Definition_Rule diff --git a/doc/manuals/kmidiff.rst b/doc/manuals/kmidiff.rst index a27d2456..40358b92 100644 --- a/doc/manuals/kmidiff.rst +++ b/doc/manuals/kmidiff.rst @@ -74,10 +74,11 @@ functions and variables) between the Kernel and its modules. In practice, though, some users might want to compare a subset of the those interfaces. -By default, ``kmidiff`` uses debug information in `DWARF`_ format, -if present, otherwise it compares interfaces using debug information -in `CTF`_ format, if present, finally, if neither is found, it uses -only `ELF`_ symbols to report which were added or removed. +By default, ``kmidiff`` uses debug information in the `DWARF`_ debug +info format, if present, otherwise it compares interfaces using `CTF`_ +or `BTF`_ debug info formats, if present. Finally, if no debug info +in these formats is found, it only considers `ELF`_ symbols and report +about their addition or removal. Users can then define a "white list" of the interfaces to compare. Such a white list is a just a file in the "INI" format that looks @@ -179,8 +180,13 @@ Options * ``--ctf`` - Extract ABI information from `CTF`_ debug information, if present in - the Kernel and Modules. + Extract ABI information from `CTF`_ debug information, if present, + in the Kernel and Modules. + + * ``--btf`` + + Extract ABI information from `BTF`_ debug information, if present, + in the Kernel and Modules. * ``--impacted-interfaces | -i`` @@ -249,3 +255,4 @@ Options .. _Linux Kernel: https://kernel.org .. _DWARF: http://www.dwarfstd.org .. _CTF: https://raw.githubusercontent.com/wiki/oracle/binutils-gdb/files/ctf-spec.pdf +.. _BTF: https://docs.kernel.org/bpf/btf.html diff --git a/include/Makefile.am b/include/Makefile.am index 6b7f1e49..22f7151d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -34,4 +34,8 @@ if CTF_READER pkginclude_HEADERS += abg-ctf-reader.h endif +if BTF_READER +pkginclude_HEADERS += abg-btf-reader.h +endif + EXTRA_DIST = abg-version.h.in diff --git a/include/abg-btf-reader.h b/include/abg-btf-reader.h new file mode 100644 index 00000000..c85ff9bd --- /dev/null +++ b/include/abg-btf-reader.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// -*- Mode: C++ -*- +// +// Copyright (C) 2022 Red Hat, Inc. +// +// Author: Dodji Seketeli + +/// @file +/// +/// This file contains the declarations of the front-end to analyze the +/// BTF information contained in an ELF file. + +#ifndef __ABG_BTF_READER_H__ +#define __ABG_BTF_READER_H__ + +#include "abg-elf-based-reader.h" + +namespace abigail +{ + +namespace btf +{ + +elf_based_reader_sptr +create_reader(const std::string& elf_path, + const vector& debug_info_root_paths, + environment& env, + bool load_all_types = false, + bool linux_kernel_mode = false); + +}//end namespace btf +}//end namespace abigail + +#endif //__ABG_BTF_READER_H__ diff --git a/include/abg-corpus.h b/include/abg-corpus.h index 90419694..fabda0f9 100644 --- a/include/abg-corpus.h +++ b/include/abg-corpus.h @@ -48,7 +48,8 @@ public: ELF_ORIGIN = 1 << 1, DWARF_ORIGIN = 1 << 2, CTF_ORIGIN = 1 << 3, - LINUX_KERNEL_BINARY_ORIGIN = 1 << 4 + BTF_ORIGIN = 1 << 4, + LINUX_KERNEL_BINARY_ORIGIN = 1 << 5 }; private: diff --git a/include/abg-elf-reader.h b/include/abg-elf-reader.h index 2d25b500..9e370c0b 100644 --- a/include/abg-elf-reader.h +++ b/include/abg-elf-reader.h @@ -97,6 +97,9 @@ class reader : public fe_iface bool has_ctf_debug_info() const; + bool + has_btf_debug_info() const; + const Dwarf* alternate_dwarf_debug_info() const; @@ -118,6 +121,9 @@ class reader : public fe_iface const Elf_Scn* find_alternate_ctf_section() const; + const Elf_Scn* + find_btf_section() const; + const vector& dt_needed()const; diff --git a/include/abg-tools-utils.h b/include/abg-tools-utils.h index ff7618fe..70745b0f 100644 --- a/include/abg-tools-utils.h +++ b/include/abg-tools-utils.h @@ -41,6 +41,8 @@ bool file_has_dwarf_debug_info(const string& elf_file_path, const vector& debug_info_root_paths); bool file_has_ctf_debug_info(const string& elf_file_path, const vector& debug_info_root_paths); +bool file_has_btf_debug_info(const string& elf_file_path, + const vector& debug_info_root_paths); bool is_dir(const string&); bool dir_exists(const string&); bool dir_is_empty(const string &); diff --git a/src/Makefile.am b/src/Makefile.am index 70cc04a5..3044c136 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,10 @@ if CTF_READER libabigail_la_SOURCES += abg-ctf-reader.cc endif +if BTF_READER +libabigail_la_SOURCES += abg-btf-reader.cc +endif + libabigail_la_LIBADD = $(DEPS_LIBS) $(FTS_LIBS) libabigail_la_LDFLAGS = -lpthread -Wl,--as-needed -no-undefined -version-info $(LIBABIGAIL_SO_CURRENT):$(LIBABIGAIL_SO_REVISION):$(LIBABIGAIL_SO_AGE) diff --git a/src/abg-btf-reader.cc b/src/abg-btf-reader.cc new file mode 100644 index 00000000..b3bcfe2f --- /dev/null +++ b/src/abg-btf-reader.cc @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// -*- Mode: C++ -*- +// +// Copyright (C) 2022 Red Hat, Inc. +// +// Author: Dodji Seketeli + +/// @file +/// +/// This file contains the definitions of the front-end to analyze the +/// BTF information contained in an ELF file. + +#include "abg-internal.h" + +#ifdef WITH_BTF + +#include +#include +#include + +#include "abg-elf-helpers.h" + +// +ABG_BEGIN_EXPORT_DECLARATIONS + +#include "abg-btf-reader.h" +#include "abg-ir.h" +#include "abg-tools-utils.h" + +ABG_END_EXPORT_DECLARATIONS +// + +namespace abigail +{ +using namespace ir; + +namespace btf +{ + +class reader; + +/// A convenience typedef for a shared pointer to +/// abigail::btf::reader. +typedef shared_ptr reader_sptr; + +static const char* +btf_offset_to_string(const ::btf* btf, uint32_t offset) +{ + if (!offset) + return "__anonymous__"; + return btf__name_by_offset(btf, offset) ?: "(invalid string offset)"; +} + +/// A convenience typedef of a map that associates a btf type id to a +/// libabigail ABI artifact. +typedef std::unordered_map +btf_type_id_to_abi_artifact_map_type; + +/// The BTF front-end abstraction type. +class reader : public elf_based_reader +{ + ::btf* btf_handle_ = nullptr; + translation_unit_sptr cur_tu_; + vector types_to_canonicalize_; + btf_type_id_to_abi_artifact_map_type btf_type_id_to_artifacts_; + + /// Getter of the handle to the BTF data as returned by libbpf. + /// + /// @return the handle to the BTF data as returned by libbpf. + ::btf* + btf_handle() + { + if (btf_handle_ == nullptr) + { + btf_handle_ = btf__parse(corpus_path().c_str(), nullptr); + if (!btf_handle_) + std::cerr << "Could not parse BTF information from file '" + << corpus_path().c_str() << "'" << std::endl; + } + return btf_handle_; + } + + /// Getter of the environment of the current front-end. + /// + /// @return The environment of the current front-end. + environment& + env() + {return options().env;} + + /// Getter of the environment of the current front-end. + /// + /// @return The environment of the current front-end. + const environment& + env() const + {return const_cast(this)->env();} + + /// Getter of the current translation unit being built. + /// + /// Actually, BTF doesn't keep track of the translation unit each + /// ABI artifact originates from. So an "artificial" translation + /// unit is built. It contains all the ABI artifacts of the binary. + /// + /// @return The current translation unit being built. + translation_unit_sptr& + cur_tu() + {return cur_tu_;} + + /// Getter of the current translation unit being built. + /// + /// Actually, BTF doesn't keep track of the translation unit each + /// ABI artifact originates from. So an "artificial" translation + /// unit is built. It contains all the ABI artifacts of the binary. + /// + /// @return The current translation unit being built. + const translation_unit_sptr& + cur_tu() const + {return cur_tu_;} + + /// Getter of the current translation unit being built. + /// + /// Actually, BTF doesn't keep track of the translation unit each + /// ABI artifact originates from. So an "artificial" translation + /// unit is built. It contains all the ABI artifacts of the binary. + /// + /// @return The current translation unit being built. + void + cur_tu(const translation_unit_sptr& tu) + {cur_tu_ = tu;} + + /// Getter of the map that associates a BTF type ID to an ABI + /// artifact. + /// + /// @return The map that associates a BTF type ID to an ABI + /// artifact. + btf_type_id_to_abi_artifact_map_type& + btf_type_id_to_artifacts() + {return btf_type_id_to_artifacts_;} + + /// Getter of the map that associates a BTF type ID to an ABI + /// artifact. + /// + /// @return The map that associates a BTF type ID to an ABI + /// artifact. + const btf_type_id_to_abi_artifact_map_type& + btf_type_id_to_artifacts() const + {return btf_type_id_to_artifacts_;} + + /// Get the ABI artifact that is associated to a given BTF type ID. + /// + /// If no ABI artifact is associated to the BTF type id, then return + /// nil. + /// + /// @return the ABI artifact that is associated to a given BTF type + /// id. + type_or_decl_base_sptr + lookup_artifact_from_btf_id(int btf_id) + { + auto i = btf_type_id_to_artifacts().find(btf_id); + if (i != btf_type_id_to_artifacts().end()) + return i->second; + return type_or_decl_base_sptr(); + } + + /// Associate an ABI artifact to a given BTF type ID. + /// + /// @param artifact the ABI artifact to consider. + /// + /// @param btf_type_id the BTF type ID to associate to @p artifact. + void + associate_artifact_to_btf_type_id(const type_or_decl_base_sptr& artifact, + int btf_type_id) + {btf_type_id_to_artifacts()[btf_type_id] = artifact;} + + /// Schecule a type for canonicalization at the end of the debug + /// info loading. + /// + /// @param t the type to schedule. + void + schedule_type_for_canonocalization(const type_base_sptr& t) + {types_to_canonicalize_.push_back(t);} + + /// Canonicalize all the types scheduled for canonicalization using + /// schedule_type_for_canonocalization(). + void + canonicalize_types() + { + for (auto t : types_to_canonicalize_) + canonicalize(t); + } + + uint64_t + nr_btf_types() const + { +#ifdef WITH_BTF__GET_NR_TYPES +#define GET_NB_TYPES btf__get_nr_types +#endif + +#ifdef WITH_BTF__TYPE_CNT +#undef GET_NB_TYPES +#define GET_NB_TYPES btf__type_cnt +#endif + +#ifndef GET_NB_TYPES + ABG_ASSERT_NOT_REACHED; + return 0; +#endif + + return GET_NB_TYPES(const_cast(this)->btf_handle()); + } + +protected: + reader() = delete; + + /// Initializer of the current instance of @ref btf::reader. + /// + /// This frees the resources used by the current instance of @ref + /// btf::reader and gets it ready to analyze another ELF + /// file. + /// + /// @param elf_path the path to the ELF file to read from. + /// + /// @param debug_info_root_paths the paths where to look for + /// seperate debug info. + /// + /// @param load_all_types if true, then load all the types described + /// in the binary, rather than loading only the types reachable from + /// the exported decls. + /// + /// @param linux_kernel_mode + void + initialize(const string& elf_path, + const vector& debug_info_root_paths, + bool load_all_types, + bool linux_kernel_mode) + { + reset(elf_path, debug_info_root_paths); + btf__free(btf_handle_); + options().load_all_types = load_all_types; + options().load_in_linux_kernel_mode = linux_kernel_mode; + } + + /// Constructor of the btf::reader type. + /// + /// @param elf_path the path to the ELF file to analyze. + /// + /// @param debug_info_root_paths the set of directory where to look + /// debug info from, for cases where the debug is split. + /// + /// @param environment the environment of the current front-end. + /// + /// @param load_all_types if true load all the types described by + /// the BTF debug info, as opposed to loading only the types + /// reachable from the decls that are defined and exported. + /// + /// @param linux_kernel_mode if true, then consider the binary being + /// analyzed as a linux kernel binary. + reader(const string& elf_path, + const vector& debug_info_root_paths, + environment& environment, + bool load_all_types, + bool linux_kernel_mode) + : elf_based_reader(elf_path, + debug_info_root_paths, + environment) + { + initialize(elf_path, debug_info_root_paths, + load_all_types, linux_kernel_mode); + } + +public: + + /// Constructor of the btf::reader type. + /// + /// @param elf_path the path to the ELF file to analyze. + /// + /// @param debug_info_root_paths the set of directory where to look + /// debug info from, for cases where the debug is split. + /// + /// @param environment the environment of the current front-end. + /// + /// @param load_all_types if true load all the types described by + /// the BTF debug info, as opposed to loading only the types + /// reachable from the decls that are defined and exported. + /// + /// @param linux_kernel_mode if true, then consider the binary being + /// analyzed as a linux kernel binary. + static btf::reader_sptr + create(const string& elf_path, + const vector& debug_info_root_paths, + environment& environment, + bool load_all_types, + bool linux_kernel_mode) + { + reader_sptr result(new reader(elf_path, debug_info_root_paths, environment, + load_all_types, linux_kernel_mode)); + return result; + } + + /// Destructor of the btf::reader type. + ~reader() + { + btf__free(btf_handle_); + } + + /// Read the ELF information as well as the BTF type information to + /// build an ABI corpus. + /// + /// @param status output parameter. The status of the analysis. + /// + /// @return the resulting ABI corpus. + corpus_sptr + read_corpus(status& status) + { + // Read the properties of the ELF file. + elf::reader::read_corpus(status); + + corpus::origin origin = corpus()->get_origin(); + origin |= corpus::BTF_ORIGIN; + corpus()->set_origin(origin); + + if ((status & STATUS_NO_SYMBOLS_FOUND) + || !(status & STATUS_OK)) + // Either we couldn't find ELF symbols or something went badly + // wrong. There is nothing we can do with this ELF file. Bail + // out. + return corpus_sptr(); + + if (find_btf_section() == nullptr) + status |= STATUS_DEBUG_INFO_NOT_FOUND; + + read_debug_info_into_corpus(); + + status |= STATUS_OK; + + return corpus(); + } + + /// Read the BTF debug info to construct the ABI corpus. + /// + /// @return the resulting ABI corpus. + corpus_sptr + read_debug_info_into_corpus() + { + btf_handle(); + + translation_unit_sptr artificial_tu + (new translation_unit(env(), "", /*address_size=*/64)); + corpus()->add(artificial_tu); + cur_tu(artificial_tu); + + int number_of_types = nr_btf_types(); + int first_type_id = 1; + + // Let's cycle through whatever is described in the BTF section + // and emit libabigail IR for it. + for (int type_id = first_type_id; + type_id < number_of_types; + ++type_id) + { + // Build IR nodes only for decls (functions and variables) + // that have associated ELF symbols that are publicly defined + // and exported, unless the user asked to load all types. + + bool do_construct_ir_node = false; + + const btf_type* t = btf__type_by_id(btf_handle(), type_id); + string name; + if (t->name_off) + name = btf_offset_to_string(btf_handle(), t->name_off); + + int kind = btf_kind(t); + if (kind == BTF_KIND_FUNC) + { + ABG_ASSERT(!name.empty()); + if (btf_vlen(t) == BTF_FUNC_GLOBAL + || btf_vlen(t) == BTF_FUNC_EXTERN + || function_symbol_is_exported(name)) + do_construct_ir_node = true; + } + else if (kind == BTF_KIND_VAR) + { + ABG_ASSERT(!name.empty()); + if (btf_vlen(t) == BTF_VAR_GLOBAL_ALLOCATED + || btf_vlen(t) == BTF_VAR_GLOBAL_EXTERN + || variable_symbol_is_exported(name)) + do_construct_ir_node = true; + } + else if (options().load_all_types) + do_construct_ir_node = true; + + if (do_construct_ir_node) + build_ir_node_from_btf_type(type_id); + } + + canonicalize_types(); + + return corpus(); + } + + /// Build an abigail IR node for a given type described by a BTF + /// type ID. The node is added to the ABI corpus. + /// + /// @param type_id the ID of the type to build and IR node for. + /// + /// @return the IR node representing the type @p type_id. + type_or_decl_base_sptr + build_ir_node_from_btf_type(int type_id) + { + type_or_decl_base_sptr result; + const btf_type *t = nullptr; + + if ((result = lookup_artifact_from_btf_id(type_id))) + return result; + + if (type_id == 0) + result = build_ir_node_for_void_type(); + else + t = btf__type_by_id(btf_handle(), type_id); + + if (!result) + { + ABG_ASSERT(t); + int type_kind = btf_kind(t); + + switch(type_kind) + { + case BTF_KIND_INT/* Integer */: + result = build_int_type(type_id); + break; + + case BTF_KIND_FLOAT/* Floating point */: + result = build_float_type(type_id); + break; + + case BTF_KIND_TYPEDEF/* Typedef*/: + result = build_typedef_type(type_id); + break; + + case BTF_KIND_PTR/* Pointer */: + result = build_pointer_type(type_id); + break; + + case BTF_KIND_ARRAY/* Array */: + result = build_array_type(type_id); + break; + + case BTF_KIND_ENUM/* Enumeration up to 32-bit values */: +#ifdef WITH_BTF_ENUM64 + case BTF_KIND_ENUM64/* Enumeration up to 64-bit values */: +#endif + result = build_enum_type(type_id); + break; + + case BTF_KIND_STRUCT/* Struct */: + case BTF_KIND_UNION/* Union */: + result = build_class_or_union_type(type_id); + break; + + case BTF_KIND_FWD/* Forward */: + result = build_class_or_union_type(type_id); + break; + + case BTF_KIND_CONST/* Const */: + case BTF_KIND_VOLATILE/* Volatile */: + case BTF_KIND_RESTRICT/* Restrict */: + result = build_qualified_type(type_id); + break; + + case BTF_KIND_FUNC/* Function */: + result = build_function_decl(type_id); + break; + + case BTF_KIND_FUNC_PROTO/* Function Proto */: + result = build_function_type(type_id); + break; + + case BTF_KIND_VAR/* Variable */: + result = build_variable_decl(type_id); + break; + +#ifdef WITH_BTF_KIND_TYPE_TAG + case BTF_KIND_TYPE_TAG/* Type Tag */: +#endif +#ifdef WITH_BTF_KIND_DECL_TAG + case BTF_KIND_DECL_TAG/* Decl Tag */: +#endif + case BTF_KIND_DATASEC/* Section */: + case BTF_KIND_UNKN/* Unknown */: + default: + ABG_ASSERT_NOT_REACHED; + break; + } + } + + add_decl_to_scope(is_decl(result), cur_tu()->get_global_scope()); + + if (type_base_sptr type = is_type(result)) + schedule_type_for_canonocalization(type); + + associate_artifact_to_btf_type_id(result, type_id); + + if (function_decl_sptr fn = is_function_decl(result)) + { + if (fn->get_is_in_public_symbol_table()) + maybe_add_fn_to_exported_decls(fn.get()); + } + else if (var_decl_sptr var = is_var_decl(result)) + { + if (var->get_is_in_public_symbol_table()) + maybe_add_var_to_exported_decls(var.get()); + } + + return result; + } + + /// Build an IR node for the "void" type. + /// + /// @return the IR node for the void type. + type_base_sptr + build_ir_node_for_void_type() + { + type_base_sptr t = env().get_void_type(); + decl_base_sptr type_declaration = get_type_declaration(t); + if (!has_scope(type_declaration)) + { + add_decl_to_scope(type_declaration, cur_tu()->get_global_scope()); + canonicalize(t); + } + + return t; + } + + /// Build an IR node for the "variadic parameter" type. + /// + /// @return the IR node for the "variadic parameter" type. + type_base_sptr + build_ir_node_for_variadic_parameter_type() + { + type_base_sptr t = env().get_variadic_parameter_type(); + decl_base_sptr t_decl = get_type_declaration(t); + if (!has_scope(t_decl)) + { + add_decl_to_scope(t_decl, cur_tu()->get_global_scope()); + canonicalize(t); + } + return t; + } + + /// Build an IR node for an integer type expressed in BTF. + /// + /// @param t a pointer a BTF type describing an integer. + /// + /// @return a pointer to @ref type_decl representing an integer + /// type. + type_or_decl_base_sptr + build_int_type(int type_id) + { + type_decl_sptr result; + + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + ABG_ASSERT(btf_kind(t) == BTF_KIND_INT); + + uint32_t info = *reinterpret_cast(t + 1); + uint64_t byte_size = 0, bit_size = 0; + string type_name; + + byte_size = t->size; + bit_size = byte_size * 8; + + if (BTF_INT_ENCODING(info) & BTF_INT_CHAR) + { + if (!(BTF_INT_ENCODING(info) & BTF_INT_SIGNED)) + type_name = "unsigned "; + type_name += "char"; + } + else if (BTF_INT_ENCODING(info) & BTF_INT_BOOL) + type_name = "bool"; + else if (!(BTF_INT_ENCODING(info) & BTF_INT_SIGNED)) + { + type_name = "unsigned "; + type_name += btf_offset_to_string(btf_handle(), t->name_off); + } + else + type_name = btf_offset_to_string(btf_handle(), t->name_off); + + location loc; + result.reset(new type_decl(env(), type_name, + bit_size, /*alignment=*/0, + loc, type_name)); + + return result; + } + + /// Build an IR node for a float type expressed in BTF. + /// + /// @return a pointer to @ref type_decl representing a float type. + type_or_decl_base_sptr + build_float_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + ABG_ASSERT(btf_kind(t) == BTF_KIND_FLOAT); + + string type_name = btf_offset_to_string(btf_handle(), t->name_off);; + uint64_t byte_size = t->size, bit_size = byte_size * 8; + location loc; + type_decl_sptr result(new type_decl(env(), type_name, bit_size, + /*alignment=*/0, loc, type_name)); + + return result; + } + + /// Build an IR type that represents the underlying type of an enum type. + /// + /// This is a sub-routine of the build_enum_type() function. + /// + /// @param enum_name the name of the enum type this type is an + /// underlying type for. + /// + /// @param enum_size the size of the enum. + /// + /// @param is_anonymous if true, the enum type is anonymous. + /// + /// @return a pointer to type_decl that represents a integer type + /// that is the underlying type of an enum type. + type_decl_sptr + build_enum_underlying_type(const string enum_name, uint64_t enum_size, + bool is_anonymous = true) + { + string underlying_type_name = + build_internal_underlying_enum_type_name(enum_name, + is_anonymous, + enum_size); + type_decl_sptr result(new type_decl(env(), underlying_type_name, + enum_size, enum_size, location())); + result->set_is_anonymous(is_anonymous); + result->set_is_artificial(true); + add_decl_to_scope(result, cur_tu()->get_global_scope()); + canonicalize(result); + return result; + } + + /// Build an IR node that represents an enum type expressed in BTF. + /// + /// @param type_id the ID of the BTF representation of the enum. + /// + /// @return a pointer to @ref enum_type_decl representing @p t. + type_or_decl_base_sptr + build_enum_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); +#ifdef WITH_BTF_ENUM64 + ABG_ASSERT(kind == BTF_KIND_ENUM || kind == BTF_KIND_ENUM64); +#else + ABG_ASSERT(kind == BTF_KIND_ENUM); +#endif + + int byte_size = t->size, bit_size = byte_size * 8; + + string enum_name; + if (t->name_off) + enum_name = btf_offset_to_string(btf_handle(), t->name_off); + bool is_anonymous = enum_name.empty(); + + int num_enms = btf_vlen(t); + enum_type_decl::enumerators enms; + string e_name; + if (kind == BTF_KIND_ENUM) + { + const struct btf_enum* e = btf_enum(t); + uint32_t e_value = 0; + for (int i = 0; i < num_enms; ++i, ++e) + { + e_name = btf_offset_to_string(btf_handle(), e->name_off); + e_value = e->val; + enms.push_back(enum_type_decl::enumerator(e_name, e_value)); + } + } +#ifdef WITH_BTF_ENUM64 + else if (kind == BTF_KIND_ENUM64) + { + const struct btf_enum64* e = + reinterpret_cast(t + 1); + uint64_t e_value = 0; + for (int i = 0; i < num_enms; ++i, ++e) + { + e_name = btf_offset_to_string(btf_handle(), e->name_off); + e_value = (static_cast(e->val_hi32) << 32) | e->val_lo32; + enms.push_back(enum_type_decl::enumerator(e_name, e_value)); + } + } +#endif + else + ABG_ASSERT_NOT_REACHED; + + type_decl_sptr underlying_type = + build_enum_underlying_type(enum_name, bit_size, is_anonymous); + enum_type_decl_sptr result(new enum_type_decl(enum_name, + location(), + underlying_type, + enms, enum_name)); + result->set_is_anonymous(is_anonymous); + return result; + } + + /// Build an IR node for a typedef that is expressed in BTF. + /// + /// @param type_id the ID of the BTF representation of a typedef. + /// + /// @return a pointer to @ref typedef_decl representing @p t. + type_or_decl_base_sptr + build_typedef_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_TYPEDEF); + + string type_name = btf_offset_to_string(btf_handle(), t->name_off); + type_base_sptr underlying_type = + is_type(build_ir_node_from_btf_type(t->type)); + if (!underlying_type) + return type_or_decl_base_sptr(); + + typedef_decl_sptr result(new typedef_decl(type_name, underlying_type, + location(), + /*linkage_name=*/type_name)); + if ((is_class_or_union_type(underlying_type) + || is_enum_type(underlying_type)) + && is_anonymous_type(underlying_type)) + get_type_declaration(underlying_type)->set_naming_typedef(result); + + return result; + } + + /// Build an IR node representing a pointer described in BTF. + /// + /// @param type_id the ID of a BTF representation of a pointer type. + /// + /// @return a pointer to pointer_type_def that represents @p t. + type_or_decl_base_sptr + build_pointer_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_PTR); + + type_base_sptr underlying_type = + is_type(build_ir_node_from_btf_type(t->type)); + if (!underlying_type) + return type_or_decl_base_sptr(); + + int size = elf_helpers::get_architecture_word_size(elf_handle()); + size *= 8; + pointer_type_def_sptr result(new pointer_type_def(underlying_type, size, + /*alignment=*/0, + location())); + return result; + } + + /// Build an IR node representing an array type described in BTF. + /// + /// @param type_id the ID of the BTF representation of an array + /// type. + /// + /// return a pointer to @ref array_type_def representing @p t. + type_or_decl_base_sptr + build_array_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_ARRAY); + + const struct btf_array* arr = btf_array(t); + + type_base_sptr underlying_type = + is_type(build_ir_node_from_btf_type(arr->type)); + if (!underlying_type) + return type_or_decl_base_sptr(); + + uint64_t lower_boud = 0; + // Note that arr->nelems can be 0; + uint64_t upper_bound = arr->nelems ? arr->nelems - 1: 0; + + array_type_def::subrange_sptr subrange(new array_type_def::subrange_type + (env(), /*name=*/"", + lower_boud, upper_bound, + location())); + add_decl_to_scope(subrange, cur_tu()->get_global_scope()); + canonicalize(subrange); + array_type_def::subranges_type subranges = {subrange}; + array_type_def_sptr result(new array_type_def(underlying_type, + subranges, location())); + + return result; + } + + /// Build an IR node representing a qualified type described in BTF. + /// + /// @param type_id the ID of the BTF representation of an array + /// type. + /// + /// @return a pointer to a qualified_type_def representing @ t. + type_or_decl_base_sptr + build_qualified_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_CONST + || kind == BTF_KIND_VOLATILE + || kind == BTF_KIND_RESTRICT); + + type_base_sptr underlying_type = + is_type(build_ir_node_from_btf_type(t->type)); + if (!underlying_type) + return type_or_decl_base_sptr(); + + qualified_type_def::CV qual = qualified_type_def::CV_NONE; + if (kind == BTF_KIND_CONST) + qual |= qualified_type_def::CV_CONST; + else if (kind == BTF_KIND_VOLATILE) + qual |= qualified_type_def::CV_VOLATILE; + else if (kind == BTF_KIND_RESTRICT) + qual |= qualified_type_def::CV_RESTRICT; + else + ABG_ASSERT_NOT_REACHED; + + qualified_type_def_sptr result(new qualified_type_def(underlying_type, + qual, location())); + return result; + } + + /// Build an IR node for a class or union type expressed in BTF. + /// + /// @param type_id the ID of a pointer to a BTF type describing a + /// class or union type. + /// + /// @return a pointer to either a @ref class_decl or a @ref + /// union_decl type representing the type expressed by @p t. + type_or_decl_base_sptr + build_class_or_union_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_STRUCT + || kind == BTF_KIND_UNION + || kind == BTF_KIND_FWD); + + string type_name; + if (t->name_off) + type_name = btf_offset_to_string(btf_handle(), t->name_off); + + bool is_anonymous = type_name.empty(); + uint64_t size = t->size; + size *= 8; + + bool is_decl_only = (kind == BTF_KIND_FWD); + + class_or_union_sptr result; + if (kind == BTF_KIND_STRUCT + || (kind == BTF_KIND_FWD + && BTF_INFO_KFLAG(t->info) == 0 /*struct*/)) + result.reset(new class_decl(env(), type_name, size, + /*alignment=*/0, + /*is_struct=*/true, + location(), + decl_base::VISIBILITY_DEFAULT, + is_anonymous)); + else if (kind == BTF_KIND_UNION + || (kind == BTF_KIND_FWD + && BTF_INFO_KFLAG(t->info) == 1/*union*/)) + result.reset(new union_decl(env(), type_name, size, location(), + decl_base::VISIBILITY_DEFAULT, + is_anonymous)); + else + ABG_ASSERT_NOT_REACHED; + + if (is_decl_only) + result->set_is_declaration_only(is_decl_only); + + add_decl_to_scope(result, cur_tu()->get_global_scope()); + + associate_artifact_to_btf_type_id(result, type_id); + + // For defined classes and unions, add data members to the type + // being built. + if (!is_decl_only) + { + const struct btf_member *m = + reinterpret_cast(t + 1); + uint64_t nb_members = btf_vlen(t); + + for (uint64_t i = 0; i < nb_members; ++i, ++m) + { + type_base_sptr member_type = + is_type(build_ir_node_from_btf_type(m->type)); + if (!member_type) + continue; + + string member_name; + if (m->name_off) + member_name = btf_offset_to_string(btf_handle(), m->name_off); + var_decl_sptr data_member(new var_decl(member_name, + member_type, + location(), + /*linkage_name=*/"")); + uint64_t offset_in_bits = + BTF_INFO_KFLAG(t->info) + ? BTF_MEMBER_BIT_OFFSET(m->offset) + : m->offset; + + result->add_data_member(data_member, + public_access, + /*is_laid_out=*/true, + /*is_static=*/false, + offset_in_bits); + } + } + return result; + } + + /// Build an IR node for a function type expressed in BTF. + /// + /// @param type_id the ID of a pointer to a BTF type describing a + /// function type. + /// + /// @return a pointer to a @ref function_type representing the + /// function type expressed by @p t. + type_or_decl_base_sptr + build_function_type(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_FUNC_PROTO); + + type_base_sptr return_type = is_type(build_ir_node_from_btf_type(t->type)); + if (return_type == nullptr) + return type_or_decl_base_sptr(); + + int address_size = elf_helpers::get_architecture_word_size(elf_handle()); + address_size *= 8; + function_type_sptr result(new function_type(env(), address_size, + /*alignment=*/0)); + result->set_return_type(return_type); + + associate_artifact_to_btf_type_id(result, type_id); + + uint16_t nb_parms = btf_vlen(t); + const struct btf_param* parm = + reinterpret_cast(t + 1); + + function_decl::parameters function_parms; + for (uint16_t i = 0; i < nb_parms; ++i, ++parm) + { + type_base_sptr parm_type; + string parm_name; + bool is_variadic = false; + + if (parm->name_off == 0 && parm->type == 0) + { + is_variadic = true; + parm_type = build_ir_node_for_variadic_parameter_type(); + } + else + { + parm_name = btf_offset_to_string(btf_handle(), parm->name_off); + parm_type = is_type(build_ir_node_from_btf_type(parm->type)); + } + + if (!parm_type) + continue; + + function_decl::parameter_sptr p + (new function_decl::parameter(parm_type, parm_name, + location(), is_variadic)); + function_parms.push_back(p); + } + result->set_parameters(function_parms); + + cur_tu()->bind_function_type_life_time(result); + + return result; + } + + /// Build an IR node for a function declaration expressed in BTF. + /// + /// @param type_id the ID of a pointer to a BTF "type" which realy + /// describes a function declaration. + /// + /// @return a pointer to a @ref function_decl representing the + /// function declaration expressed by @p t. + type_or_decl_base_sptr + build_function_decl(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_FUNC); + + function_decl_sptr result; + + string fn_name = btf_offset_to_string(btf_handle(), t->name_off); + + type_base_sptr fn_type = is_type(build_ir_node_from_btf_type(t->type)); + if (!fn_type) + return result; + + result.reset(new function_decl(fn_name, fn_type, /*is_inline=*/false, + location(), /*linkage_name=*/fn_name)); + + elf_symbol_sptr fn_sym; + if ((fn_sym = function_symbol_is_exported(fn_name))) + { + result->set_symbol(fn_sym); + result->set_is_in_public_symbol_table(true); + } + return result; + } + + /// Build an IR node for a variable declaration expressed in BTF. + /// + /// @param t a pointer to a BTF "type" describing a variable + /// declaration. + /// + /// @return a pointer to @ref var_decl representing the variable + /// declaration expressed by @p t. + type_or_decl_base_sptr + build_variable_decl(int type_id) + { + const btf_type *t = btf__type_by_id(btf_handle(), type_id); + int kind = btf_kind(t); + ABG_ASSERT(kind == BTF_KIND_VAR); + + var_decl_sptr result; + + string var_name = btf_offset_to_string(btf_handle(), t->name_off); + + type_base_sptr var_type = is_type(build_ir_node_from_btf_type(t->type)); + if (!var_type) + return result; + + result.reset(new var_decl(var_name, var_type, location(), + /*linkage_name=*/var_name)); + + elf_symbol_sptr var_sym; + if ((var_sym = variable_symbol_is_exported(var_name))) + { + result->set_symbol(var_sym); + result->set_is_in_public_symbol_table(true); + } + return result; + } + +}; // end class reader. + +/// Create and return a BTF reader (or front-end) which is an instance +/// of @ref btf::reader. +/// +/// @param elf_path the path to the path to the elf file the reader is +/// to be used for. +/// +/// @param debug_info_root_paths a vector to the paths to the +/// directories under which the debug info is to be found for @p +/// elf_path. Pass an empty vector if th debug info is not in a split +/// file. +/// +/// @param environment the environment used by the current context. +/// This environment contains resources needed by the BTF reader and +/// by the types and declarations that are to be created later. Note +/// that ABI artifacts that are to be compared all need to be created +/// within the same environment. +/// +/// Please also note that the life time of this environment object +/// must be greater than the life time of the resulting @ref +/// reader the context uses resources that are allocated in the +/// environment. +/// +/// @param load_all_types if set to false only the types that are +/// reachable from publicly exported declarations (of functions and +/// variables) are read. If set to true then all types found in the +/// debug information are loaded. +/// +/// @param linux_kernel_mode if set to true, then consider the special +/// linux kernel symbol tables when determining if a symbol is +/// exported or not. +/// +/// @return a smart pointer to the resulting btf::reader. +elf_based_reader_sptr +create_reader(const std::string& elf_path, + const vector& debug_info_root_paths, + environment& env, + bool load_all_types, + bool linux_kernel_mode) +{ + reader_sptr rdr = reader::create(elf_path, debug_info_root_paths, env, + load_all_types, linux_kernel_mode); + return rdr; +} + +} // end namespace btf +} // end namespace abigail + +#endif //WITH_BTF diff --git a/src/abg-elf-reader.cc b/src/abg-elf-reader.cc index 1f704e3f..57e7f2dc 100644 --- a/src/abg-elf-reader.cc +++ b/src/abg-elf-reader.cc @@ -274,6 +274,7 @@ struct reader::priv int alt_ctf_fd = 0; Elf* alt_ctf_handle = nullptr; Elf_Scn* alt_ctf_section = nullptr; + Elf_Scn* btf_section = nullptr; priv(reader& reeder, const std::string& elf_path, const vector& debug_info_roots) @@ -602,6 +603,13 @@ bool reader::has_ctf_debug_info() const {return (priv_->ctf_section != nullptr);} +/// Test if the binary has BTF debug info. +/// +/// @return true iff the binary has BTF debug info +bool +reader::has_btf_debug_info() const +{return (priv_->btf_section != nullptr);} + /// Getter of the handle use to access DWARF information from the /// alternate split DWARF information. /// @@ -697,6 +705,20 @@ reader::find_alternate_ctf_section() const return priv_->alt_ctf_section; } +/// Find and return a pointer to the BTF section of the current ELF +/// file. +/// +/// @return a pointer to the BTF section of the current ELF file. +const Elf_Scn* +reader::find_btf_section() const +{ + if (priv_->btf_section == nullptr) + priv_->btf_section = + elf_helpers::find_section(priv_->elf_handle, + ".BTF", SHT_PROGBITS); + return priv_->btf_section; +} + /// Get the value of the DT_NEEDED property of the current ELF file. /// /// @return the value of the DT_NEEDED property. diff --git a/src/abg-tools-utils.cc b/src/abg-tools-utils.cc index db04ccd8..81f9aa75 100644 --- a/src/abg-tools-utils.cc +++ b/src/abg-tools-utils.cc @@ -47,6 +47,9 @@ #ifdef WITH_CTF #include "abg-ctf-reader.h" #endif +#ifdef WITH_BTF +#include "abg-btf-reader.h" +#endif #include "abg-internal.h" #include "abg-regex.h" @@ -504,6 +507,34 @@ file_has_ctf_debug_info(const string& elf_file_path, return false; } +/// Test if an ELF file has BTFG debug info. +/// +/// @param elf_file_path the path to the ELF file to consider. +/// +/// @param debug_info_root a vector of pointer to directory to look +/// for debug info, in case the file is associated to split debug +/// info. If there is no split debug info then this vector can be +/// empty. Note that convert_char_stars_to_char_star_stars() can be +/// used to ease the construction of this vector. +/// +/// @return true iff the ELF file at @elf_file_path is an ELF file +/// that contains debug info. +bool +file_has_btf_debug_info(const string& elf_file_path, + const vector& debug_info_root_paths) +{ + if (guess_file_type(elf_file_path) != FILE_TYPE_ELF) + return false; + + environment env; + elf::reader r(elf_file_path, debug_info_root_paths, env); + + if (r.find_btf_section()) + return true; + + return false; +} + /// Tests if a given path is a directory or a symbolic link to a /// directory. /// @@ -2850,6 +2881,13 @@ create_best_elf_based_reader(const string& elf_file_path, #ifdef WITH_CTF if (file_has_ctf_debug_info(elf_file_path, debug_info_root_paths)) result = ctf::create_reader(elf_file_path, debug_info_root_paths, env); +#endif + } + else if (requested_fe_kind & corpus::BTF_ORIGIN) + { +#ifdef WITH_BTF + if (file_has_btf_debug_info(elf_file_path, debug_info_root_paths)) + result = btf::create_reader(elf_file_path, debug_info_root_paths, env); #endif } else @@ -2862,6 +2900,14 @@ create_best_elf_based_reader(const string& elf_file_path, // front end even if it wasn't formally requested by the user. result = ctf::create_reader(elf_file_path, debug_info_root_paths, env); #endif + +#ifdef WITH_BTF + if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths) + && file_has_btf_debug_info(elf_file_path, debug_info_root_paths)) + // The file has BTF debug info and no BTF, let's use the BTF + // front-end even if it wasn't formally requested by the user. + result = btf::create_reader(elf_file_path, debug_info_root_paths, env); +#endif } if (!result) diff --git a/tests/Makefile.am b/tests/Makefile.am index 7c515d35..2c5e7286 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,6 +30,10 @@ if CTF_READER TESTS += runtestreadctf endif +if BTF_READER +TESTS += runtestreadbtf +endif + # rather cheap tests TESTS+= \ runtestabicompat \ @@ -111,6 +115,13 @@ runtestreadctf_LDADD=libtestreadcommon.la libtestutils.la \ runtestreadctf_LDFLAGS=-pthread endif +if BTF_READER +runtestreadbtf_SOURCES=test-read-btf.cc +runtestreadbtf_LDADD=libtestreadcommon.la libtestutils.la \ + $(top_builddir)/src/libabigail.la +runtestreadbtf_LDFLAGS=-pthread +endif + runtestannotate_SOURCES=test-annotate.cc runtestannotate_LDADD=libtestutils.la $(top_builddir)/src/libabigail.la diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a7c4502c..a4740e3e 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -242,6 +242,12 @@ test-abidiff-exit/test-rhbz2114909-v0.o \ test-abidiff-exit/test-rhbz2114909-v1.cc \ test-abidiff-exit/test-rhbz2114909-v1.o \ test-abidiff-exit/test-rhbz2114909-report-1.txt \ +test-abidiff-exit/btf/test0-report-1.txt \ +test-abidiff-exit/btf/test0-report-2.txt \ +test-abidiff-exit/btf/test0-v0.c \ +test-abidiff-exit/btf/test0-v0.o \ +test-abidiff-exit/btf/test0-v1.c \ +test-abidiff-exit/btf/test0-v1.o \ \ test-diff-dwarf/test0-v0.cc \ test-diff-dwarf/test0-v0.o \ @@ -721,6 +727,13 @@ test-read-ctf/test-array-size.abi \ test-read-ctf/test-array-size.c \ test-read-ctf/test-array-size.o \ \ +test-read-btf/test0.c \ +test-read-btf/test0.o \ +test-read-btf/test0.o.abi \ +test-read-btf/test1.c \ +test-read-btf/test1.o \ +test-read-btf/test1.o.abi \ +\ test-annotate/test0.abi \ test-annotate/test1.abi \ test-annotate/test2.so.abi \ diff --git a/tests/data/test-abidiff-exit/btf/test0-report-1.txt b/tests/data/test-abidiff-exit/btf/test0-report-1.txt new file mode 100644 index 00000000..7533965a --- /dev/null +++ b/tests/data/test-abidiff-exit/btf/test0-report-1.txt @@ -0,0 +1,16 @@ +Functions changes summary: 0 Removed, 1 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added variable + +1 function with some indirect sub-type change: + + [C] 'function void fn0(const foo_type*)' has some indirect sub-type changes: + return type changed: + type name changed from 'void' to 'int' + type size changed from 0 to 32 (in bits) + mangled name changed from '' to int + parameter 1 of type 'const foo_type*' changed: + in pointed to type 'const foo_type': + entity changed from 'const foo_type' to 'typedef foo_type' + type size hasn't changed + parameter 2 of type 'int' was added + diff --git a/tests/data/test-abidiff-exit/btf/test0-report-2.txt b/tests/data/test-abidiff-exit/btf/test0-report-2.txt new file mode 100644 index 00000000..7bc1f6d0 --- /dev/null +++ b/tests/data/test-abidiff-exit/btf/test0-report-2.txt @@ -0,0 +1,53 @@ +Functions changes summary: 0 Removed, 1 Changed, 0 Added function +Variables changes summary: 0 Removed, 1 Changed, 0 Added variable + +1 function with some indirect sub-type change: + + [C] 'function void fn0(const foo_type*)' has some indirect sub-type changes: + return type changed: + type name changed from 'void' to 'int' + type size changed from 0 to 32 (in bits) + mangled name changed from '' to int + parameter 1 of type 'const foo_type*' changed: + in pointed to type 'const foo_type': + entity changed from 'const foo_type' to 'typedef foo_type' + type size hasn't changed + parameter 2 of type 'int' was added + +1 Changed variable: + + [C] 'foo_type foos[2]' was changed: + type of variable changed: + array element type 'struct foo_type' changed: + type size hasn't changed + 2 data member changes: + type of 'const int* m0' changed: + in pointed to type 'const int': + entity changed from 'const int' to 'int' + type size hasn't changed + type of 'volatile const u_type* m5' changed: + in pointed to type 'volatile const u_type': + in unqualified underlying type 'typedef u_type': + underlying type 'union u_type' changed: + type size hasn't changed + 1 data member insertion: + 'char* m2' + 2 data member changes: + type of 'ENUM_TYPE* m0' changed: + in pointed to type 'typedef ENUM_TYPE': + underlying type 'enum ENUM_TYPE' changed: + type size hasn't changed + 1 enumerator insertion: + 'ENUM_TYPE::E2_ENUM_TYPE' value '2' + type of 'ANOTHER_ENUM_TYPE* m1' changed: + in pointed to type 'typedef ANOTHER_ENUM_TYPE': + underlying type 'enum ANOTHER_ENUM_TYPE' changed: + type size hasn't changed + 1 enumerator insertion: + 'ANOTHER_ENUM_TYPE::E2_ANOTHER_ENUM_TYPE' value '2' + type changed from: + union u_type{ENUM_TYPE* m0; ANOTHER_ENUM_TYPE* m1;} + to: + union u_type{ENUM_TYPE* m0; ANOTHER_ENUM_TYPE* m1; char* m2;} + type size hasn't changed + diff --git a/tests/data/test-abidiff-exit/btf/test0-v0.c b/tests/data/test-abidiff-exit/btf/test0-v0.c new file mode 100644 index 00000000..5e5bdd57 --- /dev/null +++ b/tests/data/test-abidiff-exit/btf/test0-v0.c @@ -0,0 +1,40 @@ +/* + * Compile this to emit BTF debug info with: + * + * gcc -c -gbtf test0.c + */ + +typedef enum ENUM_TYPE +{ + E0_ENUM_TYPE = 0, + E1_ENUM_TYPE= 1 +} ENUM_TYPE; + +typedef enum ANOTHER_ENUM_TYPE +{ + E0_ANOTHER_ENUM_TYPE = 0, + E1_ANOTHER_ENUM_TYPE= 1 +} ANOTHER_ENUM_TYPE; + +typedef union u_type +{ + ENUM_TYPE *m0; + ANOTHER_ENUM_TYPE *m1; +} u_type; + +typedef struct foo_type +{ + const int *m0; + volatile char *m1; + unsigned *m2; + const volatile unsigned char *m3; + float m4[10]; + volatile const u_type *m5; +} foo_type; + +void +fn0(const foo_type* p __attribute__((unused))) +{ +} + +struct foo_type foos[2] = {0}; diff --git a/tests/data/test-abidiff-exit/btf/test0-v0.o b/tests/data/test-abidiff-exit/btf/test0-v0.o new file mode 100644 index 00000000..42c725d3 Binary files /dev/null and b/tests/data/test-abidiff-exit/btf/test0-v0.o differ diff --git a/tests/data/test-abidiff-exit/btf/test0-v1.c b/tests/data/test-abidiff-exit/btf/test0-v1.c new file mode 100644 index 00000000..c299b582 --- /dev/null +++ b/tests/data/test-abidiff-exit/btf/test0-v1.c @@ -0,0 +1,45 @@ +/* + * Compile this to emit BTF debug info with: + * + * gcc -c -gbtf test0.c + */ + +typedef enum ENUM_TYPE +{ + E0_ENUM_TYPE = 0, + E1_ENUM_TYPE= 1, + E2_ENUM_TYPE= 2 +} ENUM_TYPE; + +typedef enum ANOTHER_ENUM_TYPE +{ + E0_ANOTHER_ENUM_TYPE = 0, + E1_ANOTHER_ENUM_TYPE= 1, + E2_ANOTHER_ENUM_TYPE= 2 +} ANOTHER_ENUM_TYPE; + +typedef union u_type +{ + ENUM_TYPE *m0; + ANOTHER_ENUM_TYPE *m1; + char *m2; +} u_type; + +typedef struct foo_type +{ + int *m0; + volatile char *m1; + unsigned *m2; + const volatile unsigned char *m3; + float m4[10]; + volatile const u_type *m5; +} foo_type; + +int +fn0(foo_type* p, int a) +{ + *p->m0 = a; + return a; +} + +struct foo_type foos[2] = {0}; diff --git a/tests/data/test-abidiff-exit/btf/test0-v1.o b/tests/data/test-abidiff-exit/btf/test0-v1.o new file mode 100644 index 00000000..712d6aad Binary files /dev/null and b/tests/data/test-abidiff-exit/btf/test0-v1.o differ diff --git a/tests/data/test-read-btf/test0.c b/tests/data/test-read-btf/test0.c new file mode 100644 index 00000000..5e5bdd57 --- /dev/null +++ b/tests/data/test-read-btf/test0.c @@ -0,0 +1,40 @@ +/* + * Compile this to emit BTF debug info with: + * + * gcc -c -gbtf test0.c + */ + +typedef enum ENUM_TYPE +{ + E0_ENUM_TYPE = 0, + E1_ENUM_TYPE= 1 +} ENUM_TYPE; + +typedef enum ANOTHER_ENUM_TYPE +{ + E0_ANOTHER_ENUM_TYPE = 0, + E1_ANOTHER_ENUM_TYPE= 1 +} ANOTHER_ENUM_TYPE; + +typedef union u_type +{ + ENUM_TYPE *m0; + ANOTHER_ENUM_TYPE *m1; +} u_type; + +typedef struct foo_type +{ + const int *m0; + volatile char *m1; + unsigned *m2; + const volatile unsigned char *m3; + float m4[10]; + volatile const u_type *m5; +} foo_type; + +void +fn0(const foo_type* p __attribute__((unused))) +{ +} + +struct foo_type foos[2] = {0}; diff --git a/tests/data/test-read-btf/test0.o b/tests/data/test-read-btf/test0.o new file mode 100644 index 00000000..81d6fc02 Binary files /dev/null and b/tests/data/test-read-btf/test0.o differ diff --git a/tests/data/test-read-btf/test0.o.abi b/tests/data/test-read-btf/test0.o.abi new file mode 100644 index 00000000..165beaee --- /dev/null +++ b/tests/data/test-read-btf/test0.o.abi @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/test-read-btf/test1.c b/tests/data/test-read-btf/test1.c new file mode 100644 index 00000000..adbfb8b3 --- /dev/null +++ b/tests/data/test-read-btf/test1.c @@ -0,0 +1,20 @@ +/* + * Compile this to emit BTF debug info with: + * + * gcc -c -gbtf test0.c + */ + +struct S; +typedef struct S S; + +union U; +typedef union U U; + +S* +fn0(S* p, U* u) +{ + if (u) + ; + + return p; +} diff --git a/tests/data/test-read-btf/test1.o b/tests/data/test-read-btf/test1.o new file mode 100644 index 00000000..218e2e16 Binary files /dev/null and b/tests/data/test-read-btf/test1.o differ diff --git a/tests/data/test-read-btf/test1.o.abi b/tests/data/test-read-btf/test1.o.abi new file mode 100644 index 00000000..273039ba --- /dev/null +++ b/tests/data/test-read-btf/test1.o.abi @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test-abidiff-exit.cc b/tests/test-abidiff-exit.cc index e7f02eb5..df2f087c 100644 --- a/tests/test-abidiff-exit.cc +++ b/tests/test-abidiff-exit.cc @@ -471,6 +471,30 @@ InOutSpec in_out_specs[] = "data/test-abidiff-exit/test-rhbz2114909-report-1.txt", "output/test-abidiff-exit/test-rhbz2114909-report-1.txt" }, +#ifdef WITH_BTF + { + "data/test-abidiff-exit/btf/test0-v0.o", + "data/test-abidiff-exit/btf/test0-v1.o", + "", + "", + "", + "--no-default-suppression --btf", + abigail::tools_utils::ABIDIFF_ABI_CHANGE, + "data/test-abidiff-exit/btf/test0-report-1.txt", + "output/test-abidiff-exit/btf/test0-report-1.txt" + }, + { + "data/test-abidiff-exit/btf/test0-v0.o", + "data/test-abidiff-exit/btf/test0-v1.o", + "", + "", + "", + "--no-default-suppression --harmless --btf", + abigail::tools_utils::ABIDIFF_ABI_CHANGE, + "data/test-abidiff-exit/btf/test0-report-2.txt", + "output/test-abidiff-exit/btf/test0-report-2.txt" + }, +#endif {0, 0, 0 ,0, 0, 0, abigail::tools_utils::ABIDIFF_OK, 0, 0} }; diff --git a/tests/test-read-btf.cc b/tests/test-read-btf.cc new file mode 100644 index 00000000..837fa585 --- /dev/null +++ b/tests/test-read-btf.cc @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// -*- Mode: C++ -*- +// +// Copyright (C) 2022 Red Hat, Inc. +// +// Author: Dodji Seketeli + +/// @file +/// +/// This file is part of the BTF testsuite. It reads ELF binaries +/// containing BTF, save them in XML corpus files and diff the +/// corpus files against reference XML corpus files. + +#include +#include +#include +#include +#include +#include +#include "abg-btf-reader.h" +#include "test-read-common.h" + +using std::string; +using std::cerr; +using std::vector; + +using abigail::tests::read_common::InOutSpec; +using abigail::tests::read_common::test_task; +using abigail::tests::read_common::display_usage; +using abigail::tests::read_common::options; + +using abigail::btf::create_reader; +using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE; +using abigail::xml_writer::HASH_TYPE_ID_STYLE; +using abigail::tools_utils::emit_prefix; + +static InOutSpec in_out_specs[] = +{ + { + "data/test-read-btf/test0.o", + "", + "", + SEQUENCE_TYPE_ID_STYLE, + "data/test-read-btf/test0.o.abi", + "output/test-read-btf/test0.o.abi", + "--btf", + }, + { + "data/test-read-btf/test1.o", + "", + "", + SEQUENCE_TYPE_ID_STYLE, + "data/test-read-btf/test1.o.abi", + "output/test-read-btf/test1.o.abi", + "--btf", + }, + // This should be the last entry. + {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL, NULL} +}; + +/// Task specialization to perform BTF tests. +struct test_task_btf : public test_task +{ + test_task_btf(const InOutSpec &s, + string& a_out_abi_base, + string& a_in_elf_base, + string& a_in_abi_base); + virtual void + perform(); + + virtual + ~test_task_btf() + {} +}; // end struct test_task_btf + +/// Constructor. +/// +/// Task to be executed for each BTF test entry in @ref +/// abigail::tests::read_common::InOutSpec. +/// @param InOutSpec the array containing set of tests. +/// +/// @param a_out_abi_base the output base directory for abixml files. +/// +/// @param a_in_elf_base the input base directory for object files. +/// +/// @param a_in_elf_base the input base directory for expected +/// abixml files. +test_task_btf::test_task_btf(const InOutSpec &s, + string& a_out_abi_base, + string& a_in_elf_base, + string& a_in_abi_base) + : test_task(s, a_out_abi_base, a_in_elf_base, a_in_abi_base) + {} + +/// The thread function to execute each BTF test entry in @ref +/// abigail::tests::read_common::InOutSpec. +/// +/// This reads the corpus into memory, saves it to disk, loads it +/// again and compares the new in-memory representation against the +void +test_task_btf::perform() +{ + abigail::ir::environment env; + + set_in_elf_path(); + set_in_suppr_spec_path(); + + abigail::fe_iface::status status = + abigail::fe_iface::STATUS_UNKNOWN; + vector di_roots; + ABG_ASSERT(abigail::tools_utils::file_exists(in_elf_path)); + + abigail::elf_based_reader_sptr rdr = abigail::btf::create_reader(in_elf_path, + di_roots, env); + ABG_ASSERT(rdr); + + corpus_sptr corp = rdr->read_corpus(status); + + // if there is no output and no input, assume that we do not care about the + // actual read result, just that it succeeded. + if (!spec.in_abi_path && !spec.out_abi_path) + { + // Phew! we made it here and we did not crash! yay! + return; + } + if (!corp) + { + error_message = string("failed to read ") + in_elf_path + "\n"; + is_ok = false; + return; + } + corp->set_path(spec.in_elf_path); + // Do not take architecture names in comparison so that these + // test input binaries can come from whatever arch the + // programmer likes. + corp->set_architecture_name(""); + + if (!(is_ok = set_out_abi_path())) + return; + + if (!(is_ok = serialize_corpus(out_abi_path, corp))) + return; + + if (!(is_ok = run_abidw("--btf "))) + return; + + if (!(is_ok = run_diff())) + return; +} + +/// Create a new BTF instance for task to be execute by the testsuite. +/// +/// @param s the @ref abigail::tests::read_common::InOutSpec +/// tests container. +/// +/// @param a_out_abi_base the output base directory for abixml files. +/// +/// @param a_in_elf_base the input base directory for object files. +/// +/// @param a_in_abi_base the input base directory for abixml files. +/// +/// @return abigail::tests::read_common::test_task instance. +static test_task* +new_task(const InOutSpec* s, string& a_out_abi_base, + string& a_in_elf_base, string& a_in_abi_base) +{ + return new test_task_btf(*s, a_out_abi_base, + a_in_elf_base, a_in_abi_base); +} + +int +main(int argc, char *argv[]) +{ + options opts; + if (!parse_command_line(argc, argv, opts)) + { + if (!opts.wrong_option.empty()) + emit_prefix(argv[0], cerr) + << "unrecognized option: " << opts.wrong_option << "\n"; + display_usage(argv[0], cerr); + return 1; + } + + // compute number of tests to be executed. + const size_t num_tests = sizeof(in_out_specs) / sizeof(InOutSpec) - 1; + + return run_tests(num_tests, in_out_specs, opts, new_task); +} diff --git a/tools/abidiff.cc b/tools/abidiff.cc index 91a626a9..bf0bf9fc 100644 --- a/tools/abidiff.cc +++ b/tools/abidiff.cc @@ -23,6 +23,10 @@ #include "abg-ctf-reader.h" #endif +#ifdef WITH_BTF +#include "abg-btf-reader.h" +#endif + using std::vector; using std::string; using std::ostream; @@ -121,6 +125,9 @@ struct options #endif #ifdef WITH_CTF bool use_ctf; +#endif +#ifdef WITH_BTF + bool use_btf; #endif vector di_root_paths1; vector di_root_paths2; @@ -170,6 +177,10 @@ struct options , use_ctf() #endif +#ifdef WITH_BTF + , + use_btf() +#endif #ifdef WITH_DEBUG_SELF_COMPARISON , do_debug_self_comparison() @@ -273,6 +284,9 @@ display_usage(const string& prog_name, ostream& out) #ifdef WITH_CTF << " --ctf use CTF instead of DWARF in ELF files\n" #endif +#ifdef WITH_BTF + << " --btf use BTF instead of DWARF in ELF files\n" +#endif #ifdef WITH_DEBUG_SELF_COMPARISON << " --debug-self-comparison debug the process of comparing " "an ABI corpus against itself" @@ -639,6 +653,10 @@ parse_command_line(int argc, char* argv[], options& opts) else if (!strcmp(argv[i], "--ctf")) opts.use_ctf = true; #endif +#ifdef WITH_BTF + else if (!strcmp(argv[i], "--btf")) + opts.use_btf = true; +#endif #ifdef WITH_DEBUG_SELF_COMPARISON else if (!strcmp(argv[i], "--debug-self-comparison")) opts.do_debug_self_comparison = true; @@ -1232,6 +1250,10 @@ main(int argc, char* argv[]) #ifdef WITH_CTF if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; #endif abigail::elf_based_reader_sptr rdr = create_best_elf_based_reader(opts.file1, @@ -1305,6 +1327,10 @@ main(int argc, char* argv[]) #ifdef WITH_CTF if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; #endif abigail::elf_based_reader_sptr rdr = create_best_elf_based_reader(opts.file2, diff --git a/tools/abidw.cc b/tools/abidw.cc index 102d3a8c..ddb3846e 100644 --- a/tools/abidw.cc +++ b/tools/abidw.cc @@ -29,6 +29,9 @@ #ifdef WITH_CTF #include "abg-ctf-reader.h" #endif +#ifdef WITH_BTF +#include "abg-btf-reader.h" +#endif #include "abg-writer.h" #include "abg-reader.h" #include "abg-comparison.h" @@ -103,6 +106,9 @@ struct options bool noout; #ifdef WITH_CTF bool use_ctf; +#endif +#ifdef WITH_BTF + bool use_btf; #endif bool show_locs; bool abidiff; @@ -144,6 +150,9 @@ struct options noout(), #ifdef WITH_CTF use_ctf(false), +#endif +#ifdef WITH_BTF + use_btf(false), #endif show_locs(true), abidiff(), @@ -234,6 +243,9 @@ display_usage(const string& prog_name, ostream& out) "speed-up the analysis of the binary\n" << " --no-assume-odr-for-cplusplus do not assume the ODR to speed-up the " "analysis of the binary\n" +#ifdef WITH_BTF + << " --btf use BTF instead of DWARF in ELF files\n" +#endif << " --annotate annotate the ABI artifacts emitted in the output\n" << " --stats show statistics about various internal stuff\n" << " --verbose show verbose messages about internal stuff\n"; @@ -335,6 +347,10 @@ parse_command_line(int argc, char* argv[], options& opts) #ifdef WITH_CTF else if (!strcmp(argv[i], "--ctf")) opts.use_ctf = true; +#endif +#ifdef WITH_BTF + else if (!strcmp(argv[i], "--btf")) + opts.use_btf = true; #endif else if (!strcmp(argv[i], "--no-architecture")) opts.write_architecture = false; @@ -588,6 +604,10 @@ load_corpus_and_write_abixml(char* argv[], if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; #endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; +#endif // First of all, create a reader to read the ABI from the file // specfied in opts ... diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc index 99fc1a33..c588481c 100644 --- a/tools/abipkgdiff.cc +++ b/tools/abipkgdiff.cc @@ -93,6 +93,9 @@ #ifdef WITH_CTF #include "abg-ctf-reader.h" #endif +#ifdef WITH_BTF +#include "abg-btf-reader.h" +#endif using std::cout; using std::cerr; @@ -212,6 +215,9 @@ public: #ifdef WITH_CTF bool use_ctf; #endif +#ifdef WITH_BTF + bool use_btf; +#endif vector kabi_whitelist_packages; vector suppression_paths; @@ -256,6 +262,10 @@ public: #ifdef WITH_CTF , use_ctf() +#endif +#ifdef WITH_BTF + , + use_btf() #endif { // set num_workers to the default number of threads of the @@ -905,6 +915,9 @@ display_usage(const string& prog_name, ostream& out) "binaries inside the input package against their ABIXML representation\n" #ifdef WITH_CTF << " --ctf use CTF instead of DWARF in ELF files\n" +#endif +#ifdef WITH_BTF + << " --btf use BTF instead of DWARF in ELF files\n" #endif << " --help|-h display this help message\n" << " --version|-v display program version information" @@ -1353,6 +1366,10 @@ compare(const elf_file& elf1, #ifdef WITH_CTF if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; #endif abigail::elf_based_reader_sptr reader = create_best_elf_based_reader(elf1.path, @@ -1414,6 +1431,11 @@ compare(const elf_file& elf1, if (opts.use_ctf) ; else +#endif +#ifdef WITH_BTF + if (opts.use_btf) + ; + else #endif reader->refers_to_alt_debug_info(alt_di_path); if (!alt_di_path.empty()) @@ -1449,10 +1471,16 @@ compare(const elf_file& elf1, corpus_sptr corpus2; { corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN; + #ifdef WITH_CTF if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; #endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; +#endif + abigail::elf_based_reader_sptr reader = create_best_elf_based_reader(elf2.path, di_dirs2, @@ -1513,6 +1541,11 @@ compare(const elf_file& elf1, if (opts.use_ctf) ; else +#endif +#ifdef WITH_BTF + if (opts.use_btf) + ; + else #endif reader->refers_to_alt_debug_info(alt_di_path); if (!alt_di_path.empty()) @@ -1617,6 +1650,10 @@ compare_to_self(const elf_file& elf, #ifdef WITH_CTF if (opts.use_ctf) requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; #endif abigail::elf_based_reader_sptr reader = create_best_elf_based_reader(elf.path, @@ -3044,14 +3081,24 @@ compare_prepared_linux_kernel_packages(package& first_package, suppressions_type supprs; corpus_group_sptr corpus1, corpus2; + + corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN; +#ifdef WITH_CTF + if (opts.use_ctf) + requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; +#endif + 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); + supprs, opts.verbose, + env, requested_fe_kind); if (!corpus1) return abigail::tools_utils::ABIDIFF_ERROR; @@ -3061,9 +3108,8 @@ compare_prepared_linux_kernel_packages(package& first_package, vmlinux_path2, opts.suppression_paths, opts.kabi_whitelist_paths, - supprs, - opts.verbose, - env); + supprs, opts.verbose, + env, requested_fe_kind); if (!corpus2) return abigail::tools_utils::ABIDIFF_ERROR; @@ -3434,6 +3480,10 @@ parse_command_line(int argc, char* argv[], options& opts) #ifdef WITH_CTF else if (!strcmp(argv[i], "--ctf")) opts.use_ctf = true; +#endif +#ifdef WITH_BTF + else if (!strcmp(argv[i], "--btf")) + opts.use_btf = true; #endif else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) diff --git a/tools/kmidiff.cc b/tools/kmidiff.cc index 76fb9d92..f00895a3 100644 --- a/tools/kmidiff.cc +++ b/tools/kmidiff.cc @@ -63,6 +63,9 @@ struct options optional exported_interfaces_only; #ifdef WITH_CTF bool use_ctf; +#endif +#ifdef WITH_BTF + bool use_btf; #endif string wrong_option; string kernel_dist_root1; @@ -88,6 +91,10 @@ struct options #ifdef WITH_CTF , use_ctf(false) +#endif +#ifdef WITH_BTF + , + use_btf(false) #endif {} }; // end struct options. @@ -117,6 +124,9 @@ display_usage(const string& prog_name, ostream& out) "whitelist\n" #ifdef WITH_CTF << " --ctf use CTF instead of DWARF in ELF files\n" +#endif +#ifdef WITH_BTF + << " --btf use BTF instead of DWARF in ELF files\n" #endif << " --impacted-interfaces|-i show interfaces impacted by ABI changes\n" << " --full-impact|-f show the full impact of changes on top-most " @@ -259,6 +269,10 @@ parse_command_line(int argc, char* argv[], options& opts) #ifdef WITH_CTF else if (!strcmp(argv[i], "--ctf")) opts.use_ctf = true; +#endif +#ifdef WITH_BTF + else if (!strcmp(argv[i], "--btf")) + opts.use_btf = true; #endif else if (!strcmp(argv[i], "--impacted-interfaces") || !strcmp(argv[i], "-i")) @@ -421,11 +435,15 @@ main(int argc, char* argv[]) corpus_group_sptr group1, group2; string debug_info_root_dir; - corpus::origin requested_fe_kind = + corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN; #ifdef WITH_CTF - opts.use_ctf ? corpus::CTF_ORIGIN : + if (opts.use_ctf) + requested_fe_kind = corpus::CTF_ORIGIN; +#endif +#ifdef WITH_BTF + if (opts.use_btf) + requested_fe_kind = corpus::BTF_ORIGIN; #endif - corpus::DWARF_ORIGIN; if (!opts.kernel_dist_root1.empty()) {