!22 更新版本

Merge pull request !22 from 王佳栋/master
This commit is contained in:
openharmony_ci
2024-12-21 07:28:17 +00:00
committed by Gitee
92 changed files with 13698 additions and 2154 deletions
+3372
View File
File diff suppressed because it is too large Load Diff
+201
View File
@@ -1,3 +1,204 @@
2.4
===
Dmitry V. Levin (1):
elf-helpers: make sure config.h is included first
Dodji Seketeli (58):
Update website for the 2.3 release
release-text-template.txt: Modernize a little bit.
dwarf-reader: Don't compute canonical type while propagating one
Bug 29693 - clang-libs from f37 fails self test
Bug 30466 - harfbuzz fails self-check on f38
Bug 30467 - enlightenment fails self check on f38
configure.ac: Bump to 2.4 version
Bug 30503 - Fail to compare non-anonymous struct vs named struct data members
Bug 30461 - insight fails self-compare
fedabipkgdiff: Don't choke Koji servers with self-signed SSL certs
fedabipkgdiff: Fix previous commit
Make fe_iface::initialize independent from the kind of interface
corpus,tools-utils: Support loading a corpus, its deps & other binaries
abidw: Add --{follow,list}-dependencies & --add-binaries support
abidiff: Add --{follow,list}-dependencies & add-binaries{1,2} support
reader: Fix a long standing Thinko
ir: Remove an unnecessary comparison
reader: fix indentation
tools-utils: Fix indentation
dwarf-reader,ir: Make logging a property of the middle end
dwarf-reader: Fix some logging
abipkgdiff: Initialize libxml2 to use it in a multi-thread context
tools-utils: Avoid endless loop
{dwarf,elf}reader: Don't consider no symbol table as an error
abipkgdiff: Avoid comparing binaries that are outside of the package
ir: Add missing ABG_RETURN in the comparison engine
ir: Add fn types to type lookup maps
ir: Fix forgetting canonicalizing some function types
ir: Avoid forgetting potential seemingly duplicated member functions
ir: Really avoid canonicalizing decl-only classes
ir: Use non qualified typedef name for type canonicalization
ir: Fix qualification as non-confirmed propagated canonical types
dwarf-reader: Do not re-use typedefs in a scope
elf-reader, ir: Fix compilation on GCC 4.8.5
configure,test-diff-pkg.cc: Handle symlinks presence in dist tarball
libabigail-concepts.rst: Sort the properties of the directives
libabigail-concepts.rst: Remove trailing white spaces
test-abidiff-exit: Do not use debuginfo dir when its empty
ir: Fix output of 'debug(enum-type)'
comparison: Always apply filters on the diff graph
abg-comparison[-priv]: Better detection of incompatible unreachable type changes
doc/manuals/libabigail-concepts.rst: Fix typo
suppression: Fix indentation
suppression: Fix a typo in apidoc
Bug 30959 - Crash on malformed fn call expression
ini: Support '[' and ']' in arguments of function call expressions
init: Fix thinko in apidoc
ir: Remove redundant virtual member functions
Bug 30971 - Wrong interpretation of "has_data_member_inserted_at"
default-reporter,reporter-priv: Do not report names of anonymous enums
ir,comparison,corpus: Better support anonymous enums comparison
ir,comparison: Represent changed anonymous enums
comparison: Represent changed unreachable anonymous unions, structs & enums
Support suppressing data member insertion before a flexible array member
suppression: Make the "end" data member offset selector be named boundary
ir: Fix compilation error with GCC 4.8.5
gen-changelog.py: Don't escaping '/' with '\' in regexp
gen-changelog.py: Fix a long standing typo
Giuliano Procida (1):
operator!= fixes for C++-20
John Moon (1):
suppression: Add "changed_enumerators_regexp" property
Matthias Maennich (2):
symtab reader: use C++11 `using` syntax instead of typedefs
symtab reader: fix symtab iterator to support C++20
Yaakov Selkowitz (1):
Fix fedabipkgdiff configure check for Python 3.12
2.3
===
Aleksei Vetrov (1):
symtab: fix getting CRC in relocatable modules
Ben Woodard (1):
Have fedabipkgdiff sleep while waiting for abipkgdiff
Dodji Seketeli (92):
ir: Improve get_debug_representation
ir: Add a debug_comp_stack debugging function
Bug 29857 - Don't pop comparison operands that haven't been pushed
Bug 29857 - dwarf-reader: Resolve decl-only unions
Bug 29857 - Better detect comparison cycles in type graph
ir: Cache more aggregate type comparison results
NEWS: Update for 2.2 release
ChangeLog: Update for 2.2 release
configure: Bump version number to 2.3
Update website documentation for 2.2
Bug 29901 - abidiff hangs when comparing libgs.so.10 with itself
Bug 29934 - Handle buggy data members with empty names
dwarf-reader: Bug 29932 - Handle function DIE as type as needed
elf-reader: Don't free CTF resources too early
ir: misc cleanups
ir: Bug 29934 - Fix propagated canonical type confirmation
ir: Add sanity checking to canonical type propagation confirmation
Update copyright year for 2023
Don't use the "infinite" keyword for arrays of unknown size
dwarf-reader: Bug 29811 - Support updating of variable type
Bug 29811 - Better categorize harmless unknown array size changes
fix comparing array subrange DIEs
configure: Enable the CTF front-end by default
Add support for BTF
Update the copyright notice for the BTF reader
btf-reader: Use abigail::ir::canonicalize_types to canonicalize types
Better detect suitable libctf version
Update CTF's ctf_dict_t detection
fe-iface: Add missing virtual destructor
dwarf-reader: Remove unused code
corpus: Handle empty symbol table cases
{dwarf,elf_based}-reader,writer: Avoid duplicating corpora in corpus_group
default-reporter: Fix source location in functions change report
PR30048 - wrong pretty representation of qualified pointers
configure: Bump the CURRENT library number
ir: Add missing virtual methods overloads
ctf-reader: Fix GCC 13 warnings
abipkgdiff: Emit better logs in verbose mode
abipkgdiff: Emit error when no vmlinux is found in debug package
ini: Fix parsing list property values
suppr: Support has_data_member and has_data_member_regexp properties
suppression: Factorize out is_data_member_offset_in_range
suppression: Support the has_size_change property for suppress_type
suppression: Support offset_of_{first,last}_data_member_regexp offset selectors
comparison, suppression: Support [allow_type] directive
Misc white space fixes
abidiff: Add extensive logging
tools-utils: Support kernel stablelist
comp-filter: Don't re-visit node while applying filters to diff nodes
comparison: Add a mode to not apply filters on interface sub-graphs
comparison: When marking leaf nodes don't do unnecessary impact analysis
comp-filter: Speed up harmless/harmful categorization
tools-utils: Improve logging in build_corpus_group_from_kernel_dist_under
ir: Fix cycle detection for union types
Bug 29977 - dwarf-reader: Fix canonical DIE propagation canceling
dwarf-reader,abidiff: Fix compilation with --enable-debug-type-canonicalization
Bug 29912 - Better support an ELF symbol alias that designates several functions
Bug 29911 - fedabipkgdiff forgets to provide some debuginfo RPMs to abipkgdiff
Bug 29692 - Support binaries with empty symbol table
abipkgdiff: Fix a typo
test-symtab: Update after support for empty symtabs
Bug 29690 - Out of range exception in add_or_update_class_type
Bug 29686 - Fix testing the presence of anonymous data member in a struct
Bug 29345 - abipkgdiff is confused by symlinked binaries in RPMs
Bug 29340 - Add support for Ada range types
Fix redundancy filtering of range types
Bug rhbz#2182807 -- abipkgdiff crashes on missing debuginfo package
dwarf-reader: Support Ada subranges having upper_bound < lower_bound
abipkgdiff: Don't use user-specific filesystem info in error msg
reader: Make reader::get_scope_for_node handle subranges at array scope.
dwarf-reader: Support DW_OP_GNU_variable_value
dwarf-reader: Fix typo in comment
dwarf-reader: Support indirectly referenced subrange_type DIEs
fedabipkgdiff: Add timing data in debug logs
fedabipkgdiff: Remove busy loop when forking abipkgdiff
Bug 29339 - elf-helpers: Don't crash on unexpected ELF file
abi{dw,diff}: Better error messages when alternate debuginfo not found
abidiff,reader: Fix compilation with --debug-self-comparison
ir: Add new environment::get_type_id_from_type
ir: Recognize "void* as being equal to all other pointers in C
tests/update-test-output.py: Adapt to some broken test output
Improve self-comparison debug mode
ir: Improve debugging type_base::get_canonical_type_for
writer: Annotate pointer representation
comparison: Fix index error when interpreting scope comparison
ir: fix canonical type propagation canceling error
reader: Recognize variadic parameter type from abixml
Bug 30309 - Support absolute path to alt debug info file in DWARF
Fix the test of the patch for Bug 30309
test-abidiff-exit: Fix the command line passed to abidiff
ini: Do not crash on incorrect property value
test-ini: Fix a typo
Giuliano Procida (1):
DWARF reader: avoid C++20 operator!= overload ambiguity
Guillermo E. Martinez (4):
ctf-front-end: Add test for alias symbols
ctf-reader: Fix missing initializer for member in test suite
abipkgdiff: Fix kernel package detection when comparing kABIs
tools-utils: Fix looking for vmlinux binary in debuginfo package
Mark Wielaard (1):
doc: Fix some typos and add some missing references
Petr Pavlu (2):
Fix de-initialization of elf::reader::priv
abidiff: Fix handling of linux-kernel-mode
Xiaole He (1):
elf-reader: reclaim fd and mem before break
2.2
===
Aleksei Vetrov (1):
+120 -34
View File
@@ -5,6 +5,9 @@
__attribute__((visibility("hidden"))) */
#define HAS_GCC_VISIBILITY_ATTRIBUTE 1
/* struct ctf_dict_t is present */
#define HAVE_CTF_DICT_T 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
@@ -68,8 +71,8 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <minix/config.h> header file. */
/* #undef HAVE_MINIX_CONFIG_H */
/* Defined to 1 if elf.h has R_AARCH64_ABS64 macro defined */
#define HAVE_R_AARCH64_ABS64_MACRO 1
@@ -80,6 +83,9 @@
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
@@ -98,6 +104,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the <wchar.h> header file. */
#define HAVE_WCHAR_H 1
/* Defined if libdw has the function dwarf_getalt */
#define LIBDW_HAS_DWARF_GETALT 1
@@ -117,7 +126,7 @@
#define PACKAGE_NAME "libabigail"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libabigail 2.2"
#define PACKAGE_STRING "libabigail 2.4"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libabigail"
@@ -126,42 +135,131 @@
#define PACKAGE_URL "http://sourceware.org/libabigail"
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.2"
#define PACKAGE_VERSION "2.4"
/* Define to 1 if you have the ANSI C header files. */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
/* Enable general extensions on macOS. */
#ifndef _DARWIN_C_SOURCE
# define _DARWIN_C_SOURCE 1
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable X/Open compliant socket functions that do not require linking
with -lxnet on HP-UX 11.11. */
#ifndef _HPUX_ALT_XOPEN_SOCKET_API
# define _HPUX_ALT_XOPEN_SOCKET_API 1
#endif
/* Identify the host operating system as Minix.
This macro does not affect the system headers' behavior.
A future release of Autoconf may stop defining this macro. */
#ifndef _MINIX
/* # undef _MINIX */
#endif
/* Enable general extensions on NetBSD.
Enable NetBSD compatibility extensions on Minix. */
#ifndef _NETBSD_SOURCE
# define _NETBSD_SOURCE 1
#endif
/* Enable OpenBSD compatibility extensions on NetBSD.
Oddly enough, this does nothing on OpenBSD. */
#ifndef _OPENBSD_SOURCE
# define _OPENBSD_SOURCE 1
#endif
/* Define to 1 if needed for POSIX-compatible behavior. */
#ifndef _POSIX_SOURCE
/* # undef _POSIX_SOURCE */
#endif
/* Define to 2 if needed for POSIX-compatible behavior. */
#ifndef _POSIX_1_SOURCE
/* # undef _POSIX_1_SOURCE */
#endif
/* Enable POSIX-compatible threading on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1
#endif
/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
# define __STDC_WANT_IEC_60559_BFP_EXT__ 1
#endif
/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
# define __STDC_WANT_IEC_60559_DFP_EXT__ 1
#endif
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1
#endif
/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1
#endif
/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
#ifndef __STDC_WANT_LIB_EXT2__
# define __STDC_WANT_LIB_EXT2__ 1
#endif
/* Enable extensions specified by ISO/IEC 24747:2009. */
#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
# define __STDC_WANT_MATH_SPEC_FUNCS__ 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable X/Open extensions. Define to 500 only if necessary
to make mbstate_t available. */
#ifndef _XOPEN_SOURCE
/* # undef _XOPEN_SOURCE */
#endif
/* Version number of package */
#define VERSION "2.2"
#define VERSION "2.4"
/* Defined if user enabled BTF usage */
#define WITH_BTF 1
/* struct btf_enum64 is present */
#define WITH_BTF_ENUM64 1
/* The BTF_KIND_DECL_TAG enumerator is present */
/* #undef WITH_BTF_KIND_DECL_TAG */
/* The BTF_KIND_TYPE_TAG enumerator is present */
/* #undef WITH_BTF_KIND_TYPE_TAG */
/* The function btf__get_nr_types is present */
/* #undef WITH_BTF__GET_NR_TYPES */
/* The function btf__type_cnt is present */
#define WITH_BTF__TYPE_CNT 1
/* Defined if user enables and system has the libctf library */
/* #undef WITH_CTF */
#define WITH_CTF 1
/* compile the deb package support in abipkgdiff */
#define WITH_DEB 1
/* compile support of debugging canonical type propagation */
/* #undef WITH_DEBUG_CT_PROPAGATION */
/* compile support of debugging abidw --abidiff */
/* #undef WITH_DEBUG_SELF_COMPARISON */
@@ -173,31 +271,19 @@
#define WITH_RPM 1
/* has rpm/zstd support */
/* #undef WITH_RPM_ZSTD */
#define WITH_RPM_ZSTD 1
/* compile support of abilint --show-type-use */
/* #undef WITH_SHOW_TYPE_USE_IN_ABILINT */
/* symbolic links are kept in the distribution tarball */
#define WITH_SYMLINKS_KEPT_IN_DIST 1
/* compile the GNU tar archive support in abipkgdiff */
#define WITH_TAR 1
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
+288
View File
@@ -0,0 +1,288 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Defined if the compiler supports the attribution visibility syntax
__attribute__((visibility("hidden"))) */
#undef HAS_GCC_VISIBILITY_ATTRIBUTE
/* struct ctf_dict_t is present */
#undef HAVE_CTF_DICT_T
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if dwarf.h has the DW_FORM_line_strp enumerator */
#undef HAVE_DW_FORM_line_strp
/* Define to 1 if dwarf.h has the DW_FORM_strx enumerators */
#undef HAVE_DW_FORM_strx
/* Define to 1 if dwarf.h has the DW_FORM_strx1 enumerator */
#undef HAVE_DW_FORM_strx1
/* Define to 1 if dwarf.h has the DW_FORM_strx2 enumerator */
#undef HAVE_DW_FORM_strx2
/* Define to 1 if dwarf.h has the DW_FORM_strx3 enumerator */
#undef HAVE_DW_FORM_strx3
/* Define to 1 if dwarf.h has the DW_FORM_strx4 enumerator */
#undef HAVE_DW_FORM_strx4
/* Define to 1 if dwarf.h has the DW_LANG_C11 enumerator */
#undef HAVE_DW_LANG_C11_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_C_plus_plus_03 enumerator */
#undef HAVE_DW_LANG_C_plus_plus_03_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_C_plus_plus_11 enumerator */
#undef HAVE_DW_LANG_C_plus_plus_11_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_C_plus_plus_14 enumerator */
#undef HAVE_DW_LANG_C_plus_plus_14_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_D enumerator */
#undef HAVE_DW_LANG_D_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_Go enumerator */
#undef HAVE_DW_LANG_Go_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_Mips_Assembler enumerator */
#undef HAVE_DW_LANG_Mips_Assembler_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_Python enumerator */
#undef HAVE_DW_LANG_Python_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_Rust enumerator */
#undef HAVE_DW_LANG_Rust_enumerator
/* Define to 1 if dwarf.h has the DW_LANG_UPC enumerator */
#undef HAVE_DW_LANG_UPC_enumerator
/* Defined to 1 if elf.h has EM_AARCH64 macro defined */
#undef HAVE_EM_AARCH64_MACRO
/* Defined to 1 if elf.h has EM_TILEGX macro defined */
#undef HAVE_EM_TILEGX_MACRO
/* Defined to 1 if elf.h has EM_TILEPR0 macro defined */
#undef HAVE_EM_TILEPRO_MACRO
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <minix/config.h> header file. */
#undef HAVE_MINIX_CONFIG_H
/* Defined to 1 if elf.h has R_AARCH64_ABS64 macro defined */
#undef HAVE_R_AARCH64_ABS64_MACRO
/* Defined to 1 if elf.h has R_AARCH64_PREL32 macro defined */
#undef HAVE_R_AARCH64_PREL32_MACRO
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the <wchar.h> header file. */
#undef HAVE_WCHAR_H
/* Defined if libdw has the function dwarf_getalt */
#undef LIBDW_HAS_DWARF_GETALT
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define to 1 if assertions should be disabled. */
#undef NDEBUG
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable general extensions on macOS. */
#ifndef _DARWIN_C_SOURCE
# undef _DARWIN_C_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable X/Open compliant socket functions that do not require linking
with -lxnet on HP-UX 11.11. */
#ifndef _HPUX_ALT_XOPEN_SOCKET_API
# undef _HPUX_ALT_XOPEN_SOCKET_API
#endif
/* Identify the host operating system as Minix.
This macro does not affect the system headers' behavior.
A future release of Autoconf may stop defining this macro. */
#ifndef _MINIX
# undef _MINIX
#endif
/* Enable general extensions on NetBSD.
Enable NetBSD compatibility extensions on Minix. */
#ifndef _NETBSD_SOURCE
# undef _NETBSD_SOURCE
#endif
/* Enable OpenBSD compatibility extensions on NetBSD.
Oddly enough, this does nothing on OpenBSD. */
#ifndef _OPENBSD_SOURCE
# undef _OPENBSD_SOURCE
#endif
/* Define to 1 if needed for POSIX-compatible behavior. */
#ifndef _POSIX_SOURCE
# undef _POSIX_SOURCE
#endif
/* Define to 2 if needed for POSIX-compatible behavior. */
#ifndef _POSIX_1_SOURCE
# undef _POSIX_1_SOURCE
#endif
/* Enable POSIX-compatible threading on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
# undef __STDC_WANT_IEC_60559_BFP_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
# undef __STDC_WANT_IEC_60559_DFP_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
# undef __STDC_WANT_IEC_60559_TYPES_EXT__
#endif
/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
#ifndef __STDC_WANT_LIB_EXT2__
# undef __STDC_WANT_LIB_EXT2__
#endif
/* Enable extensions specified by ISO/IEC 24747:2009. */
#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
# undef __STDC_WANT_MATH_SPEC_FUNCS__
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable X/Open extensions. Define to 500 only if necessary
to make mbstate_t available. */
#ifndef _XOPEN_SOURCE
# undef _XOPEN_SOURCE
#endif
/* Version number of package */
#undef VERSION
/* Defined if user enabled BTF usage */
#undef WITH_BTF
/* struct btf_enum64 is present */
#undef WITH_BTF_ENUM64
/* The BTF_KIND_DECL_TAG enumerator is present */
#undef WITH_BTF_KIND_DECL_TAG
/* The BTF_KIND_TYPE_TAG enumerator is present */
#undef WITH_BTF_KIND_TYPE_TAG
/* The function btf__get_nr_types is present */
#undef WITH_BTF__GET_NR_TYPES
/* The function btf__type_cnt is present */
#undef WITH_BTF__TYPE_CNT
/* Defined if user enables and system has the libctf library */
#undef WITH_CTF
/* compile the deb package support in abipkgdiff */
#undef WITH_DEB
/* compile support of debugging canonical type propagation */
#undef WITH_DEBUG_CT_PROPAGATION
/* compile support of debugging abidw --abidiff */
#undef WITH_DEBUG_SELF_COMPARISON
/* compile support of debugging type canonicalization while using abidw
--debug-tc */
#undef WITH_DEBUG_TYPE_CANONICALIZATION
/* compile the rpm package support in abipkgdiff */
#undef WITH_RPM
/* has rpm/zstd support */
#undef WITH_RPM_ZSTD
/* compile support of abilint --show-type-use */
#undef WITH_SHOW_TYPE_USE_IN_ABILINT
/* symbolic links are kept in the distribution tarball */
#undef WITH_SYMLINKS_KEPT_IN_DIST
/* compile the GNU tar archive support in abipkgdiff */
#undef WITH_TAR
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES
+158 -11
View File
@@ -3,7 +3,7 @@
dnl Libabigail version number is handled here with the major and minor
dnl version numbers.
m4_define([version_major], [2])
m4_define([version_minor], [2])
m4_define([version_minor], [4])
dnl Below are the numbers to handle libabigail.so's versionning
dnl following the libtool's versionning scheme to handle shared
@@ -25,7 +25,7 @@ dnl then increment LIBABIGAIL_SO_AGE.
dnl
dnl 4. If any interfaces have been removed or changed since the last
dnl public release, then set LIBABIGAIL_SO_AGE to 0.
m4_define([libabigail_so_current], [1])
m4_define([libabigail_so_current], [3])
m4_define([libabigail_so_revision], [0])
m4_define([libabigail_so_age], [0])
@@ -62,11 +62,29 @@ AM_MAINTAINER_MODE([enable])
AM_SILENT_RULES([yes])
dnl By default, the tar command used by 'make dist and make distcheck'
dnl is "tar --format=ustar -chf" where the "-h" option actually
dnl follows symbolic links. So it copies the targets of the symlinks
dnl that are present in the tarball. Unfortunately, there are
dnl tests that need to keep the symlinks intact in the tarball.
dnl
dnl So let's define a tar command without the -h option.
am__tar='tar --format=ustar -cf - "$$tardir"'
VERSION_MAJOR=version_major
VERSION_MINOR=version_minor
VERSION_REVISION=0
dnl The major version number of the abixml version should be changed
dnl only if a new version of libabigail cannot read an old version of
dnl abixml. This should be very rare and should be avoided as much as
dnl possible.
ABIXML_VERSION_MAJOR=2
ABIXML_VERSION_MINOR=1
dnl The minor version number of the abixml version should be changed
dnl each time and old version of libabigail can't handle a new version
dnl of abixml. Note however that when the major version number is
dnl changed, the minor version number should be set to zero.
ABIXML_VERSION_MINOR=2
LIBABIGAIL_SO_CURRENT=libabigail_so_current
LIBABIGAIL_SO_REVISION=libabigail_so_revision
LIBABIGAIL_SO_AGE=libabigail_so_age
@@ -116,6 +134,12 @@ AC_ARG_ENABLE(debug-type-canonicalization,
ENABLE_DEBUG_TYPE_CANONICALIZATION=$enableval,
ENABLE_DEBUG_TYPE_CANONICALIZATION=no)
AC_ARG_ENABLE(debug-ct-propagation,
AS_HELP_STRING([--enable-debug-ct-propagation=yes|no],
[enable debugging of canonical type propagation (default is no)]),
ENABLE_DEBUG_CT_PROPAGATION=$enableval,
ENABLE_DEBUG_CT_PROPAGATION=no)
AC_ARG_ENABLE(show-type-use-in-abilint,
AS_HELP_STRING([--enable-show-type-use-in-abilint=yes|no],
['enable abilint --show-type-use'(default is no)]),
@@ -193,8 +217,14 @@ AC_ARG_ENABLE(ctf,
AS_HELP_STRING([--enable-ctf=yes|no],
[disable support of ctf files)]),
ENABLE_CTF=$enableval,
ENABLE_CTF=no)
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 *************************************************
@@ -311,21 +341,117 @@ AC_SUBST([ELF_LIBS])
dnl check for libctf presence if CTF code has been enabled by command line
dnl argument, and then define CTF flag (to build CTF file code) if libctf is
dnl found on the system
CTF_LIBS=
if test x$ENABLE_CTF = xyes; then
if test x$ENABLE_CTF != xno; then
CTF_HEADER=no
AC_CHECK_HEADER([ctf.h],
[CTF_HEADER=yes],
[AC_MSG_NOTICE([could not find ctf.h, going to disable CTF support])])
LIBCTF=
AC_CHECK_LIB(ctf, ctf_open, [LIBCTF=yes], [LIBCTF=no])
if test x$CTF_HEADER = xyes; then
AC_CHECK_LIB(ctf, ctf_open, [LIBCTF=yes], [LIBCTF=no])
fi
if test x$LIBCTF = xyes; then
AC_MSG_NOTICE([activating CTF code])
dnl Test if struct struct ctf_dict_t is present.
AC_LANG(C++)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <ctf-api.h>
ctf_dict_t* c;]])],
[HAVE_CTF_DICT_T=yes],
[HAVE_CTF_DICT_T=no])
if test x$HAVE_CTF_DICT_T = xyes; then
AC_DEFINE([HAVE_CTF_DICT_T], 1, [struct ctf_dict_t is present])
fi
fi
if test x$HAVE_CTF_DICT_T = xno; then
AC_MSG_NOTICE([Some needed data structures are missing from ctf-api.h. Disabling CTF support.])
LIBCTF=no
fi
if test x$LIBCTF = xyes; then
AC_MSG_NOTICE([CTF support enabled])
AC_DEFINE([WITH_CTF], 1,
[Defined if user enables and system has the libctf library])
CTF_LIBS=-lctf
ENABLE_CTF=yes
else
AC_MSG_NOTICE([CTF enabled but no libctf found])
AC_MSG_NOTICE([no suitable libctf found, CTF support was disabled])
ENABLE_CTF=no
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 <bpf/btf.h>])
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 <bpf/btf.h>])
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 <bpf/btf.h>])
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 <bpf/btf.h>])
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 <bpf/btf.h>])
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)
@@ -402,6 +528,13 @@ fi
AM_CONDITIONAL(ENABLE_RPM, test x$ENABLE_RPM = xyes)
dnl There is a test that needs symlinks support in the distribution tarball. If symlinks are
dnl removed from the tarball, then the test should be disabled.
m4_define([symlink_file], [tests/data/test-diff-pkg/symlink-dir-test1/dir1/symlinks/foo.o])
if test -L "$srcdir"/symlink_file; then
AC_DEFINE([WITH_SYMLINKS_KEPT_IN_DIST], 1, [symbolic links are kept in the distribution tarball])
fi
dnl enable the debugging of self comparison when doing abidw --debug-abidiff <binary>
if test x$ENABLE_DEBUG_SELF_COMPARISON = xyes; then
AC_DEFINE([WITH_DEBUG_SELF_COMPARISON], 1, [compile support of debugging abidw --abidiff])
@@ -434,6 +567,15 @@ fi
AM_CONDITIONAL(ENABLE_DEBUG_TYPE_CANONICALIZATION, test x$ENABLE_DEBUG_TYPE_CANONICALIZATION = xyes)
if test x$ENABLE_DEBUG_CT_PROPAGATION = xyes; then
AC_DEFINE([WITH_DEBUG_CT_PROPAGATION],
1,
[compile support of debugging canonical type propagation])
AC_MSG_NOTICE([support of debugging canonical type propagation is enabled])
else
AC_MSG_NOTICE([support of debugging canonical type propagation is disabled])
fi
dnl Check for the dpkg program
if test x$ENABLE_DEB = xauto -o x$ENABLE_DEB = xyes; then
AC_CHECK_PROG(HAS_DPKG, dpkg, yes, no)
@@ -614,13 +756,15 @@ if test x$CHECK_DEPS_FOR_FEDABIPKGDIFF = xyes; then
# urllib.parse. Oh well.
if test x$PYTHON = xpython3; then
URLPARSE_MODULE=urllib.parse
IMPORT_MODULE=importlib.machinery
else
URLPARSE_MODULE=urlparse
IMPORT_MODULE=imp
fi
REQUIRED_PYTHON_MODULES_FOR_FEDABIPKGDIFF="\
argparse logging os re subprocess sys $URLPARSE_MODULE \
xdg koji mock rpm imp tempfile mimetypes shutil six"
xdg koji mock rpm $IMPORT_MODULE tempfile mimetypes shutil six"
AX_CHECK_PYTHON_MODULES([$REQUIRED_PYTHON_MODULES_FOR_FEDABIPKGDIFF],
[$PYTHON],
@@ -719,7 +863,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
@@ -760,6 +904,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"
@@ -1063,12 +1208,14 @@ AC_MSG_NOTICE([
Enable abilint --show-type-use <type-id> : ${ENABLE_SHOW_TYPE_USE_IN_ABILINT}
Enable self comparison debugging : ${ENABLE_DEBUG_SELF_COMPARISON}
Enable type canonicalization debugging : ${ENABLE_DEBUG_TYPE_CANONICALIZATION}
Enable propagated canonical type debugging : ${ENABLE_DEBUG_CT_PROPAGATION}
Enable deb support in abipkgdiff : ${ENABLE_DEB}
Enable GNU tar archive support in abipkgdiff : ${ENABLE_TAR}
Enable bash completion : ${ENABLE_BASH_COMPLETION}
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}
+112 -2
View File
@@ -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
@@ -176,6 +177,72 @@ Options
library that the tool has to consider. The tool will thus filter
out ABI changes on types that are not defined in public headers.
* ``--add-binaries1`` <*bin1,bin2,bin3,..*>
For each of the comma-separated binaries given in argument to this
option, if the binary is found in the directory specified by the
``--added-binaries-dir1`` option, then ``abidiff`` loads the ABI
corpus of the binary and adds it to a set of corpora (called an
ABI Corpus Group) that includes the first argument of ``abidiff``.
That ABI corpus group is then compared against the second corpus
group given in argument to ``abidiff``.
* ``--add-binaries2`` <*bin1,bin2,bin3,..*>
For each of the comma-separated binaries given in argument to this
option, if the binary is found in the directory specified by the
``--added-binaries-dir2`` option, then ``abidiff`` loads the ABI
corpus of the binary and adds it to a set of corpora(called an ABI
Corpus Group) that includes the second argument of ``abidiff``.
That ABI corpus group is then compared against the first corpus
group given in argument to ``abidiff``.
* ``--follow-dependencies | --fdeps``
For each dependency of the first argument of ``abidiff``, if it's
found in the directory specified by the ``--added-binaries-dir1``
option, then construct an ABI corpus out of the dependency, add it
to a set of corpora (called an ABI Corpus Group) that includes the
first argument of ``abidiff``.
Similarly, for each dependency of the second argument of
``abidiff``, if it's found in the directory specified by the
``--added-binaries-dir2`` option, then construct an ABI corpus out
of the dependency, add it to an ABI corpus group that includes the
second argument of ``abidiff``.
These two ABI corpus groups are then compared against each other.
Said otherwise, this makes ``abidiff`` compare the set of its
first input and its dependencies against the set of its second
input and its dependencies.
* ``list-dependencies | --ldeps``
This option lists all the dependencies of the input arguments of
``abidiff`` that are found in the directories specified by the
options ``--added-binaries-dir1`` and ``--added-binaries-dir2``
* ``--added-binaries-dir1 | --abd1`` <added-binaries-directory-1>
This option is to be used in conjunction with the
``--add-binaries1``, ``--follow-dependencies`` and
``--list-dependencies`` options. Binaries referred to by these
options, if found in the directory `added-binaries-directory-1`,
are loaded as ABI corpus and are added to the first ABI corpus group
that is to be used in the comparison.
* ``--added-binaries-dir2 | --abd2`` <added-binaries-directory-2>
This option is to be used in conjunction with the
``--add-binaries2``, ``--follow-dependencies`` and
``--list-dependencies`` options. Binaries referred to by these
options, if found in the directory `added-binaries-directory-2`,
are loaded as ABI corpus and are added to the second ABI corpus
group to be used in the comparison.
* ``--no-linux-kernel-mode``
Without this option, if abidiff detects that the binaries it is
@@ -600,11 +667,28 @@ Options
This option disables those optimizations.
* ``--no-change-categorization | -x``
This option disables the categorization of changes into harmless
and harmful changes. Note that this categorization is a
pre-requisite for the filtering of changes so this option disables
that filtering. The goal of this option is to speed-up the
execution of the program for cases where the graph of changes is
huge and where the user is just interested in looking at, for
instance, leaf node changes without caring about their possible
impact on interfaces. In that case, this option would be used
along with the ``--leaf-changes-only`` one.
* ``--ctf``
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.
@@ -827,9 +911,35 @@ Usage examples
$
4. Comparing two sets of binaries that are passed on the command line: ::
$ abidiff --add-binaries1=file2-v1 \
--add-binaries2=file2-v2,file2-v1 \
--added-binaries-dir1 dir1 \
--added-binaries-dir2 dir2 \
file1-v1 file1-v2
Note that the files ``file2-v1``, and ``file2-v2`` are to be
found in ``dir1`` and ``dir2`` or in the current directory.
5. Compare two libraries and their dependencies: ::
$ abidiff --follow-dependencies \
--added-binaries-dir1 /some/where \
--added-binaries-dir2 /some/where/else \
foo bar
This compares the set of binaries comprised by ``foo`` and its
dependencies against the set of binaries comprised by ``bar`` and
its dependencies.
.. _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
.. _Linux Kernel: https://kernel.org/
+76 -7
View File
@@ -21,14 +21,14 @@ 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
.. _abidiff_invocation_label:
.. _abidw_invocation_label:
Invocation
==========
@@ -44,14 +44,49 @@ Options
Display a short help about the command and exit.
* `--version | -v`
* ``--version | -v``
Display the version of the program and exit.
* `--abixml-version`
* ``--abixml-version``
Display the version of the ABIXML format emitted by this program and exit.
* ``--add-binaries`` <*bin1,bin2,...*>
For each of the comma-separated binaries given in argument to this
option, if the binary is found in the directory specified by the
`--added-binaries-dir` option, then load the ABI corpus of the
binary and add it to a set of ABI corpora (called a ABI Corpus
Group) made of the binary denoted by the Argument of
``abidw``. That corpus group is then serialized out.
* ``--follow-dependencies``
For each dependency of the input binary of ``abidw``, if it is
found in the directory specified by the ``--added-binaries-dir``
option, then construct an ABI corpus out of the dependency and add
it to a set of ABI corpora (called an ABI Corpus Group) along with
the ABI corpus of the input binary of the program. The ABI Corpus
Group is then serialized out.
* ``--list-dependencies``
For each dependency of the input binary of``abidw``, if it's found
in the directory specified by the ``--added-binaries-dir`` option,
then the name of the dependency is printed out.
* ``--added-binaries-dir | --abd`` <*dir-path*>
This option is to be used in conjunction with the
``--add-binaries``, the ``--follow-dependencies`` or the
``--list-dependencies`` option. Binaries listed as arguments of
the ``--add-binaries`` option or being dependencies of the input
binary in the case of the ``--follow-dependencies`` option and
found in the directory <*dir-path*> are going to be loaded as ABI
corpus and added to the set of ABI corpora (called an ABI corpus
group) built and serialized.
* ``--debug-info-dir | -d`` <*dir-path*>
In cases where the debug info for *path-to-elf-file* is in a
@@ -368,6 +403,39 @@ Options
Emit verbose logs about the progress of miscellaneous internal
things.
Usage examples
==============
1. Emitting an ``ABIXML`` representation of a binary: ::
$ abidw binary > binary.abi
2. Emitting an ``ABIXML`` representation of a set of binaries
specified on the command line: ::
$ abidw --added-binaries=bin1,bin2,bin3 \
--added-binaries-dir /some/where \
binary > binaries.abi
Note that the binaries bin1, bin2 and bin3 are to be found in the
directory ``/some/where``. A representation of the ABI of the
set of binaries ``binary, bin1, bin2`` and ``bin3`` called an
``ABI corpus group`` is serialized in the file binaries.abi.
3. Emitting an ``ABIXML`` representation of a binary and its
dependencies: ::
$ abidw --follow-dependencies \
--added-binaries-dir /some/where \
binary > binary.abi
Note that only the dependencies that are found in the directory
``/some/where`` are analysed. Their ABIs, along with the ABI the
binary named ``binary`` are represented as an ABI corpus group
and serialized in the file ``binary.abi``, in the ABIXML format.
Notes
=====
@@ -389,6 +457,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
+12 -5
View File
@@ -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,7 +578,9 @@ 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
.. _DWZ: https://sourceware.org/dwz
.. _Linux Kernel: https://kernel.org/
+24 -6
View File
@@ -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
@@ -177,10 +178,26 @@ Options
the :ref:`default suppression specification files
<abidiff_default_supprs_label>` are loaded .
* ``--no-change-categorization | -x``
This option disables the categorization of changes into harmless
and harmful changes. Note that this categorization is a
pre-requisite for the filtering of changes so this option disables
that filtering. The goal of this option is to speed-up the
execution of the program for cases where the graph of changes is
huge and where the user is just interested in looking at, for
instance, leaf node changes without caring about their possible
impact on interfaces.
* ``--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 +266,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
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
####################
#################################
Overview of the Abigail framework
####################
#################################
**ABIGAIL** stands for the Application Binary Interface Generic
Analysis and Instrumentation Library.
+3 -3
View File
@@ -104,7 +104,7 @@ def output_commits():
hash = lin[16:].strip()
try:
rel = release_refs[hash]
print("=== release %d.%d.%d ===\n" % (int(rel[0]), int(rel[1]), int(rel[2])))
except:
pass
buf = []
@@ -121,7 +121,7 @@ def output_commits():
def get_rel_tags():
# Populate the release_refs dict with the tags for previous releases
reltagre = re.compile("^([a-z0-9]{40}) refs\/tags\/GNET-([0-9]+)[-_.]([0-9]+)[-_.]([0-9]+)")
reltagre = re.compile("^([a-z0-9]{40}) refs/tags/libabigail-([0-9]+)[-_.]([0-9]+)[-_.]([0-9]+)")
cmd = ['git', 'show-ref', '--tags', '--dereference']
p = subprocess.Popen(args=cmd, shell=False,
@@ -134,7 +134,7 @@ def get_rel_tags():
release_refs[sha] = (maj, min, nano)
def find_start_tag():
starttagre = re.compile("^([a-z0-9]{40}) refs\/tags\/CHANGELOG_START")
starttagre = re.compile("^([a-z0-9]{40}) refs/tags/CHANGELOG_START")
cmd = ['git', 'show-ref', '--tags']
p = subprocess.Popen(args=cmd, shell=False,
stdout=subprocess.PIPE,
+4
View File
@@ -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
+34
View File
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022-2023 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<char**>& 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__
+8 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -91,6 +91,13 @@ has_anonymous_data_member_change(const diff_sptr &d);
bool
has_data_member_replaced_by_anon_dm(const diff* diff);
bool
is_var_1_dim_unknown_size_array_change(const diff*);
bool
is_var_1_dim_unknown_size_array_change(const var_decl_sptr& var1,
const var_decl_sptr& var2);
struct filter_base;
/// Convenience typedef for a shared pointer to filter_base
typedef shared_ptr<filter_base> filter_base_sptr;
+140 -21
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -17,6 +17,7 @@
#include "abg-corpus.h"
#include "abg-diff-utils.h"
#include "abg-reporter.h"
#include "abg-suppression.h"
namespace abigail
{
@@ -46,14 +47,6 @@ using diff_utils::insertion;
using diff_utils::deletion;
using diff_utils::edit_script;
class diff;
/// Convenience typedef for a shared_ptr for the @ref diff class
typedef shared_ptr<diff> diff_sptr;
/// Convenience typedef for a weak_ptr for the @ref diff class
typedef weak_ptr<diff> diff_wptr;
/// Hasher for @ref diff_sptr.
struct diff_sptr_hasher
{
@@ -261,14 +254,6 @@ typedef unordered_map<string, elf_symbol_sptr> string_elf_symbol_map;
/// value is a @ref var_diff_sptr.
typedef unordered_map<string, var_diff_sptr> string_var_diff_ptr_map;
class diff_context;
/// Convenience typedef for a shared pointer of @ref diff_context.
typedef shared_ptr<diff_context> diff_context_sptr;
/// Convenience typedef for a weak pointer of @ref diff_context.
typedef weak_ptr<diff_context> diff_context_wptr;
class diff_node_visitor;
class diff_traversable_base;
@@ -438,6 +423,25 @@ enum diff_category
/// variable didn't change.
BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY = 1 << 21,
/// A diff node in this category carries a change that must be
/// reported, even if the diff node is also in the
/// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
/// Typically, this node matches a suppression specification like
/// the [allow_type] directive.
HAS_ALLOWED_CHANGE_CATEGORY = 1 << 22,
/// A diff node in this category has a descendant node that is in
/// the HAS_ALLOWED_CHANGE_CATEGORY category. Nodes in this
/// category must be reported, even if they are also in the
/// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY = 1 << 23,
/// A diff node in this category has a parent node that is in the
/// HAS_ALLOWED_CHANGE_CATEGORY category. Nodes in this category
/// must be reported, even if they are also in the
/// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY = 1 << 24,
/// A special enumerator that is the logical 'or' all the
/// enumerators above.
///
@@ -466,6 +470,9 @@ enum diff_category
| VAR_TYPE_CV_CHANGE_CATEGORY
| VOID_PTR_TO_PTR_CHANGE_CATEGORY
| BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY
| HAS_ALLOWED_CHANGE_CATEGORY
| HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
| HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
}; // enum diff_category
diff_category
@@ -541,6 +548,12 @@ public:
string_diff_ptr_map&
get_typedef_diff_map();
const string_diff_ptr_map&
get_subrange_diff_map() const;
string_diff_ptr_map&
get_subrange_diff_map();
const string_diff_ptr_map&
get_array_diff_map() const;
@@ -642,6 +655,12 @@ public:
~diff_context();
bool
do_log() const;
void
do_log(bool);
void
set_corpus_diff(const corpus_diff_sptr&);
@@ -730,15 +749,30 @@ public:
void
maybe_apply_filters(corpus_diff_sptr diff);
suppr::suppressions_type&
const suppr::suppressions_type&
suppressions() const;
suppr::suppressions_type&
suppressions();
const suppr::suppressions_type&
negated_suppressions() const;
const suppr::suppressions_type&
direct_suppressions() const;
void
add_suppression(const suppr::suppression_sptr suppr);
void
add_suppressions(const suppr::suppressions_type& supprs);
bool
perform_change_categorization() const;
void
perform_change_categorization(bool);
void
show_leaf_changes_only(bool f);
@@ -950,6 +984,12 @@ protected:
type_or_decl_base_sptr second_subject,
diff_context_sptr ctxt);
bool
do_log() const;
void
do_log(bool);
void
begin_traversing();
@@ -1037,6 +1077,9 @@ public:
bool
is_filtered_out_wrt_non_inherited_categories() const;
bool
is_filtered_out_without_looking_at_allowed_changes() const;
bool
is_suppressed() const;
@@ -1049,6 +1092,15 @@ public:
bool
has_local_changes_to_be_reported() const;
bool
is_allowed_by_specific_negated_suppression() const;
bool
has_descendant_allowed_by_specific_negated_suppression() const;
bool
has_parent_allowed_by_specific_negated_suppression() const;
virtual const string&
get_pretty_representation() const;
@@ -1363,6 +1415,61 @@ compute_diff(reference_type_def_sptr first,
reference_type_def_sptr second,
diff_context_sptr ctxt);
class subrange_diff;
/// A convenience typedef for a shared pointer to subrange_diff type.
typedef shared_ptr<subrange_diff> subrange_diff_sptr;
/// The abstraction of the diff between two subrange types.
class subrange_diff : public type_diff_base
{
struct priv;
std::unique_ptr<priv> priv_;
protected:
subrange_diff(const array_type_def::subrange_sptr& first,
const array_type_def::subrange_sptr& second,
const diff_sptr& underlying_type_diff,
const diff_context_sptr ctxt = diff_context_sptr());
public:
const array_type_def::subrange_sptr
first_subrange() const;
const array_type_def::subrange_sptr
second_subrange() const;
const diff_sptr
underlying_type_diff() const;
virtual const string&
get_pretty_representation() const;
virtual bool
has_changes() const;
virtual enum change_kind
has_local_changes() const;
virtual void
report(ostream&, const string& indent = "") const;
virtual void
chain_into_hierarchy();
friend subrange_diff_sptr
compute_diff(array_type_def::subrange_sptr first,
array_type_def::subrange_sptr second,
diff_context_sptr ctxt);
}; // end subrange_diff
subrange_diff_sptr
compute_diff(array_type_def::subrange_sptr first,
array_type_def::subrange_sptr second,
diff_context_sptr ctxt);
class array_diff;
/// Convenience typedef for a shared pointer on a @ref
@@ -1604,6 +1711,12 @@ public:
const string_decl_base_sptr_map&
deleted_data_members() const;
const unsigned_var_diff_sptr_map&
changed_data_members() const;
const var_diff_sptrs_type&
sorted_changed_data_members() const;
const edit_script&
member_fns_changes() const;
@@ -1619,9 +1732,6 @@ public:
const string_member_function_sptr_map&
inserted_member_fns() const;
const var_diff_sptrs_type&
sorted_changed_data_members() const;
size_t
count_filtered_changed_data_members(bool local_only = false) const;
@@ -2309,6 +2419,12 @@ public:
/// A convenience typedef for a shared pointer to @ref diff_stats
typedef shared_ptr<diff_stats> diff_stats_sptr;
bool
do_log() const;
void
do_log(bool);
corpus_sptr
first_corpus() const;
@@ -2803,6 +2919,9 @@ is_class_or_union_diff(const diff* d);
const class_or_union_diff*
is_anonymous_class_or_union_diff(const diff* d);
const subrange_diff*
is_subrange_diff(const diff* diff);
const array_diff*
is_array_diff(const diff* diff);
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+19 -7
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
@@ -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:
@@ -68,6 +69,12 @@ public:
const environment&
get_environment() const;
bool
do_log() const;
void
do_log(bool);
void
add(const translation_unit_sptr&);
@@ -211,7 +218,7 @@ public:
virtual const functions&
get_functions() const;
const vector<function_decl*>*
const std::unordered_set<function_decl*>*
lookup_functions(const string& id) const;
void
@@ -295,13 +302,13 @@ operator&=(corpus::origin &l, corpus::origin r);
/// parameters needed.
class corpus::exported_decls_builder
{
class priv;
std::unique_ptr<priv> priv_;
// Forbid default construction.
exported_decls_builder();
public:
class priv;
std::unique_ptr<priv> priv_;
friend class corpus;
exported_decls_builder(functions& fns,
@@ -320,6 +327,9 @@ public:
functions&
exported_functions();
std::unordered_set<function_decl*>*
fn_id_maps_to_several_fns(function_decl*);
const variables&
exported_variables() const;
@@ -327,7 +337,7 @@ public:
exported_variables();
void
maybe_add_fn_to_exported_fns(const function_decl*);
maybe_add_fn_to_exported_fns(function_decl*);
void
maybe_add_var_to_exported_vars(const var_decl*);
@@ -356,6 +366,8 @@ public:
void add_corpus(const corpus_sptr&);
bool has_corpus(const string&);
const corpora_type&
get_corpora() const;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2021-2022 Oracle, Inc.
// Copyright (C) 2021-2023 Oracle, Inc.
//
// Author: Jose E. Marchesi
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2019-2022 Google, Inc.
// Copyright (C) 2019-2023 Google, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
///
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+7 -3
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -50,17 +50,21 @@ public:
~elf_based_reader();
virtual void
reset(const std::string& elf_path,
const vector<char**>& debug_info_root_paths);
initialize(const std::string& elf_path,
const vector<char**>& debug_info_root_paths);
virtual ir::corpus_sptr
read_and_add_corpus_to_group(ir::corpus_group& group,
fe_iface::status& status);
virtual void
initialize(const string& elf_path,
const vector<char**>& debug_info_root_paths,
bool load_all_types,
bool linux_kernel_mode) = 0;
virtual void
initialize(const std::string& corpus_path);
};//end class elf_based_reader
typedef std::shared_ptr<elf_based_reader> elf_based_reader_sptr;
+13 -4
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -72,9 +72,12 @@ class reader : public fe_iface
~reader();
void
reset(const std::string& elf_path,
const vector<char**>& debug_info_roots);
virtual void
initialize(const std::string& elf_path,
const vector<char**>& debug_info_roots);
virtual void
initialize(const std::string& elf_path);
const vector<char**>&
debug_info_root_paths() const;
@@ -97,6 +100,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 +124,9 @@ class reader : public fe_iface
const Elf_Scn*
find_alternate_ctf_section() const;
const Elf_Scn*
find_btf_section() const;
const vector<string>&
dt_needed()const;
+3 -3
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -74,8 +74,8 @@ protected:
virtual ~fe_iface();
void
reset(const std::string& corpus_path, environment& e);
virtual void
initialize(const std::string& corpus_path);
const options_type&
options() const;
+148 -5
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
@@ -11,12 +11,14 @@
#include <stdint.h>
#include <cstddef>
#include <cstdlib>
#include <regex.h>
#include <list>
#include <memory>
#include <ostream>
#include <string>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <utility> // for std::rel_ops, at least.
#include <vector>
#include "abg-interned-str.h"
@@ -54,10 +56,38 @@ using std::weak_ptr;
using std::unordered_map;
using std::string;
using std::vector;
using std::unordered_set;
typedef unordered_set<string> string_set_type;
// Pull in relational operators.
using namespace std::rel_ops;
namespace comparison
{
class diff_context;
/// Convenience typedef for a shared pointer of @ref diff_context.
typedef shared_ptr<diff_context> diff_context_sptr;
/// Convenience typedef for a weak pointer of @ref diff_context.
typedef weak_ptr<diff_context> diff_context_wptr;
class diff;
/// Convenience typedef for a shared_ptr for the @ref diff class
typedef shared_ptr<diff> diff_sptr;
/// Convenience typedef for a weak_ptr for the @ref diff class
typedef weak_ptr<diff> diff_wptr;
}
namespace regex
{
/// A convenience typedef for a shared pointer of regex_t.
typedef std::shared_ptr<regex_t> regex_t_sptr;
}// end namespace regex
namespace ir
{
@@ -162,6 +192,9 @@ typedef shared_ptr<class_decl> class_decl_sptr;
/// Convenience typedef for a vector of @ref class_decl_sptr
typedef vector<class_decl_sptr> classes_type;
/// Convenience typedef for a vector of @ref class_or_union_sptr
typedef vector<class_or_union_sptr> classes_or_unions_type;
/// Convenience typedef for a weak pointer on a @ref class_decl.
typedef weak_ptr<class_decl> class_decl_wptr;
@@ -418,6 +451,9 @@ is_integral_type(const type_or_decl_base_sptr&);
typedef_decl_sptr
is_typedef(const type_or_decl_base_sptr);
const typedef_decl*
is_typedef(const type_or_decl_base*);
const typedef_decl*
is_typedef(const type_base*);
@@ -445,11 +481,22 @@ is_class_type(const type_or_decl_base*);
class_decl_sptr
is_class_type(const type_or_decl_base_sptr&);
bool
is_declaration_only_class_or_union_type(const type_base *t);
var_decl_sptr
has_flexible_array_data_member(const class_decl&);
var_decl_sptr
has_flexible_array_data_member(const class_decl*);
var_decl_sptr
has_flexible_array_data_member(const class_decl_sptr&);
bool
is_declaration_only_class_or_union_type(const type_base_sptr&);
is_declaration_only_class_or_union_type(const type_base *t,
bool look_through_decl_only = false);
bool
is_declaration_only_class_or_union_type(const type_base_sptr& t,
bool look_through_decl_only = false);
class_or_union*
is_class_or_union_type(const type_or_decl_base*);
@@ -457,6 +504,14 @@ is_class_or_union_type(const type_or_decl_base*);
class_or_union_sptr
is_class_or_union_type(const type_or_decl_base_sptr&);
bool
class_or_union_types_of_same_kind(const class_or_union *,
const class_or_union*);
bool
class_or_union_types_of_same_kind(const class_or_union_sptr&,
const class_or_union_sptr&);
bool
is_union_type(const type_or_decl_base&);
@@ -481,6 +536,9 @@ is_pointer_type(const type_or_decl_base*);
pointer_type_def_sptr
is_pointer_type(const type_or_decl_base_sptr&);
bool
is_typedef_ptr_or_ref_to_decl_only_class_or_union_type(const type_base* t);
reference_type_def*
is_reference_type(type_or_decl_base*);
@@ -493,6 +551,15 @@ is_reference_type(const type_or_decl_base_sptr&);
const type_base*
is_void_pointer_type(const type_base*);
const type_base_sptr
is_void_pointer_type(const type_base_sptr&);
const type_base*
is_void_pointer_type_equivalent(const type_base*);
const type_base*
is_void_pointer_type_equivalent(const type_base&);
qualified_type_def*
is_qualified_type(const type_or_decl_base*);
@@ -648,6 +715,12 @@ is_data_member(const decl_base *);
const var_decl_sptr
get_next_data_member(const class_or_union_sptr&, const var_decl_sptr&);
var_decl_sptr
get_last_data_member(const class_or_union&);
var_decl_sptr
get_last_data_member(const class_or_union*);
var_decl_sptr
get_last_data_member(const class_or_union_sptr&);
@@ -675,6 +748,15 @@ is_anonymous_data_member(const var_decl*);
bool
is_anonymous_data_member(const var_decl&);
bool
is_data_member_of_anonymous_class_or_union(const var_decl&);
bool
is_data_member_of_anonymous_class_or_union(const var_decl*);
bool
is_data_member_of_anonymous_class_or_union(const var_decl_sptr&);
const var_decl_sptr
get_first_non_anonymous_data_member(const var_decl_sptr);
@@ -688,8 +770,12 @@ anonymous_data_member_to_class_or_union(const var_decl*);
class_or_union_sptr
anonymous_data_member_to_class_or_union(const var_decl_sptr&);
class_or_union_sptr
anonymous_data_member_to_class_or_union(const var_decl&);
bool
scope_anonymous_or_typedef_named(const decl_base&);
anonymous_data_member_exists_in_class(const var_decl& anon_dm,
const class_or_union& clazz);
bool
is_anonymous_or_typedef_named(const decl_base&);
@@ -733,6 +819,11 @@ get_data_member_offset(const decl_base_sptr);
uint64_t
get_absolute_data_member_offset(const var_decl&);
bool
get_next_data_member_offset(const class_or_union*,
const var_decl_sptr&,
uint64_t&);
bool
get_next_data_member_offset(const class_or_union_sptr&,
const var_decl_sptr&,
@@ -960,6 +1051,9 @@ get_function_type_name(const function_type*, bool internal = false);
interned_string
get_function_type_name(const function_type&, bool internal = false);
interned_string
get_function_id_or_pretty_representation(function_decl *fn);
interned_string
get_method_type_name(const method_type_sptr&, bool internal = false);
@@ -1029,6 +1123,33 @@ get_class_or_union_flat_representation(const class_or_union_sptr& cou,
bool internal,
bool qualified_name = true);
string
get_enum_flat_representation(const enum_type_decl& enum_type,
const string& indent,
bool one_line,
bool internal,
bool qualified_names);
string
get_enum_flat_representation(const enum_type_decl* enum_type,
const string& indent,
bool one_line,
bool internal,
bool qualified_names);
string
get_enum_flat_representation(const enum_type_decl_sptr& enum_type,
const string& indent,
bool one_line,
bool qualified_names);
string
get_class_or_enum_flat_representation(const type_base& coe,
const string& indent,
bool one_line,
bool internal,
bool qualified_name);
string
get_debug_representation(const type_or_decl_base*);
@@ -1056,6 +1177,9 @@ debug(const decl_base* artifact);
bool
debug_equals(const type_or_decl_base *l, const type_or_decl_base *r);
void
debug_comp_stack(const environment& env);
bool
odr_is_relevant(const type_or_decl_base&);
@@ -1137,6 +1261,9 @@ lookup_class_type(const interned_string&, const corpus&);
const type_base_wptrs_type*
lookup_class_types(const interned_string&, const corpus&);
const type_base_wptrs_type*
lookup_union_types(const interned_string&, const corpus&);
bool
lookup_decl_only_class_types(const interned_string&,
const corpus&,
@@ -1145,6 +1272,9 @@ lookup_decl_only_class_types(const interned_string&,
const type_base_wptrs_type*
lookup_class_types(const string&, const corpus&);
const type_base_wptrs_type*
lookup_union_types(const string&, const corpus&);
class_decl_sptr
lookup_class_type_per_location(const interned_string&, const corpus&);
@@ -1414,6 +1544,12 @@ is_non_canonicalized_type(const type_base *);
bool
is_non_canonicalized_type(const type_base_sptr&);
bool
is_unique_type(const type_base_sptr&);
bool
is_unique_type(const type_base*);
/// For a given type, return its exemplar type.
///
/// For a given type, its exemplar type is either its canonical type
@@ -1445,6 +1581,13 @@ build_internal_underlying_enum_type_name(const string &base_name,
bool is_anonymous,
uint64_t size);
var_decl_sptr
find_first_data_member_matching_regexp(const class_or_union& t,
const regex::regex_t_sptr& r);
var_decl_sptr
find_last_data_member_matching_regexp(const class_or_union& t,
const regex::regex_t_sptr& regex);
} // end namespace ir
using namespace abigail::ir;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+101 -13
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -162,9 +162,15 @@ public:
const type_base_sptr&
get_void_type() const;
const type_base_sptr&
get_void_pointer_type() const;
const type_base_sptr&
get_variadic_parameter_type() const;
static string&
get_variadic_parameter_type_name();
bool
canonicalization_is_done() const;
@@ -189,6 +195,12 @@ public:
bool
is_void_type(const type_base*) const;
bool
is_void_pointer_type(const type_base_sptr&) const;
bool
is_void_pointer_type(const type_base*) const;
bool
is_variadic_parameter_type(const type_base*) const;
@@ -244,17 +256,26 @@ public:
type_base* get_canonical_type(const char* name, unsigned index);
#ifdef WITH_DEBUG_SELF_COMPARISON
unordered_map<string, uintptr_t>&
const unordered_map<string, uintptr_t>&
get_type_id_canonical_type_map() const;
unordered_map<string, uintptr_t>&
get_type_id_canonical_type_map();
const unordered_map<uintptr_t, string>&
get_pointer_type_id_map() const;
unordered_map<uintptr_t, string>&
get_pointer_type_id_map();
string
get_type_id_from_pointer(uintptr_t ptr);
get_type_id_from_pointer(uintptr_t ptr) const;
string
get_type_id_from_type(const type_base *ptr) const;
uintptr_t
get_canonical_type_from_type_id(const char*);
get_canonical_type_from_type_id(const char*) const;
#endif
friend class class_or_union;
@@ -537,6 +558,10 @@ typedef unordered_set<const type_or_decl_base*,
/// value is a @ref type_base_wptr.
typedef unordered_map<string, type_base_wptr> string_type_base_wptr_map_type;
/// A convenience typedef for a map which key is a string and which
/// value is a @ref type_base_sptr.
typedef unordered_map<string, type_base_sptr> string_type_base_sptr_map_type;
/// A convenience typedef for a map which key is an @ref
/// interned_string and which value is a @ref type_base_wptr.
typedef unordered_map<interned_string, type_base_wptr, hash_interned_string>
@@ -1090,6 +1115,9 @@ public:
elf_symbol_sptr
get_alias_which_equals(const elf_symbol& other) const;
elf_symbol_sptr
get_alias_with_default_symbol_version() const;
string
get_aliases_id_string(const string_elf_symbols_map_type& symtab,
bool include_symbol_itself = true) const;
@@ -1684,15 +1712,15 @@ public:
void
set_is_declaration_only(bool f);
friend type_base_sptr
canonicalize(type_base_sptr);
friend bool
equals(const decl_base&, const decl_base&, change_kind*);
friend bool
equals(const var_decl&, const var_decl&, change_kind*);
friend bool
var_equals_modulo_types(const var_decl&, const var_decl&, change_kind*);
friend bool
maybe_compare_as_member_decls(const decl_base& l,
const decl_base& r,
@@ -1884,9 +1912,6 @@ public:
friend void
remove_decl_from_scope(decl_base_sptr decl);
friend type_base_sptr
canonicalize(type_base_sptr);
};//end class scope_decl
bool
@@ -2093,7 +2118,14 @@ public:
virtual bool
operator==(const type_decl&) const;
bool operator!=(const type_decl&)const;
virtual bool
operator!=(const type_base&)const;
virtual bool
operator!=(const decl_base&)const;
virtual bool
operator!=(const type_decl&)const;
virtual void
get_qualified_name(interned_string& qualified_name,
@@ -2353,8 +2385,8 @@ equals(const reference_type_def&, const reference_type_def&, change_kind*);
/// Abstracts a reference type.
class reference_type_def : public virtual type_base, public virtual decl_base
{
type_base_wptr pointed_to_type_;
bool is_lvalue_;
struct priv;
std::unique_ptr<priv> priv_;
// Forbidden.
reference_type_def();
@@ -2553,6 +2585,12 @@ public:
bool
operator==(const subrange_type& o) const;
bool
operator!=(const decl_base& o) const;
bool
operator!=(const type_base& o) const;
bool
operator!=(const subrange_type& o) const;
@@ -2635,6 +2673,11 @@ is_subrange_type(const type_or_decl_base *type);
array_type_def::subrange_sptr
is_subrange_type(const type_or_decl_base_sptr &type);
bool
equals(const array_type_def::subrange_type&,
const array_type_def::subrange_type&,
change_kind*);
bool
equals(const enum_type_decl&, const enum_type_decl&, change_kind*);
@@ -2685,6 +2728,9 @@ public:
const enumerators&
get_enumerators() const;
const enumerators&
get_sorted_enumerators() const;
enumerators&
get_enumerators();
@@ -2767,6 +2813,10 @@ public:
set_enum_type(enum_type_decl*);
}; // end class enum_type_def::enumerator
bool
is_enumerator_present_in_enum(const enum_type_decl::enumerator &enr,
const enum_type_decl &enom);
bool
equals(const typedef_decl&, const typedef_decl&, change_kind*);
@@ -2818,6 +2868,13 @@ public:
void
set_underlying_type(const type_base_sptr&);
virtual void
get_qualified_name(interned_string& qualified_name,
bool internal = false) const;
virtual const interned_string&
get_qualified_name(bool internal = false) const;
virtual bool
traverse(ir_node_visitor&);
@@ -2876,6 +2933,9 @@ public:
bool
equals(const var_decl&, const var_decl&, change_kind*);
bool
var_equals_modulo_types(const var_decl&, const var_decl&, change_kind*);
bool
equals_modulo_cv_qualifier(const array_type_def*, const array_type_def*);
@@ -2912,6 +2972,9 @@ public:
const type_base_sptr
get_type() const;
void
set_type(type_base_sptr&);
const type_base*
get_naked_type() const;
@@ -3431,6 +3494,9 @@ public:
const std::list<template_parameter_sptr>&
get_template_parameters() const;
virtual bool
operator==(const decl_base& o) const;
virtual bool
operator==(const template_decl& o) const;
@@ -3507,6 +3573,12 @@ public:
virtual bool
operator==(const type_base&) const;
virtual bool
operator==(const type_decl&) const;
virtual bool
operator==(const decl_base&) const;
virtual bool
operator==(const template_parameter&) const;
@@ -3585,6 +3657,9 @@ public:
virtual bool
operator==(const type_base&) const;
virtual bool
operator==(const decl_base&) const;
virtual bool
operator==(const template_parameter&) const;
@@ -4205,6 +4280,9 @@ public:
virtual bool
operator==(const type_base&) const;
virtual bool
operator==(const class_or_union&) const;
virtual bool
operator==(const class_decl&) const;
@@ -4376,6 +4454,9 @@ public:
virtual bool
operator==(const type_base&) const;
virtual bool
operator==(const class_or_union&) const;
virtual bool
operator==(const union_decl&) const;
@@ -4545,6 +4626,10 @@ const var_decl*
lookup_data_member(const type_base* type,
const char* dm_name);
const var_decl_sptr
lookup_data_member(const type_base_sptr& type,
const var_decl_sptr& dm);
const function_decl::parameter*
get_function_parameter(const decl_base* fun,
unsigned parm_num);
@@ -4635,6 +4720,9 @@ public:
virtual bool
operator==(const member_base& o) const;
virtual bool
operator==(const decl_base&) const;
virtual bool
operator==(const member_class_template&) const;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
///
+19 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -32,6 +32,7 @@ class qualified_type_diff;
class distinct_diff;
class pointer_diff;
class reference_diff;
class subrange_diff;
class array_diff;
class base_diff;
class class_or_union_diff;
@@ -92,6 +93,10 @@ public:
report(const array_diff& d, std::ostream& out,
const std::string& indent = "") const = 0;
virtual void
report(const subrange_diff& d, std::ostream& out,
const std::string& indent = "") const = 0;
virtual void
report(const base_diff& d, std::ostream& out,
const std::string& indent = "") const = 0;
@@ -173,6 +178,11 @@ public:
std::ostream& out,
const std::string& indent) const;
void
report_underlying_changes_of_qualified_type (const qualified_type_diff& d,
ostream& out,
const string& indent) const;
virtual void
report(const qualified_type_diff& d, std::ostream& out,
const std::string& indent = "") const;
@@ -203,6 +213,10 @@ public:
report(const function_type_diff& d, std::ostream& out,
const std::string& indent = "") const;
virtual void
report(const subrange_diff& d, std::ostream& out,
const std::string& indent = "") const;
virtual void
report(const array_diff& d, std::ostream& out,
const std::string& indent = "") const;
@@ -285,6 +299,10 @@ public:
report(const function_type_diff& d, std::ostream& out,
const std::string& indent = "") const;
virtual void
report(const subrange_diff& d, std::ostream& out,
const std::string& indent = "") const;
virtual void
report(const array_diff& d, std::ostream& out,
const std::string& indent = "") const;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
///
+136 -9
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -11,7 +11,7 @@
#include <unordered_set>
#include "abg-ini.h"
#include "abg-comparison.h"
#include "abg-ir.h"
namespace abigail
{
@@ -29,15 +29,24 @@ class fe_iface;
/// that are defined in this namespace.
namespace suppr
{
using namespace abigail::comparison;
using std::unordered_set;
using std::string;
using std::shared_ptr;
using std::vector;
using comparison::diff;
using comparison::diff_context_sptr;
/// Base type of the suppression specifications types.
/// Base type of a direct suppression specifications types.
///
/// This abstracts a suppression specification. It's a way to specify
/// how to drop reports about a particular diff node on the floor, if
/// it matches the supppression specification.
///
/// Note that a direct suppression specification suppresses (for
/// reporting purposes) the diff node that it matches. A negated
/// suppression specification, however, suppresses a diff node that it
/// DOES NOT match. A Negated suppression specification is abstracted
/// by the class @ref negated_suppression_base.
class suppression_base
{
public:
@@ -140,6 +149,36 @@ typedef shared_ptr<type_suppression> type_suppression_sptr;
/// Convenience typedef for vector of @ref type_suppression_sptr.
typedef vector<type_suppression_sptr> type_suppressions_type;
/// The base class of suppression specifications that are defined by
/// the negation of matching clauses.
///
/// A direct suppression specification suppresses (for reporting
/// purposes) the diff node that it matches. A negated suppression
/// specification suppresses a diff node that it DOES NOT match.
class negated_suppression_base
{
public:
negated_suppression_base();
virtual ~negated_suppression_base();
}; // end class negated_suppression_base.
/// A convenience typedef for a shared pointer to @ref
/// negated_suppression_base.
typedef shared_ptr<negated_suppression_base> negated_suppression_sptr;
/// Convenience typedef for a vector of @ref negated_suppression_sptr
typedef vector<negated_suppression_sptr> negated_suppressions_type;
bool
is_negated_suppression(const suppression_base&);
const negated_suppression_base*
is_negated_suppression(const suppression_base*);
negated_suppression_sptr
is_negated_suppression(const suppression_sptr&);
/// Abstraction of a type suppression specification.
///
/// Specifies under which condition reports about a type diff node
@@ -243,6 +282,24 @@ public:
void
set_reach_kind(reach_kind k);
bool
get_has_size_change() const;
void
set_has_size_change(bool flag);
const string_set_type&
get_potential_data_member_names() const;
void
set_potential_data_member_names(const string_set_type&) const;
const string&
get_potential_data_member_names_regex_str() const;
void
set_potential_data_member_names_regex_str(const string&) const;
void
set_data_member_insertion_ranges(const insertion_ranges& r);
@@ -273,6 +330,12 @@ public:
void
set_changed_enumerator_names(const vector<string>&);
const vector<regex::regex_t_sptr>&
get_changed_enumerators_regexp() const;
void
set_changed_enumerators_regexp(const vector<regex::regex_t_sptr>&);
virtual bool
suppresses_diff(const diff* diff) const;
@@ -303,6 +366,7 @@ public:
class boundary;
class integer_boundary;
class fn_call_expr_boundary;
class named_boundary;
/// Convenience typedef for a shared_ptr to @ref boundary
typedef shared_ptr<boundary> boundary_sptr;
@@ -314,6 +378,10 @@ public:
/// fn_call_expr_boundary
typedef shared_ptr<fn_call_expr_boundary> fn_call_expr_boundary_sptr;
/// Convenience typedef for a shared_ptr to a @ref
/// named_boundary
typedef shared_ptr<named_boundary> named_boundary_sptr;
insertion_range();
insertion_range(boundary_sptr begin, boundary_sptr end);
@@ -321,7 +389,7 @@ public:
boundary_sptr
begin() const;
boundary_sptr
boundary_sptr
end() const;
static insertion_range::integer_boundary_sptr
@@ -333,10 +401,13 @@ public:
static insertion_range::fn_call_expr_boundary_sptr
create_fn_call_expr_boundary(const string&);
static insertion_range::named_boundary_sptr
create_named_boundary(const string&);
static bool
eval_boundary(boundary_sptr boundary,
class_decl_sptr context,
uint64_t& value);
eval_boundary(const boundary_sptr boundary,
const class_or_union* context,
uint64_t& value);
static bool
boundary_value_is_end(uint64_t value);
@@ -348,6 +419,9 @@ is_integer_boundary(type_suppression::insertion_range::boundary_sptr);
type_suppression::insertion_range::fn_call_expr_boundary_sptr
is_fn_call_expr_boundary(type_suppression::insertion_range::boundary_sptr);
type_suppression::insertion_range::named_boundary_sptr
is_named_boundary(type_suppression::insertion_range::boundary_sptr);
/// The abstraction of the boundary of an @ref insertion_range, in the
/// context of a @ref type_suppression
class type_suppression::insertion_range::boundary
@@ -395,6 +469,53 @@ public:
~fn_call_expr_boundary();
}; //end class type_suppression::insertion_range::fn_call_expr_boundary
/// An @ref insertion_range boundary that is expressed as a named
/// constant that is to be evaluated later in the context of a given
/// type and resolved to a bit offset.
class type_suppression::insertion_range::named_boundary
: public type_suppression::insertion_range::boundary
{
struct priv;
std::unique_ptr<priv> priv_;
named_boundary();
public:
named_boundary(const string& name);
const string& get_name() const;
}; //end class type_suppression::insertion_range::named_boundary
/// Abstraction of a negated type suppression specification.
///
/// A negated type suppression suppresses a type if the negation of
/// the equivalent propositions for a @ref type_suppression are valid.
class negated_type_suppression : virtual public type_suppression,
virtual public negated_suppression_base
{
public:
negated_type_suppression(const string& label,
const string& type_name_regexp,
const string& type_name);
virtual bool
suppresses_diff(const diff* diff) const;
bool
suppresses_type(const type_base_sptr& type,
const diff_context_sptr& ctxt) const;
bool
suppresses_type(const type_base_sptr& type) const;
bool
suppresses_type(const type_base_sptr& type,
const scope_decl* type_scope) const;
virtual ~negated_type_suppression();
};// end class negated_type_suppression
class function_suppression;
/// Convenience typedef for a shared pointer to function_suppression.
@@ -887,6 +1008,12 @@ is_type_suppressed(const fe_iface& fe,
const location& type_location,
bool& type_is_private,
bool require_drop_property = false);
bool
is_data_member_offset_in_range(const var_decl_sptr&,
const type_suppression::insertion_range_sptr&,
const class_or_union*);
} // end namespace suppr
+44 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
///@file
@@ -30,10 +30,12 @@ using std::string;
using std::set;
using std::shared_ptr;
void initialize();
const char* get_system_libdir();
const char* get_anonymous_struct_internal_name_prefix();
const char* get_anonymous_union_internal_name_prefix();
const char* get_anonymous_enum_internal_name_prefix();
const char* get_anonymous_subrange_internal_name_prefix();
bool file_exists(const string&);
bool is_regular_file(const string&);
@@ -41,6 +43,8 @@ bool file_has_dwarf_debug_info(const string& elf_file_path,
const vector<char**>& debug_info_root_paths);
bool file_has_ctf_debug_info(const string& elf_file_path,
const vector<char**>& debug_info_root_paths);
bool file_has_btf_debug_info(const string& elf_file_path,
const vector<char**>& debug_info_root_paths);
bool is_dir(const string&);
bool dir_exists(const string&);
bool dir_is_empty(const string &);
@@ -68,6 +72,9 @@ bool sorted_strings_common_prefix(vector<string>&, string&);
string get_library_version_string();
string get_abixml_version_string();
bool execute_command_and_get_output(const string&, vector<string>&);
void get_comma_separated_args_of_option(const string& input_str,
const string& option,
vector<string>& arguments);
bool get_dsos_provided_by_rpm(const string& rpm_path,
set<string>& provided_dsos);
string trim_white_space(const string&);
@@ -122,6 +129,38 @@ find_file_under_dir(const string& root_dir,
const string& file_path_to_look_for,
string& result);
bool
find_file_under_dirs(const vector<string>& root_dirs,
const string& file_path_to_look_for,
string& result);
bool
get_dependencies(const corpus&, const vector<string>&, set<string>&);
void
add_binaries_into_corpus_group(const fe_iface_sptr& reader,
const vector<string>& binaries,
const vector<string>& deps_dirs,
corpus_group& group);
void
add_dependencies_into_corpus_group(const fe_iface_sptr& reader,
const corpus& korpus,
const vector<string>& deps_dirs,
corpus_group& group);
corpus_group_sptr
stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr& reader,
const corpus_sptr& korpus,
const vector<string>& binaries,
const vector<string>& deps_dirs);
corpus_group_sptr
stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr& reader,
const corpus_sptr& korpus,
const vector<string>& deps_dirs);
class temp_file;
/// Convenience typedef for a shared_ptr to @ref temp_file.
@@ -298,6 +337,10 @@ bool
file_is_kernel_package(const string& file_path,
file_type file_type);
bool
rpm_contains_file(const string& rpm_path,
const string& file_name);
bool
file_is_kernel_debuginfo_package(const string& file_path,
file_type file_type);
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+2 -2
View File
@@ -2,9 +2,9 @@
#ifndef __ABG_VERSION_H__
#define __ABG_VERSION_H__
#define ABIGAIL_VERSION_MAJOR "2"
#define ABIGAIL_VERSION_MINOR "2"
#define ABIGAIL_VERSION_MINOR "4"
#define ABIGAIL_VERSION_REVISION "0"
#define ABIGAIL_VERSION_SUFFIX ""
#define ABIGAIL_ABIXML_VERSION_MAJOR "2"
#define ABIGAIL_ABIXML_VERSION_MINOR "1"
#define ABIGAIL_ABIXML_VERSION_MINOR "2"
#endif
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+4
View File
@@ -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)
File diff suppressed because it is too large Load Diff
+96 -51
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -41,7 +41,7 @@ void
apply_filter(filter_base& filter, corpus_diff_sptr d)
{
bool s = d->context()->visiting_a_node_twice_is_forbidden();
d->context()->forbid_visiting_a_node_twice(false);
d->context()->forbid_visiting_a_node_twice(true);
d->traverse(filter);
d->context()->forbid_visiting_a_node_twice(s);
}
@@ -633,6 +633,85 @@ has_data_member_replaced_by_anon_dm(const diff* diff)
return !c->data_members_replaced_by_adms().empty();
}
/// Test if we are looking at two variables which types are both one
/// dimension array, with one of them being of unknow size and the two
/// variables having the same symbol size.
///
/// This can happen in the case of these two declarations, for instance:
///
/// unsigned int array[];
///
/// and:
///
/// unsigned int array[] ={0};
///
/// In both cases, the size of the ELF symbol of the variable 'array'
/// is 32 bits, but, at least in the first case
bool
is_var_1_dim_unknown_size_array_change(const var_decl_sptr& var1,
const var_decl_sptr& var2)
{
type_base_sptr /*first type*/ft =
peel_qualified_or_typedef_type(var1->get_type());
type_base_sptr /*second type*/st =
peel_qualified_or_typedef_type(var2->get_type());
array_type_def_sptr /*first array type*/fat = is_array_type(ft);
array_type_def_sptr /*second array type*/sat = is_array_type(st);
// The types of the variables must be arrays.
if (!fat || !sat)
return false;
// The arrays must have one dimension and at least one of them must
// be of unknown size.
if (fat->get_subranges().size() != 1
|| sat->get_subranges().size() != 1
|| (!fat->is_infinite() && !sat->is_infinite()))
return false;
// The variables must be equal modulo their type.
if (!var_equals_modulo_types(*var1, *var2, nullptr))
return false;
// The symbols of the variables must be defined and of the same
// non-zero size.
if (!var1->get_symbol()
|| !var2->get_symbol()
|| var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
return false;
return true;
}
/// Test if we are looking at a diff that carries a change of
/// variables which types are both one dimension array, with one of
/// them being of unknow size and the two variables having the same
/// symbol size.
///
/// This can happen in the case of these two declarations, for instance:
///
/// unsigned int array[];
///
/// and:
///
/// unsigned int array[] ={0};
///
/// In both cases, the size of the ELF symbol of the variable 'array'
/// is 32 bits, but, at least in the first case
bool
is_var_1_dim_unknown_size_array_change(const diff* diff)
{
const var_diff* d = is_var_diff(diff);
if (!d)
return false;
var_decl_sptr f = d->first_var(), s = d->second_var();
return is_var_1_dim_unknown_size_array_change(f, s);
}
/// Test if a class_diff node has static members added or removed.
///
/// @param diff the diff node to consider.
@@ -696,14 +775,10 @@ class_diff_has_harmless_odr_violation_change(const diff* dif)
class_decl_sptr first = d->first_class_decl();
class_decl_sptr second = d->second_class_decl();
if (equals(*first, *second, 0))
{
class_decl_sptr fc = is_class_type(first->get_canonical_type());
class_decl_sptr sc = is_class_type(second->get_canonical_type());
if (!equals(*fc, *sc, 0))
return true;
}
if (first->get_qualified_name() == second->get_qualified_name()
&& first != second
&& first->get_corpus() == second->get_corpus())
return true;
return false;
}
@@ -1564,18 +1639,9 @@ has_var_type_cv_qual_change(const diff* dif)
if (!var_dif)
return false;
{
// Make sure the variable diff does carry a type change at least
change_kind ch_kind = NO_CHANGE_KIND;
if (equals(*var_dif->first_var(), *var_dif->second_var(), &ch_kind))
return false;
if (!(ch_kind & LOCAL_TYPE_CHANGE_KIND || ch_kind & SUBTYPE_CHANGE_KIND))
return false;
}
diff *type_dif = var_dif->type_diff().get();
ABG_ASSERT(type_dif);
if (!type_dif)
return false;
return type_diff_has_cv_qual_change_only(type_dif);
}
@@ -1601,9 +1667,9 @@ has_void_ptr_to_ptr_change(const diff* dif)
f = peel_qualified_or_typedef_type(f);
s = peel_qualified_or_typedef_type(s);
if (is_void_pointer_type(f)
if (is_void_pointer_type_equivalent(f)
&& is_pointer_type(s)
&& !is_void_pointer_type(s)
&& !is_void_pointer_type_equivalent(s)
&& f->get_size_in_bits() == s->get_size_in_bits())
return true;
}
@@ -1615,9 +1681,9 @@ has_void_ptr_to_ptr_change(const diff* dif)
f = peel_qualified_or_typedef_type(f);
s = peel_qualified_or_typedef_type(s);
if (is_void_pointer_type(f)
if (is_void_pointer_type_equivalent(f)
&& is_pointer_type(s)
&& !is_void_pointer_type(s)
&& !is_void_pointer_type_equivalent(s)
&& f->get_size_in_bits() == s->get_size_in_bits())
return true;
}
@@ -1629,9 +1695,9 @@ has_void_ptr_to_ptr_change(const diff* dif)
f = peel_qualified_or_typedef_type(f);
s = peel_qualified_or_typedef_type(s);
if (is_void_pointer_type(f)
if (is_void_pointer_type_equivalent(f)
&& is_pointer_type(s)
&& !is_void_pointer_type(s)
&& !is_void_pointer_type_equivalent(s)
&& f->get_size_in_bits() == s->get_size_in_bits())
return true;
}
@@ -1652,30 +1718,9 @@ has_void_ptr_to_ptr_change(const diff* dif)
///
/// @return true iff @p dif contains the benign array type size change.
static bool
has_benign_infinite_array_change(const diff* dif)
has_benign_array_of_unknown_size_change(const diff* dif)
{
if (const var_diff* var_dif = is_var_diff(dif))
{
if (!var_dif->first_var()->get_symbol()
|| var_dif->second_var()->get_symbol())
return false;
if (var_dif->first_var()->get_symbol()->get_size()
!= var_dif->second_var()->get_symbol()->get_size())
return false;
const diff *d = var_dif->type_diff().get();
if (!d)
return false;
d = peel_qualified_diff(d);
if (const array_diff *a = is_array_diff(d))
{
array_type_def_sptr f = a->first_array(), s = a->second_array();
if (f->is_infinite() != s->is_infinite())
return true;
}
}
return false;
return is_var_1_dim_unknown_size_array_change(dif);
}
/// Test if a union diff node does have changes that don't impact its
@@ -1769,7 +1814,7 @@ categorize_harmless_diff_node(diff *d, bool pre)
if (has_void_ptr_to_ptr_change(d))
category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
if (has_benign_infinite_array_change(d))
if (has_benign_array_of_unknown_size_change(d))
category |= BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY;
if (category)
+37 -2
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -174,11 +174,22 @@ struct diff_context::priv
unordered_diff_sptr_set live_diffs_;
vector<diff_sptr> canonical_diffs;
vector<filtering::filter_base_sptr> filters_;
// All the suppressions specifications are stored in this data
// member.
suppressions_type suppressions_;
// The negated suppressions specifications that are in
// suppressions_ are stored here. Each time suppressions_ is
// modified, this data member should be cleared.
suppressions_type negated_suppressions_;
// The non-negated suppressions specifications that are in
// suppressions_ are stored here. Each time suppressions_ is
// modified, this data member should be cleared.
suppressions_type direct_suppressions_;
pointer_map visited_diff_nodes_;
corpus_diff_sptr corpus_diff_;
ostream* default_output_stream_;
ostream* error_output_stream_;
bool perform_change_categorization_;
bool leaf_changes_only_;
bool forbid_visiting_a_node_twice_;
bool reset_visited_diffs_for_each_interface_;
@@ -202,12 +213,14 @@ struct diff_context::priv
bool show_unreachable_types_;
bool show_impacted_interfaces_;
bool dump_diff_tree_;
bool do_log_;
priv()
: allowed_category_(EVERYTHING_CATEGORY),
reporter_(),
default_output_stream_(),
error_output_stream_(),
perform_change_categorization_(true),
leaf_changes_only_(),
forbid_visiting_a_node_twice_(true),
reset_visited_diffs_for_each_interface_(),
@@ -230,7 +243,8 @@ struct diff_context::priv
show_added_syms_unreferenced_by_di_(true),
show_unreachable_types_(false),
show_impacted_interfaces_(true),
dump_diff_tree_()
dump_diff_tree_(),
do_log_()
{}
};// end struct diff_context::priv
@@ -304,6 +318,13 @@ public:
if (ctxt->get_allowed_category() == EVERYTHING_CATEGORY)
return false;
// If this node is on the path of a node that *must* be reported,
// then do not filter it.
if (category & (HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
| HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
| HAS_ALLOWED_CHANGE_CATEGORY))
return false;
/// We don't want to display nodes suppressed by a user-provided
/// suppression specification or by a "private type" suppression
/// specification.
@@ -393,6 +414,17 @@ struct pointer_diff::priv
{}
};//end struct pointer_diff::priv
/// The internal type for the impl idiom implementation of @ref
/// subrange_diff.
struct subrange_diff::priv
{
diff_sptr underlying_type_diff_;
priv(diff_sptr u)
: underlying_type_diff_(u)
{}
}; // end struct subrange_diff::priv
struct array_diff::priv
{
/// The diff between the two array element types.
@@ -1127,6 +1159,9 @@ struct corpus_diff::priv
size_t &num_filtered_removed,
size_t &num_filtered_changed);
const string_diff_sptr_map&
changed_unreachable_types() const;
const vector<diff_sptr>&
changed_unreachable_types_sorted() const;
+916 -69
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+75 -27
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
/// @file
///
@@ -40,6 +40,12 @@ typedef vector<regex_t_sptr> regex_t_sptrs_type;
/// Convenience typedef for a hash map which key is a string and which
/// data is a vector of abigail::ir::function_decl*
typedef unordered_map<string, vector<function_decl*> > str_fn_ptrs_map_type;
/// Convenience typedef for a hash map which key is a string and which
/// data is a set of abigail::ir::function_decl*
typedef unordered_map<string, std::unordered_set<function_decl*> >
str_fn_ptr_set_map_type;
/// Convenience typedef for a hash map which key is a string and
/// which data is an abigail::ir::var_decl*.
typedef unordered_map<string, var_decl*> str_var_ptr_map_type;
@@ -63,7 +69,7 @@ class corpus::exported_decls_builder::priv
// template parameters of the second instantiation are just typedefs
// of the first instantiation, for instance. So there can be cases
// where one ID appertains to more than one function.
str_fn_ptrs_map_type id_fns_map_;
str_fn_ptr_set_map_type id_fns_map_;
str_var_ptr_map_type id_var_map_;
strings_type& fns_suppress_regexps_;
regex_t_sptrs_type compiled_fns_suppress_regexp_;
@@ -197,7 +203,7 @@ public:
///
/// @return a map which key is a string and which data is a pointer
/// to a function.
const str_fn_ptrs_map_type&
const str_fn_ptr_set_map_type&
id_fns_map() const
{return id_fns_map_;}
@@ -210,7 +216,7 @@ public:
///
/// @return a map which key is a string and which data is a pointer
/// to a function.
str_fn_ptrs_map_type&
str_fn_ptr_set_map_type&
id_fns_map()
{return id_fns_map_;}
@@ -267,11 +273,11 @@ public:
///
/// @return the pointer to the vector of functions with ID @p fn_id,
/// or nil if no function with that ID exists.
vector<function_decl*>*
std::unordered_set<function_decl*>*
fn_id_is_in_id_fns_map(const string& fn_id)
{
str_fn_ptrs_map_type& m = id_fns_map();
str_fn_ptrs_map_type::iterator i = m.find(fn_id);
str_fn_ptr_set_map_type& m = id_fns_map();
str_fn_ptr_set_map_type::iterator i = m.find(fn_id);
if (i == m.end())
return 0;
return &i->second;
@@ -286,47 +292,87 @@ public:
/// @p fn, that are present in the id-functions map, or nil if no
/// function with the same ID as @p fn is present in the
/// id-functions map.
vector<function_decl*>*
std::unordered_set<function_decl*>*
fn_id_is_in_id_fns_map(const function_decl* fn)
{
string fn_id = fn->get_id();
return fn_id_is_in_id_fns_map(fn_id);
}
/// Test if a given function is present in a vector of functions.
/// Test if a given function is present in a set of functions.
///
/// The function compares the ID and the qualified name of
/// functions.
///
/// @param fn the function to consider.
///
/// @parm fns the vector of functions to consider.
/// @parm fns the set of functions to consider.
static bool
fn_is_in_fns(const function_decl* fn, const vector<function_decl*>& fns)
fn_is_in_fns(function_decl* fn,
const std::unordered_set<function_decl*>& fns)
{
if (fns.empty())
return false;
if (fns.find(fn) != fns.end())
return true;
const string fn_id = fn->get_id();
for (vector<function_decl*>::const_iterator i = fns.begin();
i != fns.end();
++i)
if ((*i)->get_id() == fn_id
&& (*i)->get_qualified_name() == fn->get_qualified_name())
for (const auto f : fns)
if (f->get_id() == fn_id
&& f->get_qualified_name() == fn->get_qualified_name())
return true;
return false;
}
/// Test if a given function is present in a set of functions,
/// by looking at the pretty representation of the function, in
/// addition to looking at its ID.
///
/// This is useful because sometimes a given ELF symbol (alias)
/// might be for several different functions. In that case, using
/// the function pretty representation might be a way to
/// differentiate the functions having the same ELF symbol alias.
///
/// The function compares the ID and the qualified name of
/// functions.
///
/// @param fn the function to consider.
///
/// @parm fns the set of functions to consider.
///
/// @return true if @p fn is present in @p fns.
static bool
fn_is_in_fns_by_repr(function_decl* fn,
const std::unordered_set<function_decl*>& fns,
string& pretty_representation)
{
if (!fn_is_in_fns(fn, fns))
return false;
const string repr = fn->get_pretty_representation();
const string fn_id = fn->get_id();
for (const auto f : fns)
if (f->get_id() == fn_id
&& f->get_pretty_representation() == repr)
{
pretty_representation = repr;
return true;
}
return false;
}
/// Test if a function is in the id-functions map.
///
/// @param fn the function to consider.
///
/// @return true iff the function is in the id-functions map.
bool
fn_is_in_id_fns_map(const function_decl* fn)
fn_is_in_id_fns_map(function_decl* fn)
{
vector<function_decl*>* fns = fn_id_is_in_id_fns_map(fn);
std::unordered_set<function_decl*>* fns = fn_id_is_in_id_fns_map(fn);
if (fns && fn_is_in_fns(fn, *fns))
return true;
return false;
@@ -344,10 +390,10 @@ public:
// First associate the function id to the function.
string fn_id = fn->get_id();
vector<function_decl*>* fns = fn_id_is_in_id_fns_map(fn_id);
std::unordered_set<function_decl*>* fns = fn_id_is_in_id_fns_map(fn_id);
if (!fns)
fns = &(id_fns_map()[fn_id] = vector<function_decl*>());
fns->push_back(fn);
fns = &(id_fns_map()[fn_id] = std::unordered_set<function_decl*>());
fns->insert(fn);
// Now associate all aliases of the underlying symbol to the
// function too.
@@ -361,8 +407,8 @@ public:
goto loop;
fns = fn_id_is_in_id_fns_map(fn_id);
if (!fns)
fns = &(id_fns_map()[fn_id] = vector<function_decl*>());
fns->push_back(fn);
fns = &(id_fns_map()[fn_id] = std::unordered_set<function_decl*>());
fns->insert(fn);
loop:
sym = sym->get_next_alias();
}
@@ -403,12 +449,12 @@ public:
///
/// @param fn the function to add to the set of exported functions.
void
add_fn_to_exported(const function_decl* fn)
add_fn_to_exported(function_decl* fn)
{
if (!fn_is_in_id_fns_map(fn))
{
fns_.push_back(const_cast<function_decl*>(fn));
add_fn_to_id_fns_map(const_cast<function_decl*>(fn));
fns_.push_back(fn);
add_fn_to_id_fns_map(fn);
}
}
@@ -701,6 +747,7 @@ struct corpus::priv
type_maps type_per_loc_map_;
mutable vector<type_base_wptr> types_not_reachable_from_pub_ifaces_;
unordered_set<interned_string, hash_interned_string> *pub_type_pretty_reprs_;
bool do_log;
private:
priv();
@@ -723,7 +770,8 @@ public:
group(),
origin_(ARTIFICIAL_ORIGIN),
path(p),
pub_type_pretty_reprs_()
pub_type_pretty_reprs_(),
do_log()
{}
type_maps&
+103 -25
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
@@ -13,6 +13,7 @@
#include <cstring>
#include <stdexcept>
#include <unordered_map>
#include <set>
#include "abg-internal.h"
@@ -109,6 +110,28 @@ corpus::functions&
corpus::exported_decls_builder::exported_functions()
{return priv_->fns_;}
/// Test if a given function ID maps to several functions in the same corpus.
///
/// The magic of ELF symbol aliases makes it possible for an ELF
/// symbol alias to designate several different functions. This
/// function tests if the ELF symbol of a given function has a aliases
/// that designates another function or not.
///
/// @param fn the function to consider.
///
/// @return the set of functions designated by the ELF symbol of @p
/// fn, or nullptr if the function ID maps to just @p fn.
std::unordered_set<function_decl*>*
corpus::exported_decls_builder::fn_id_maps_to_several_fns(function_decl* fn)
{
std::unordered_set<function_decl*> *fns_for_id =
priv_->fn_id_is_in_id_fns_map(fn);
if (fns_for_id && fns_for_id->size() > 1)
return fns_for_id;
return nullptr;
}
/// Getter for the reference to the vector of exported variables.
/// This vector is shared with with the @ref corpus. It's where the
/// set of exported variable is ultimately stored.
@@ -133,7 +156,7 @@ corpus::exported_decls_builder::exported_variables()
///
/// @param fn the function to add the set of exported functions.
void
corpus::exported_decls_builder::maybe_add_fn_to_exported_fns(const function_decl* fn)
corpus::exported_decls_builder::maybe_add_fn_to_exported_fns(function_decl* fn)
{
if (!fn->get_is_in_public_symbol_table())
return;
@@ -313,9 +336,15 @@ corpus::priv::get_sorted_fun_symbols() const
{
if (!sorted_fun_symbols)
{
auto filter = symtab_->make_filter();
filter.set_functions();
sorted_fun_symbols = elf_symbols(symtab_->begin(filter), symtab_->end());
if (symtab_)
{
auto filter = symtab_->make_filter();
filter.set_functions();
sorted_fun_symbols = elf_symbols(symtab_->begin(filter),
symtab_->end());
}
else
sorted_fun_symbols = elf_symbols();
}
return *sorted_fun_symbols;
}
@@ -349,13 +378,18 @@ corpus::priv::get_sorted_undefined_fun_symbols() const
{
if (!sorted_undefined_fun_symbols)
{
auto filter = symtab_->make_filter();
filter.set_functions();
filter.set_undefined_symbols();
filter.set_public_symbols(false);
if (symtab_)
{
auto filter = symtab_->make_filter();
filter.set_functions();
filter.set_undefined_symbols();
filter.set_public_symbols(false);
sorted_undefined_fun_symbols =
elf_symbols(symtab_->begin(filter), symtab_->end());
sorted_undefined_fun_symbols =
elf_symbols(symtab_->begin(filter), symtab_->end());
}
else
sorted_undefined_fun_symbols = elf_symbols();
}
return *sorted_undefined_fun_symbols;
}
@@ -446,10 +480,16 @@ corpus::priv::get_sorted_var_symbols() const
{
if (!sorted_var_symbols)
{
auto filter = symtab_->make_filter();
filter.set_variables();
if (symtab_)
{
auto filter = symtab_->make_filter();
filter.set_variables();
sorted_var_symbols = elf_symbols(symtab_->begin(filter), symtab_->end());
sorted_var_symbols = elf_symbols(symtab_->begin(filter),
symtab_->end());
}
else
sorted_var_symbols = elf_symbols();
}
return *sorted_var_symbols;
}
@@ -483,13 +523,18 @@ corpus::priv::get_sorted_undefined_var_symbols() const
{
if (!sorted_undefined_var_symbols)
{
auto filter = symtab_->make_filter();
filter.set_variables();
filter.set_undefined_symbols();
filter.set_public_symbols(false);
if (symtab_)
{
auto filter = symtab_->make_filter();
filter.set_variables();
filter.set_undefined_symbols();
filter.set_public_symbols(false);
sorted_undefined_var_symbols =
elf_symbols(symtab_->begin(filter), symtab_->end());
sorted_undefined_var_symbols =
elf_symbols(symtab_->begin(filter), symtab_->end());
}
else
sorted_undefined_var_symbols = elf_symbols();
}
return *sorted_undefined_var_symbols;
}
@@ -611,6 +656,20 @@ const environment&
corpus::get_environment() const
{return priv_->env;}
/// Test if logging was requested.
///
/// @return true iff logging was requested.
bool
corpus::do_log() const
{return priv_->do_log;}
/// Request logging, or not.
///
/// @param f true iff logging is requested.
void
corpus::do_log(bool f)
{priv_->do_log = f;}
/// Add a translation unit to the current ABI Corpus.
///
/// Note that two translation units with the same path (as returned by
@@ -715,7 +774,7 @@ corpus::recording_types_reachable_from_public_interface_supported()
void
corpus::record_type_as_reachable_from_public_interfaces(const type_base& t)
{
string repr = get_pretty_representation(&t, /*internal=*/true);
string repr = get_pretty_representation(&t, /*internal=*/false);
interned_string s = t.get_environment().intern(repr);
priv_->get_public_types_pretty_representations()->insert(s);
}
@@ -733,7 +792,7 @@ corpus::record_type_as_reachable_from_public_interfaces(const type_base& t)
bool
corpus::type_is_reachable_from_public_interfaces(const type_base& t) const
{
string repr = get_pretty_representation(&t, /*internal=*/true);
string repr = get_pretty_representation(&t, /*internal=*/false);
interned_string s = t.get_environment().intern(repr);
return (priv_->get_public_types_pretty_representations()->find(s)
@@ -1278,12 +1337,11 @@ corpus::get_functions() const
///
/// @return the vector functions which ID is @p id, or nil if no
/// function with that ID was found.
const vector<function_decl*>*
const std::unordered_set<function_decl*>*
corpus::lookup_functions(const string& id) const
{
exported_decls_builder_sptr b = get_exported_decls_builder();
str_fn_ptrs_map_type::const_iterator i =
b->priv_->id_fns_map_.find(id);
auto i = b->priv_->id_fns_map_.find(id);
if (i == b->priv_->id_fns_map_.end())
return 0;
return &i->second;
@@ -1597,6 +1655,7 @@ operator&=(corpus::origin &l, corpus::origin r)
/// Type of the private data of @ref corpus_group
struct corpus_group::priv
{
std::set<string> corpora_paths;
corpora_type corpora;
istring_function_decl_ptr_map_type fns_map;
vector<function_decl*> fns;
@@ -1686,6 +1745,10 @@ corpus_group::add_corpus(const corpus_sptr& corp)
if (!corp)
return;
if (!corp->get_path().empty()
&& has_corpus(corp->get_path()))
return;
// Ensure the new architecture name matches the current one.
string cur_arch = get_architecture_name(),
corp_arch = corp->get_architecture_name();
@@ -1701,6 +1764,7 @@ corpus_group::add_corpus(const corpus_sptr& corp)
priv_->corpora.push_back(corp);
corp->set_group(this);
priv_->corpora_paths.insert(corp->get_path());
/// Add the unreferenced function and variable symbols of this
/// corpus to the unreferenced symbols of the current corpus group.
@@ -1708,6 +1772,20 @@ corpus_group::add_corpus(const corpus_sptr& corp)
priv_->add_unref_var_symbols(get_unreferenced_variable_symbols());
}
/// Test if a corpus of a given path has been added to the group.
///
/// @param path the path to the corpus to consider.
///
/// @return true iff a corpus with path @p path is already present in
/// the group⋅
bool
corpus_group::has_corpus(const string& path)
{
if (priv_->corpora_paths.find(path) != priv_->corpora_paths.end())
return true;
return false;
}
/// Getter of the vector of corpora held by the current @ref
/// corpus_group.
///
+88 -31
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2021-2022 Oracle, Inc.
// Copyright (C) 2021-2023 Oracle, Inc.
//
// Author: Jose E. Marchesi
@@ -14,6 +14,7 @@
#include "config.h"
#include <fcntl.h> /* For open(3) */
#include <sstream>
#include <iostream>
#include <memory>
#include <map>
@@ -61,7 +62,15 @@ process_ctf_base_type(reader *rdr,
static decl_base_sptr
build_ir_node_for_variadic_parameter_type(reader &rdr,
translation_unit_sptr tunit);
const translation_unit_sptr& tunit);
static decl_base_sptr
build_ir_node_for_void_type(reader& rdr,
const translation_unit_sptr& tunit);
static type_or_decl_base_sptr
build_ir_node_for_void_pointer_type(reader& rdr,
const translation_unit_sptr& tunit);
static function_type_sptr
process_ctf_function_type(reader *rdr,
@@ -131,7 +140,7 @@ class reader : public elf_based_reader
/// A map associating CTF type ids with libabigail IR types. This
/// is used to reuse already generated types.
unordered_map<string,type_base_sptr> types_map;
string_type_base_sptr_map_type types_map;
/// A set associating unknown CTF type ids
std::set<ctf_id_t> unknown_types_set;
@@ -203,8 +212,10 @@ public:
void
canonicalize_all_types(void)
{
for (auto t = types_map.begin(); t != types_map.end(); t++)
canonicalize (t->second);
canonicalize_types
(types_map.begin(), types_map.end(),
[](const string_type_base_sptr_map_type::const_iterator& i)
{return i->second;});
}
/// Constructor.
@@ -277,7 +288,7 @@ public:
{
load_all_types = load_all_types;
linux_kernel_mode = linux_kernel_mode;
reset(elf_path, debug_info_root_paths);
elf_based_reader::initialize(elf_path, debug_info_root_paths);
}
/// Setter of the current translation unit.
@@ -334,12 +345,8 @@ public:
// for vmlinux.ctfa should be provided with --debug-info-dir
// option.
for (const auto& path : debug_info_root_paths())
{
ctfa_dirname = *path;
ctfa_file = ctfa_dirname + "/vmlinux.ctfa";
if (file_exists(ctfa_file))
return true;
}
if (tools_utils::find_file_under_dir(*path, "vmlinux.ctfa", ctfa_file))
return true;
return false;
}
@@ -426,10 +433,10 @@ public:
&& corpus_group())
{
tools_utils::base_name(corpus_path(), dict_name);
if (dict_name != "vmlinux")
// remove .ko suffix
dict_name.erase(dict_name.length() - 3, 3);
// remove .* suffix
std::size_t pos = dict_name.find(".");
if (pos != string::npos)
dict_name.erase(pos);
std::replace(dict_name.begin(), dict_name.end(), '-', '_');
}
@@ -729,7 +736,7 @@ process_ctf_typedef(reader *rdr,
const char *typedef_name = ctf_type_name_raw(ctf_dictionary, ctf_type);
if (corpus_sptr corp = rdr->should_reuse_type_from_corpus_group())
if (result = lookup_typedef_type(typedef_name, *corp))
if ((result = lookup_typedef_type(typedef_name, *corp)))
return result;
type_base_sptr utype = rdr->build_type(ctf_dictionary, ctf_utype);
@@ -801,8 +808,9 @@ process_ctf_base_type(reader *rdr,
&& type_encoding.cte_format == CTF_INT_SIGNED)
{
/* This is the `void' type. */
type_base_sptr void_type = rdr->env().get_void_type();
decl_base_sptr type_declaration = get_type_declaration(void_type);
decl_base_sptr type_declaration = build_ir_node_for_void_type(*rdr,
tunit);
type_base_sptr void_type = is_type(type_declaration);
result = is_type_decl(type_declaration);
canonicalize(result);
}
@@ -814,7 +822,7 @@ process_ctf_base_type(reader *rdr,
integral_type int_type;
if (parse_integral_type(type_name, int_type))
normalized_type_name = int_type.to_string();
if (result = lookup_basic_type(normalized_type_name, *corp))
if ((result = lookup_basic_type(normalized_type_name, *corp)))
return result;
}
@@ -842,21 +850,65 @@ process_ctf_base_type(reader *rdr,
///
/// @param rdr the read context to use.
///
/// @param tunit the translation unit it should belong to.
///
/// @return the variadic parameter type.
static decl_base_sptr
build_ir_node_for_variadic_parameter_type(reader &rdr,
translation_unit_sptr tunit)
const translation_unit_sptr& tunit)
{
const ir::environment& env = rdr.env();
type_base_sptr t = env.get_variadic_parameter_type();
decl_base_sptr type_declaration = get_type_declaration(t);
if (!has_scope(type_declaration))
add_decl_to_scope(type_declaration, tunit->get_global_scope());
add_decl_to_scope(type_declaration, tunit->get_global_scope());
canonicalize(t);
return type_declaration;
}
/// Build the IR node for a void type.
///
/// Note that this returns the unique pointer
/// environment::get_void_type(), which is added to the current
/// translation unit if it's the first it's being used.
///
/// @param rdr the read context to use.
///
/// @param tunit the translation unit it should belong to.
///
/// @return the void type type.
static decl_base_sptr
build_ir_node_for_void_type(reader& rdr, const translation_unit_sptr& tunit)
{
const environment& env = rdr.env();
type_base_sptr t = env.get_void_type();
add_decl_to_scope(is_decl(t), tunit->get_global_scope());
canonicalize(t);
return is_decl(t);
}
/// Build the IR node for a void pointer type.
///
/// Note that this returns the unique pointer
/// environment::get_void_pointer_type(), which is added to the
/// current translation unit if it's the first it's being used.
///
/// @param rdr the read context to use.
///
/// @param tunit the translation unit it should belong to.
///
/// @return the void pointer type.
static type_or_decl_base_sptr
build_ir_node_for_void_pointer_type(reader& rdr,
const translation_unit_sptr& tunit)
{
const environment& env = rdr.env();
type_base_sptr t = env.get_void_pointer_type();
add_decl_to_scope(is_decl(t), tunit->get_global_scope());
canonicalize(t);
return is_decl(t);
}
/// Build and return a function type libabigail IR.
///
/// @param rdr the read context.
@@ -1036,7 +1088,7 @@ process_ctf_forward_type(reader *rdr,
{
if (!type_is_anonymous)
if (corpus_sptr corp = rdr->should_reuse_type_from_corpus_group())
if (result = lookup_class_type(type_name, *corp))
if ((result = lookup_class_type(type_name, *corp)))
return is_type(result);
class_decl_sptr
@@ -1081,7 +1133,7 @@ process_ctf_struct_type(reader *rdr,
if (!struct_type_is_anonymous)
if (corpus_sptr corp = rdr->should_reuse_type_from_corpus_group())
if (result = lookup_class_type(struct_type_name, *corp))
if ((result = lookup_class_type(struct_type_name, *corp)))
return result;
/* The libabigail IR encodes C struct types in `class' IR nodes. */
@@ -1134,7 +1186,7 @@ process_ctf_union_type(reader *rdr,
if (!union_type_is_anonymous)
if (corpus_sptr corp = rdr->should_reuse_type_from_corpus_group())
if (result = lookup_union_type(union_type_name, *corp))
if ((result = lookup_union_type(union_type_name, *corp)))
return result;
/* Create the corresponding libabigail union IR node. */
@@ -1440,10 +1492,15 @@ process_ctf_pointer_type(reader *rdr,
if (result)
return result;
result.reset(new pointer_type_def(target_type,
ctf_type_size(ctf_dictionary, ctf_type) * 8,
ctf_type_align(ctf_dictionary, ctf_type) * 8,
location()));
if (rdr->env().is_void_type(target_type))
result = is_pointer_type(build_ir_node_for_void_pointer_type(*rdr, tunit));
else
result.reset(new pointer_type_def(target_type,
ctf_type_size(ctf_dictionary,
ctf_type) * 8,
ctf_type_align(ctf_dictionary,
ctf_type) * 8,
location()));
if (result)
{
add_decl_to_scope(result, tunit->get_global_scope());
@@ -1475,7 +1532,7 @@ process_ctf_enum_type(reader *rdr,
if (!enum_name.empty())
if (corpus_sptr corp = rdr->should_reuse_type_from_corpus_group())
if (result = lookup_enum_type(enum_name, *corp))
if ((result = lookup_enum_type(enum_name, *corp)))
return result;
/* Build a signed integral type for the type of the enumerators, aka
+259 -196
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -162,7 +162,9 @@ default_reporter::report(const enum_diff& d, ostream& out,
{
out << indent
<< " '"
<< i->get_qualified_name()
<< (first->get_is_anonymous()
? i->get_name()
: i->get_qualified_name())
<< "' value '"
<< i->get_value()
<< "'";
@@ -181,7 +183,9 @@ default_reporter::report(const enum_diff& d, ostream& out,
{
out << indent
<< " '"
<< i->get_qualified_name()
<< (second->get_is_anonymous()
? i->get_name()
:i->get_qualified_name())
<< "' value '"
<< i->get_value()
<< "'";
@@ -201,7 +205,9 @@ default_reporter::report(const enum_diff& d, ostream& out,
{
out << indent
<< " '"
<< i->first.get_qualified_name()
<< (first->get_is_anonymous()
? i->first.get_name()
: i->first.get_qualified_name())
<< "' from value '"
<< i->first.get_value() << "' to '"
<< i->second.get_value() << "'";
@@ -272,7 +278,8 @@ default_reporter::report(const typedef_diff& d,
typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
report_non_type_typedef_changes(d, out, indent);
if (!d.is_filtered_out_without_looking_at_allowed_changes())
report_non_type_typedef_changes(d, out, indent);
diff_sptr dif = d.underlying_type_diff();
if (dif && dif->has_changes())
@@ -347,6 +354,38 @@ default_reporter::report_local_qualified_type_changes(const qualified_type_diff&
return false;
}
/// For a @ref qualified_type_diff node, report the changes of its
/// underlying type.
///
/// @param d the @ref qualified_type_diff node to consider.
///
/// @param out the output stream to emit the report to.
///
/// @param indent the white string to use for indentation.
///
/// @return true iff a local change has been emitted. In this case,
/// the local change is a name change.
void
default_reporter::report_underlying_changes_of_qualified_type
(const qualified_type_diff& d, ostream& out, const string& indent) const
{
if (!d.to_be_reported())
return;
diff_sptr dif = d.leaf_underlying_type_diff();
ABG_ASSERT(dif);
ABG_ASSERT(dif->to_be_reported());
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
"unqualified "
"underlying type");
string fltname = dif->first_subject()->get_pretty_representation();
out << indent << "in unqualified underlying type '" << fltname << "'";
report_loc_info(dif->second_subject(), *d.context(), out);
out << ":\n";
dif->report(out, indent + " ");
}
/// Report a @ref qualified_type_diff in a serialized form.
///
/// @param d the @ref qualified_type_diff node to consider.
@@ -364,24 +403,14 @@ default_reporter::report(const qualified_type_diff& d, ostream& out,
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_qualified_type(),
d.second_qualified_type());
if (report_local_qualified_type_changes(d, out, indent))
// The local change was emitted and it's a name change. If the
// type name changed, the it means the type changed altogether.
// It makes a little sense to detail the changes in extenso here.
return;
if (!d.is_filtered_out_without_looking_at_allowed_changes())
if (report_local_qualified_type_changes(d, out, indent))
// The local change was emitted and it's a name change. If the
// type name changed, the it means the type changed altogether.
// It makes a little sense to detail the changes in extenso here.
return;
diff_sptr dif = d.leaf_underlying_type_diff();
ABG_ASSERT(dif);
ABG_ASSERT(dif->to_be_reported());
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
"unqualified "
"underlying type");
string fltname = dif->first_subject()->get_pretty_representation();
out << indent << "in unqualified underlying type '" << fltname << "'";
report_loc_info(dif->second_subject(), *d.context(), out);
out << ":\n";
dif->report(out, indent + " ");
report_underlying_changes_of_qualified_type(d, out, indent);
}
/// Report the @ref pointer_diff in a serialized form.
@@ -474,8 +503,9 @@ default_reporter::report(const reference_diff& d, ostream& out,
enum change_kind k = ir::NO_CHANGE_KIND;
equals(*d.first_reference(), *d.second_reference(), &k);
if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
report_local_reference_type_changes(d, out, indent);
if (!d.is_filtered_out_without_looking_at_allowed_changes())
if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
report_local_reference_type_changes(d, out, indent);
if (k & SUBTYPE_CHANGE_KIND)
if (diff_sptr dif = d.underlying_type_diff())
@@ -503,6 +533,9 @@ void
default_reporter::report(const fn_parm_diff& d, ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
function_decl::parameter_sptr f = d.first_parameter(),
s = d.second_parameter();
@@ -514,26 +547,23 @@ default_reporter::report(const fn_parm_diff& d, ostream& out,
type_has_sub_type_changes(d.first_parameter()->get_type(),
d.second_parameter()->get_type());
if (d.to_be_reported())
{
diff_sptr type_diff = d.type_diff();
ABG_ASSERT(type_diff->has_changes());
diff_sptr type_diff = d.type_diff();
ABG_ASSERT(type_diff->has_changes());
out << indent;
if (f->get_is_artificial())
out << "implicit ";
out << "parameter " << f->get_index();
report_loc_info(f, *d.context(), out);
out << " of type '"
<< f->get_type_pretty_representation();
out << indent;
if (f->get_is_artificial())
out << "implicit ";
out << "parameter " << f->get_index();
report_loc_info(f, *d.context(), out);
out << " of type '"
<< f->get_type_pretty_representation();
if (has_sub_type_change)
out << "' has sub-type changes:\n";
else
out << "' changed:\n";
if (has_sub_type_change)
out << "' has sub-type changes:\n";
else
out << "' changed:\n";
type_diff->report(out, indent + " ");
}
type_diff->report(out, indent + " ");
}
/// For a @ref function_type_diff node, report the local changes
@@ -645,8 +675,30 @@ default_reporter::report(const function_type_diff& d, ostream& out,
dif->report(out, indent);
}
report_local_function_type_changes(d, out, indent);
if (!d.is_filtered_out_without_looking_at_allowed_changes())
report_local_function_type_changes(d, out, indent);
}
/// Report about the change carried by a @ref subrange_diff diff node
/// in a serialized form.
///
/// @param d the diff node to consider.
///
/// @param out the output stream to report to.
///
/// @param indent the indentation string to use in the report.
void
default_reporter::report(const subrange_diff& d, std::ostream& out,
const std::string& indent) const
{
if (!diff_to_be_reported(&d))
return;
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subrange(),
d.second_subrange(),
"range type");
represent(d, d.context(), out,indent, /*local_only=*/false);
}
/// Report a @ref array_diff in a serialized form.
@@ -678,10 +730,11 @@ default_reporter::report(const array_diff& d, ostream& out,
dif->report(out, indent + " ");
}
report_name_size_and_alignment_changes(d.first_array(),
d.second_array(),
d.context(),
out, indent);
if (!d.is_filtered_out_without_looking_at_allowed_changes())
report_name_size_and_alignment_changes(d.first_array(),
d.second_array(),
d.context(),
out, indent);
}
/// Generates a report for an intance of @ref base_diff.
@@ -702,30 +755,32 @@ default_reporter::report(const base_diff& d, ostream& out,
string repr = f->get_base_class()->get_pretty_representation();
bool emitted = false;
if (f->get_is_static() != s->get_is_static())
if (!d.is_filtered_out_without_looking_at_allowed_changes())
{
if (f->get_is_static())
out << indent << "is no more static";
else
out << indent << "now becomes static";
emitted = true;
if (f->get_is_static() != s->get_is_static())
{
if (f->get_is_static())
out << indent << "is no more static";
else
out << indent << "now becomes static";
emitted = true;
}
if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
&& (f->get_access_specifier() != s->get_access_specifier()))
{
if (emitted)
out << ", ";
out << "has access changed from '"
<< f->get_access_specifier()
<< "' to '"
<< s->get_access_specifier()
<< "'";
emitted = true;
}
}
if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
&& (f->get_access_specifier() != s->get_access_specifier()))
{
if (emitted)
out << ", ";
out << "has access changed from '"
<< f->get_access_specifier()
<< "' to '"
<< s->get_access_specifier()
<< "'";
emitted = true;
}
if (class_diff_sptr dif = d.get_underlying_class_diff())
{
if (dif->to_be_reported())
@@ -1493,135 +1548,138 @@ default_reporter::report(const function_decl_diff& d, ostream& out,
linkage_names2 =
s2->get_aliases_id_string(sc->get_fun_symbol_map());
/// If the set of linkage names of the function have changed, report
/// it.
if (linkage_names1 != linkage_names2)
if (!d.is_filtered_out_without_looking_at_allowed_changes())
{
if (linkage_names1.empty())
/// If the set of linkage names of the function have changed, report
/// it.
if (linkage_names1 != linkage_names2)
{
out << indent << ff->get_pretty_representation()
<< " didn't have any linkage name, and it now has: '"
<< linkage_names2 << "'\n";
}
else if (linkage_names2.empty())
{
out << indent << ff->get_pretty_representation()
<< " did have linkage names '" << linkage_names1
<< "'\n"
<< indent << "but it doesn't have any linkage name anymore\n";
}
else
out << indent << "linkage names of "
<< ff->get_pretty_representation()
<< "\n" << indent << "changed from '"
<< linkage_names1 << "' to '" << linkage_names2 << "'\n";
}
if (qn1 != qn2
&& d.type_diff()
&& d.type_diff()->to_be_reported())
{
// So the function has sub-type changes that are to be
// reported. Let's see if the function name changed too; if it
// did, then we'd report that change right before reporting the
// sub-type changes.
string frep1 = d.first_function_decl()->get_pretty_representation(),
frep2 = d.second_function_decl()->get_pretty_representation();
out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
<< "' now becomes '"
<< frep2 << " {" << linkage_names2 << "}" << "'\n";
}
maybe_report_diff_for_symbol(ff->get_symbol(),
sf->get_symbol(),
d.context(), out, indent);
// Now report about inline-ness changes
if (ff->is_declared_inline() != sf->is_declared_inline())
{
out << indent;
if (ff->is_declared_inline())
out << sf->get_pretty_representation()
<< " is not declared inline anymore\n";
else
out << sf->get_pretty_representation()
<< " is now declared inline\n";
}
// Report about vtable offset changes.
if (is_member_function(ff) && is_member_function(sf))
{
bool ff_is_virtual = get_member_function_is_virtual(ff),
sf_is_virtual = get_member_function_is_virtual(sf);
if (ff_is_virtual != sf_is_virtual)
{
out << indent;
if (ff_is_virtual)
out << ff->get_pretty_representation()
<< " is no more declared virtual\n";
else
out << ff->get_pretty_representation()
<< " is now declared virtual\n";
}
size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
sf_vtable_offset = get_member_function_vtable_offset(sf);
if (ff_is_virtual && sf_is_virtual
&& (ff_vtable_offset != sf_vtable_offset))
{
out << indent
<< "the vtable offset of " << ff->get_pretty_representation()
<< " changed from " << ff_vtable_offset
<< " to " << sf_vtable_offset << "\n";
}
// the parent types (classe or union) of the two member
// functions.
class_or_union_sptr f =
is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
class_or_union_sptr s =
is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
class_decl_sptr fc = is_class_type(f);
class_decl_sptr sc = is_class_type(s);
// Detect if the virtual member function changes above
// introduced a vtable change or not.
bool vtable_added = false, vtable_removed = false;
if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
{
if (fc && sc)
if (linkage_names1.empty())
{
vtable_added = !fc->has_vtable() && sc->has_vtable();
vtable_removed = fc->has_vtable() && !sc->has_vtable();
out << indent << ff->get_pretty_representation()
<< " didn't have any linkage name, and it now has: '"
<< linkage_names2 << "'\n";
}
else if (linkage_names2.empty())
{
out << indent << ff->get_pretty_representation()
<< " did have linkage names '" << linkage_names1
<< "'\n"
<< indent << "but it doesn't have any linkage name anymore\n";
}
else
out << indent << "linkage names of "
<< ff->get_pretty_representation()
<< "\n" << indent << "changed from '"
<< linkage_names1 << "' to '" << linkage_names2 << "'\n";
}
bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
|| (ff_vtable_offset != sf_vtable_offset));
bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
if (vtable_added)
out << indent
<< " note that a vtable was added to "
<< fc->get_pretty_representation()
<< "\n";
else if (vtable_removed)
out << indent
<< " note that the vtable was removed from "
<< fc->get_pretty_representation()
<< "\n";
else if (vtable_changed)
if (qn1 != qn2
&& d.type_diff()
&& d.type_diff()->to_be_reported())
{
// So the function has sub-type changes that are to be
// reported. Let's see if the function name changed too; if it
// did, then we'd report that change right before reporting the
// sub-type changes.
string frep1 = d.first_function_decl()->get_pretty_representation(),
frep2 = d.second_function_decl()->get_pretty_representation();
out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
<< "' now becomes '"
<< frep2 << " {" << linkage_names2 << "}" << "'\n";
}
maybe_report_diff_for_symbol(ff->get_symbol(),
sf->get_symbol(),
d.context(), out, indent);
// Now report about inline-ness changes
if (ff->is_declared_inline() != sf->is_declared_inline())
{
out << indent;
if (incompatible_change)
out << " note that this is an ABI incompatible "
"change to the vtable of ";
if (ff->is_declared_inline())
out << sf->get_pretty_representation()
<< " is not declared inline anymore\n";
else
out << " note that this induces a change to the vtable of ";
out << fc->get_pretty_representation()
<< "\n";
out << sf->get_pretty_representation()
<< " is now declared inline\n";
}
// Report about vtable offset changes.
if (is_member_function(ff) && is_member_function(sf))
{
bool ff_is_virtual = get_member_function_is_virtual(ff),
sf_is_virtual = get_member_function_is_virtual(sf);
if (ff_is_virtual != sf_is_virtual)
{
out << indent;
if (ff_is_virtual)
out << ff->get_pretty_representation()
<< " is no more declared virtual\n";
else
out << ff->get_pretty_representation()
<< " is now declared virtual\n";
}
size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
sf_vtable_offset = get_member_function_vtable_offset(sf);
if (ff_is_virtual && sf_is_virtual
&& (ff_vtable_offset != sf_vtable_offset))
{
out << indent
<< "the vtable offset of " << ff->get_pretty_representation()
<< " changed from " << ff_vtable_offset
<< " to " << sf_vtable_offset << "\n";
}
// the parent types (classe or union) of the two member
// functions.
class_or_union_sptr f =
is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
class_or_union_sptr s =
is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
class_decl_sptr fc = is_class_type(f);
class_decl_sptr sc = is_class_type(s);
// Detect if the virtual member function changes above
// introduced a vtable change or not.
bool vtable_added = false, vtable_removed = false;
if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
{
if (fc && sc)
{
vtable_added = !fc->has_vtable() && sc->has_vtable();
vtable_removed = fc->has_vtable() && !sc->has_vtable();
}
}
bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
|| (ff_vtable_offset != sf_vtable_offset));
bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
if (vtable_added)
out << indent
<< " note that a vtable was added to "
<< fc->get_pretty_representation()
<< "\n";
else if (vtable_removed)
out << indent
<< " note that the vtable was removed from "
<< fc->get_pretty_representation()
<< "\n";
else if (vtable_changed)
{
out << indent;
if (incompatible_change)
out << " note that this is an ABI incompatible "
"change to the vtable of ";
else
out << " note that this induces a change to the vtable of ";
out << fc->get_pretty_representation()
<< "\n";
}
}
}
// Report about function type differences.
@@ -1648,15 +1706,20 @@ default_reporter::report(const var_diff& d, ostream& out,
decl_base_sptr first = d.first_var(), second = d.second_var();
string n = first->get_pretty_representation();
report_name_size_and_alignment_changes(first, second,
d.context(),
out, indent);
if (!d.is_filtered_out_without_looking_at_allowed_changes())
{
report_name_size_and_alignment_changes(first, second,
d.context(),
out, indent);
maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
d.second_var()->get_symbol(),
d.context(), out, indent);
maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
d.second_var()->get_symbol(),
d.context(), out, indent);
maybe_report_diff_for_member(first, second, d.context(), out, indent);
maybe_report_diff_for_member(first, second, d.context(), out, indent);
maybe_report_diff_for_variable(first, second, d.context(), out, indent);
}
if (diff_sptr dif = d.type_diff())
{
@@ -1842,7 +1905,7 @@ default_reporter::report(const corpus_diff& d, ostream& out,
function_decl_sptr fn = (*i)->first_function_decl();
out << indent << " [C] '"
<< fn->get_pretty_representation() << "'";
report_loc_info((*i)->second_function_decl(), *ctxt, out);
report_loc_info((*i)->first_function_decl(), *ctxt, out);
out << " has some indirect sub-type changes:\n";
if (// The symbol of the function has aliases and the
// function is not a cdtor (yeah because c++ cdtors
@@ -1859,7 +1922,7 @@ default_reporter::report(const corpus_diff& d, ostream& out,
// was playing tricks with symbol names and
// versions).
(is_c_language(get_translation_unit(fn)->get_language())
&& fn->get_name() != fn->get_symbol()->get_name()))
&& fn->get_name() != fn->get_symbol()->get_name()))
{
// As the name of the symbol of the function doesn't
// seem to be obvious here, make sure to tell the
@@ -1935,7 +1998,7 @@ default_reporter::report(const corpus_diff& d, ostream& out,
emitted = true;
}
if (emitted)
out << "\n";
out << "\n";
}
if (ctxt->show_added_vars())
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
#include <cstring>
+488 -188
View File
File diff suppressed because it is too large Load Diff
+21 -8
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -61,7 +61,7 @@ elf_based_reader::elf_based_reader(const std::string& elf_path,
elf_based_reader::~elf_based_reader()
{delete priv_;}
/// Reset (re-initialize) the resources used by the current reader.
/// (re)Initialize) the resources used by the current reader.
///
/// This frees the resources of the current reader and gets it ready
/// to read data from another ELF file.
@@ -71,13 +71,28 @@ elf_based_reader::~elf_based_reader()
/// @param debug_info_root_paths a vector of paths to look into for
/// split debug info files.
void
elf_based_reader::reset(const std::string& elf_path,
const vector<char**>& debug_info_root_paths)
elf_based_reader::initialize(const std::string& elf_path,
const vector<char**>& debug_info_root_paths)
{
elf::reader::reset(elf_path, debug_info_root_paths);
elf::reader::initialize(elf_path, debug_info_root_paths);
priv_->initialize();
}
/// (re)Initialize the resources used by the current reader.
///
/// This invokes fe_iface::initialize as wel as the virtual pure
/// elf_based_reader::initialize() interface.
///
/// @param corpus_path path to the corpus to be built.
void
elf_based_reader::initialize(const std::string& corpus_path)
{
fe_iface::initialize(corpus_path);
vector<char**> v;
initialize(corpus_path, v, /*load_all_type=*/false,
/*linux_kernel_mode=*/false);
}
/// Read an ABI corpus and add it to a given corpus group.
///
/// @param group the corpus group to consider. The new corpus is
@@ -92,10 +107,8 @@ ir::corpus_sptr
elf_based_reader::read_and_add_corpus_to_group(ir::corpus_group& group,
fe_iface::status& status)
{
group.add_corpus(corpus());
ir::corpus_sptr corp = read_corpus(status);
if (status & fe_iface::STATUS_OK)
group.add_corpus(corp);
return corp;
}
+14 -7
View File
@@ -1,17 +1,19 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2020-2022 Google, Inc.
// Copyright (C) 2020-2023 Google, Inc.
/// @file
///
/// This contains the definitions of the ELF utilities for the dwarf reader.
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <elfutils/libdwfl.h>
#include <sstream>
#include "abg-elf-helpers.h"
#include "abg-tools-utils.h"
@@ -920,10 +922,11 @@ bool
get_crc_for_symbol(Elf* elf_handle, GElf_Sym* crc_symbol, uint32_t& crc_value)
{
size_t crc_section_index = crc_symbol->st_shndx;
uint64_t crc_symbol_value = crc_symbol->st_value;
GElf_Addr crc_symbol_address =
maybe_adjust_et_rel_sym_addr_to_abs_addr(elf_handle, crc_symbol);
if (crc_section_index == SHN_ABS)
{
crc_value = crc_symbol_value;
crc_value = crc_symbol_address;
return true;
}
@@ -940,10 +943,10 @@ get_crc_for_symbol(Elf* elf_handle, GElf_Sym* crc_symbol, uint32_t& crc_value)
if (kcrctab_data == NULL)
return false;
if (crc_symbol_value < sheader->sh_addr)
if (crc_symbol_address < sheader->sh_addr)
return false;
size_t offset = crc_symbol_value - sheader->sh_addr;
size_t offset = crc_symbol_address - sheader->sh_addr;
if (offset + sizeof(uint32_t) > kcrctab_data->d_size
|| offset + sizeof(uint32_t) > sheader->sh_size)
return false;
@@ -1519,13 +1522,17 @@ get_soname_of_elf_file(const string& path, string &soname)
Elf_Scn* scn = gelf_offscn (elf, phdr->p_offset);
GElf_Shdr shdr_mem;
GElf_Shdr* shdr = gelf_getshdr (scn, &shdr_mem);
if (!(shdr == NULL || (shdr->sh_type == SHT_DYNAMIC
|| shdr->sh_type == SHT_PROGBITS)))
// This program header doesn't look like one we are
// looking for. Skip to the next.
continue;
size_t entsize = (shdr != NULL && shdr->sh_entsize != 0
? shdr->sh_entsize
: gelf_fsize (elf, ELF_T_DYN, 1, EV_CURRENT));
int maxcnt = (shdr != NULL
? shdr->sh_size / entsize : INT_MAX);
ABG_ASSERT (shdr == NULL || (shdr->sh_type == SHT_DYNAMIC
|| shdr->sh_type == SHT_PROGBITS));
Elf_Data* data = elf_getdata (scn, NULL);
if (data == NULL)
break;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2020-2022 Google, Inc.
// Copyright (C) 2020-2023 Google, Inc.
/// @file
///
+126 -28
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -132,6 +132,11 @@ find_alt_dwarf_debug_info_path(const vector<char**> root_dirs,
return false;
string altfile_name = tools_utils::trim_leading_string(alt_file_name, "../");
// In case the alt dwarf debug info file is to be found under
// "/usr/lib/debug", look for it under the provided root directories
// instead.
altfile_name = tools_utils::trim_leading_string(altfile_name,
"/usr/lib/debug/");
for (vector<char**>::const_iterator i = root_dirs.begin();
i != root_dirs.end();
@@ -225,10 +230,10 @@ find_alt_dwarf_debug_info(Dwfl_Module *elf_module,
// If we reach this point it means we have found the path to the
// alternate debuginfo file and it's in alt_file_path. So let's
// open it and read it.
int fd = open(alt_file_path.c_str(), O_RDONLY);
if (fd == -1)
alt_fd = open(alt_file_path.c_str(), O_RDONLY);
if (alt_fd == -1)
return result;
result = dwarf_begin(fd, DWARF_C_READ);
result = dwarf_begin(alt_fd, DWARF_C_READ);
#ifdef LIBDW_HAS_DWARF_GETALT
Dwarf_Addr bias = 0;
@@ -271,7 +276,10 @@ struct reader::priv
string alt_dwarf_path;
int alt_dwarf_fd = 0;
Elf_Scn* ctf_section = nullptr;
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<char**>& debug_info_roots)
@@ -281,6 +289,12 @@ struct reader::priv
initialize(debug_info_roots);
}
~priv()
{
clear_alt_dwarf_debug_info_data();
clear_alt_ctf_debug_info_data();
}
/// Reset the private data of @elf elf::reader.
///
/// @param debug_info_roots the vector of new directories where to
@@ -288,11 +302,26 @@ struct reader::priv
void
initialize(const vector<char**>& debug_info_roots)
{
debug_info_root_paths = debug_info_roots;
clear_alt_dwarf_debug_info_data();
clear_alt_ctf_debug_info_data();
elf_handle = nullptr;
symtab_section = nullptr;
elf_architecture.clear();
dt_needed.clear();
symt.reset();
debug_info_root_paths = debug_info_roots;
memset(&offline_callbacks, 0, sizeof(offline_callbacks));
dwfl_handle.reset();
elf_module = nullptr;
elf_handle = nullptr;
dwarf_handle = nullptr;
alt_dwarf_handle = nullptr;
alt_dwarf_path.clear();
alt_dwarf_fd = 0;
ctf_section = nullptr;
alt_ctf_section = nullptr;
alt_ctf_handle = nullptr;
alt_ctf_fd = 0;
}
/// Setup the necessary plumbing to open the ELF file and find all
@@ -348,6 +377,23 @@ struct reader::priv
return result;
}
/// Clear the resources related to the alternate DWARF data.
void
clear_alt_dwarf_debug_info_data()
{
if (alt_dwarf_fd)
{
if (alt_dwarf_handle)
{
dwarf_end(alt_dwarf_handle);
alt_dwarf_handle = nullptr;
}
close(alt_dwarf_fd);
alt_dwarf_fd = 0;
}
alt_dwarf_path.clear();
}
/// Locate the DWARF debug info in the ELF file.
///
/// This also knows how to locate split debug info.
@@ -379,6 +425,22 @@ struct reader::priv
alt_dwarf_fd);
}
/// Clear the resources related to the alternate CTF data.
void
clear_alt_ctf_debug_info_data()
{
if (alt_ctf_fd)
{
close(alt_ctf_fd);
alt_ctf_fd = 0;
}
if (alt_ctf_handle)
{
elf_end(alt_ctf_handle);
alt_ctf_handle = nullptr;
}
}
/// Locate the CTF "alternate" debug information associated with the
/// current ELF file ( and split out somewhere else).
///
@@ -386,6 +448,9 @@ struct reader::priv
void
locate_alt_ctf_debug_info()
{
if (alt_ctf_section)
return;
Elf_Scn *section =
elf_helpers::find_section(elf_handle,
".gnu_debuglink",
@@ -405,24 +470,20 @@ struct reader::priv
if (!tools_utils::find_file_under_dir(*path, name, file_path))
continue;
int fd;
if ((fd = open(file_path.c_str(), O_RDONLY)) == -1)
if ((alt_ctf_fd = open(file_path.c_str(), O_RDONLY)) == -1)
continue;
Elf *hdl;
if ((hdl = elf_begin(fd, ELF_C_READ, nullptr)) == nullptr)
{
close(fd);
continue;
}
if ((alt_ctf_handle = elf_begin(alt_ctf_fd,
ELF_C_READ,
nullptr)) == nullptr)
continue;
// unlikely .ctf was designed to be present in stripped file
alt_ctf_section =
elf_helpers::find_section(hdl, ".ctf", SHT_PROGBITS);
break;
elf_helpers::find_section(alt_ctf_handle, ".ctf", SHT_PROGBITS);
elf_end(hdl);
close(fd);
if (alt_ctf_section)
break;
}
}
@@ -466,8 +527,8 @@ reader::reader(const string& elf_path,
reader::~reader()
{delete priv_;}
/// Resets (erase) the resources used by the current @ref
/// elf::reader type.
/// Re-initialize the resources used by the current @ref elf::reader
/// type.
///
/// This lets the reader in a state where it's ready to read from
/// another ELF file.
@@ -477,11 +538,10 @@ reader::~reader()
/// @param debug_info_roots a vector of directory paths to look into
/// for split debug information files.
void
reader::reset(const std::string& elf_path,
const vector<char**>& debug_info_roots)
reader::initialize(const std::string& elf_path,
const vector<char**>& debug_info_roots)
{
fe_iface::options_type opts = options();
fe_iface::reset(elf_path, opts.env);
fe_iface::initialize(elf_path);
corpus_path(elf_path);
priv_->initialize(debug_info_roots);
priv_->crack_open_elf_file();
@@ -489,6 +549,20 @@ reader::reset(const std::string& elf_path,
priv_->locate_ctf_debug_info();
}
/// Re-initialize the resources used by the current @ref elf::reader
/// type.
///
/// This lets the reader in a state where it's ready to read from
/// another ELF file.
///
/// @param elf_path the new ELF path to read from.
void
reader::initialize(const std::string& elf_path)
{
vector<char**> v;
initialize(elf_path, v);
}
/// Getter of the vector of directory paths to look into for split
/// debug information files.
///
@@ -547,6 +621,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.
///
@@ -642,6 +723,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.
@@ -881,11 +976,14 @@ reader::read_corpus(status& status)
corpus()->set_architecture_name(elf_architecture());
// See if we could find symbol tables.
if (!symtab() || !symtab()->has_symbols())
if (!symtab())
{
status |= STATUS_NO_SYMBOLS_FOUND;
// We found no ELF symbol, so we can't handle the binary.
return corpus_sptr();
status |= STATUS_NO_SYMBOLS_FOUND | STATUS_OK;
// We found no ELF symbol, so we can't handle the binary. Note
// that we could have found a symbol table with no defined &
// exported ELF symbols in it. Both cases are handled as an
// empty corpus.
return corpus();
}
// Set symbols information to the corpus.
+9 -12
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2022 Red Hat, Inc.
// Copyright (C) 2022-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -43,11 +43,11 @@ struct fe_iface::priv
void
initialize()
{
//TODO: initialize the options.
corpus_path.clear();
dt_soname.clear();
suppressions.clear();
corpus_group.reset();
corpus.reset();
}
}; //end struct fe_iface::priv
@@ -78,14 +78,11 @@ fe_iface::~fe_iface()
///
/// @param corpus_path the path to the file for which a new corpus is
/// to be created.
///
/// @param e the environment in which the Front End operates.
void
fe_iface::reset(const std::string& corpus_path,
environment& e)
fe_iface::initialize(const std::string& corpus_path)
{
delete priv_;
priv_ = new fe_iface::priv(corpus_path, e);
priv_->initialize();
priv_->corpus_path = corpus_path;
}
/// Getter of the the options of the current Front End Interface.
@@ -310,7 +307,7 @@ fe_iface::maybe_add_fn_to_exported_decls(const function_decl* fn)
if (fn)
if (corpus::exported_decls_builder* b =
corpus()->get_exported_decls_builder().get())
b->maybe_add_fn_to_exported_fns(fn);
b->maybe_add_fn_to_exported_fns(const_cast<function_decl*>(fn));
}
/// Try and add the representation of the ABI of a variable to the set
@@ -397,13 +394,13 @@ status_to_diagnostic_string(fe_iface::status s)
std::string str;
if (s & fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)
str += "could not find debug info\n";
str += "could not find debug info";
if (s & fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
str += "could not find alternate debug info\n";
str += "could not find alternate debug info";
if (s & fe_iface::STATUS_NO_SYMBOLS_FOUND)
str += "could not load ELF symbols\n";
str += "could not load ELF symbols";
return str;
}
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+5 -4
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -145,7 +145,7 @@ char_is_function_name_char(int b)
static bool
char_is_function_argument_char(int b)
{
if (char_is_delimiter(b) || b == '(' || b == ')')
if (b == '(' || b == ')')
return false;
return true;
}
@@ -1353,7 +1353,7 @@ public:
}
list_property_value_sptr list = read_list_property_value();
if (list->get_content().size() == 1)
if (list && list->get_content().size() == 1)
result.reset(new string_property_value(list->get_content()[0]));
else
result = list;
@@ -1365,7 +1365,7 @@ public:
///
/// A string is just a contiguous set of characters that test
/// positive when passed to
/// read_context::char_is_property_name_char().
/// read_context::char_is_property_value_char().
///
/// Note that all escaped characters are suitable to be in a string.
///
@@ -1440,6 +1440,7 @@ public:
char c = 0;
read_next_char(c);
ABG_ASSERT(c == ',');
skip_white_spaces();
}
if (!content.empty())
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+316 -85
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -337,7 +337,7 @@ struct type_base::priv
/// If the current canonical type was set as the result of the
/// "canonical type propagation optimization", then clear it.
void
bool
clear_propagated_canonical_type()
{
if (canonical_type_propagated_ && !propagated_canonical_type_confirmed_)
@@ -345,7 +345,9 @@ struct type_base::priv
canonical_type.reset();
naked_canonical_type = nullptr;
set_canonical_type_propagated(false);
return true;
}
return false;
}
}; // end struct type_base::priv
@@ -369,6 +371,13 @@ typedef std::pair<uint64_t, uint64_t> uint64_t_pair_type;
/// A convenience typedef for a set of @ref uint64_t_pair
typedef unordered_set<uint64_t_pair_type,
uint64_t_pair_hash> uint64_t_pairs_set_type;
/// A convenience typedef for a set of pointer to @ref class_or_union
typedef unordered_set<const class_or_union*> class_set_type;
/// A convenience typedef for a set of pointer to @ref function_type.
typedef unordered_set<const function_type*> fn_set_type;
/// A convenience typedef for a map which key is a pair of uint64_t
/// and which value is a boolean. This is initially intended to cache
/// the result of comparing two (sub-)types.
@@ -382,17 +391,20 @@ struct environment::priv
canonical_types_map_type canonical_types_;
mutable vector<type_base_sptr> sorted_canonical_types_;
type_base_sptr void_type_;
type_base_sptr void_pointer_type_;
type_base_sptr variadic_marker_type_;
// The set of pairs of class types being currently compared. It's
// used to avoid endless loops while recursively comparing types.
// This should be empty when none of the 'equal' overloads are
// currently being invoked.
uint64_t_pairs_set_type classes_being_compared_;
class_set_type left_classes_being_compared_;
class_set_type right_classes_being_compared_;
// The set of pairs of function types being currently compared. It's used
// to avoid endless loops while recursively comparing types. This
// should be empty when none of the 'equal' overloads are currently
// being invoked.
uint64_t_pairs_set_type fn_types_being_compared_;
fn_set_type left_fn_types_being_compared_;
fn_set_type right_fn_types_being_compared_;
// This is a cache for the result of comparing two sub-types (of
// either class or function types) that are designated by their
// memory address in the IR.
@@ -449,6 +461,14 @@ struct environment::priv
// must be cleared.
pointer_set types_with_non_confirmed_propagated_ct_;
pointer_set recursive_types_;
#ifdef WITH_DEBUG_CT_PROPAGATION
// Set of types which propagated canonical type has been cleared
// during the "canonical type propagation optimization" phase. Those
// types are tracked in this set to ensure that they are later
// canonicalized. This means that at the end of the
// canonicalization process, this set must be empty.
mutable pointer_set types_with_cleared_propagated_ct_;
#endif
#ifdef WITH_DEBUG_SELF_COMPARISON
// This is used for debugging purposes.
// When abidw is used with the option --debug-abidiff, some
@@ -474,6 +494,7 @@ struct environment::priv
bool decl_only_class_equals_definition_;
bool use_enum_binary_only_equality_;
bool allow_type_comparison_results_caching_;
bool do_log_;
optional<bool> analyze_exported_interfaces_only_;
#ifdef WITH_DEBUG_SELF_COMPARISON
bool self_comparison_debug_on_;
@@ -500,7 +521,8 @@ struct environment::priv
do_on_the_fly_canonicalization_(true),
decl_only_class_equals_definition_(false),
use_enum_binary_only_equality_(true),
allow_type_comparison_results_caching_(false)
allow_type_comparison_results_caching_(false),
do_log_(false)
#ifdef WITH_DEBUG_SELF_COMPARISON
,
self_comparison_debug_on_(false)
@@ -533,6 +555,14 @@ struct environment::priv
allow_type_comparison_results_caching() const
{return allow_type_comparison_results_caching_;}
void
do_log(bool f)
{do_log_ = f;}
bool
do_log() const
{return do_log_;}
/// Cache the result of comparing two sub-types.
///
/// @param first the first sub-type that has been compared. Its
@@ -605,48 +635,6 @@ struct environment::priv
clear_type_comparison_results_cache()
{type_comparison_results_cache_.clear();}
/// Dumps a textual representation (to the standard error output) of
/// the content of the set of classes being currently compared using
/// the @ref equal overloads.
///
/// This function is for debugging purposes.
void
dump_classes_being_compared()
{
std::cerr << "classes being compared: " << classes_being_compared_.size()
<< "\n"
<< "=====================================\n";
for (auto& p : classes_being_compared_)
{
class_or_union* c = reinterpret_cast<class_or_union*>(p.first);
std::cerr << "'" << c->get_pretty_representation()
<< " / (" << std::hex << p.first << "," << p.second << ")"
<< "'\n";
}
std::cerr << "=====================================\n";
}
/// Dumps a textual representation (to the standard error output) of
/// the content of the set of classes being currently compared using
/// the @ref equal overloads.
///
/// This function is for debugging purposes.
void
dump_fn_types_being_compared()
{
std::cerr << "fn_types being compared: " << fn_types_being_compared_.size()
<< "\n"
<< "=====================================\n";
for (auto& p : fn_types_being_compared_)
{
function_type* c = reinterpret_cast<function_type*>(p.first);
std::cerr << "'" << c->get_pretty_representation()
<< " / (" << std::hex << p.first << "," << p.second << ")"
<< "'\n";
}
std::cerr << "=====================================\n";
}
/// Push a pair of operands on the stack of operands of the current
/// type comparison, during type canonicalization.
///
@@ -827,6 +815,11 @@ struct environment::priv
dest.priv_->canonical_type = canonical;
dest.priv_->naked_canonical_type = canonical.get();
dest.priv_->set_canonical_type_propagated(true);
#ifdef WITH_DEBUG_CT_PROPAGATION
// If dest was previously a type which propagated canonical type
// has been cleared, let the book-keeping system know.
erase_type_with_cleared_propagated_canonical_type(&dest);
#endif
return true;
}
@@ -845,13 +838,14 @@ struct environment::priv
for (auto i : types_with_non_confirmed_propagated_ct_)
{
type_base *t = reinterpret_cast<type_base*>(i);
ABG_ASSERT(t->get_environment().priv_->is_recursive_type(t)
|| t->priv_->depends_on_recursive_type());
t->priv_->set_does_not_depend_on_recursive_type(dependant_type);
if (!t->priv_->depends_on_recursive_type())
{
to_remove.insert(i);
t->priv_->set_propagated_canonical_type_confirmed(true);
#ifdef WITH_DEBUG_SELF_COMPARISON
check_abixml_canonical_type_propagation_during_self_comp(t);
#endif
}
}
@@ -883,6 +877,9 @@ struct environment::priv
env.priv_->remove_from_types_with_non_confirmed_propagated_ct(t);
env.priv_->set_is_not_recursive(t);
t->priv_->set_propagated_canonical_type_confirmed(true);
#ifdef WITH_DEBUG_SELF_COMPARISON
check_abixml_canonical_type_propagation_during_self_comp(t);
#endif
}
/// Mark all the types that have been the target of canonical type
@@ -899,14 +896,65 @@ struct environment::priv
for (auto i : types_with_non_confirmed_propagated_ct_)
{
type_base *t = reinterpret_cast<type_base*>(i);
ABG_ASSERT(t->get_environment().priv_->is_recursive_type(t)
|| t->priv_->depends_on_recursive_type());
t->priv_->set_does_not_depend_on_recursive_type();
t->priv_->set_propagated_canonical_type_confirmed(true);
#ifdef WITH_DEBUG_SELF_COMPARISON
check_abixml_canonical_type_propagation_during_self_comp(t);
#endif
}
types_with_non_confirmed_propagated_ct_.clear();
}
#ifdef WITH_DEBUG_CT_PROPAGATION
/// Getter for the set of types which propagated canonical type has
/// been cleared during the "canonical type propagation
/// optimization" phase. Those types are tracked in this set to
/// ensure that they are later canonicalized. This means that at
/// the end of the canonicalization process, this set must be empty.
///
/// @return the set of types which propagated canonical type has
/// been cleared.
const pointer_set&
types_with_cleared_propagated_ct() const
{return types_with_cleared_propagated_ct_;}
/// Getter for the set of types which propagated canonical type has
/// been cleared during the "canonical type propagation
/// optimization" phase. Those types are tracked in this set to
/// ensure that they are later canonicalized. This means that at
/// the end of the canonicalization process, this set must be empty.
///
/// @return the set of types which propagated canonical type has
/// been cleared.
pointer_set&
types_with_cleared_propagated_ct()
{return types_with_cleared_propagated_ct_;}
/// Record a type which propagated canonical type has been cleared
/// during the "canonical type propagation optimization phase".
///
/// @param t the type to record.
void
record_type_with_cleared_propagated_canonical_type(const type_base* t)
{
uintptr_t ptr = reinterpret_cast<uintptr_t>(t);
types_with_cleared_propagated_ct_.insert(ptr);
}
/// Erase a type (which propagated canonical type has been cleared
/// during the "canonical type propagation optimization phase") from
/// the set of types that have been recorded by the invocation of
/// record_type_with_cleared_propagated_canonical_type()
///
/// @param t the type to erase from the set.
void
erase_type_with_cleared_propagated_canonical_type(const type_base* t)
{
uintptr_t ptr = reinterpret_cast<uintptr_t>(t);
types_with_cleared_propagated_ct_.erase(ptr);
}
#endif //WITH_DEBUG_CT_PROPAGATION
/// Collect the types that depends on a given "target" type.
///
/// Walk a set of types and if they depend directly or indirectly on
@@ -974,7 +1022,7 @@ struct environment::priv
type_base_sptr canonical = t->priv_->canonical_type.lock();
if (canonical)
{
t->priv_->clear_propagated_canonical_type();
clear_propagated_canonical_type(t);
t->priv_->set_does_not_depend_on_recursive_type();
}
}
@@ -1008,18 +1056,37 @@ struct environment::priv
const environment& env = t->get_environment();
env.priv_->cancel_ct_propagation_for_types_dependant_on(t);
if (t->priv_->depends_on_recursive_type()
|| env.priv_->is_recursive_type(t))
// This cannot carry any tentative canonical type at this
// point.
clear_propagated_canonical_type(t);
// Reset the marking of the type as it no longer carries a
// tentative canonical type that might be later canceled.
t->priv_->set_does_not_depend_on_recursive_type();
env.priv_->remove_from_types_with_non_confirmed_propagated_ct(t);
env.priv_->clear_type_comparison_results_cache();
}
/// Clear the propagated canonical type of a given type.
///
/// This function also updates the book-keeping of the set of types
/// which propagated canonical types have been cleared.
///
/// Please note that at the end of the canonicalization of all the
/// types in the system, all the types which propagated canonical
/// type has been cleared must be canonicalized.
///
/// @param t the type to
void
clear_propagated_canonical_type(const type_base *t)
{
if (t->priv_->clear_propagated_canonical_type())
{
// This cannot carry any tentative canonical type at this
// point.
if (t->priv_->canonical_type_propagated()
&& !t->priv_->propagated_canonical_type_confirmed())
t->priv_->clear_propagated_canonical_type();
// Reset the marking of the type as it no longer carries a
// tentative canonical type that might be later cancelled.
t->priv_->set_does_not_depend_on_recursive_type();
env.priv_->remove_from_types_with_non_confirmed_propagated_ct(t);
#ifdef WITH_DEBUG_CT_PROPAGATION
// let the book-keeping system know that t has its propagated
// canonical type cleared.
record_type_with_cleared_propagated_canonical_type(t)
#endif
;
}
}
@@ -1047,7 +1114,64 @@ struct environment::priv
types_with_non_confirmed_propagated_ct_.erase(i);
}
/// Cancel the propagated canonical types of all the types which
/// propagated canonical type have not yet been confirmed.
void
cancel_all_non_confirmed_propagated_canonical_types()
{
vector<uintptr_t> to_erase;
for (auto i : types_with_non_confirmed_propagated_ct_)
to_erase.push_back(i);
for (auto i : to_erase)
{
type_base *t = reinterpret_cast<type_base*>(i);
cancel_ct_propagation(t);
}
}
#ifdef WITH_DEBUG_SELF_COMPARISON
const unordered_map<string, uintptr_t>&
get_type_id_canonical_type_map() const
{return type_id_canonical_type_map_;}
unordered_map<string, uintptr_t>&
get_type_id_canonical_type_map()
{return type_id_canonical_type_map_;}
const unordered_map<uintptr_t, string>&
get_pointer_type_id_map() const
{return pointer_type_id_map_;}
unordered_map<uintptr_t, string>&
get_pointer_type_id_map()
{return pointer_type_id_map_;}
string
get_type_id_from_pointer(uintptr_t ptr) const
{
auto it = get_pointer_type_id_map().find(ptr);
if (it != get_pointer_type_id_map().end())
return it->second;
return "";
}
string
get_type_id_from_type(const type_base *t) const
{return get_type_id_from_pointer(reinterpret_cast<uintptr_t>(t));}
uintptr_t
get_canonical_type_from_type_id(const char* type_id) const
{
if (!type_id)
return 0;
auto it = get_type_id_canonical_type_map().find(type_id);
if (it != get_type_id_canonical_type_map().end())
return it->second;
return 0;
}
/// When debugging self comparison, verify that a type T
/// de-serialized from abixml has the same canonical type as the
/// initial type built from DWARF that was serialized into T in the
@@ -1055,10 +1179,11 @@ struct environment::priv
///
/// @param t deserialized type (from abixml) to consider.
///
/// @param c the canonical type @p t should have.
/// @param c the canonical type that @p t has, as computed freshly
/// from the abixml file.
///
/// @return true iff @p c is the canonical type that @p t should
/// have.
/// @return true iff @p c has the same value as the canonical type
/// that @p t had before being serialized into abixml.
bool
check_canonical_type_from_abixml_during_self_comp(const type_base* t,
const type_base* c)
@@ -1105,6 +1230,45 @@ struct environment::priv
return false;
}
/// When debugging self comparison, verify that a type T
/// de-serialized from abixml has the same canonical type as the
/// initial type built from DWARF that was serialized into T in the
/// first place.
///
/// @param t deserialized type (from abixml) to consider.
///
/// @return true iff @p c is the canonical type that @p t should
/// have.
bool
check_abixml_canonical_type_propagation_during_self_comp(const type_base* t)
{
if (t->get_corpus()
&& t->get_corpus()->get_origin() == ir::corpus::NATIVE_XML_ORIGIN)
{
type_base* c = t->get_naked_canonical_type();
if (c && !check_canonical_type_from_abixml_during_self_comp(t, c))
{
string repr = t->get_pretty_representation(true, true);
string type_id = get_type_id_from_type(t);
std::cerr << "error: canonical type propagation error for '"
<< repr
<< "' of type-id: '"
<< type_id
<< "' / type: @"
<< std::hex
<< t
<< "/ canon: @"
<< c
<< ", should have had canonical type: "
<< std::hex
<< get_canonical_type_from_type_id(type_id.c_str())
<< "\n";
return false;
}
}
return true;
}
/// When debugging self comparison, verify that a type T
/// de-serialized from abixml has the same canonical type as the
/// initial type built from DWARF that was serialized into T in the
@@ -1125,6 +1289,71 @@ struct environment::priv
#endif
};// end struct environment::priv
/// Compute the canonical type for all the IR types of the system.
///
/// After invoking this function, the time it takes to compare two
/// types of the IR is equivalent to the time it takes to compare
/// their pointer value. That is faster than performing a structural
/// (A.K.A. member-wise) comparison.
///
/// Note that this function performs some sanity checks after* the
/// canonicalization process. It ensures that at the end of the
/// canonicalization process, all types have been canonicalized. This
/// is important because the canonicalization algorithm sometimes
/// clears some canonical types after having speculatively set them
/// for performance purposes. At the end of the process however, all
/// types must be canonicalized, and this function detects violations
/// of that assertion.
///
/// @tparam input_iterator the type of the input iterator of the @p
/// beging and @p end.
///
/// @tparam deref_lambda a lambda function which takes in parameter
/// the input iterator of type @p input_iterator and dereferences it
/// to return the type to canonicalize.
///
/// @param begin an iterator pointing to the first type of the set of types
/// to canonicalize.
///
/// @param end an iterator pointing to the end (after the last type) of
/// the set of types to canonicalize.
///
/// @param deref a lambda function that knows how to dereference the
/// iterator @p begin to return the type to canonicalize.
template<typename input_iterator,
typename deref_lambda>
void
canonicalize_types(const input_iterator& begin,
const input_iterator& end,
deref_lambda deref)
{
if (begin == end)
return;
int i;
input_iterator t;
// First, let's compute the canonical type of this type.
for (t = begin,i = 0; t != end; ++t, ++i)
{
if (deref(t)->get_environment().priv_->do_log())
std::cerr << "#" << std::dec << i << " ";
canonicalize(deref(t));
}
#ifdef WITH_DEBUG_CT_PROPAGATION
// Then now, make sure that all types -- which propagated canonical
// type has been cleared -- have been canonicalized. In other
// words, the set of types which have been recorded because their
// propagated canonical type has been cleared must be empty.
const environment& env = deref(begin)->get_environment();
pointer_set to_canonicalize =
env.priv_->types_with_cleared_propagated_ct();
ABG_ASSERT(to_canonicalize.empty());
#endif // WITH_DEBUG_CT_PROPAGATION
}
// <class_or_union::priv definitions>
struct class_or_union::priv
{
@@ -1174,9 +1403,9 @@ struct class_or_union::priv
const class_or_union& second) const
{
const environment& env = first.get_environment();
env.priv_->classes_being_compared_.insert
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>(&second)));
env.priv_->left_classes_being_compared_.insert(&first);
env.priv_->right_classes_being_compared_.insert(&second);
}
/// Mark a pair of classes or unions as being currently compared
@@ -1236,9 +1465,9 @@ struct class_or_union::priv
const class_or_union& second) const
{
const environment& env = first.get_environment();
env.priv_->classes_being_compared_.erase
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>(&second)));
env.priv_->left_classes_being_compared_.erase(&first);
env.priv_->right_classes_being_compared_.erase(&second);
}
/// If a pair of class_or_union has been previously marked as
@@ -1277,10 +1506,11 @@ struct class_or_union::priv
const class_or_union& second) const
{
const environment& env = first.get_environment();
return env.priv_->
classes_being_compared_.count
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>((&second))));
return (env.priv_->left_classes_being_compared_.count(&first)
|| env.priv_->right_classes_being_compared_.count(&second)
|| env.priv_->right_classes_being_compared_.count(&first)
|| env.priv_->left_classes_being_compared_.count(&second));
}
/// Test if a pair of class_or_union is being currently compared.
@@ -1337,9 +1567,9 @@ struct function_type::priv
const function_type& second) const
{
const environment& env = first.get_environment();
env.priv_->fn_types_being_compared_.insert
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>(&second)));
env.priv_->left_fn_types_being_compared_.insert(&first);
env.priv_->right_fn_types_being_compared_.insert(&second);
}
/// Mark a given pair of @ref function_type as being compared.
@@ -1354,9 +1584,9 @@ struct function_type::priv
const function_type& second) const
{
const environment& env = first.get_environment();
env.priv_->fn_types_being_compared_.erase
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>(&second)));
env.priv_->left_fn_types_being_compared_.erase(&first);
env.priv_->right_fn_types_being_compared_.erase(&second);
}
/// Tests if a @ref function_type is currently being compared.
@@ -1369,9 +1599,10 @@ struct function_type::priv
const function_type& second) const
{
const environment& env = first.get_environment();
return env.priv_->fn_types_being_compared_.count
(std::make_pair(reinterpret_cast<uint64_t>(&first),
reinterpret_cast<uint64_t>(&second)));
return (env.priv_->left_fn_types_being_compared_.count(&first)
||
env.priv_->right_fn_types_being_compared_.count(&second));
}
};// end struc function_type::priv
+1694 -283
View File
File diff suppressed because it is too large Load Diff
+34 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -140,6 +140,9 @@ report_type_changes_from_diff_maps(const leaf_reporter& reporter,
// typedefs
report_diffs(reporter, maps.get_typedef_diff_map(), out, indent);
// subranges
report_diffs(reporter, maps.get_subrange_diff_map(), out, indent);
// arrays
report_diffs(reporter, maps.get_array_diff_map(), out, indent);
@@ -207,6 +210,12 @@ leaf_reporter::report(const qualified_type_diff& d, ostream& out,
return;
report_local_qualified_type_changes(d, out, indent);
// Note that changes that are local to the underlying type of a
// qualified type are considered to be local to the qualified type
// itself. So let's go ahead and report the local changes of the
// underlying type.
report_underlying_changes_of_qualified_type(d, out, indent);
}
/// Report the changes carried by a @ref pointer_diff node.
@@ -430,6 +439,30 @@ leaf_reporter::report(const scope_diff& d,
out << "\n";
}
/// Report about the change carried by a @ref subrange_diff diff node
/// in a serialized form.
///
/// @param d the diff node to consider.
///
/// @param out the output stream to report to.
///
/// @param indent the indentation string to use in the report.
void
leaf_reporter::report(const subrange_diff& d, std::ostream& out,
const std::string& indent) const
{
if (!diff_to_be_reported(&d))
return;
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subrange(),
d.second_subrange(),
"range type");
represent(d, d.context(), out,indent, /*local_only=*/true);
maybe_report_interfaces_impacted_by_diff(&d, out, indent);
}
/// Report the changes carried by a @ref array_diff node.
///
/// @param out the output stream to report to.
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+334 -124
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
///
@@ -62,8 +62,8 @@ static bool read_is_artificial(xmlNodePtr, bool&);
static bool read_tracking_non_reachable_types(xmlNodePtr, bool&);
static bool read_is_non_reachable_type(xmlNodePtr, bool&);
static bool read_naming_typedef_id_string(xmlNodePtr, string&);
#ifdef WITH_DEBUG_SELF_COMPARISON
static bool read_type_id_string(xmlNodePtr, string&);
#ifdef WITH_DEBUG_SELF_COMPARISON
static bool maybe_map_type_with_type_id(const type_base_sptr&,
xmlNodePtr);
static bool maybe_map_type_with_type_id(const type_base_sptr&,
@@ -98,6 +98,12 @@ read_symbol_db_from_input(reader& rdr,
static translation_unit_sptr
read_translation_unit_from_input(fe_iface& rdr);
static decl_base_sptr
build_ir_node_for_void_type(reader& rdr);
static decl_base_sptr
build_ir_node_for_void_pointer_type(reader& rdr);
/// The ABIXML reader object.
///
/// This abstracts the context in which the current ABI
@@ -109,6 +115,9 @@ class reader : public fe_iface
{
public:
typedef unordered_map<string, vector<type_base_sptr> >
types_map_type;
typedef unordered_map<string,
vector<type_base_sptr> >::const_iterator
const_types_map_it;
@@ -136,7 +145,7 @@ public:
get_artifact_used_by_relation_map(reader& rdr);
private:
unordered_map<string, vector<type_base_sptr> > m_types_map;
types_map_type m_types_map;
unordered_map<string, shared_ptr<function_tdecl> > m_fn_tmpl_map;
unordered_map<string, shared_ptr<class_tdecl> > m_class_tmpl_map;
vector<type_base_sptr> m_types_to_canonicalize;
@@ -165,6 +174,13 @@ public:
{
}
/// Test if logging was requested.
///
/// @return true iff logging was requested.
bool
do_log() const
{return options().do_log;}
/// Getter for the flag that tells us if we are tracking types that
/// are not reachable from global functions and variables.
///
@@ -328,6 +344,12 @@ public:
get_scope_for_node(xmlNodePtr node,
access_specifier& access);
scope_decl_sptr
get_scope_for_node(xmlNodePtr node);
scope_decl*
get_scope_ptr_for_node(xmlNodePtr node);
// This is defined later, after build_type() is declared, because it
// uses it.
type_base_sptr
@@ -571,7 +593,7 @@ public:
/// Associate an ID with a type.
///
/// @param type the type to associate witht he ID.
/// @param type the type to associate with the ID.
///
/// @param id the ID to associate to the type.
///
@@ -721,19 +743,34 @@ public:
{record_artifacts_as_used_in_fn_type(fn_type.get());}
#endif
/// This function must be called on each declaration that is created
/// during the parsing. It adds the declaration to the scope that
/// its XML node belongs to and updates the state of the parsing
/// context accordingly.
///
/// @param decl the newly created declaration.
///
/// @param node the xml node @p decl originated from.
void
push_decl_to_scope(const decl_base_sptr& decl, xmlNodePtr node)
{
scope_decl* scope = nullptr;
scope = get_scope_ptr_for_node(node);
return push_decl_to_scope(decl, scope);
}
/// This function must be called on each declaration that is created during
/// the parsing. It adds the declaration to the current scope, and updates
/// the state of the parsing context accordingly.
///
/// @param decl the newly created declaration.
void
push_decl_to_current_scope(decl_base_sptr decl,
bool add_to_current_scope)
push_decl_to_scope(const decl_base_sptr& decl,
scope_decl* scope)
{
ABG_ASSERT(decl);
if (add_to_current_scope)
add_decl_to_scope(decl, get_cur_scope());
if (scope)
add_decl_to_scope(decl, scope);
if (!decl->get_translation_unit())
decl->set_translation_unit(get_translation_unit());
ABG_ASSERT(decl->get_translation_unit());
@@ -748,16 +785,19 @@ public:
///
/// @param id the unique ID to be associated to t
///
/// @param scope the scope to add the type to.
///
/// @return true upon successful completion.
///
bool
push_and_key_type_decl(shared_ptr<type_base> t, const string& id,
bool add_to_current_scope)
push_and_key_type_decl(const type_base_sptr& t,
const string& id,
scope_decl* scope)
{
shared_ptr<decl_base> decl = dynamic_pointer_cast<decl_base>(t);
decl_base_sptr decl = get_type_declaration(t);
ABG_ASSERT(decl);
push_decl_to_current_scope(decl, add_to_current_scope);
push_decl_to_scope(decl, scope);
if (!t->get_translation_unit())
t->set_translation_unit(get_translation_unit());
ABG_ASSERT(t->get_translation_unit());
@@ -765,6 +805,31 @@ public:
return true;
}
/// This function must be called on each type decl that is created
/// during the parsing. It adds the type decl to the current scope
/// and associates a unique ID to it.
///
/// @param t the type to consider.
///
/// @param node the XML it originates from.
///
/// @return true upon successful completion.
///
bool
push_and_key_type_decl(const type_base_sptr& t,
const xmlNodePtr node,
bool add_to_current_scope)
{
string id;
if (!read_type_id_string(node, id))
return false;
scope_decl* scope = nullptr;
if (add_to_current_scope && !is_unique_type(t))
scope = get_scope_ptr_for_node(node);
return push_and_key_type_decl(t, id, scope);
}
/// Getter for the object that determines if a given declaration
/// ought to be put in the set of exported decls of the current
/// corpus.
@@ -844,8 +909,8 @@ public:
void
maybe_check_abixml_canonical_type_stability(type_base_sptr& t)
{
if (!get_environment()->self_comparison_debug_is_on()
|| get_environment()->get_type_id_canonical_type_map().empty())
if (!get_environment().self_comparison_debug_is_on()
|| get_environment().get_type_id_canonical_type_map().empty())
return ;
if (class_decl_sptr c = is_class_type(t))
@@ -857,15 +922,15 @@ public:
// Let's get the type-id of this type as recorded in the
// originating abixml file.
string type_id =
get_environment()->get_type_id_from_pointer(reinterpret_cast<uintptr_t>(t.get()));
get_environment().get_type_id_from_pointer(reinterpret_cast<uintptr_t>(t.get()));
if (!type_id.empty())
{
// Now let's get the canonical type that initially led to the
// serialization of a type with this type-id, when the abixml
// was being serialized.
auto j = get_environment()->get_type_id_canonical_type_map().find(type_id);
if (j == get_environment()->get_type_id_canonical_type_map().end())
auto j = get_environment().get_type_id_canonical_type_map().find(type_id);
if (j == get_environment().get_type_id_canonical_type_map().end())
{
if (t->get_naked_canonical_type())
std::cerr << "error: no type with type-id: '"
@@ -874,13 +939,13 @@ public:
}
else if (j->second
!= reinterpret_cast<uintptr_t>(t->get_canonical_type().get()))
// So thecanonical type of 't' (at abixml de-serialization
// So the canonical type of 't' (at abixml de-serialization
// time) is different from the canonical type that led to
// the serialization of 't' at abixml serialization time.
// Report this because it needs further debugging.
std::cerr << "error: canonical type for type '"
<< t->get_pretty_representation(/*internal=*/false,
/*qualified=*/false)
<< t->get_pretty_representation(/*internal=*/true,
/*qualified=*/true)
<< "' of type-id '" << type_id
<< "' changed from '" << std::hex
<< j->second << "' to '" << std::hex
@@ -1100,9 +1165,8 @@ public:
return nil;
#ifdef WITH_DEBUG_SELF_COMPARISON
if (get_environment()->self_comparison_debug_is_on())
get_environment()->
set_self_comparison_debug_input(corpus());
if (get_environment().self_comparison_debug_is_on())
get_environment().set_self_comparison_debug_input(corpus());
#endif
if (!corpus_group())
@@ -1161,9 +1225,8 @@ public:
else
{
#ifdef WITH_DEBUG_SELF_COMPARISON
if (get_environment()->self_comparison_debug_is_on())
get_environment()->
set_self_comparison_debug_input(corpus());
if (get_environment().self_comparison_debug_is_on())
get_environment().set_self_comparison_debug_input(corpus());
#endif
if (!corpus_group())
@@ -1233,8 +1296,23 @@ public:
}
tools_utils::timer t;
if (do_log())
{
std::cerr << "perform late type canonicalization ...\n";
t.start();
}
perform_late_type_canonicalizing();
if (do_log())
{
t.stop();
std::cerr << "late type canonicalization DONE@"
<< corpus()->get_path()
<< ":" << t << "\n";
}
get_environment().canonicalization_is_done(true);
if (call_reader_next)
@@ -1319,11 +1397,11 @@ build_function_parameter (reader&, const xmlNodePtr);
static function_decl_sptr
build_function_decl(reader&, const xmlNodePtr,
class_or_union_sptr, bool);
class_or_union_sptr, bool, bool);
static function_decl_sptr
build_function_decl_if_not_suppressed(reader&, const xmlNodePtr,
class_or_union_sptr, bool);
class_or_union_sptr, bool, bool);
static bool
function_is_suppressed(const reader& rdr,
@@ -1355,7 +1433,7 @@ static shared_ptr<function_type>
build_function_type(reader&, const xmlNodePtr, bool);
static array_type_def::subrange_sptr
build_subrange_type(reader&, const xmlNodePtr);
build_subrange_type(reader&, const xmlNodePtr, bool);
static array_type_def_sptr
build_array_type_def(reader&, const xmlNodePtr, bool);
@@ -1455,8 +1533,7 @@ static decl_base_sptr handle_class_tdecl(reader&, xmlNodePtr, bool);
/// @return the IR node representing the scope of the IR node for the
/// XML node given in argument.
scope_decl_sptr
reader::get_scope_for_node(xmlNodePtr node,
access_specifier& access)
reader::get_scope_for_node(xmlNodePtr node, access_specifier& access)
{
scope_decl_sptr nil, scope;
if (!node)
@@ -1468,7 +1545,9 @@ reader::get_scope_for_node(xmlNodePtr node,
&& (xmlStrEqual(parent->name, BAD_CAST("data-member"))
|| xmlStrEqual(parent->name, BAD_CAST("member-type"))
|| xmlStrEqual(parent->name, BAD_CAST("member-function"))
|| xmlStrEqual(parent->name, BAD_CAST("member-template"))))
|| xmlStrEqual(parent->name, BAD_CAST("member-template"))
|| xmlStrEqual(parent->name, BAD_CAST("template-parameter-type-composition"))
|| xmlStrEqual(parent->name, BAD_CAST("array-type-def"))))
{
read_access(parent, access);
parent = parent->parent;
@@ -1499,6 +1578,40 @@ reader::get_scope_for_node(xmlNodePtr node,
return scope;
}
/// Get the IR node representing the scope for a given XML node.
///
/// This function might trigger the building of a full sub-tree of IR.
///
/// @param node the XML for which to return the scope decl. If its
/// parent XML node has no corresponding IR node, that IR node is constructed.
///
/// @return the IR node representing the scope of the IR node for the
/// XML node given in argument.
scope_decl_sptr
reader::get_scope_for_node(xmlNodePtr node)
{
access_specifier access;
return get_scope_for_node(node, access);
}
/// Get the IR node representing the scope for a given XML node.
///
/// This function might trigger the building of a full sub-tree of IR.
///
/// @param node the XML for which to return the scope decl. If its
/// parent XML node has no corresponding IR node, that IR node is constructed.
///
/// @return the IR node representing the scope of the IR node for the
/// XML node given in argument.
scope_decl*
reader::get_scope_ptr_for_node(xmlNodePtr node)
{
scope_decl_sptr scope = get_scope_for_node(node);
if (scope)
return scope.get();
return nullptr;
}
/// Get the type declaration IR node that matches a given XML type node ID.
///
/// If no IR node has been built for this ID, this function builds the
@@ -1509,8 +1622,7 @@ reader::get_scope_for_node(xmlNodePtr node,
///
/// @return the type declaration for the ID given in parameter.
type_base_sptr
reader::build_or_get_type_decl(const string& id,
bool add_decl_to_scope)
reader::build_or_get_type_decl(const string& id, bool add_decl_to_scope)
{
type_base_sptr t = get_type_decl(id);
@@ -2861,8 +2973,6 @@ read_elf_symbol_visibility(xmlNodePtr node, elf_symbol::visibility& v)
}
return false;
}
#ifdef WITH_DEBUG_SELF_COMPARISON
/// Read the value of the 'id' attribute from a given XML node.
///
/// @param node the XML node to consider.
@@ -2881,6 +2991,7 @@ read_type_id_string(xmlNodePtr node, string& type_id)
return false;
}
#ifdef WITH_DEBUG_SELF_COMPARISON
/// Associate a type-id string with the type that was constructed from
/// it.
///
@@ -2906,8 +3017,8 @@ maybe_map_type_with_type_id(const type_base_sptr& t,
|| is_non_canonicalized_type(t.get()))
return false;
env.get_pointer_type_id_map()[reinterpret_cast<uintptr_t>(t.get())] =
type_id;
const_cast<environment&>(env).
get_pointer_type_id_map()[reinterpret_cast<uintptr_t>(t.get())] = type_id;
return true;
}
@@ -3011,7 +3122,10 @@ build_namespace_decl(reader& rdr,
const environment& env = rdr.get_environment();
namespace_decl_sptr decl(new namespace_decl(env, name, loc));
maybe_set_artificial_location(rdr, node, decl);
rdr.push_decl_to_current_scope(decl, add_to_current_scope);
rdr.push_decl_to_scope(decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
rdr.map_xml_node_to_decl(node, decl);
for (xmlNodePtr n = xmlFirstElementChild(node);
@@ -3158,12 +3272,15 @@ build_elf_symbol_from_reference(reader& rdr, const xmlNodePtr node)
if (name.empty())
return nil;
const elf_symbols& symbols =
rdr.corpus()->get_symtab()->lookup_symbol(name);
if (rdr.corpus()->get_symtab())
{
const elf_symbols& symbols =
rdr.corpus()->get_symtab()->lookup_symbol(name);
for (const auto& symbol : symbols)
if (symbol->get_id_string() == sym_id)
return symbol;
for (const auto& symbol : symbols)
if (symbol->get_id_string() == sym_id)
return symbol;
}
}
return nil;
@@ -3323,16 +3440,21 @@ build_function_parameter(reader& rdr, const xmlNodePtr node)
/// shared_ptr<function_decl> that is returned is then really a
/// shared_ptr<method_decl>.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// @param add_to_current_scope if set to yes, the result of
/// this function is added to its current scope.
///
/// @param add_to_exported_decls if set to yes, the resulting of this
/// function is added to the set of decls exported by the current
/// corpus being built.
///
/// @return a pointer to a newly created function_decl upon successful
/// completion, a null pointer otherwise.
static function_decl_sptr
build_function_decl(reader& rdr,
build_function_decl(reader& rdr,
const xmlNodePtr node,
class_or_union_sptr as_method_decl,
bool add_to_current_scope)
bool add_to_current_scope,
bool add_to_exported_decls)
{
function_decl_sptr nil;
@@ -3347,6 +3469,16 @@ build_function_decl(reader& rdr,
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name"))
mangled_name = xml::unescape_xml_string(CHAR_STR(s));
if (as_method_decl
&& !mangled_name.empty()
&& as_method_decl->find_member_function_sptr(mangled_name))
{
function_decl_sptr result =
as_method_decl->find_member_function_sptr(mangled_name);
if (result)
return result;
}
string inline_prop;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "declared-inline"))
inline_prop = CHAR_STR(s);
@@ -3411,7 +3543,10 @@ build_function_decl(reader& rdr,
bind));
maybe_set_artificial_location(rdr, node, fn_decl);
rdr.push_decl_to_current_scope(fn_decl, add_to_current_scope);
rdr.push_decl_to_scope(fn_decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
RECORD_ARTIFACTS_AS_USED_IN_FN_DECL(rdr, fn_decl);
elf_symbol_sptr sym = build_elf_symbol_from_reference(rdr, node);
@@ -3425,7 +3560,8 @@ build_function_decl(reader& rdr,
rdr.maybe_canonicalize_type(fn_type, !add_to_current_scope);
rdr.maybe_add_fn_to_exported_decls(fn_decl.get());
if (add_to_exported_decls)
rdr.maybe_add_fn_to_exported_decls(fn_decl.get());
return fn_decl;
}
@@ -3448,16 +3584,21 @@ build_function_decl(reader& rdr,
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @param add_to_exported_decls if set to yes, the resulting of this
/// function is added to the set of decls exported by the current
/// corpus being built.
///
/// @return a pointer to a newly created function_decl upon successful
/// completion. If the function was suppressed by a suppression
/// specification then returns nil.
static function_decl_sptr
build_function_decl_if_not_suppressed(reader& rdr,
const xmlNodePtr node,
class_or_union_sptr as_method_decl,
bool add_to_current_scope)
build_function_decl_if_not_suppressed(reader& rdr,
const xmlNodePtr node,
class_or_union_sptr as_method_decl,
bool add_to_current_scope,
bool add_to_exported_decls)
{
function_decl_sptr fn;
function_decl_sptr fn;
if (function_is_suppressed(rdr, node))
// The function was suppressed by at least one suppression
@@ -3466,7 +3607,8 @@ build_function_decl_if_not_suppressed(reader& rdr,
;
else
fn = build_function_decl(rdr, node, as_method_decl,
add_to_current_scope);
add_to_current_scope,
add_to_exported_decls);
return fn;
}
@@ -3650,7 +3792,10 @@ build_var_decl(reader& rdr,
if (sym)
decl->set_symbol(sym);
rdr.push_decl_to_current_scope(decl, add_to_current_scope);
rdr.push_decl_to_scope(decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
if (add_to_current_scope)
{
// This variable is really being kept in the IR, so let's record
@@ -3664,6 +3809,46 @@ build_var_decl(reader& rdr,
return decl;
}
/// Build the IR node for a void type.
///
/// @param rdr the ABIXML reader to use.
///
/// @return the void type node.
static decl_base_sptr
build_ir_node_for_void_type(reader& rdr)
{
const environment& env = rdr.get_environment();
type_base_sptr t = env.get_void_type();
add_decl_to_scope(is_decl(t), rdr.get_translation_unit()->get_global_scope());
decl_base_sptr type_declaration = get_type_declaration(t);
canonicalize(t);
return type_declaration;
}
/// Build the IR node for a "pointer to void type".
///
/// That IR node is shared across the ABI corpus.
///
/// Note that this function just gets that IR node from the
/// environment and, if it's not added to any scope yet, adds it to
/// the global scope associated to the current translation unit.
///
/// @param rdr the DWARF reader to consider.
///
/// @return the IR node.
static decl_base_sptr
build_ir_node_for_void_pointer_type(reader& rdr)
{
const environment& env = rdr.get_environment();
type_base_sptr t = env.get_void_pointer_type();
add_decl_to_scope(is_decl(t), rdr.get_translation_unit()->get_global_scope());
decl_base_sptr type_declaration = get_type_declaration(t);
canonicalize(t);
return type_declaration;
}
/// Build a type_decl from a "type-decl" XML Node.
///
/// @param rdr the context of the parsing.
@@ -3675,7 +3860,7 @@ build_var_decl(reader& rdr,
///
/// @return a pointer to type_decl upon successful completion, a null
/// pointer otherwise.
static shared_ptr<type_decl>
static type_decl_sptr
build_type_decl(reader& rdr,
const xmlNodePtr node,
bool add_to_current_scope)
@@ -3732,12 +3917,18 @@ build_type_decl(reader& rdr,
}
const environment& env = rdr.get_environment();
type_decl_sptr decl(new type_decl(env, name, size_in_bits,
alignment_in_bits, loc));
type_decl_sptr decl;
if (name == env.get_variadic_parameter_type_name())
decl = is_type_decl(env.get_variadic_parameter_type());
else if (name == "void")
decl = is_type_decl(build_ir_node_for_void_type(rdr));
else
decl.reset(new type_decl(env, name, size_in_bits,
alignment_in_bits, loc));
maybe_set_artificial_location(rdr, node, decl);
decl->set_is_anonymous(is_anonymous);
decl->set_is_declaration_only(is_decl_only);
if (rdr.push_and_key_type_decl(decl, id, add_to_current_scope))
if (rdr.push_and_key_type_decl(decl, node, add_to_current_scope))
{
rdr.map_xml_node_to_decl(node, decl);
return decl;
@@ -3825,7 +4016,7 @@ build_qualified_type_decl(reader& rdr,
{
decl.reset(new qualified_type_def(underlying_type, cv, loc));
maybe_set_artificial_location(rdr, node, decl);
rdr.push_and_key_type_decl(decl, id, add_to_current_scope);
rdr.push_and_key_type_decl(decl, node, add_to_current_scope);
RECORD_ARTIFACT_AS_USED_BY(rdr, underlying_type, decl);
}
@@ -3886,25 +4077,29 @@ build_pointer_type_def(reader& rdr,
location loc;
read_location(rdr, node, loc);
// Create the pointer type /before/ the pointed-to type. After the
// creation, the type is 'keyed' using rdr.push_and_key_type_decl.
// This means that the type can be retrieved from its type ID. This
// is so that if the pointed-to type indirectly uses this pointer
// type (via recursion) then that is made possible.
pointer_type_def_sptr t(new pointer_type_def(rdr.get_environment(),
size_in_bits,
alignment_in_bits,
loc));
maybe_set_artificial_location(rdr, node, t);
if (rdr.push_and_key_type_decl(t, id, add_to_current_scope))
rdr.map_xml_node_to_decl(node, t);
type_base_sptr pointed_to_type =
rdr.build_or_get_type_decl(type_id, true);
ABG_ASSERT(pointed_to_type);
t->set_pointed_to_type(pointed_to_type);
pointer_type_def_sptr t;
if (rdr.get_environment().is_void_type(pointed_to_type))
t = is_pointer_type(build_ir_node_for_void_pointer_type(rdr));
else
// Create the pointer type /before/ the pointed-to type. After the
// creation, the type is 'keyed' using rdr.push_and_key_type_decl.
// This means that the type can be retrieved from its type ID. This
// is so that if the pointed-to type indirectly uses this pointer
// type (via recursion) then that is made possible.
t.reset(new pointer_type_def(pointed_to_type,
size_in_bits,
alignment_in_bits,
loc));
maybe_set_artificial_location(rdr, node, t);
if (rdr.push_and_key_type_decl(t, node, add_to_current_scope))
rdr.map_xml_node_to_decl(node, t);
RECORD_ARTIFACT_AS_USED_BY(rdr, pointed_to_type, t);
return t;
}
@@ -3977,7 +4172,7 @@ build_reference_type_def(reader& rdr,
is_lvalue, size_in_bits,
alignment_in_bits, loc));
maybe_set_artificial_location(rdr, node, t);
if (rdr.push_and_key_type_decl(t, id, add_to_current_scope))
if (rdr.push_and_key_type_decl(t, node, add_to_current_scope))
rdr.map_xml_node_to_decl(node, t);
type_base_sptr pointed_to_type =
@@ -4034,16 +4229,16 @@ build_function_type(reader& rdr,
{
method_class_type =
is_class_or_union_type(rdr.build_or_get_type_decl(method_class_id,
/*add_decl_to_scope=*/true));
/*add_decl_to_scope=*/true));
ABG_ASSERT(method_class_type);
}
function_type_sptr fn_type(is_method_t
? new method_type(method_class_type,
/*is_const=*/false,
size, align)
: new function_type(return_type,
parms, size, align));
function_type_sptr fn_type(is_method_t
? new method_type(method_class_type,
/*is_const=*/false,
size, align)
: new function_type(return_type,
parms, size, align));
rdr.get_translation_unit()->bind_function_type_life_time(fn_type);
rdr.key_type_decl(fn_type, id);
@@ -4087,8 +4282,9 @@ build_function_type(reader& rdr,
/// @return a pointer to a newly built array_type_def::subrange_type
/// upon successful completion, a null pointer otherwise.
static array_type_def::subrange_sptr
build_subrange_type(reader& rdr,
const xmlNodePtr node)
build_subrange_type(reader& rdr,
const xmlNodePtr node,
bool add_to_current_scope)
{
array_type_def::subrange_sptr nil;
@@ -4128,7 +4324,7 @@ build_subrange_type(reader& rdr,
bool is_infinite = false;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "length"))
{
if (string(CHAR_STR(s)) == "infinite")
if (string(CHAR_STR(s)) == "infinite" || string(CHAR_STR(s)) == "unknown")
is_infinite = true;
else
length = strtoull(CHAR_STR(s), NULL, 0);
@@ -4187,6 +4383,9 @@ build_subrange_type(reader& rdr,
maybe_set_artificial_location(rdr, node, p);
p->is_infinite(is_infinite);
if (rdr.push_and_key_type_decl(p, node, add_to_current_scope))
rdr.map_xml_node_to_decl(node, p);
return p;
}
@@ -4259,7 +4458,8 @@ build_array_type_def(reader& rdr,
size_in_bits = strtoull(CHAR_STR(s), &endptr, 0);
if (*endptr != '\0')
{
if (!strcmp(CHAR_STR(s), "infinite"))
if (!strcmp(CHAR_STR(s), "infinite")
||!strcmp(CHAR_STR(s), "unknown"))
size_in_bits = (size_t) -1;
else
return nil;
@@ -4284,7 +4484,7 @@ build_array_type_def(reader& rdr,
if (xmlStrEqual(n->name, BAD_CAST("subrange")))
{
if (array_type_def::subrange_sptr s =
build_subrange_type(rdr, n))
build_subrange_type(rdr, n, /*add_to_current_scope=*/true))
{
MAYBE_MAP_TYPE_WITH_TYPE_ID(s, n);
if (add_to_current_scope)
@@ -4303,7 +4503,7 @@ build_array_type_def(reader& rdr,
array_type_def_sptr ar_type(new array_type_def(type, subranges, loc));
maybe_set_artificial_location(rdr, node, ar_type);
if (rdr.push_and_key_type_decl(ar_type, id, add_to_current_scope))
if (rdr.push_and_key_type_decl(ar_type, node, add_to_current_scope))
rdr.map_xml_node_to_decl(node, ar_type);
RECORD_ARTIFACT_AS_USED_BY(rdr, type, ar_type);
@@ -4479,7 +4679,7 @@ build_enum_type_decl(reader& rdr,
t->set_is_anonymous(is_anonymous);
t->set_is_artificial(is_artificial);
t->set_is_declaration_only(is_decl_only);
if (rdr.push_and_key_type_decl(t, id, add_to_current_scope))
if (rdr.push_and_key_type_decl(t, node, add_to_current_scope))
{
maybe_set_naming_typedef(rdr, node, t);
rdr.map_xml_node_to_decl(node, t);
@@ -4520,13 +4720,6 @@ build_typedef_decl(reader& rdr,
id = CHAR_STR(s);
ABG_ASSERT(!id.empty());
if (type_base_sptr t = rdr.get_type_decl(id))
{
typedef_decl_sptr result = is_typedef(t);
ABG_ASSERT(result);
return result;
}
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
@@ -4544,7 +4737,7 @@ build_typedef_decl(reader& rdr,
typedef_decl_sptr t(new typedef_decl(name, underlying_type, loc));
maybe_set_artificial_location(rdr, node, t);
rdr.push_and_key_type_decl(t, id, add_to_current_scope);
rdr.push_and_key_type_decl(t, node, add_to_current_scope);
rdr.map_xml_node_to_decl(node, t);
RECORD_ARTIFACT_AS_USED_BY(rdr, underlying_type, t);
@@ -4786,7 +4979,10 @@ build_class_decl(reader& rdr,
ABG_ASSERT(!is_decl_only || !is_def_of_decl);
rdr.push_decl_to_current_scope(decl, add_to_current_scope);
rdr.push_decl_to_scope(decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
rdr.map_xml_node_to_decl(node, decl);
rdr.key_type_decl(decl, id);
@@ -4795,7 +4991,7 @@ build_class_decl(reader& rdr,
maybe_set_naming_typedef(rdr, node, decl);
for (xmlNodePtr n = xmlFirstElementChild(node);
!is_decl_only && n;
n;
n = xmlNextElementSibling(n))
{
if (xmlStrEqual(n->name, BAD_CAST("base-class")))
@@ -4930,8 +5126,6 @@ build_class_decl(reader& rdr,
}
else if (xmlStrEqual(n->name, BAD_CAST("member-function")))
{
rdr.map_xml_node_to_decl(n, decl);
access_specifier access =
is_struct
? public_access
@@ -4959,7 +5153,8 @@ build_class_decl(reader& rdr,
{
if (function_decl_sptr f =
build_function_decl_if_not_suppressed(rdr, p, decl,
/*add_to_cur_sc=*/true))
/*add_to_cur_sc=*/true,
/*add_to_exported_decls=*/false))
{
method_decl_sptr m = is_method_decl(f);
ABG_ASSERT(m);
@@ -4971,6 +5166,8 @@ build_class_decl(reader& rdr,
set_member_function_is_ctor(m, is_ctor);
set_member_function_is_dtor(m, is_dtor);
set_member_function_is_const(m, is_const);
rdr.map_xml_node_to_decl(p, m);
rdr.maybe_add_fn_to_exported_decls(f.get());
break;
}
}
@@ -5199,7 +5396,10 @@ build_union_decl(reader& rdr,
ABG_ASSERT(!is_decl_only || !is_def_of_decl);
rdr.push_decl_to_current_scope(decl, add_to_current_scope);
rdr.push_decl_to_scope(decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
rdr.map_xml_node_to_decl(node, decl);
rdr.key_type_decl(decl, id);
@@ -5312,7 +5512,8 @@ build_union_decl(reader& rdr,
{
if (function_decl_sptr f =
build_function_decl_if_not_suppressed(rdr, p, decl,
/*add_to_cur_sc=*/true))
/*add_to_cur_sc=*/true,
/*add_to_exported_decls=*/false))
{
method_decl_sptr m = is_method_decl(f);
ABG_ASSERT(m);
@@ -5321,6 +5522,7 @@ build_union_decl(reader& rdr,
set_member_function_is_ctor(m, is_ctor);
set_member_function_is_dtor(m, is_dtor);
set_member_function_is_const(m, is_const);
rdr.maybe_add_fn_to_exported_decls(f.get());
break;
}
}
@@ -5413,7 +5615,12 @@ build_function_tdecl(reader& rdr,
function_tdecl_sptr fn_tmpl_decl(new function_tdecl(env, loc, vis, bind));
maybe_set_artificial_location(rdr, node, fn_tmpl_decl);
rdr.push_decl_to_current_scope(fn_tmpl_decl, add_to_current_scope);
rdr.push_decl_to_scope(fn_tmpl_decl,
add_to_current_scope
? rdr.get_scope_ptr_for_node(node)
: nullptr);
rdr.key_fn_tmpl_decl(fn_tmpl_decl, id);
rdr.map_xml_node_to_decl(node, fn_tmpl_decl);
unsigned parm_index = 0;
for (xmlNodePtr n = xmlFirstElementChild(node);
@@ -5428,7 +5635,8 @@ build_function_tdecl(reader& rdr,
}
else if (function_decl_sptr f =
build_function_decl_if_not_suppressed(rdr, n, class_decl_sptr(),
/*add_to_current_scope=*/true))
/*add_to_current_scope=*/true,
/*add_to_exported_decls=*/true))
fn_tmpl_decl->set_pattern(f);
}
@@ -5449,12 +5657,12 @@ build_function_tdecl(reader& rdr,
///
/// @return the newly built function_tdecl upon successful
/// completion, a null pointer otherwise.
static shared_ptr<class_tdecl>
build_class_tdecl(reader& rdr,
static class_tdecl_sptr
build_class_tdecl(reader& rdr,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<class_tdecl> nil, result;
class_tdecl_sptr nil, result;
if (!xmlStrEqual(node->name, BAD_CAST("class-template-decl")))
return nil;
@@ -5476,7 +5684,10 @@ build_class_tdecl(reader& rdr,
class_tdecl_sptr class_tmpl (new class_tdecl(env, loc, vis));
maybe_set_artificial_location(rdr, node, class_tmpl);
rdr.push_decl_to_current_scope(class_tmpl, add_to_current_scope);
if (add_to_current_scope)
rdr.push_decl_to_scope(class_tmpl, node);
rdr.key_class_tmpl_decl(class_tmpl, id);
rdr.map_xml_node_to_decl(node, class_tmpl);
unsigned parm_index = 0;
for (xmlNodePtr n = xmlFirstElementChild(node);
@@ -5555,10 +5766,9 @@ build_type_tparameter(reader& rdr,
maybe_set_artificial_location(rdr, node, result);
if (id.empty())
rdr.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result),
/*add_to_current_scope=*/true);
rdr.push_decl_to_scope(is_decl(result), node);
else
rdr.push_and_key_type_decl(result, id, /*add_to_current_scope=*/true);
rdr.push_and_key_type_decl(result, node, /*add_to_current_scope=*/true);
rdr.maybe_canonicalize_type(result, /*force_delay=*/false);
@@ -5592,8 +5802,7 @@ build_type_composition(reader& rdr,
type_base_sptr composed_type;
result.reset(new type_composition(index, tdecl, composed_type));
rdr.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result),
/*add_to_current_scope=*/true);
rdr.push_decl_to_scope(is_decl(result), node);
for (xmlNodePtr n = xmlFirstElementChild(node);
n;
@@ -5665,8 +5874,7 @@ build_non_type_tparameter(reader& rdr,
r.reset(new non_type_tparameter(index, tdecl, name, type, loc));
maybe_set_artificial_location(rdr, node, r);
rdr.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(r),
/*add_to_current_scope=*/true);
rdr.push_decl_to_scope(is_decl(r), node);
return r;
}
@@ -5721,7 +5929,7 @@ build_template_tparameter(reader& rdr,
template_tparameter_sptr result(new template_tparameter(index, tdecl,
name, loc));
maybe_set_artificial_location(rdr, node, result);
rdr.push_decl_to_current_scope(result, /*add_to_current_scope=*/true);
rdr.push_decl_to_scope(result, node);
// Go parse template parameters that are children nodes
int parm_index = 0;
@@ -5795,6 +6003,7 @@ build_type(reader& rdr,
|| (t = build_reference_type_def(rdr, node , add_to_current_scope))
|| (t = build_function_type(rdr, node, add_to_current_scope))
|| (t = build_array_type_def(rdr, node, add_to_current_scope))
|| (t = build_subrange_type(rdr, node, add_to_current_scope))
|| (t = build_enum_type_decl_if_not_suppressed(rdr, node,
add_to_current_scope))
|| (t = build_typedef_decl(rdr, node, add_to_current_scope))
@@ -6005,7 +6214,8 @@ handle_function_decl(reader& rdr,
bool add_to_current_scope)
{
return build_function_decl_if_not_suppressed(rdr, node, class_decl_sptr(),
add_to_current_scope);
add_to_current_scope,
/*add_to_exported_decls=*/true);
}
/// Parse a 'class-decl' xml element.
@@ -6214,7 +6424,7 @@ read_corpus_from_abixml_file(const string& path,
bool
load_canonical_type_ids(fe_iface& iface, const string &file_path)
{
xml_reader::reader& rdr = dynamic_cast<xml_reader::reader&>(iface)
abixml::reader& rdr = dynamic_cast<abixml::reader&>(iface);
xmlDocPtr doc = xmlReadFile(file_path.c_str(), NULL, XML_PARSE_NOERROR);
if (!doc)
@@ -6273,7 +6483,7 @@ load_canonical_type_ids(fe_iface& iface, const string &file_path)
// that are not canonicalized. Look into function
// hash_as_canonical_type_or_constant for the details.
&& v != 0xdeadbabe)
rdr.get_environment()->get_type_id_canonical_type_map()[id] = v;
rdr.get_environment().get_type_id_canonical_type_map()[id] = v;
}
}
return true;
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
/// @file
///
+127 -8
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -402,8 +402,12 @@ represent(const var_diff_sptr &diff,
const bool o_anon = !!is_anonymous_data_member(o);
const bool n_anon = !!is_anonymous_data_member(n);
const bool is_strict_anonymous_data_member_change = o_anon && n_anon;
const string o_name = o->get_qualified_name();
const string n_name = n->get_qualified_name();
const string o_name = (is_data_member_of_anonymous_class_or_union(o)
? o->get_name()
: o->get_qualified_name());
const string n_name = (is_data_member_of_anonymous_class_or_union(n)
? n->get_name()
: n->get_qualified_name());
const uint64_t o_size = get_var_size_in_bits(o);
const uint64_t n_size = get_var_size_in_bits(n);
const uint64_t o_offset = get_data_member_offset(o);
@@ -712,6 +716,77 @@ represent(const var_diff_sptr &diff,
out << "\n";
}
/// Represent the changes carried by an instance of @ref subrange_diff
/// that represent a difference between two ranges.
///
/// @param diff diff the diff node to represent.
///
/// @param ctxt the diff context to use.
///
/// @param local_only if true, only display local changes.
///
/// @param out the output stream to send the representation to.
///
/// @param indent the indentation string to use for the change report.
void
represent(const subrange_diff& d,
const diff_context_sptr ctxt,
ostream& out,
const string& indent,
bool local_only)
{
array_type_def::subrange_sptr o = d.first_subrange();
array_type_def::subrange_sptr n = d.second_subrange();
string oor = o->get_pretty_representation();
string nr = n->get_pretty_representation();
string on = o->get_name();
string nn = n->get_name();
int64_t olb = o->get_lower_bound();
int64_t nlb = n->get_lower_bound();
int64_t oub = o->get_upper_bound();
int64_t nub = n->get_upper_bound();
if (on != nn)
{
out << indent << "name of range changed from '"
<< on << "' to '" << nn << "'\n";
}
if (olb != nlb)
{
out << indent << "lower bound of range '"
<< on
<< "' change from '";
emit_num_value(olb, *ctxt, out);
out << "' to '";
emit_num_value(nlb, *ctxt, out);
out << "'\n";
}
if (oub != nub)
{
out << indent << "upper bound of range '"
<< on
<< "' change from '";
emit_num_value(oub, *ctxt, out);
out << "' to '";
emit_num_value(nub, *ctxt, out);
out << "'\n";
}
if (!local_only)
{
diff_sptr dif = d.underlying_type_diff();
if (dif && dif->to_be_reported())
{
// report range underlying type changes
out << indent << "underlying type of range '"
<< oor << "' changed:\n";
dif->report(out, indent + " ");
}
}
}
/// Report the size and alignment changes of a type.
///
/// @param first the first type to consider.
@@ -763,12 +838,12 @@ report_size_and_alignment_changes(type_or_decl_base_sptr first,
// arrays ...
out << indent << "array type size changed from ";
if (first_array->is_infinite())
out << "infinity";
out << "\'unknown\'";
else
emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
out << " to ";
if (second_array->is_infinite())
out << "infinity";
out << "\'unknown\'";
else
emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
out << "\n";
@@ -797,14 +872,14 @@ report_size_and_alignment_changes(type_or_decl_base_sptr first,
<< " changed length from ";
if ((*i)->is_infinite())
out << "infinity";
out << "\'unknown\'";
else
out << (*i)->get_length();
out << " to ";
if ((*j)->is_infinite())
out << "infinity";
out << "\'unknown\'";
else
out << (*j)->get_length();
out << "\n";
@@ -894,7 +969,8 @@ report_name_size_and_alignment_changes(decl_base_sptr first,
string fn = first->get_qualified_name(),
sn = second->get_qualified_name();
if (fn != sn)
if (!(first->get_is_anonymous() && second->get_is_anonymous())
&& fn != sn)
{
if (!(ctxt->get_allowed_category() & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
&& filtering::has_harmless_name_change(first, second))
@@ -1070,6 +1146,49 @@ maybe_report_diff_for_member(const decl_base_sptr& decl1,
return reported;
}
/// Report the differences between two generic variables.
///
/// @param decl1 the first version of the variable.
///
/// @param decl2 the second version of the variable.
///
/// @param ctxt the context of the diff.
///
/// @param out the output stream to emit the change report to.
///
/// @param indent the indentation prefix to emit.
///
/// @return true if any text has been emitted to the output stream.
bool
maybe_report_diff_for_variable(const decl_base_sptr& decl1,
const decl_base_sptr& decl2,
const diff_context_sptr& ctxt,
ostream& out,
const string& indent)
{
bool reported = false;
var_decl_sptr var1 = is_var_decl(decl1);
var_decl_sptr var2 = is_var_decl(decl2);
if (!var1 || !var2)
return reported;
if (filtering::is_var_1_dim_unknown_size_array_change(var1, var2))
{
uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
out << indent;
show_offset_or_size("size of variable symbol (",
var_size_in_bits, *ctxt, out);
out << ") hasn't changed\n"
<< indent << "but it does have a harmless type change\n";
reported = true;
}
return reported;
}
/// Report the difference between two ELF symbols, if there is any.
///
/// @param symbol1 the first symbol to consider.
+15 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -156,6 +156,13 @@ represent(const var_diff_sptr &diff,
const string& indent = "",
bool local_only = false);
void
represent(const subrange_diff& diff,
const diff_context_sptr ctxt,
ostream& out,
const string& indent = "",
bool local_only = false);
void
report_size_and_alignment_changes(type_or_decl_base_sptr first,
type_or_decl_base_sptr second,
@@ -206,6 +213,13 @@ maybe_report_diff_for_member(const decl_base_sptr& decl1,
ostream& out,
const string& indent);
bool
maybe_report_diff_for_variable(const decl_base_sptr& decl1,
const decl_base_sptr& decl2,
const diff_context_sptr& ctxt,
ostream& out,
const string& indent);
void
maybe_report_diff_for_symbol(const elf_symbol_sptr& symbol1,
const elf_symbol_sptr& symbol2,
+43 -3
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -570,11 +570,22 @@ class type_suppression::priv
type_suppression::type_kind type_kind_;
bool consider_reach_kind_;
type_suppression::reach_kind reach_kind_;
bool has_size_change_;
// The data members a class needs to have to match this suppression
// specification. These might be selected by a regular expression.
string_set_type potential_data_members_;
// The regular expression string that selects the potential data
// members of the class.
string potential_data_members_regex_str_;
// The compiled regular expression that selects the potential data
// members of the class.
mutable regex::regex_t_sptr potential_data_members_regex_;
type_suppression::insertion_ranges insertion_ranges_;
unordered_set<string> source_locations_to_keep_;
unordered_set<string> source_locations_to_keep_;
string source_location_to_keep_regex_str_;
mutable regex::regex_t_sptr source_location_to_keep_regex_;
mutable vector<string> changed_enumerator_names_;
mutable vector<regex::regex_t_sptr> changed_enumerators_regexp_;
priv();
@@ -590,7 +601,8 @@ public:
consider_type_kind_(consider_type_kind),
type_kind_(type_kind),
consider_reach_kind_(consider_reach_kind),
reach_kind_(reach_kind)
reach_kind_(reach_kind),
has_size_change_(false)
{}
/// Get the regular expression object associated to the 'type_name_regex'
@@ -677,6 +689,34 @@ public:
set_source_location_to_keep_regex(regex::regex_t_sptr r)
{source_location_to_keep_regex_ = r;}
/// Getter for the "potential_data_member_names_regex" object.
///
/// This regex object matches the names of the data members that are
/// needed for this suppression specification to select the type.
///
/// @return the "potential_data_member_names_regex" object.
const regex::regex_t_sptr
get_potential_data_member_names_regex() const
{
if (!potential_data_members_regex_
&& !potential_data_members_regex_str_.empty())
{
potential_data_members_regex_ =
regex::compile(potential_data_members_regex_str_);
}
return potential_data_members_regex_;
}
/// Setter for the "potential_data_member_names_regex" object.
///
/// This regex object matches the names of the data members that are
/// needed for this suppression specification to select the type.
///
/// @param r the new "potential_data_member_names_regex" object.
void
set_potential_data_member_names_regex(regex::regex_t_sptr &r)
{potential_data_members_regex_ = r;}
friend class type_suppression;
}; // class type_suppression::priv
+586 -100
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2022 Red Hat, Inc.
// Copyright (C) 2016-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -24,6 +24,7 @@ ABG_BEGIN_EXPORT_DECLARATIONS
#include "abg-suppression.h"
#include "abg-tools-utils.h"
#include "abg-fe-iface.h"
#include "abg-comparison.h"
ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>
@@ -36,9 +37,28 @@ namespace abigail
namespace suppr
{
// Inject the abigail::comparison namespace in here.
using namespace comparison;
using std::dynamic_pointer_cast;
using regex::regex_t_sptr;
/// @return the string constant "offset_of_flexible_array_data_member".
static const string&
OFFSET_OF_FLEXIBLE_ARRAY_DATA_MEMBER_STRING()
{
static string s = "offset_of_flexible_array_data_member";
return s;
}
/// @return the string constant "end";
static const string&
END_STRING()
{
static string s = "end";
return s;
}
// <parsing stuff>
// section parsing
@@ -270,6 +290,67 @@ suppression_base::has_soname_related_property() const
&& get_soname_not_regex_str().empty()));
}
/// Constructor of the @ref negated_suppression_base.
negated_suppression_base::negated_suppression_base()
{
}
/// Destructor of the @ref negated_suppression_base.
negated_suppression_base::~negated_suppression_base()
{
}
/// Test if a suppression specification is a negated suppression.
///
/// @param s the suppression to consider.
///
/// @return true iff @p s is an instance of @ref
/// negated_suppression_base.
bool
is_negated_suppression(const suppression_base& s)
{
bool result = true;
try
{
dynamic_cast<const negated_suppression_base&>(s);
}
catch (...)
{
result = false;
}
return result;
}
/// Test if a suppression specification is a negated suppression.
///
/// @param s the suppression to consider.
///
/// @return true a pointer to the @ref negated_suppression_base which
/// @p s, or nil if it's not a negated suppression.
/// negated_suppression_base.
const negated_suppression_base*
is_negated_suppression(const suppression_base* s)
{
const negated_suppression_base* result = nullptr;
result = dynamic_cast<const negated_suppression_base*>(s);
return result;
}
/// Test if a suppression specification is a negated suppression.
///
/// @param s the suppression to consider.
///
/// @return true a pointer to the @ref negated_suppression_base which
/// @p s, or nil if it's not a negated suppression.
/// negated_suppression_base.
negated_suppression_sptr
is_negated_suppression(const suppression_sptr& s)
{
negated_suppression_sptr result;
result = dynamic_pointer_cast<negated_suppression_base>(s);
return result;
}
/// Check if the SONAMEs of the two binaries being compared match the
/// content of the properties "soname_regexp" and "soname_not_regexp"
/// of the current suppression specification.
@@ -569,6 +650,52 @@ void
type_suppression::set_reach_kind(reach_kind k)
{priv_->reach_kind_ = k;}
/// Getter of the "has_size_change" property.
///
/// @return the value of the "has_size_change" property.
bool
type_suppression::get_has_size_change() const
{return priv_->has_size_change_;}
/// Setter of the "has_size_change" property.
///
/// @param flag the new value of the "has_size_change" property.
void
type_suppression::set_has_size_change(bool flag)
{priv_->has_size_change_ = flag;}
/// Getter of the "potential_data_member_names" property.
///
/// @return the set of potential data member names of this
/// suppression.
const unordered_set<string>&
type_suppression::get_potential_data_member_names() const
{return priv_->potential_data_members_;}
/// Setter of the "potential_data_member_names" property.
///
/// @param s the new set of potential data member names of this
/// suppression.
void
type_suppression::set_potential_data_member_names
(const string_set_type& s) const
{priv_->potential_data_members_ = s;}
/// Getter of the "potential_data_member_names_regex" string.
///
/// @return the "potential_data_member_names_regex" string.
const string&
type_suppression::get_potential_data_member_names_regex_str() const
{return priv_->potential_data_members_regex_str_;}
/// Setter of the "potential_data_member_names_regex" string.
///
/// @param d the new "potential_data_member_names_regex" string.
void
type_suppression::set_potential_data_member_names_regex_str
(const string& d) const
{priv_->potential_data_members_regex_str_ = d;}
/// Setter for the vector of data member insertion ranges that
/// specifies where a data member is inserted as far as this
/// suppression specification is concerned.
@@ -659,6 +786,28 @@ void
type_suppression::set_changed_enumerator_names(const vector<string>& n)
{priv_->changed_enumerator_names_ = n;}
/// Getter of the vector of the regular expression strings for changed
/// enumerators that are supposed to be suppressed. Note that this
/// will be "valid" only if the type suppression has the
/// 'type_kind = enum' property.
///
/// @return the vector of the regular expression strings that are
/// supposed to match enumertor names to be suppressed.
const vector<regex::regex_t_sptr>&
type_suppression::get_changed_enumerators_regexp() const
{return priv_->changed_enumerators_regexp_;}
/// Setter of the vector of the regular expression strings for changed
/// enumerators that are supposed to be suppressed. Note that this
/// will be "valid" only if the type suppression has the
/// 'type_kind = enum' property.
///
/// @param n the vector of the regular expression strings that are
/// supposed to match enumertor names to be suppressed.
void
type_suppression::set_changed_enumerators_regexp(const vector<regex::regex_t_sptr>& n)
{priv_->changed_enumerators_regexp_ = n;}
/// Evaluate this suppression specification on a given diff node and
/// say if the diff node should be suppressed or not.
///
@@ -778,6 +927,43 @@ type_suppression::suppresses_diff(const diff* diff) const
// Now let's consider class diffs in the context of a suppr spec
// that contains properties like "has_data_member_inserted_*".
const class_or_union_diff* cou_diff = is_class_or_union_diff(d);
if (cou_diff)
{
class_or_union_sptr f = cou_diff->first_class_or_union();
// We are looking at the a class or union diff ...
if (!get_potential_data_member_names().empty())
{
// ... and the suppr spec has a:
//
// "has_data_member = {foo, bar}" property
//
for (string var_name : get_potential_data_member_names())
if (!f->find_data_member(var_name))
return false;
}
if (!get_potential_data_member_names_regex_str().empty())
{
if (const regex_t_sptr& data_member_name_regex =
priv_->get_potential_data_member_names_regex())
{
bool data_member_matched = false;
for (var_decl_sptr dm : f->get_data_members())
{
if (regex::match(data_member_name_regex, dm->get_name()))
{
data_member_matched = true;
break;
}
}
if (!data_member_matched)
return false;
}
}
}
// Evaluate has_data_member_inserted_*" clauses.
const class_diff* klass_diff = dynamic_cast<const class_diff*>(d);
if (klass_diff)
{
@@ -786,75 +972,54 @@ type_suppression::suppresses_diff(const diff* diff) const
{
// ... and the suppr spec contains a
// "has_data_member_inserted_*" clause ...
if (klass_diff->deleted_data_members().empty()
&& (klass_diff->first_class_decl()->get_size_in_bits()
<= klass_diff->second_class_decl()->get_size_in_bits()))
if ((klass_diff->first_class_decl()->get_size_in_bits()
== klass_diff->second_class_decl()->get_size_in_bits())
|| get_has_size_change())
{
// That "has_data_member_inserted_*" clause doesn't hold
// if the class has deleted data members or shrunk.
// if the class changed size, unless the user specified
// that suppression applies to types that have size
// change.
const class_decl_sptr& first_type_decl =
klass_diff->first_class_decl();
for (string_decl_base_sptr_map::const_iterator m =
klass_diff->inserted_data_members().begin();
m != klass_diff->inserted_data_members().end();
++m)
if (klass_diff->inserted_data_members().empty()
&& klass_diff->changed_data_members().empty())
// So there is a has_data_member_inserted_* clause,
// but no data member was inserted. That means the
// clause is falsified.
return false;
// All inserted data members must be in an allowed
// insertion range.
for (const auto& m : klass_diff->inserted_data_members())
{
decl_base_sptr member = m->second;
size_t dm_offset = get_data_member_offset(member);
decl_base_sptr member = m.second;
bool matched = false;
for (insertion_ranges::const_iterator i =
get_data_member_insertion_ranges().begin();
i != get_data_member_insertion_ranges().end();
++i)
{
type_suppression::insertion_range_sptr range = *i;
uint64_t range_begin_val = 0, range_end_val = 0;
if (!type_suppression::insertion_range::eval_boundary
(range->begin(), first_type_decl, range_begin_val))
break;
if (!type_suppression::insertion_range::eval_boundary
(range->end(), first_type_decl, range_end_val))
break;
uint64_t range_begin = range_begin_val;
uint64_t range_end = range_end_val;
if (insertion_range::boundary_value_is_end(range_begin)
&& insertion_range::boundary_value_is_end(range_end))
{
// This idiom represents the predicate
// "has_data_member_inserted_at = end"
if (dm_offset >
get_data_member_offset(get_last_data_member
(first_type_decl)))
{
// So the data member was added after
// last data member of the klass. That
// matches the suppr spec
// "has_data_member_inserted_at = end".
matched = true;
continue;
}
}
if (range_begin > range_end)
// Wrong suppr spec. Ignore it.
continue;
if (dm_offset < range_begin || dm_offset > range_end)
// The offset of the added data member doesn't
// match the insertion range specified. So
// the diff object won't be suppressed.
continue;
// If we reached this point, then all the
// insertion range constraints have been
// satisfied. So
for (const auto& range : get_data_member_insertion_ranges())
if (is_data_member_offset_in_range(is_var_decl(member),
range,
first_type_decl.get()))
matched = true;
}
if (!matched)
return false;
}
// Similarly, each data member that replaced another one
// must be in an allowed insertion range.
for (const auto& m : klass_diff->changed_data_members())
{
var_decl_sptr member = m.second->second_var();
bool matched = false;
for (const auto& range : get_data_member_insertion_ranges())
if (is_data_member_offset_in_range(member, range,
first_type_decl.get()))
matched = true;
if (!matched)
return false;
}
@@ -875,9 +1040,12 @@ type_suppression::suppresses_diff(const diff* diff) const
// ... and yet carries some changed enumerators!
&& !enum_dif->changed_enumerators().empty())
{
// Make sure that all changed enumerators are listed in the
// vector of enumerator names returned by the
// get_changed_enumerator_names() member function.
// Make sure that all changed enumerators are either:
// 1. listed in the vector of enumerator names returned
// by the get_changed_enumerator_names() member function
// 2. match a regular expression returned by the
// get_changed_enumerators_regexp() member function
bool matched = true;
for (string_changed_enumerator_map::const_iterator i =
enum_dif->changed_enumerators().begin();
@@ -885,13 +1053,20 @@ type_suppression::suppresses_diff(const diff* diff) const
++i)
{
matched &= true;
if (std::find(get_changed_enumerator_names().begin(),
get_changed_enumerator_names().end(),
i->first) == get_changed_enumerator_names().end())
{
matched &= false;
break;
}
if ((std::find(get_changed_enumerator_names().begin(),
get_changed_enumerator_names().end(),
i->first) == get_changed_enumerator_names().end())
&&
(std::find_if(get_changed_enumerators_regexp().begin(),
get_changed_enumerators_regexp().end(),
[&] (const regex_t_sptr& enum_regexp)
{
return regex::match(enum_regexp, i->first);
}) == get_changed_enumerators_regexp().end()))
{
matched &= false;
break;
}
}
if (!matched)
return false;
@@ -1323,6 +1498,25 @@ type_suppression::insertion_range::create_fn_call_expr_boundary(const string& s)
return result;
}
/// Create a named boundary.
///
/// The return value is to be used as a boundary for an instance of
/// @ref type_suppression::insertion_range. The value of that
/// boundary is a named constant that is to be evaluated to an integer
/// value, in the context of a @ref class_decl. That evaluate is
/// performed by the function
/// type_suppression::insertion_range::eval_boundary().
///
/// @param name the name of the boundary.
///
/// @return the newly created named boundary.
type_suppression::insertion_range::named_boundary_sptr
type_suppression::insertion_range::create_named_boundary(const string& name)
{
named_boundary_sptr result(new named_boundary(name));
return result;
}
/// Evaluate an insertion range boundary to get a resulting integer
/// value.
///
@@ -1335,9 +1529,9 @@ type_suppression::insertion_range::create_fn_call_expr_boundary(const string& s)
/// @return true iff the evaluation was successful and @p value
/// contains the resulting value.
bool
type_suppression::insertion_range::eval_boundary(boundary_sptr boundary,
class_decl_sptr context,
uint64_t& value)
type_suppression::insertion_range::eval_boundary(const boundary_sptr boundary,
const class_or_union* context,
uint64_t& value)
{
if (integer_boundary_sptr b = is_integer_boundary(boundary))
{
@@ -1347,38 +1541,84 @@ type_suppression::insertion_range::eval_boundary(boundary_sptr boundary,
else if (fn_call_expr_boundary_sptr b = is_fn_call_expr_boundary(boundary))
{
ini::function_call_expr_sptr fn_call = b->as_function_call_expr();
if ((fn_call->get_name() == "offset_of"
|| fn_call->get_name() == "offset_after")
if (fn_call
&& (fn_call->get_name() == "offset_of"
|| fn_call->get_name() == "offset_after"
|| fn_call->get_name() == "offset_of_first_data_member_regexp"
|| fn_call->get_name() == "offset_of_last_data_member_regexp")
&& fn_call->get_arguments().size() == 1)
{
string member_name = fn_call->get_arguments()[0];
for (class_decl::data_members::const_iterator it =
context->get_data_members().begin();
it != context->get_data_members().end();
++it)
if (fn_call->get_name() == "offset_of"
|| fn_call->get_name() == "offset_after")
{
if (!get_data_member_is_laid_out(**it))
continue;
if ((*it)->get_name() == member_name)
string member_name = fn_call->get_arguments()[0];
for (class_decl::data_members::const_iterator it =
context->get_data_members().begin();
it != context->get_data_members().end();
++it)
{
if (fn_call->get_name() == "offset_of")
value = get_data_member_offset(*it);
else if (fn_call->get_name() == "offset_after")
if (!get_data_member_is_laid_out(**it))
continue;
if ((*it)->get_name() == member_name)
{
if (!get_next_data_member_offset(context, *it, value))
if (fn_call->get_name() == "offset_of")
value = get_data_member_offset(*it);
else if (fn_call->get_name() == "offset_after")
{
value = get_data_member_offset(*it) +
(*it)->get_type()->get_size_in_bits();
if (!get_next_data_member_offset(context, *it, value))
{
value = get_data_member_offset(*it) +
(*it)->get_type()->get_size_in_bits();
}
}
else
// We should not reach this point.
abort();
return true;
}
else
// We should not reach this point.
abort();
}
}
else if (fn_call->get_name() == "offset_of_first_data_member_regexp"
|| fn_call->get_name() == "offset_of_last_data_member_regexp")
{
string name_regexp = fn_call->get_arguments()[0];
auto r = regex::compile(name_regexp);
var_decl_sptr dm;
if (fn_call->get_name() == "offset_of_first_data_member_regexp")
dm = find_first_data_member_matching_regexp(*context, r);
else if (fn_call->get_name() == "offset_of_last_data_member_regexp")
dm = find_last_data_member_matching_regexp(*context, r);
if (dm)
{
value = get_data_member_offset(dm);
return true;
}
}
}
}
else if (named_boundary_sptr b = is_named_boundary(boundary))
{
if (b->get_name() == OFFSET_OF_FLEXIBLE_ARRAY_DATA_MEMBER_STRING())
{
// Look at the last data member of 'context' and make sure
// its type is an array with non-finite size.
if (var_decl_sptr dm = has_flexible_array_data_member(is_class_type(context)))
{
value = get_data_member_offset(dm);
return true;
}
}
else if (b->get_name() == END_STRING())
{
// The 'end' of a struct is represented by the value
// std::numeric_limits<uint64_t>::max(), recognized by
// type_suppression::insertion_range::boundary_value_is_end.
value = std::numeric_limits<uint64_t>::max();
return true;
}
}
return false;
}
@@ -1408,7 +1648,8 @@ is_integer_boundary(type_suppression::insertion_range::boundary_sptr b)
{return dynamic_pointer_cast<type_suppression::insertion_range::integer_boundary>(b);}
/// Tests if a given instance of @ref
/// type_suppression::insertion_range::boundary is actually an function call expression boundary.
/// type_suppression::insertion_range::boundary is actually a
/// function call expression boundary.
///
/// @param b the boundary to test.
///
@@ -1420,6 +1661,18 @@ type_suppression::insertion_range::fn_call_expr_boundary_sptr
is_fn_call_expr_boundary(type_suppression::insertion_range::boundary_sptr b)
{return dynamic_pointer_cast<type_suppression::insertion_range::fn_call_expr_boundary>(b);}
/// Test if a given instance of @ref
/// type_suppression::insertion_range::boundary is actually a named boundary.
///
/// @param b the boundary to consider.
///
/// @return the instance of @ref
/// type_suppression::insertion_range::named_boundary if @p b is a
/// named boundary, or nil.
type_suppression::insertion_range::named_boundary_sptr
is_named_boundary(type_suppression::insertion_range::boundary_sptr b)
{return dynamic_pointer_cast<type_suppression::insertion_range::named_boundary>(b);}
/// The private data type of @ref
/// type_suppression::insertion_range::boundary.
struct type_suppression::insertion_range::boundary::priv
@@ -1521,6 +1774,35 @@ type_suppression::insertion_range::fn_call_expr_boundary::operator ini::function
type_suppression::insertion_range::fn_call_expr_boundary::~fn_call_expr_boundary()
{}
/// The private data type for the @ref
/// type_suppression::insertion_range::named_boundary.
struct type_suppression::insertion_range::named_boundary::priv
{
string name_;
priv()
{}
priv(const string& name)
: name_(name)
{}
}; // end struct type_suppression::insertion_range::named_boundary::priv
/// Constructor for @ref
/// type_suppression::insertion_range::named_boundary
///
/// @param name the name of the @ref named_boundary type.
type_suppression::insertion_range::named_boundary::named_boundary(const string& name)
: priv_(new priv(name))
{}
/// Getter for the name of the named boundary.
///
/// @return the name of the named boundary.
const string&
type_suppression::insertion_range::named_boundary::get_name() const
{return priv_->name_;}
/// Test if an instance of @ref suppression is an instance of @ref
/// type_suppression.
///
@@ -1535,6 +1817,52 @@ is_type_suppression(suppression_sptr suppr)
// </type_suppression stuff>
// <negated_type_suppression stuff>
/// Constructor for @ref negated_type_suppression.
///
/// @param label the label of the suppression. This is just a free
/// form comment explaining what the suppression is about.
///
/// @param type_name_regexp the regular expression describing the
/// types about which diff reports should be suppressed. If it's an
/// empty string, the parameter is ignored.
///
/// @param type_name the name of the type about which diff reports
/// should be suppressed. If it's an empty string, the parameter is
/// ignored.
///
/// Note that parameter @p type_name_regexp and @p type_name_regexp
/// should not necessarily be populated. It usually is either one or
/// the other that the user wants.
negated_type_suppression::negated_type_suppression(const string& label,
const string& type_name_regexp,
const string& type_name)
: type_suppression(label, type_name_regexp, type_name),
negated_suppression_base()
{
}
/// Evaluate this suppression specification on a given diff node and
/// say if the diff node should be suppressed or not.
///
/// @param diff the diff node to evaluate this suppression
/// specification against.
///
/// @return true if @p diff should be suppressed.
bool
negated_type_suppression::suppresses_diff(const diff* diff) const
{
return !type_suppression::suppresses_diff(diff);
}
/// Destructor of the @ref negated_type_suppression type.
negated_type_suppression::~negated_type_suppression()
{
}
// </negated_type_suppression stuff>
/// Parse the value of the "type_kind" property in the "suppress_type"
/// section.
///
@@ -1597,7 +1925,8 @@ read_type_suppression(const ini::config::section& section)
{
type_suppression_sptr result;
if (section.get_name() != "suppress_type")
if (section.get_name() != "suppress_type"
&& section.get_name() != "allow_type")
return result;
static const char *const sufficient_props[] = {
@@ -1626,6 +1955,13 @@ read_type_suppression(const ini::config::section& section)
? drop_artifact->get_value()->as_string()
: "";
ini::simple_property_sptr has_size_change =
is_simple_property(section.find_property("has_size_change"));
string has_size_change_str = has_size_change
? has_size_change->get_value()->as_string()
: "";
ini::simple_property_sptr label =
is_simple_property(section.find_property("label"));
string label_str = label ? label->get_value()->as_string() : "";
@@ -1720,6 +2056,56 @@ read_type_suppression(const ini::config::section& section)
read_suppression_reach_kind(reach_kind_prop->get_value()->as_string());
}
// Support has_data_member = {}
string_set_type potential_data_member_names;
if (ini::property_sptr propertee = section.find_property("has_data_member"))
{
// This is either has_data_member = {foo, blah} or
// has_data_member = foo.
ini::tuple_property_value_sptr tv;
ini::string_property_value_sptr sv;
if (ini::tuple_property_sptr prop = is_tuple_property(propertee))
// Value is of the form {foo,blah}
tv = prop->get_value();
else if (ini::simple_property_sptr prop = is_simple_property(propertee))
// Value is of the form foo.
sv = prop->get_value();
// Ensure that the property value has the form {"foo", "blah", ...};
// Meaning it's a tuple of one element which is a list or a string.
if (tv
&& tv->get_value_items().size() == 1
&& (is_list_property_value(tv->get_value_items().front())
|| is_string_property_value(tv->get_value_items().front())))
{
ini::list_property_value_sptr val =
is_list_property_value(tv->get_value_items().front());
if (!val)
{
// We have just one potential data member name,as a
// string_property_value.
string name =
is_string_property_value(tv->get_value_items().front())
->as_string();
potential_data_member_names.insert(name);
}
else
for (const string& name : val->get_content())
potential_data_member_names.insert(name);
}
else if (sv)
{
string name = sv->as_string();
potential_data_member_names.insert(name);
}
}
// Support has_data_member_regexp = str
string potential_data_member_names_regexp_str;
if (ini::simple_property_sptr prop =
is_simple_property(section.find_property("has_data_member_regexp")))
potential_data_member_names_regexp_str = prop->get_value()->as_string();
// Support has_data_member_inserted_at
vector<type_suppression::insertion_range_sptr> insert_ranges;
bool consider_data_member_insertion = false;
@@ -1730,8 +2116,10 @@ read_type_suppression(const ini::config::section& section)
// has_data_member_inserted_at = <one-string-property-value>
string ins_point = prop->get_value()->as_string();
type_suppression::insertion_range::boundary_sptr begin, end;
if (ins_point == "end")
begin = type_suppression::insertion_range::create_integer_boundary(-1);
if (ins_point == END_STRING())
begin = type_suppression::insertion_range::create_named_boundary(ins_point);
else if (ins_point == OFFSET_OF_FLEXIBLE_ARRAY_DATA_MEMBER_STRING())
begin = type_suppression::insertion_range::create_named_boundary(ins_point);
else if (isdigit(ins_point[0]))
begin = type_suppression::insertion_range::create_integer_boundary
(atoi(ins_point.c_str()));
@@ -1758,9 +2146,9 @@ read_type_suppression(const ini::config::section& section)
// and not (for instance):
// has_data_member_inserted_between = {{0 , end}, {1, foo}}
//
// This means that the tuple_property_value contains just one
// value, which is a list_property that itself contains 2
// values.
// This means that the tuple_property_value contains just one
// value, which is a list_property that itself contains 2
// values.
type_suppression::insertion_range::boundary_sptr begin, end;
ini::tuple_property_value_sptr v = prop->get_value();
if (v
@@ -1905,7 +2293,39 @@ read_type_suppression(const ini::config::section& section)
changed_enumerator_names.push_back(p->get_value()->as_string());
}
result.reset(new type_suppression(label_str, name_regex_str, name_str));
/// Support 'changed_enumerators_regexp = .*_foo, bar_[0-9]+, baz'
///
/// If the current type is an enum and if it carries changed
/// enumerators that match regular expressions listed in the
/// changed_enumerators_regexp property value then it should be
/// suppressed.
ini::property_sptr changed_enumerators_regexp_prop =
section.find_property("changed_enumerators_regexp");
vector<regex_t_sptr> changed_enumerators_regexp;
if (changed_enumerators_regexp_prop)
{
if (ini::list_property_sptr p =
is_list_property(changed_enumerators_regexp_prop))
{
for (string e : p->get_value()->get_content())
changed_enumerators_regexp.push_back(regex::compile(e));
}
else if (ini::simple_property_sptr p =
is_simple_property(changed_enumerators_regexp_prop))
{
changed_enumerators_regexp.push_back(
regex::compile(p->get_value()->as_string())
);
}
}
if (section.get_name() == "suppress_type")
result.reset(new type_suppression(label_str, name_regex_str, name_str));
else if (section.get_name() == "allow_type")
result.reset(new negated_type_suppression(label_str, name_regex_str,
name_str));
if (consider_type_kind)
{
@@ -1919,6 +2339,13 @@ read_type_suppression(const ini::config::section& section)
result->set_reach_kind(reach_kind);
}
if (!potential_data_member_names.empty())
result->set_potential_data_member_names(potential_data_member_names);
if (!potential_data_member_names_regexp_str.empty())
result->set_potential_data_member_names_regex_str
(potential_data_member_names_regexp_str);
if (consider_data_member_insertion)
result->set_data_member_insertion_ranges(insert_ranges);
@@ -1950,10 +2377,17 @@ read_type_suppression(const ini::config::section& section)
|| !srcloc_not_in.empty())))
result->set_drops_artifact_from_ir(true);
if (has_size_change_str == "yes" || has_size_change_str == "true")
result->set_has_size_change(true);
if (result->get_type_kind() == type_suppression::ENUM_TYPE_KIND
&& !changed_enumerator_names.empty())
result->set_changed_enumerator_names(changed_enumerator_names);
if (result->get_type_kind() == type_suppression::ENUM_TYPE_KIND
&& !changed_enumerators_regexp.empty())
result->set_changed_enumerators_regexp(changed_enumerators_regexp);
return result;
}
@@ -4836,5 +5270,57 @@ is_type_suppressed(const fe_iface& fe,
return false;
}
/// Test if a data memer offset is in a given insertion range.
///
/// @param dm the data member to consider.
///
/// @param range the insertion range to consider.
///
/// @param the class (or union) type to consider as the context in
/// which to evaluate the insertion range denoted by @p range.
///
/// @return true iff the offset of the data member @p dm is in the
/// insertion range @p range in the context of the type denoted by @p
/// context.
bool
is_data_member_offset_in_range(const var_decl_sptr& dm,
const type_suppression::insertion_range_sptr& range,
const class_or_union* context)
{
ABG_ASSERT(dm && range && context);
uint64_t range_begin = 0, range_end = 0;
if (!type_suppression::insertion_range::eval_boundary (range->begin(),
context,
range_begin))
return false;
if (!type_suppression::insertion_range::eval_boundary (range->end(),
context,
range_end))
return false;
if (range_begin > range_end)
// wrong range, ignore it.
return false;
uint64_t dm_offset = get_data_member_offset(dm);
if (type_suppression::insertion_range::boundary_value_is_end(range_begin)
&& type_suppression::insertion_range::boundary_value_is_end(range_end))
{
// This idiom represents the predicate
// "has_data_member_inserted_at = end"
if (dm_offset > get_data_member_offset(get_last_data_member(context)))
return true;
return false;
}
if (dm_offset < range_begin || dm_offset > range_end)
// The offset of the data member is outside the range.
return false;
return true;
}
}// end namespace suppr
} // end namespace abigail
+2 -2
View File
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2020-2022 Google, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
// Copyright (C) 2020-2023 Google, Inc.
//
// Author: Matthias Maennich
+14 -13
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2020-2022 Google, Inc.
// Copyright (C) 2020-2023 Google, Inc.
//
// Author: Matthias Maennich
@@ -99,7 +99,7 @@ private:
/// Base iterator for our custom iterator based on whatever the const_iterator
/// is for a vector of symbols.
/// As of writing this, std::vector<elf_symbol_sptr>::const_iterator.
typedef elf_symbols::const_iterator base_iterator;
using base_iterator = elf_symbols::const_iterator;
/// An iterator to walk a vector of elf_symbols filtered by symtab_filter.
///
@@ -110,11 +110,12 @@ typedef elf_symbols::const_iterator base_iterator;
class symtab_iterator : public base_iterator
{
public:
typedef base_iterator::value_type value_type;
typedef base_iterator::reference reference;
typedef base_iterator::pointer pointer;
typedef base_iterator::difference_type difference_type;
typedef std::forward_iterator_tag iterator_category;
using value_type = base_iterator::value_type;
using reference = base_iterator::reference;
using pointer = base_iterator::pointer;
using difference_type = base_iterator::difference_type;
using iterator_category = std::forward_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
/// Construct the iterator based on a pair of underlying iterators and a
/// symtab_filter object. Immediately fast forward to the next element that
@@ -172,7 +173,7 @@ private:
/// Convenience declaration of a unique_ptr<symtab>
class symtab;
typedef std::unique_ptr<symtab> symtab_ptr;
using symtab_ptr = std::unique_ptr<symtab>;
/// symtab is the actual data container of the symtab_reader implementation.
///
@@ -201,7 +202,7 @@ typedef std::unique_ptr<symtab> symtab_ptr;
class symtab
{
public:
typedef std::function<bool(const elf_symbol_sptr&)> symbol_predicate;
using symbol_predicate = std::function<bool(const elf_symbol_sptr&)>;
/// Indicate whether any (kernel) symbols have been seen at construction.
///
@@ -215,7 +216,7 @@ public:
/// The (only) iterator type we offer is a const_iterator implemented by the
/// symtab_iterator.
typedef symtab_iterator const_iterator;
using const_iterator = symtab_iterator;
/// Obtain an iterator to the beginning of the symtab according to the filter
/// criteria. Whenever this iterator advances, it skips elements that do not
@@ -271,12 +272,12 @@ private:
bool has_ksymtab_entries_;
/// Lookup map name->symbol(s)
typedef std::unordered_map<std::string, std::vector<elf_symbol_sptr>>
name_symbol_map_type;
using name_symbol_map_type =
std::unordered_map<std::string, std::vector<elf_symbol_sptr>>;
name_symbol_map_type name_symbol_map_;
/// Lookup map addr->symbol
typedef std::unordered_map<GElf_Addr, elf_symbol_sptr> addr_symbol_map_type;
using addr_symbol_map_type = std::unordered_map<GElf_Addr, elf_symbol_sptr>;
addr_symbol_map_type addr_symbol_map_;
/// Lookup map function entry address -> symbol
+428 -42
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
///@file
@@ -33,7 +33,8 @@
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <libxml/parser.h>
#include <libxml/xmlversion.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
@@ -47,6 +48,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"
@@ -73,6 +77,20 @@ using namespace abigail::ini;
namespace tools_utils
{
/// This function needs to be called before any libabigail function.
///
/// Users of libabigail must call it prior to using any of the
/// functions of the library.
///
/// It intends to initialize the underlying libraries that might need
/// initialization, especially, libxml2, in multi-threaded environments.
void
initialize()
{
LIBXML_TEST_VERSION;
xmlInitParser();
}
/// Get the value of $libdir variable of the autotools build
/// system. This is where shared libraries are usually installed.
///
@@ -498,12 +516,40 @@ file_has_ctf_debug_info(const string& elf_file_path,
// vmlinux.ctfa could be provided with --debug-info-dir
for (const auto& path : debug_info_root_paths)
if (dir_contains_ctf_archive(*path, vmlinux))
if (find_file_under_dir(*path, "vmlinux.ctfa", vmlinux))
return true;
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<char**>& 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.
///
@@ -521,16 +567,25 @@ is_dir(const string& path)
if (S_ISDIR(st.st_mode))
return true;
string symlink_target_path;
if (maybe_get_symlink_target_file_path(path, symlink_target_path))
return is_dir(symlink_target_path);
if (S_ISLNK(st.st_mode))
{
string symlink_target_path;
if (maybe_get_symlink_target_file_path(path, symlink_target_path))
{
if (!get_stat(path, &st))
return false;
if (S_ISDIR(st.st_mode))
return true;
}
}
return false;
}
static const char* ANONYMOUS_STRUCT_INTERNAL_NAME = "__anonymous_struct__";
static const char* ANONYMOUS_UNION_INTERNAL_NAME = "__anonymous_union__";
static const char* ANONYMOUS_ENUM_INTERNAL_NAME = "__anonymous_enum__";
static const char* ANONYMOUS_STRUCT_INTERNAL_NAME = "__anonymous_struct__";
static const char* ANONYMOUS_UNION_INTERNAL_NAME = "__anonymous_union__";
static const char* ANONYMOUS_ENUM_INTERNAL_NAME = "__anonymous_enum__";
static const char* ANONYMOUS_SUBRANGE_INTERNAL_NAME = "__anonymous_range__";
static int ANONYMOUS_STRUCT_INTERNAL_NAME_LEN =
strlen(ANONYMOUS_STRUCT_INTERNAL_NAME);
@@ -555,6 +610,9 @@ const char*
get_anonymous_union_internal_name_prefix()
{return ANONYMOUS_UNION_INTERNAL_NAME;}
static int ANONYMOUS_SUBRANGE_INTERNAL_NAME_LEN =
strlen(ANONYMOUS_SUBRANGE_INTERNAL_NAME);
/// Getter of the prefix for the name of anonymous enums.
///
/// @reaturn the prefix for the name of anonymous enums.
@@ -562,6 +620,13 @@ const char*
get_anonymous_enum_internal_name_prefix()
{return ANONYMOUS_ENUM_INTERNAL_NAME;}
/// Getter of the prefix for the name of anonymous range.
///
/// @reaturn the prefix for the name of anonymous range.
const char*
get_anonymous_subrange_internal_name_prefix()
{return ANONYMOUS_SUBRANGE_INTERNAL_NAME;}
/// Compare two fully qualified decl names by taking into account that
/// they might have compontents that are anonymous types/namespace names.
///
@@ -658,9 +723,6 @@ maybe_get_symlink_target_file_path(const string& file_path,
if (!get_stat(file_path, &st))
return false;
if (!S_ISLNK(st.st_mode))
return false;
char *link_target_path = realpath(file_path.c_str(), NULL);
if (!link_target_path)
return false;
@@ -1025,9 +1087,9 @@ split_string(const string& input_string,
/// @return true iff the function could find a prefix for the suffix
/// @p suffix in the input string @p input_string.
bool
string_suffix(const string& input_string,
const string& prefix,
string& suffix)
string_suffix(const string& input_string,
const string& prefix,
string& suffix)
{
// Some basic sanity check before we start hostilities.
if (prefix.length() >= input_string.length())
@@ -1193,6 +1255,34 @@ execute_command_and_get_output(const string& cmd, vector<string>& lines)
return true;
}
/// Get a vector of arguments from a string containing a
/// comma-separated list of those arguments.
///
/// @param input_str the input string containing the comma-separated
/// list of arguments The input string has the form
/// "option=arg1,arg2,arg3,arg4".
///
/// @param option if the content of the input string @p input_str is
/// "option=arg1,arg2,arg3", then this parameter should be "option".
///
/// @param arguments this is set by the fonction the the arguments
/// that were a comma-separated list of arguments on the right hand
/// side of the '=' sign in the string @p input_str.
void
get_comma_separated_args_of_option(const string& input_str,
const string& option,
vector<string>& arguments)
{
string s = input_str;
string_suffix(s, option, s);
if (string_begins_with(s, "\""))
s = s.substr(1);
if (string_ends_with(s, "\""))
s = s.substr(0, s.size() - 1);
split_string(s, ",", arguments);
}
/// Get the SONAMEs of the DSOs advertised as being "provided" by a
/// given RPM. That set can be considered as being the set of
/// "public" DSOs of the RPM.
@@ -1733,34 +1823,64 @@ get_rpm_arch(const string& str, string& arch)
/// Tests if a given file name designates a kernel package.
///
/// @param file_name the file name to consider.
/// @param file_path the path to the file 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(const string& file_name, file_type file_type)
file_is_kernel_package(const string& file_path, 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");
if (rpm_contains_file(file_path, "vmlinuz"))
result = true;
}
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 file_name;
base_name(file_path, file_name);
string package_name;
if (get_deb_name(file_name, package_name))
result = (string_begins_with(package_name, "linux-image"));
}
return result;
}
/// Test if an RPM package contains a given file.
///
/// @param rpm_path the path to the RPM package.
///
/// @param file_name the file name to test the presence for in the
/// rpm.
///
/// @return true iff the file named @file_name is present in the RPM.
bool
rpm_contains_file(const string& rpm_path, const string& file_name)
{
vector<string> query_output;
// We don't check the return value of this command because on some
// system, the command can issue errors but still emit a valid
// output. We'll rather rely on the fact that the command emits a
// valid output or not.
execute_command_and_get_output("rpm -qlp "
+ rpm_path + " 2> /dev/null",
query_output);
for (auto& line : query_output)
{
line = trim_white_space(line);
if (string_ends_with(line, file_name))
return true;
}
return false;
}
/// Tests if a given file name designates a kernel debuginfo package.
///
/// @param file_name the file name to consider.
@@ -2090,7 +2210,8 @@ gen_suppr_spec_from_kernel_abi_whitelists
++section_iter)
{
std::string section_name = (*section_iter)->get_name();
if (!string_ends_with(section_name, "whitelist"))
if (!string_ends_with(section_name, "whitelist")
&& !string_ends_with(section_name, "stablelist"))
continue;
for (ini::config::properties_type::const_iterator
prop_iter = (*section_iter)->get_properties().begin(),
@@ -2229,13 +2350,20 @@ load_default_user_suppressions(suppr::suppressions_type& supprs)
///
/// @param entry the FTSENT* to consider.
///
/// @param fname the file name (or end of path) to consider.
/// @param fname the file name (or end of path) to consider. The file
/// name can also be a path that is relative to the root directory the
/// current visit is started from. The root directory is given by @p
/// root_dir.
///
/// @param root_dir the root dir from which the directory visit is
/// being performed.
///
/// @return true iff @p entry denotes a file which path ends with @p
/// fname.
static bool
entry_of_file_with_name(const FTSENT *entry,
const string& fname)
const string& fname,
const string& root_dir)
{
if (entry == NULL
|| (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
@@ -2246,6 +2374,11 @@ entry_of_file_with_name(const FTSENT *entry,
string fpath = ::basename(entry->fts_path);
if (fpath == fname)
return true;
fpath = trim_leading_string(entry->fts_path, root_dir);
if (fpath == fname)
return true;
return false;
}
@@ -2271,25 +2404,240 @@ find_file_under_dir(const string& root_dir,
if (!file_hierarchy)
return false;
string r = root_dir;
if (!string_ends_with(r, "/"))
r += "/";
FTSENT *entry;
while ((entry = fts_read(file_hierarchy)))
{
if (entry_of_file_with_name(entry, file_path_to_look_for, r))
{
result = entry->fts_path;
return true;
}
// 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 (entry_of_file_with_name(entry, file_path_to_look_for))
{
result = entry->fts_path;
return true;
}
}
fts_close(file_hierarchy);
return false;
}
/// Find a given file possibly under a set of directories and return
/// its absolute path.
///
/// @param root_dirs the vector of root directories under which to
/// look for.
///
/// @param file_path_to_look_for the file to look for under the
/// directory @p root_dir.
///
/// @param result the resulting path to @p file_path_to_look_for.
/// This is set iff the file has been found.
bool
find_file_under_dirs(const vector<string>& root_dirs,
const string& file_path_to_look_for,
string& result)
{
if (root_dirs.empty())
return find_file_under_dir(".", file_path_to_look_for, result);
for (const auto& root_dir : root_dirs)
if (find_file_under_dir(root_dir, file_path_to_look_for, result))
return true;
return false;
}
/// Get the dependencies of an ABI corpus, which are found in a set of
/// directories. Note that the dependencies are listed as properties
/// of the ABI corpus.
///
/// If the corpus has a dependency that is not found under any of the
/// given directories, then the dependency is ignored and not
/// returned.
///
/// @param korpus the ABI corpus to consider.
///
/// @param deps_dirs the list of directories where to look for the
/// dependencies.
///
/// @param dependencies output parameter that is set the dependencies
/// of the corpus denoted by @p korpus which are found in the
/// directories @p deps_dirs. This is set iff the function returns
/// true.
///
/// @return true iff some dependencies of the corpus @p korpus were
/// found in directories @p deps_dirs.
bool
get_dependencies(const corpus& korpus,
const vector<string>& deps_dirs,
set<string>& dependencies)
{
const vector<string>& set_of_needed = korpus.get_needed();
if (set_of_needed.empty())
return false;
bool found_at_least_one_dependency =false;
for (const auto& n :set_of_needed)
{
string dependency;
if (dependencies.find(n) == dependencies.end()
&& find_file_under_dirs(deps_dirs, n, dependency))
{
dependencies.insert(dependency);
found_at_least_one_dependency = true;
}
}
return found_at_least_one_dependency;
}
/// For each binary of a vector of binaries, if the binary is present
/// in at least one of the directories listed in a given vector,
/// construct a corpus and add it to a corpus group.
///
/// @param reader the reader used to read the binaries into an ABI corpus.
///
/// @param binaries the vector of binaries to read and add to a corpus
/// group.
///
/// @param deps_dirs the vector of directories where to look for the
/// binaries in @p binaries.
///
/// @param group the corpus group to add the corpus.
void
add_binaries_into_corpus_group(const fe_iface_sptr& reader,
const vector<string>& binaries,
const vector<string>& deps_dirs,
corpus_group& group)
{
vector<string> bins;
for (const auto& b : binaries)
{
string bin;
if (find_file_under_dirs(deps_dirs, b, bin))
bins.push_back(bin);
}
for (const auto& b : bins)
{
if (group.has_corpus(b))
continue;
reader->initialize(b);
fe_iface::status stat = fe_iface::STATUS_UNKNOWN;
corpus_sptr c = reader->read_corpus(stat);
if (c && (stat & fe_iface::STATUS_OK))
group.add_corpus(c);
}
}
/// For each dependency of a given corpus, if it is present in at
/// least one of the directories listed in a given vector, construct a
/// corpus and add it to a corpus group.
///
/// @param reader the reader used to read the binaries into an ABI corpus.
///
/// @param korpus the corpus to consider.
///
/// @param deps_dirs the vector of directories where to look for the
/// dependencies of @p korpus.
///
/// @param group the corpus group to add the corpus.
void
add_dependencies_into_corpus_group(const fe_iface_sptr& reader,
const corpus& korpus,
const vector<string>& deps_dirs,
corpus_group& group)
{
set<string> deps;
if (!get_dependencies(korpus, deps_dirs, deps))
return;
for (const auto& dep: deps)
{
if (group.has_corpus(dep))
continue;
reader->initialize(dep);
fe_iface::status stat = fe_iface::STATUS_UNKNOWN;
corpus_sptr c = reader->read_corpus(stat);
if (c && (stat & fe_iface::STATUS_OK))
{
group.add_corpus(c);
add_dependencies_into_corpus_group(reader, *c, deps_dirs, group);
}
}
}
/// Create a corpus group made of a given korpus and a set of binaries
/// found in a set of directories.
///
/// @param reader the reader to use to read the binaries.
///
/// @param korpus the ABI corpus to add to the corpus group.
///
/// @param binaries the set of binaries to add to the corpus group, if
/// they are present one of the directories denoted by the vector @p
/// deps_dirs.
///
/// @param bins_dirs the directories where the binaries listed in @p
/// binaries are to be found.
///
/// @return a corpus group made of @p korpus and the binaries listed
/// in @p binaries and found in at least one of the directories found
/// in @p bins_dirs.
corpus_group_sptr
stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr& reader,
const corpus_sptr& korpus,
const vector<string>& binaries,
const vector<string>& bins_dirs)
{
corpus_group_sptr result (new corpus_group(korpus->get_environment(),
korpus->get_path()));
result->add_corpus(korpus);
add_binaries_into_corpus_group(reader, binaries, bins_dirs, *result);
return result;
}
/// Create a corpus group made of a given korpus and the subset of its
/// dependencies that can be found found in a set of directories.
///
/// @param reader the reader to use to read the binaries.
///
/// @param korpus the ABI corpus to add to the corpus group along with
/// its dependencies that can be found in a subset of directories.
///
/// @param deps_dirs the directories where the dependencies of the ABI
/// corpus denoted by @p korpus binaries are to be found.
///
/// @return a corpus group made of @p korpus and the subset of its
/// dependencies found in at least one of the directories denoted by
/// @p deps_dirs.
corpus_group_sptr
stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr& reader,
const corpus_sptr& korpus,
const vector<string>& deps_dirs)
{
corpus_group_sptr result (new corpus_group(korpus->get_environment(),
korpus->get_path()));
result->add_corpus(korpus);
add_dependencies_into_corpus_group(reader, *korpus, deps_dirs, *result);
return result;
}
/// If we were given suppression specification files or kabi whitelist
/// files, this function parses those, come up with suppression
/// specifications as a result, and set them to the read context.
@@ -2310,10 +2658,10 @@ find_file_under_dir(const string& root_dir,
///
/// @param opts the options to consider.
static void
load_generate_apply_suppressions(elf_based_reader &rdr,
vector<string>& suppr_paths,
vector<string>& kabi_whitelist_paths,
suppressions_type& supprs)
load_generate_apply_suppressions(elf_based_reader& rdr,
vector<string>& suppr_paths,
vector<string>& kabi_whitelist_paths,
suppressions_type& supprs)
{
if (supprs.empty())
{
@@ -2511,19 +2859,29 @@ get_binary_paths_from_kernel_dist(const string& dist_root,
string debug_info_root;
if (dir_exists(dist_root + "/lib/modules"))
{
dist_root + "/lib/modules";
kernel_modules_root = dist_root + "/lib/modules";
debug_info_root = debug_info_root_path.empty()
? dist_root
? dist_root + "/usr/lib/debug"
: 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 = dist_root;
if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
// If vmlinux_path is empty, we want to look for it under
// debug_info_root, because this is where Enterprise Linux packages
// put it. Modules however are to be looked for under
// kernel_modules_root.
if (// So, Let's look for modules under kernel_modules_root ...
find_vmlinux_and_module_paths(kernel_modules_root,
vmlinux_path,
module_paths)
// ... and if vmlinux_path is empty, look for vmlinux under the
// debug info root.
|| find_vmlinux_and_module_paths(debug_info_root,
vmlinux_path,
module_paths))
found = true;
std::sort(module_paths.begin(), module_paths.end());
@@ -2761,7 +3119,10 @@ build_corpus_group_from_kernel_dist_under(const string& root,
if (verbose)
std::cerr << "Analysing kernel dist root '"
<< root << "' ... " << std::flush;
<< root
<< "' with vmlinux path: '"
<< vmlinux_path
<< "' ... " << std::flush;
timer t;
@@ -2781,6 +3142,16 @@ build_corpus_group_from_kernel_dist_under(const string& root,
vector<char**> di_roots;
di_roots.push_back(&di_root_ptr);
#ifdef WITH_CTF
shared_ptr<char> di_root_ctf;
if (requested_fe_kind & corpus::CTF_ORIGIN)
{
di_root_ctf = make_path_absolute(root.c_str());
char *di_root_ctf_ptr = di_root_ctf.get();
di_roots.push_back(&di_root_ctf_ptr);
}
#endif
abigail::elf_based_reader_sptr reader =
create_best_elf_based_reader(vmlinux,
di_roots,
@@ -2850,6 +3221,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 +3240,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)
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
#include <stdexcept>
#include <fstream>
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
#include <stdexcept>
#include <fstream>
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
#include <stdexcept>
#include <fstream>
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+83 -30
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
/// @file
///
@@ -230,7 +230,8 @@ class write_context
class_tmpl_shared_ptr_map m_class_tmpl_id_map;
string_elf_symbol_sptr_map_type m_fun_symbol_map;
string_elf_symbol_sptr_map_type m_var_symbol_map;
unordered_set<interned_string, hash_interned_string> m_emitted_decls_set;
unordered_set<interned_string, hash_interned_string> m_emitted_decls_set;
unordered_set<string> m_emitted_corpora_set;
write_context();
@@ -818,6 +819,42 @@ public:
m_emitted_decls_set.insert(irepr);
}
/// Test if a corpus has already been emitted.
///
/// A corpus is emitted if it's been recorded as having been emitted
/// by the function record_corpus_as_emitted().
///
/// @param corp the corpus to consider.
///
/// @return true iff the corpus @p corp has been emitted.
bool
corpus_is_emitted(const corpus_sptr& corp)
{
if (!corp)
return false;
if (m_emitted_corpora_set.find(corp->get_path())
== m_emitted_corpora_set.end())
return false;
return true;
}
/// Record the corpus has having been emitted.
///
/// @param corp the corpus to consider.
void
record_corpus_as_emitted(const corpus_sptr& corp)
{
if (!corp)
return;
const string& path = corp->get_path();
ABG_ASSERT(!path.empty());
m_emitted_corpora_set.insert(path);
}
/// Get the set of types that have been emitted.
///
/// @return the set of types that have been emitted.
@@ -884,6 +921,9 @@ static bool write_reference_type_def(const reference_type_def_sptr&,
write_context&, unsigned);
static bool write_array_type_def(const array_type_def_sptr&,
write_context&, unsigned);
static bool write_array_subrange_type(const array_type_def::subrange_sptr&,
write_context&,
unsigned);
static bool write_enum_type_decl(const enum_type_decl_sptr&,
write_context&, unsigned);
static bool write_typedef_decl(const typedef_decl_sptr&,
@@ -1470,7 +1510,7 @@ static void
write_array_size_and_alignment(const shared_ptr<array_type_def> decl, ostream& o)
{
if (decl->is_infinite())
o << " size-in-bits='" << "infinite" << "'";
o << " size-in-bits='" << "unknown" << "'";
else {
size_t size_in_bits = decl->get_size_in_bits();
if (size_in_bits)
@@ -1932,6 +1972,9 @@ write_decl(const decl_base_sptr& decl, write_context& ctxt, unsigned indent)
<reference_type_def>(decl), ctxt, indent)
|| write_array_type_def(dynamic_pointer_cast
<array_type_def>(decl), ctxt, indent)
|| write_array_subrange_type(dynamic_pointer_cast
<array_type_def::subrange_type>(decl),
ctxt, indent)
|| write_enum_type_decl(dynamic_pointer_cast<enum_type_decl>(decl),
ctxt, indent)
|| write_typedef_decl(dynamic_pointer_cast<typedef_decl>(decl),
@@ -2698,15 +2741,19 @@ write_pointer_type_def(const pointer_type_def_sptr& decl,
ostream& o = ctxt.get_ostream();
annotate(decl, ctxt, indent);
do_indent(o, indent);
string i;
o << "<pointer-type-def ";
type_base_sptr pointed_to_type = decl->get_pointed_to_type();
annotate(decl->get_canonical_type(), ctxt, indent);
i = ctxt.get_id_for_type(pointed_to_type);
do_indent(o, indent);
o << "<pointer-type-def type-id='"
<< ctxt.get_id_for_type(pointed_to_type)
<< "'";
o << "type-id='" << i << "'";
ctxt.record_type_as_referenced(pointed_to_type);
@@ -2716,7 +2763,7 @@ write_pointer_type_def(const pointer_type_def_sptr& decl,
: decl->get_translation_unit()->get_address_size()),
0);
string i = id;
i = id;
if (i.empty())
i = ctxt.get_id_for_type(decl);
@@ -2857,21 +2904,19 @@ write_array_subrange_type(const array_type_def::subrange_sptr& decl,
o << " length='";
if (decl->is_infinite())
o << "infinite";
o << "unknown";
else
o << decl->get_length();
o << "'";
if (decl->get_lower_bound())
{
ABG_ASSERT(decl->is_infinite()
|| (decl->get_length() ==
(uint64_t) (decl->get_upper_bound()
- decl->get_lower_bound() + 1)));
o << " lower-bound='" << decl->get_lower_bound() << "' upper-bound='"
<< decl->get_upper_bound() << "'";
}
ABG_ASSERT(decl->is_infinite()
|| decl->get_length() == 0
|| (decl->get_length() ==
(uint64_t) (decl->get_upper_bound()
- decl->get_lower_bound() + 1)));
o << " lower-bound='" << decl->get_lower_bound() << "' upper-bound='"
<< decl->get_upper_bound() << "'";
type_base_sptr underlying_type = decl->get_underlying_type();
if (underlying_type)
@@ -4588,6 +4633,7 @@ write_corpus(write_context& ctxt,
out << "</abi-corpus>\n";
ctxt.clear_referenced_types();
ctxt.record_corpus_as_emitted(corpus);
return true;
}
@@ -4639,7 +4685,10 @@ std::ostream& out = ctxt.get_ostream();
group->get_corpora().begin();
c != group->get_corpora().end();
++c)
write_corpus(ctxt, *c, get_indent_to_level(ctxt, indent, 1), true);
{
ABG_ASSERT(!ctxt.corpus_is_emitted(*c));
write_corpus(ctxt, *c, get_indent_to_level(ctxt, indent, 1), true);
}
do_indent_to_level(ctxt, indent, 0);
out << "</abi-corpus-group>\n";
@@ -4861,16 +4910,20 @@ write_type_record(xml_writer::write_context& ctxt,
// <c>0x25f9ba8</c>
// </type>
string id = ctxt.get_id_for_type (const_cast<type_base*>(type));
o << " <type>\n"
<< " <id>" << id << "</id>\n"
<< " <c>"
<< std::hex
<< (type->get_canonical_type()
? reinterpret_cast<uintptr_t>(type->get_canonical_type().get())
: 0xdeadbabe)
<< "</c>\n"
<< " </type>\n";
type_base* canonical = type->get_naked_canonical_type();
string id ;
if (canonical)
{
id = ctxt.get_id_for_type (const_cast<type_base*>(type));
o << " <type>\n"
<< " <id>" << id << "</id>\n"
<< " <c>"
<< std::hex
<< reinterpret_cast<uintptr_t>(canonical)
<< "</c>\n"
<< " </type>\n";
}
}
/// Serialize the map that is stored at
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2014-2022 Red Hat, Inc.
// Copyright (C) 2014-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+482 -48
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -13,6 +13,7 @@
#include <memory>
#include <string>
#include <vector>
#include <set>
#include "abg-config.h"
#include "abg-comp-filter.h"
#include "abg-suppression.h"
@@ -23,7 +24,12 @@
#include "abg-ctf-reader.h"
#endif
#ifdef WITH_BTF
#include "abg-btf-reader.h"
#endif
using std::vector;
using std::set;
using std::string;
using std::ostream;
using std::cout;
@@ -56,6 +62,10 @@ using abigail::tools_utils::load_default_system_suppressions;
using abigail::tools_utils::load_default_user_suppressions;
using abigail::tools_utils::abidiff_status;
using abigail::tools_utils::create_best_elf_based_reader;
using abigail::tools_utils::stick_corpus_and_dependencies_into_corpus_group;
using abigail::tools_utils::stick_corpus_and_binaries_into_corpus_group;
using abigail::tools_utils::add_dependencies_into_corpus_group;
using abigail::tools_utils::get_dependencies;
using namespace abigail;
@@ -110,6 +120,9 @@ struct options
bool show_impacted_interfaces;
bool assume_odr_for_cplusplus;
bool leverage_dwarf_factorization;
bool perform_change_categorization;
bool follow_dependencies;
bool list_dependencies;
bool dump_diff_tree;
bool show_stats;
bool do_log;
@@ -121,11 +134,18 @@ struct options
#endif
#ifdef WITH_CTF
bool use_ctf;
#endif
#ifdef WITH_BTF
bool use_btf;
#endif
vector<char*> di_root_paths1;
vector<char*> di_root_paths2;
vector<char**> prepared_di_root_paths1;
vector<char**> prepared_di_root_paths2;
vector<string> added_bins_dirs1;
vector<string> added_bins_dirs2;
vector<string> added_bins1;
vector<string> added_bins2;
options()
: display_usage(),
@@ -163,13 +183,12 @@ struct options
show_impacted_interfaces(),
assume_odr_for_cplusplus(true),
leverage_dwarf_factorization(true),
perform_change_categorization(true),
follow_dependencies(),
list_dependencies(),
dump_diff_tree(),
show_stats(),
do_log()
#ifdef WITH_CTF
,
use_ctf()
#endif
#ifdef WITH_DEBUG_SELF_COMPARISON
,
do_debug_self_comparison()
@@ -177,6 +196,14 @@ struct options
#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
,
do_debug_type_canonicalization()
#endif
#ifdef WITH_CTF
,
use_ctf()
#endif
#ifdef WITH_BTF
,
use_btf()
#endif
{}
@@ -211,6 +238,15 @@ display_usage(const string& prog_name, ostream& out)
<< " --header-file1|--hf1 <path> the path to one header of file1\n"
<< " --headers-dir2|--hd2 <path> the path to headers of file2\n"
<< " --header-file2|--hf2 <path> the path to one header of file2\n"
<< " --added-binaries-dir1 the path to the dependencies of file1\n"
<< " --added-binaries-dir2 the path to the dependencies of file2\n"
<< " --add-binaries1 <bin1,bin2,.>. build corpus groups with "
"extra binaries added to the first one and compare them\n"
<< " --add-binaries2 <bin1,bin2,..> build corpus groups with "
"extra binaries added to the second one and compare them\n"
<< " --follow-dependencies|--fdeps build corpus groups with the "
"dependencies of the input files\n"
<< " --list-dependencies|--ldeps show the dependencies of the input files\n"
<< " --drop-private-types drop private types from "
"internal representation\n"
<< " --exported-interfaces-only analyze exported interfaces only\n"
@@ -265,6 +301,8 @@ display_usage(const string& prog_name, ostream& out)
<< " --impacted-interfaces display interfaces impacted by leaf changes\n"
<< " --no-leverage-dwarf-factorization do not use DWZ optimisations to "
"speed-up the analysis of the binary\n"
<< " --no-change-categorization | -x don't perform categorization "
"of changes, for speed purposes\n"
<< " --no-assume-odr-for-cplusplus do not assume the ODR to speed-up the "
"analysis of the binary\n"
<< " --dump-diff-tree emit a debug dump of the internal diff tree to "
@@ -273,6 +311,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"
@@ -405,6 +446,80 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.header_files2.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--follow-dependencies")
|| !strcmp(argv[i], "--fdeps"))
opts.follow_dependencies = true;
else if (!strcmp(argv[i], "--list-dependencies")
|| !strcmp(argv[i], "--ldeps"))
opts.list_dependencies = true;
else if (!strcmp(argv[i], "--added-binaries-dir1")
|| !strcmp(argv[i], "--abd1"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
opts.added_bins_dirs1.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--added-binaries-dir2")
|| !strcmp(argv[i], "--abd2"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
opts.added_bins_dirs2.push_back(argv[j]);
++i;
}
else if (!strncmp(argv[i], "--add-binaries1=",
strlen("--add-binaries1=")))
tools_utils::get_comma_separated_args_of_option(argv[i],
"--add-binaries1=",
opts.added_bins1);
else if (!strcmp(argv[i], "--add-binaries1"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
string s = argv[j];
if (s.find(','))
tools_utils::split_string(s, ",", opts.added_bins1);
else
opts.added_bins1.push_back(s);
++i;
}
else if (!strncmp(argv[i], "--add-binaries2=",
strlen("--add-binaries2=")))
tools_utils::get_comma_separated_args_of_option(argv[i],
"--add-binaries2=",
opts.added_bins2);
else if (!strcmp(argv[i], "--add-binaries2"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
string s = argv[j];
if (s.find(','))
tools_utils::split_string(s, ",", opts.added_bins2);
else
opts.added_bins2.push_back(s);
++i;
}
else if (!strcmp(argv[i], "--kmi-whitelist")
|| !strcmp(argv[i], "-w"))
{
@@ -434,6 +549,8 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.exported_interfaces_only = true;
else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
opts.exported_interfaces_only = false;
else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
opts.linux_kernel_mode = false;
else if (!strcmp(argv[i], "--no-default-suppression"))
opts.no_default_supprs = true;
else if (!strcmp(argv[i], "--no-architecture"))
@@ -625,6 +742,9 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.show_impacted_interfaces = true;
else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
opts.leverage_dwarf_factorization = false;
else if (!strcmp(argv[i], "--no-change-categorization")
|| !strcmp(argv[i], "-x"))
opts.perform_change_categorization = false;
else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
opts.leverage_dwarf_factorization = false;
else if (!strcmp(argv[i], "--dump-diff-tree"))
@@ -637,6 +757,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;
@@ -732,6 +856,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
{
ctxt->default_output_stream(&cout);
ctxt->error_output_stream(&cerr);
ctxt->perform_change_categorization(opts.perform_change_categorization);
ctxt->show_leaf_changes_only(opts.leaf_changes_only);
ctxt->show_hex_values(opts.show_hexadecimal_values);
ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
@@ -810,6 +935,8 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
}
ctxt->dump_diff_tree(opts.dump_diff_tree);
ctxt->do_log(opts.do_log);
}
/// Set a bunch of tunable buttons on the ELF-based reader from the
@@ -914,7 +1041,9 @@ set_native_xml_reader_options(abigail::fe_iface& rdr,
const options& opts)
{
abixml::consider_types_not_reachable_from_public_interfaces(rdr,
opts.show_all_types);
opts.show_all_types);
rdr.options().do_log = opts.do_log;
}
/// Set the regex patterns describing the functions to drop from the
@@ -1015,7 +1144,9 @@ handle_error(abigail::fe_iface::status status_code,
const string& prog_name,
const options& opts)
{
if (!(status_code & abigail::fe_iface::STATUS_OK))
if (!(status_code & abigail::fe_iface::STATUS_OK)
|| status_code & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND
|| status_code & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
emit_prefix(prog_name, cerr)
<< "failed to read input file " << opts.file1 << "\n";
@@ -1078,10 +1209,10 @@ handle_error(abigail::fe_iface::status status_code,
emit_prefix(prog_name, cerr)
<< "could not find the alternate debug info file";
if (rdr->alternate_dwarf_debug_info())
if (!rdr->alternate_dwarf_debug_info_path().empty())
cerr << " at: "
<< rdr->alternate_dwarf_debug_info_path()
<< "\n";
<< rdr->alternate_dwarf_debug_info_path();
cerr << "\n";
}
if (status_code & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
@@ -1122,6 +1253,63 @@ emit_incompatible_format_version_error_message(const string& file_path1,
<< "'" << file_path2 << "' (" << version2 << ")\n";
}
/// Display the dependencies of two corpora.
///
/// @param prog_name the name of the current abidiff program.
///
/// @param corp1 the first corpus to consider.
///
/// @param corp2 the second corpus to consider.
///
/// @param deps1 the dependencies to display.
///
/// @param deps2 the dependencies to display.
static void
display_dependencies(const string& prog_name,
const corpus_sptr& corp1,
const corpus_sptr& corp2,
const set<string>& deps1,
const set<string>& deps2)
{
if (deps1.empty())
emit_prefix(prog_name, cout)
<< "No dependencies found for '" << corp1->get_path() << "':\n";
else
{
emit_prefix(prog_name, cout)
<< "dependencies of '" << corp1->get_path() << "':\n\t";
int n = 0;
for (const auto& dep : deps1)
{
if (n)
cout << ", ";
cout << dep;
++n;
}
cout << "\n";
}
if (deps2.empty())
emit_prefix(prog_name, cout)
<< "No dependencies found for '" << corp2->get_path() << "':\n";
else
{
emit_prefix(prog_name, cout)
<< "dependencies of '" << corp2->get_path() << "':\n\t";
int n = 0;
for (const auto& dep : deps2)
{
if (n)
cout << ", ";
cout << dep;
++n;
}
cout << "\n";
}
}
int
main(int argc, char* argv[])
{
@@ -1230,12 +1418,17 @@ 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,
opts.prepared_di_root_paths1,
env, requested_fe_kind,
opts.show_all_types);
opts.show_all_types,
opts.linux_kernel_mode);
ABG_ASSERT(rdr);
set_generic_options(*rdr, opts);
set_suppressions(*rdr, opts);
@@ -1248,6 +1441,20 @@ main(int argc, char* argv[])
return handle_error(c1_status, rdr.get(),
argv[0], opts);
if (!opts.added_bins1.empty())
g1 = stick_corpus_and_binaries_into_corpus_group(rdr, c1,
opts.added_bins1,
opts.added_bins_dirs1);
if (opts.follow_dependencies)
{
if (g1)
add_dependencies_into_corpus_group(rdr, *c1,
opts.added_bins_dirs1,
*g1);
else
g1 = stick_corpus_and_dependencies_into_corpus_group(rdr, c1,
opts.added_bins_dirs1);
}
}
break;
case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
@@ -1265,8 +1472,7 @@ main(int argc, char* argv[])
case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
{
abigail::fe_iface_sptr rdr =
abixml::create_reader(opts.file1,
env);
abixml::create_reader(opts.file1, env);
assert(rdr);
set_suppressions(*rdr, opts);
set_native_xml_reader_options(*rdr, opts);
@@ -1302,12 +1508,17 @@ 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,
opts.prepared_di_root_paths2,
env, requested_fe_kind,
opts.show_all_types);
opts.show_all_types,
opts.linux_kernel_mode);
ABG_ASSERT(rdr);
set_generic_options(*rdr, opts);
@@ -1320,6 +1531,21 @@ main(int argc, char* argv[])
&& (c2_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
&& (c2_status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)))
return handle_error(c2_status, rdr.get(), argv[0], opts);
if (!opts.added_bins2.empty())
g2 = stick_corpus_and_binaries_into_corpus_group(rdr, c2,
opts.added_bins2,
opts.added_bins_dirs2);
if (opts.follow_dependencies)
{
if (g2)
add_dependencies_into_corpus_group(rdr, *c2,
opts.added_bins_dirs2,
*g2);
else
g2 = stick_corpus_and_dependencies_into_corpus_group(rdr, c2,
opts.added_bins_dirs2);
}
}
break;
case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
@@ -1353,6 +1579,34 @@ main(int argc, char* argv[])
break;
}
if (!opts.added_bins1.empty()
|| !opts.added_bins2.empty())
{
// We were requested to compare a set of binaries against
// another set of binaries. Let's make sure we construct
// two ABI construct groups in all cases.
if (!g1 && c1)
{
// We don't have a corpus group for the first argument.
// Let's build one and stick the ABI corpus at hand in
// it.
g1.reset(new corpus_group(c1->get_environment(),
c1->get_path()));
g1->add_corpus(c1);
}
if (!g2 && c2)
{
// We don't have a corpus group for the second argument.
// Let's build one and stick the ABI corpus at hand in
// it.
g2.reset(new corpus_group(c2->get_environment(),
c2->get_path()));
g2->add_corpus(c1);
}
}
if (!!c1 != !!c2
|| !!t1 != !!t2
|| !!g1 != !!g2)
@@ -1379,43 +1633,38 @@ main(int argc, char* argv[])
if (t1)
{
tools_utils::timer t;
if (opts.do_log)
{
t.start();
std::cerr << "Compute diff ...\n";
}
translation_unit_diff_sptr diff = compute_diff(t1, t2, ctxt);
if (diff->has_changes())
diff->report(cout);
}
else if (c1)
{
if (opts.show_symtabs)
if (opts.do_log)
{
display_symtabs(c1, c2, cout);
return abigail::tools_utils::ABIDIFF_OK;
t.stop();
std::cerr << "diff computed!:" << t << "\n";
}
const auto c1_version = c1->get_format_major_version_number();
const auto c2_version = c2->get_format_major_version_number();
if (c1_version != c2_version)
{
emit_incompatible_format_version_error_message(opts.file1,
c1_version,
opts.file2,
c2_version,
argv[0]);
return abigail::tools_utils::ABIDIFF_ERROR;
}
set_corpus_keep_drop_regex_patterns(opts, c1);
set_corpus_keep_drop_regex_patterns(opts, c2);
corpus_diff_sptr diff = compute_diff(c1, c2, ctxt);
if (diff->has_net_changes())
status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
if (diff->has_incompatible_changes())
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
if (diff->has_changes())
diff->report(cout);
{
tools_utils::timer t;
if (opts.do_log)
{
t.start();
std::cerr << "Computing the report ...\n";
}
diff->report(cout);
if (opts.do_log)
{
t.stop();
std::cerr << "Report computed!:" << t << "\n";
}
}
}
else if (g1)
{
@@ -1438,17 +1687,202 @@ main(int argc, char* argv[])
}
adjust_diff_context_for_kmidiff(*ctxt);
tools_utils::timer t;
if (opts.do_log)
{
t.start();
std::cerr << "Compute diff ...\n";
}
corpus_diff_sptr diff = compute_diff(g1, g2, ctxt);
if (opts.do_log)
{
t.stop();
diff->do_log(true);
std::cerr << "diff computed!:" << t << "\n";
}
if (opts.do_log)
{
std::cerr << "Computing net changes ...\n";
t.start();
}
if (diff->has_net_changes())
status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
if (opts.do_log)
{
t.stop();
std::cerr << "net changes computed!: "<< t << "\n";
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing incompatible changes ...\n";
}
if (diff->has_incompatible_changes())
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
if (diff->has_changes())
diff->report(cout);
if (opts.do_log)
{
t.stop();
std::cerr << "incompatible changes computed!: "<< t << "\n";
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing changes ...\n";
}
if (diff->has_changes())
{
if (opts.do_log)
{
t.stop();
std::cerr << "changes computed!: "<< t << "\n";
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing report ...\n";
}
diff->report(cout);
if (opts.do_log)
{
t.stop();
std::cerr << "Report computed!:" << t << "\n";
}
}
else
{
if (opts.do_log)
{
t.stop();
std::cerr << "changes computed!: "<< t << "\n";
}
}
if (opts.list_dependencies)
{
set<string> deps1, deps2;
get_dependencies(*c1, opts.added_bins_dirs1, deps1);
get_dependencies(*c2, opts.added_bins_dirs2, deps2);
display_dependencies(argv[0], c1, c2, deps1, deps2);
}
}
else if (c1)
{
if (opts.show_symtabs)
{
display_symtabs(c1, c2, cout);
return abigail::tools_utils::ABIDIFF_OK;
}
if (opts.list_dependencies)
{
set<string> deps1, deps2;
get_dependencies(*c1, opts.added_bins_dirs1, deps1);
get_dependencies(*c2, opts.added_bins_dirs2, deps2);
display_dependencies(argv[0], c1, c2, deps1, deps2);
return abigail::tools_utils::ABIDIFF_OK;
}
const auto c1_version = c1->get_format_major_version_number();
const auto c2_version = c2->get_format_major_version_number();
if (c1_version != c2_version)
{
emit_incompatible_format_version_error_message(opts.file1,
c1_version,
opts.file2,
c2_version,
argv[0]);
return abigail::tools_utils::ABIDIFF_ERROR;
}
set_corpus_keep_drop_regex_patterns(opts, c1);
set_corpus_keep_drop_regex_patterns(opts, c2);
tools_utils::timer t;
if (opts.do_log)
{
t.start();
std::cerr << "Compute diff ...\n";
}
corpus_diff_sptr diff = compute_diff(c1, c2, ctxt);
if (opts.do_log)
{
t.stop();
std::cerr << "diff computed!:" << t << "\n";
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing net changes ...\n";
}
if (diff->has_net_changes())
{
if (opts.do_log)
{
t.stop();
std::cerr << "net changes computed!: "<< t << "\n";
}
status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing incompatible changes ...\n";
}
if (diff->has_incompatible_changes())
{
if (opts.do_log)
{
t.stop();
std::cerr << "incompatible changes computed!: "<< t << "\n";
}
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing changes ...\n";
}
if (diff->has_changes())
{
if (opts.do_log)
{
t.stop();
std::cerr << "changes computed!: "<< t << "\n";
}
if (opts.do_log)
{
t.start();
std::cerr << "Computing report ...\n";
}
diff->report(cout);
if (opts.do_log)
{
t.stop();
std::cerr << "Report computed!:" << t << "\n";
}
}
}
else
status = abigail::tools_utils::ABIDIFF_ERROR;
+158 -11
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -22,6 +22,7 @@
#include <memory>
#include <string>
#include <vector>
#include <set>
#include "abg-config.h"
#include "abg-tools-utils.h"
#include "abg-corpus.h"
@@ -29,6 +30,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"
@@ -40,7 +44,9 @@ using std::cout;
using std::ostream;
using std::ofstream;
using std::vector;
using std::set;
using std::shared_ptr;
using std::static_pointer_cast;
using abg_compat::optional;
using abigail::tools_utils::emit_prefix;
using abigail::tools_utils::temp_file;
@@ -49,6 +55,9 @@ using abigail::tools_utils::check_file;
using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
using abigail::tools_utils::timer;
using abigail::tools_utils::create_best_elf_based_reader;
using abigail::tools_utils::stick_corpus_and_dependencies_into_corpus_group;
using abigail::tools_utils::stick_corpus_and_binaries_into_corpus_group;
using abigail::tools_utils::add_dependencies_into_corpus_group;
using abigail::ir::environment_sptr;
using abigail::ir::environment;
using abigail::corpus;
@@ -68,6 +77,7 @@ using abigail::xml_writer::create_write_context;
using abigail::xml_writer::type_id_style_kind;
using abigail::xml_writer::write_context_sptr;
using abigail::xml_writer::write_corpus;
using abigail::xml_writer::write_corpus_group;
using abigail::abixml::read_corpus_from_abixml_file;
using namespace abigail;
@@ -81,6 +91,8 @@ struct options
vector<char**> prepared_di_root_paths;
vector<string> headers_dirs;
vector<string> header_files;
vector<string> added_bins_dirs;
vector<string> added_bins;
string vmlinux;
vector<string> suppression_paths;
vector<string> kabi_whitelist_paths;
@@ -101,8 +113,13 @@ struct options
bool corpus_group_for_linux;
bool show_stats;
bool noout;
bool follow_dependencies;
bool list_dependencies;
#ifdef WITH_CTF
bool use_ctf;
#endif
#ifdef WITH_BTF
bool use_btf;
#endif
bool show_locs;
bool abidiff;
@@ -142,8 +159,13 @@ struct options
corpus_group_for_linux(false),
show_stats(),
noout(),
follow_dependencies(),
list_dependencies(),
#ifdef WITH_CTF
use_ctf(false),
#endif
#ifdef WITH_BTF
use_btf(false),
#endif
show_locs(true),
abidiff(),
@@ -220,6 +242,12 @@ display_usage(const string& prog_name, ostream& out)
<< " --vmlinux <path> the path to the vmlinux binary to consider to emit "
"the ABI of the union of vmlinux and its modules\n"
<< " --abidiff compare the loaded ABI against itself\n"
<< " --add-binaries <bin1,bin2,...> build a corpus group with "
"the added inaries\n"
<< " --follow-dependencies build a corpus group with the dependencies\n"
<< " --list-dependencies list the dependencies of a given binary\n"
<< " --added-binaries-dir|--abd <dir-of-deps> where to look for dependencies "
"or added binaries\n"
#ifdef WITH_DEBUG_SELF_COMPARISON
<< " --debug-abidiff debug the process of comparing the loaded ABI against itself\n"
#endif
@@ -234,6 +262,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";
@@ -282,6 +313,15 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.headers_dirs.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--added-binaries-dir")
|| !strcmp(argv[i], "--abd"))
{
int j = i + 1;
if (j >= argc)
return false;
opts.added_bins_dirs.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--header-file")
|| !strcmp(argv[i], "--hf"))
{
@@ -332,9 +372,35 @@ parse_command_line(int argc, char* argv[], options& opts)
}
else if (!strcmp(argv[i], "--noout"))
opts.noout = true;
else if (!strcmp(argv[i], "--follow-dependencies"))
opts.follow_dependencies = true;
else if (!strcmp(argv[i], "--list-dependencies"))
opts.list_dependencies = true;
else if (!strncmp(argv[i], "--add-binaries=",
strlen("--add-binaries=")))
tools_utils::get_comma_separated_args_of_option(argv[i],
"--add-binaries=",
opts.added_bins);
else if (!strcmp(argv[i], "--add-binaries"))
{
int j = i + 1;
if (j >= argc)
return false;
string s = argv[j];
if (s.find(','))
tools_utils::split_string(s, ",", opts.added_bins);
else
opts.added_bins.push_back(s);
++i;
}
#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;
@@ -582,12 +648,17 @@ load_corpus_and_write_abixml(char* argv[],
#endif
corpus_sptr corp;
corpus_group_sptr corp_group;
fe_iface::status s = fe_iface::STATUS_UNKNOWN;
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
// First of all, create a reader to read the ABI from the file
// specfied in opts ...
@@ -642,11 +713,6 @@ load_corpus_and_write_abixml(char* argv[],
emit_prefix(argv[0], cerr)
<< "read corpus from elf file in: " << t << "\n";
// Clear some resources to gain back some space.
t.start();
reader.reset();
t.stop();
if (opts.do_log)
emit_prefix(argv[0], cerr)
<< "reset reader ELF in: " << t << "\n";
@@ -689,10 +755,71 @@ load_corpus_and_write_abixml(char* argv[],
emit_prefix(argv[0], cerr)
<< "Could not read ELF symbol information from "
<< opts.in_file_path << "\n";
else if (s & fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
emit_prefix(argv[0], cerr)
<< "Could not read alternate debug info file";
if (!reader->alternate_dwarf_debug_info_path().empty())
cerr << " '" << reader->alternate_dwarf_debug_info_path() << "'";
cerr << " for '"
<< opts.in_file_path << "'.\n";
emit_prefix(argv[0], cerr)
<< "You might have forgotten to install some "
"additional needed debug info\n";
}
return 1;
}
if (opts.list_dependencies)
{
// Show the dependencies of the corpus and display them.
set<string> dependencies;
if (tools_utils::get_dependencies(*corp, opts.added_bins_dirs,
dependencies))
{
cout << "Dependencies of '" << corp->get_path()
<< "':\n\t";
int n = 0;
for (const auto& dep : dependencies)
{
if (n)
cout << ", ";
cout << dep;
++n;
}
cout << "\n";
}
}
if (!opts.added_bins.empty())
corp_group =
stick_corpus_and_binaries_into_corpus_group(reader, corp,
opts.added_bins,
opts.added_bins_dirs);
if (opts.follow_dependencies)
{
// load the dependencies of the corpus and put them all into a
// corpus group.
// If a corpus_group already exists, use that one ...
if (!corp_group->is_empty())
add_dependencies_into_corpus_group(reader, *corp,
opts.added_bins_dirs,
*corp_group);
else
// .. otherwise, create a new corpus group.
corp_group =
stick_corpus_and_dependencies_into_corpus_group(reader, corp,
opts.added_bins_dirs);
}
// Clear some resources to gain back some space.
t.start();
reader.reset();
t.stop();
// Now create a write context and write out an ABI XML description
// of the read corpus.
t.start();
@@ -712,7 +839,10 @@ load_corpus_and_write_abixml(char* argv[],
// against the ABI of the input ELF file.
temp_file_sptr tmp_file = temp_file::create();
set_ostream(*write_ctxt, tmp_file->get_stream());
write_corpus(*write_ctxt, corp, 0);
if (corp_group)
write_corpus_group(*write_ctxt, corp_group, 0);
else
write_corpus(*write_ctxt, corp, 0);
tmp_file->get_stream().flush();
#ifdef WITH_DEBUG_SELF_COMPARISON
@@ -731,7 +861,14 @@ load_corpus_and_write_abixml(char* argv[],
#endif
t.start();
fe_iface::status sts;
corpus_sptr corp2 = rdr->read_corpus(sts);
corpus_sptr corp2;
corpus_group_sptr corp_group2;
if (corp_group)
corp_group2 = abixml::read_corpus_group_from_input(*rdr);
else
corp2 = rdr->read_corpus(sts);
t.stop();
if (opts.do_log)
emit_prefix(argv[0], cerr)
@@ -749,7 +886,11 @@ load_corpus_and_write_abixml(char* argv[],
set_diff_context(ctxt);
ctxt->show_locs(opts.show_locs);
t.start();
corpus_diff_sptr diff = compute_diff(corp, corp2, ctxt);
corpus_diff_sptr diff =
corp_group2
? compute_diff(corp_group, corp_group2, ctxt)
: compute_diff(corp, corp2, ctxt);
t.stop();
if (opts.do_log)
emit_prefix(argv[0], cerr)
@@ -790,7 +931,10 @@ load_corpus_and_write_abixml(char* argv[],
}
set_ostream(*write_ctxt, of);
t.start();
write_corpus(*write_ctxt, corp, 0);
if (corp_group)
write_corpus_group(*write_ctxt, corp_group, 0);
else
write_corpus(*write_ctxt, corp, 0);
t.stop();
if (opts.do_log)
emit_prefix(argv[0], cerr)
@@ -801,7 +945,10 @@ load_corpus_and_write_abixml(char* argv[],
else
{
t.start();
exit_code = !write_corpus(*write_ctxt, corp, 0);
exit_code =
corp_group
? !write_corpus_group(*write_ctxt, corp_group, 0)
: !write_corpus(*write_ctxt, corp, 0);
t.stop();
if (opts.do_log)
emit_prefix(argv[0], cerr)
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+349 -161
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2015-2022 Red Hat, Inc.
// Copyright (C) 2015-2023 Red Hat, Inc.
//
// Author: Sinny Kumari
@@ -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<string> kabi_whitelist_packages;
vector<string> 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
@@ -271,6 +281,9 @@ get_interesting_files_under_dir(const string dir,
options& opts,
vector<string>& interesting_files);
static string
get_pretty_printed_list_of_packages(const vector<string>& packages);
/// Abstract ELF files from the packages which ABIs ought to be
/// compared
class elf_file
@@ -716,12 +729,16 @@ public:
if (system(cmd.c_str()))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " FAILED\n";
emit_prefix("abipkgdiff", cerr)
<< "Erasing temporary extraction directory"
<< " FAILED\n";
}
else
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " DONE\n";
emit_prefix("abipkgdiff", cerr)
<< "Erasing temporary extraction directory"
<< " DONE\n";
}
}
@@ -905,6 +922,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"
@@ -934,7 +954,7 @@ extract_rpm(const string& package_path,
<< package_path
<< " to "
<< extracted_package_dir_path
<< " ...";
<< " ...\n";
string cmd = "test -d " + extracted_package_dir_path
+ " || mkdir -p " + extracted_package_dir_path + " ; cd " +
@@ -944,12 +964,22 @@ extract_rpm(const string& package_path,
if (system(cmd.c_str()))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " FAILED\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting package "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " FAILED\n";
return false;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " DONE\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting package "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " DONE\n";
return true;
}
@@ -987,13 +1017,22 @@ extract_deb(const string& package_path,
if (system(cmd.c_str()))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " FAILED\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting package "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " FAILED\n";
return false;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " DONE\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting package "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " DONE\n";
return true;
}
@@ -1040,12 +1079,22 @@ extract_tar(const string& package_path,
if (system(cmd.c_str()))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " FAILED\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting tar archive "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " FAILED\n";
return false;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << " DONE\n";
emit_prefix("abipkgdiff", cerr)
<< "Extracting tar archive "
<< package_path
<< " to "
<< extracted_package_dir_path
<< " DONE\n";
return true;
}
@@ -1084,12 +1133,18 @@ erase_created_temporary_directories_parent(const options &opts)
if (system(cmd.c_str()))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << "FAILED\n";
emit_prefix("abipkgdiff", cerr)
<< "Erasing temporary extraction parent directory "
<< package::extracted_packages_parent_dir()
<< "FAILED\n";
}
else
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr) << "DONE\n";
emit_prefix("abipkgdiff", cerr)
<< "Erasing temporary extraction parent directory "
<< package::extracted_packages_parent_dir()
<< "DONE\n";
}
}
@@ -1250,6 +1305,68 @@ set_generic_options(abigail::elf_based_reader& rdr, const options& opts)
opts.assume_odr_for_cplusplus;
}
/// Emit an error message on standard error about alternate debug info
/// not being found.
///
/// @param reader the ELF based reader being used.
///
/// @param elf_file the ELF file being looked at.
///
/// @param opts the options passed to the tool.
///
/// @param is_old_package if this is true, then we are looking at the
/// first (the old) package of the comparison. Otherwise, we are
/// looking at the second (the newest) package of the comparison.
static void
emit_alt_debug_info_not_found_error(abigail::elf_based_reader& reader,
const elf_file& elf_file,
const options& opts,
ostream& out,
bool is_old_package)
{
ABG_ASSERT(is_old_package
? !opts.debug_packages1.empty()
: !opts.debug_packages2.empty());
string filename;
tools_utils::base_name(elf_file.path, filename);
emit_prefix("abipkgdiff", out)
<< "While reading elf file '"
<< filename
<< "', could not find alternate debug info in provided "
"debug info package(s) "
<< get_pretty_printed_list_of_packages(is_old_package
? opts.debug_packages1
: opts.debug_packages2)
<< "\n";
string alt_di_path;
#ifdef WITH_CTF
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())
{
emit_prefix("abipkgdiff", out)
<< "The alternate debug info file being looked for is: "
<< alt_di_path << "\n";
}
else
emit_prefix("abipkgdiff", out) << "\n";
emit_prefix("abipkgdiff", out)
<< "You must provide the additional "
<< "debug info package that contains that alternate "
<< "debug info file, using an additional --d1/--d2 switch\n";
}
/// Compare the ABI two elf files, using their associated debug info.
///
/// The result of the comparison is emitted to standard output.
@@ -1289,6 +1406,7 @@ compare(const elf_file& elf1,
abigail::ir::environment& env,
corpus_diff_sptr& diff,
diff_context_sptr& ctxt,
ostream& out,
abigail::fe_iface::status* detailed_error_status = 0)
{
char *di_dir1 = (char*) debug_dir1.c_str(),
@@ -1353,6 +1471,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,
@@ -1381,6 +1503,15 @@ compare(const elf_file& elf1,
bail_out = true;
}
if (c1_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
emit_alt_debug_info_not_found_error(*reader, elf1, opts, out,
/*is_old_package=*/true);
if (detailed_error_status)
*detailed_error_status = c1_status;
bail_out = true;
}
if (opts.fail_if_no_debug_info)
{
bool debug_info_error = false;
@@ -1401,31 +1532,6 @@ compare(const elf_file& elf1,
debug_info_error = true;
}
if (c1_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "while reading file" << elf1.path << "\n";
emit_prefix("abipkgdiff", cerr)
<< "Could not find alternate debug info file";
string alt_di_path;
#ifdef WITH_CTF
if (opts.use_ctf)
;
else
#endif
reader->refers_to_alt_debug_info(alt_di_path);
if (!alt_di_path.empty())
cerr << ": " << alt_di_path << "\n";
else
cerr << "\n";
if (detailed_error_status)
*detailed_error_status = c1_status;
debug_info_error = true;
}
if (debug_info_error)
bail_out = true;
}
@@ -1449,10 +1555,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,
@@ -1480,6 +1592,15 @@ compare(const elf_file& elf1,
bail_out = true;
}
if (c2_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
emit_alt_debug_info_not_found_error(*reader, elf2, opts, out,
/*is_old_package=*/false);
if (detailed_error_status)
*detailed_error_status = c2_status;
bail_out = true;
}
if (opts.fail_if_no_debug_info)
{
bool debug_info_error = false;
@@ -1500,31 +1621,6 @@ compare(const elf_file& elf1,
debug_info_error = true;
}
if (c2_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "while reading file" << elf2.path << "\n";
emit_prefix("abipkgdiff", cerr)
<< "Could not find alternate debug info file";
string alt_di_path;
#ifdef WITH_CTF
if (opts.use_ctf)
;
else
#endif
reader->refers_to_alt_debug_info(alt_di_path);
if (!alt_di_path.empty())
cerr << ": " << alt_di_path << "\n";
else
cerr << "\n";
if (detailed_error_status)
*detailed_error_status = c2_status;
debug_info_error = true;
}
if (debug_info_error)
bail_out = true;
}
@@ -1589,6 +1685,7 @@ compare_to_self(const elf_file& elf,
abigail::ir::environment& env,
corpus_diff_sptr& diff,
diff_context_sptr& ctxt,
ostream& out,
abigail::fe_iface::status* detailed_error_status = 0)
{
char *di_dir = (char*) debug_dir.c_str();
@@ -1617,6 +1714,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,
@@ -1633,13 +1734,21 @@ compare_to_self(const elf_file& elf,
emit_prefix("abipkgdiff", cerr)
<< "Could not read file '"
<< elf.path
<< "' propertly\n";
<< "' properly\n";
if (detailed_error_status)
*detailed_error_status = c_status;
return abigail::tools_utils::ABIDIFF_ERROR;
}
else if (c_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
{
emit_alt_debug_info_not_found_error(*reader, elf, opts, out,
/*is_old_package=*/true);
if (detailed_error_status)
*detailed_error_status = c_status;
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
@@ -1746,7 +1855,10 @@ compare_to_self(const elf_file& elf,
diff = compute_diff(corp, reread_corp, ctxt);
if (opts.verbose)
emit_prefix("abipkgdfiff", cerr)
<< "... Comparing the ABIs: DONE\n";
<< "Comparing the ABIs: of \n"
<< " '" << corp->get_path() << "' against \n"
<< " '" << abi_file_path << "':"
<< "DONE\n";
abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
if (diff->has_changes())
@@ -1943,8 +2055,8 @@ maybe_handle_kabi_whitelist_pkg(const package& pkg, options &opts)
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());
bool is_linux_kernel_package = file_is_kernel_package(pkg.path(),
pkg.type());
if (!is_linux_kernel_package)
return false;
@@ -1957,7 +2069,7 @@ maybe_handle_kabi_whitelist_pkg(const package& pkg, options &opts)
return false;
string rpm_arch;
if (!get_rpm_arch(pkg_name, rpm_arch))
if (!get_rpm_arch(pkg.base_name(), rpm_arch))
return false;
string kabi_wl_path = kabi_wl_pkg->extracted_dir_path();
@@ -2068,6 +2180,60 @@ public:
status(abigail::tools_utils::ABIDIFF_OK)
{}
void
maybe_emit_pretty_error_message_to_output(const corpus_diff_sptr& diff,
abigail::fe_iface::status detailed_status)
{
// If there is an ABI change, tell the user about it.
if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
||( diff && diff->has_net_changes()))
{
diff->report(out, /*prefix=*/" ");
string name = args->elf1.name;
pretty_output +=
string("================ changes of '") + name + "'===============\n"
+ out.str()
+ "================ end of changes of '"
+ name + "'===============\n\n";
}
else
{
if (args->opts.show_identical_binaries)
{
out << "No ABI change detected\n";
pretty_output += out.str();
}
}
// If an error happened while comparing the two binaries, tell the
// user about it.
if (status & abigail::tools_utils::ABIDIFF_ERROR)
{
string diagnostic =
abigail::status_to_diagnostic_string(detailed_status);
if (diagnostic.empty())
diagnostic =
"Unknown error. Please run the tool again with --verbose\n";
string name = args->elf1.name;
std::stringstream o;
emit_prefix("abipkgdiff", o)
<< "==== Error happened during processing of '"
<< name
<< "' ====\n";
emit_prefix("abipkgdiff", o)
<< diagnostic
<< ":\n"
<< out.str();
emit_prefix("abipkgdiff", o)
<< "==== End of error for '"
<< name
<< "' ====\n\n";
pretty_output += o.str();
}
}
/// The job performed by the task.
///
/// This compares two ELF files, gets the resulting test report and
@@ -2088,44 +2254,9 @@ public:
status |= compare(args->elf1, args->debug_dir1, args->private_types_suppr1,
args->elf2, args->debug_dir2, args->private_types_suppr2,
args->opts, env, diff, ctxt, &detailed_status);
args->opts, env, diff, ctxt, out, &detailed_status);
// If there is an ABI change, tell the user about it.
if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
||( diff && diff->has_net_changes()))
{
diff->report(out, /*prefix=*/" ");
string name = args->elf1.name;
pretty_output +=
string("================ changes of '") + name + "'===============\n"
+ out.str()
+ "================ end of changes of '"
+ name + "'===============\n\n";
}
else
{
if (args->opts.show_identical_binaries)
out << "No ABI change detected\n";
}
// If an error happened while comparing the two binaries, tell the
// user about it.
if (status & abigail::tools_utils::ABIDIFF_ERROR)
{
string diagnostic =
abigail::status_to_diagnostic_string(detailed_status);
if (diagnostic.empty())
diagnostic =
"Unknown error. Please run the tool again with --verbose\n";
string name = args->elf1.name;
pretty_output +=
"==== Error happened during processing of '" + name + "' ====\n";
pretty_output += diagnostic;
pretty_output +=
"==== End of error for '" + name + "' ====\n";
}
maybe_emit_pretty_error_message_to_output(diff, detailed_status);
}
}; // end class compare_task
@@ -2160,44 +2291,14 @@ public:
abigail::fe_iface::STATUS_UNKNOWN;
status |= compare_to_self(args->elf1, args->debug_dir1,
args->opts, env, diff, ctxt,
args->opts, env, diff, ctxt, out,
&detailed_status);
string name = args->elf1.name;
if (status == abigail::tools_utils::ABIDIFF_OK)
pretty_output += "==== SELF CHECK SUCCEEDED for '"+ name + "' ====\n";
else if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
||( diff && diff->has_net_changes()))
{
// There is an ABI change, tell the user about it.
diff->report(out, /*indent=*/" ");
pretty_output +=
string("======== comparing'") + name +
"' to itself wrongly yielded result: ===========\n"
+ out.str()
+ "===SELF CHECK FAILED for '"+ name + "'\n";
}
// If an error happened while comparing the two binaries, tell the
// user about it.
if (status & abigail::tools_utils::ABIDIFF_ERROR)
{
string diagnostic =
abigail::status_to_diagnostic_string(detailed_status);
if (diagnostic.empty())
diagnostic =
"Unknown error. Please run the tool again with --verbose\n";
string name = args->elf1.name;
pretty_output +=
"==== Error happened during self check of '" + name + "' ====\n";
pretty_output += diagnostic;
pretty_output +=
"==== SELF CHECK FAILED for '" + name + "' ====\n";
}
else
maybe_emit_pretty_error_message_to_output(diff, detailed_status);
}
}; // end class self_compare
@@ -2222,14 +2323,20 @@ typedef shared_ptr<self_compare_task> self_compare_task_sptr;
/// function only looks for a file name which name is the same as the
/// value of this parameter.
///
/// @param parent_dir_name the name of the directory that the file
/// name denoted by @p entry should belong to. If it doesn't (because
/// it's a symlink that resolves to a file outside of that directory)
/// then the vector of paths of is not updated.
///
/// @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_package_content(const FTSENT *entry,
options &opts,
const string& file_name_to_look_for,
unordered_set<string>& paths)
maybe_update_package_content(const FTSENT* entry,
options& opts,
const string& file_name_to_look_for,
const string& parent_dir_name,
unordered_set<string>& paths)
{
if (entry == NULL
|| (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
@@ -2239,6 +2346,15 @@ maybe_update_package_content(const FTSENT *entry,
string path = entry->fts_path;
maybe_get_symlink_target_file_path(path, path);
string parent_dir = parent_dir_name;
maybe_get_symlink_target_file_path(parent_dir, parent_dir);
if (!parent_dir_name.empty())
{
string s;
if (!string_suffix(path, parent_dir, s))
return;
}
if (!file_name_to_look_for.empty())
{
@@ -2290,7 +2406,7 @@ get_interesting_files_under_dir(const string dir,
FTSENT *entry;
unordered_set<string> files;
while ((entry = fts_read(file_hierarchy)))
maybe_update_package_content(entry, opts, file_name_to_look_for, files);
maybe_update_package_content(entry, opts, file_name_to_look_for, dir, files);
for (unordered_set<string>::const_iterator i = files.begin();
i != files.end();
@@ -2304,6 +2420,33 @@ get_interesting_files_under_dir(const string dir,
return is_ok;
}
/// Return a string representing a list of packages that can be
/// printed out to the user.
///
/// @param packages a vector of package names
///
/// @return a string representing the list of packages @p packages.
static string
get_pretty_printed_list_of_packages(const vector<string>& packages)
{
if (packages.empty())
return string();
bool need_comma = false;
std::stringstream o;
for (auto p : packages)
{
string filename;
tools_utils::base_name(p, filename);
if (need_comma)
o << ", ";
else
need_comma = true;
o << "'" << filename << "'";
}
return o.str();
}
/// Create maps of the content of a given package.
///
/// The maps contain relevant metadata about the content of the
@@ -2333,8 +2476,7 @@ create_maps_of_package_content(package& package, options& opts)
// 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,
bool is_linux_kernel_package = file_is_kernel_package(package.path(),
package.type());
if (is_linux_kernel_package)
{
@@ -2344,7 +2486,7 @@ create_maps_of_package_content(package& package, options& opts)
is_ok = true;
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< " Analysis of " << package.path() << " DONE\n";
<< " Analysis of linux package " << package.path() << " DONE\n";
return is_ok;
}
@@ -2369,6 +2511,12 @@ create_maps_of_package_content(package& package, options& opts)
++file)
{
elf_file_sptr e (new elf_file(*file));
string resolved_e_path;
// The path 'e->path' might contain symlinks. Let's resolve
// them so we can see if 'e->path' has already been seen before,
// for instance.
real_path(e->path, resolved_e_path);
if (opts.compare_dso_only)
{
if (e->type != abigail::elf::ELF_TYPE_DSO)
@@ -2423,7 +2571,13 @@ create_maps_of_package_content(package& package, options& opts)
// base name. So let's consider the full path of the binary
// inside the extracted directory.
string key = e->name;
package.convert_path_to_unique_suffix(e->path, key);
package.convert_path_to_unique_suffix(resolved_e_path, key);
if (package.path_elf_file_sptr_map().find(key)
!= package.path_elf_file_sptr_map().end())
// 'key' has already been seen before. So we won't map it
// twice.
continue;
package.path_elf_file_sptr_map()[key] = e;
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
@@ -2453,11 +2607,18 @@ create_maps_of_package_content(package& package, options& opts)
}
}
if (package.convert_path_to_unique_suffix(e->path, key))
if (package.convert_path_to_unique_suffix(resolved_e_path, key))
{
dir_name(key, key);
key += string("/@soname:") + e->soname;
}
if (package.path_elf_file_sptr_map().find(key)
!= package.path_elf_file_sptr_map().end())
// 'key' has already been seen before. So we won't do itl
// twice.
continue;
package.path_elf_file_sptr_map()[key] = e;
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
@@ -3029,10 +3190,22 @@ compare_prepared_linux_kernel_packages(package& first_package,
string vmlinux_path1, vmlinux_path2;
if (!get_vmlinux_path_from_kernel_dist(debug_dir1, vmlinux_path1))
return abigail::tools_utils::ABIDIFF_ERROR;
{
emit_prefix("abipkgdiff", cerr)
<< "Could not find vmlinux in debuginfo package '"
<< first_package.path()
<< "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (!get_vmlinux_path_from_kernel_dist(debug_dir2, vmlinux_path2))
return abigail::tools_utils::ABIDIFF_ERROR;
{
emit_prefix("abipkgdiff", cerr)
<< "Could not find vmlinux in debuginfo package '"
<< second_package.path()
<< "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
string dist_root1 = first_package.extracted_dir_path();
string dist_root2 = second_package.extracted_dir_path();
@@ -3044,14 +3217,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 +3244,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;
@@ -3117,7 +3299,7 @@ compare_prepared_package(package& first_package, package& second_package,
{
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
if (abigail::tools_utils::file_is_kernel_package(first_package.base_name(),
if (abigail::tools_utils::file_is_kernel_package(first_package.path(),
first_package.type()))
{
opts.show_symbols_not_referenced_by_debug_info = false;
@@ -3434,6 +3616,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"))
@@ -3631,14 +3817,14 @@ main(int argc, char* argv[])
| abigail::tools_utils::ABIDIFF_ERROR);
}
if (file_is_kernel_package(first_package->base_name(),
if (file_is_kernel_package(first_package->path(),
abigail::tools_utils::FILE_TYPE_RPM)
|| file_is_kernel_package(second_package->base_name(),
|| file_is_kernel_package(second_package->path(),
abigail::tools_utils::FILE_TYPE_RPM))
{
if (file_is_kernel_package(first_package->base_name(),
if (file_is_kernel_package(first_package->path(),
abigail::tools_utils::FILE_TYPE_RPM)
!= file_is_kernel_package(second_package->base_name(),
!= file_is_kernel_package(second_package->path(),
abigail::tools_utils::FILE_TYPE_RPM))
{
emit_prefix("abipkgdiff", cerr)
@@ -3716,6 +3902,8 @@ main(int argc, char* argv[])
| abigail::tools_utils::ABIDIFF_ERROR);
}
abigail::tools_utils::initialize();
if (opts.self_check)
return compare_to_self(first_package, opts);
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 Red Hat, Inc.
// Copyright (C) 2013-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
+21 -33
View File
@@ -3,7 +3,7 @@
# -*- coding: utf-8 -*-
# -*- Mode: Python
#
# Copyright (C) 2013-2016 Red Hat, Inc.
# Copyright (C) 2013-2023 Red Hat, Inc.
#
# Author: Chenxiong Qi
@@ -20,6 +20,7 @@ import shutil
import six
import subprocess
import sys
import time
from collections import namedtuple
from itertools import chain
@@ -29,6 +30,9 @@ import xdg.BaseDirectory
import rpm
import koji
from timeit import default_timer as timer
from datetime import timedelta
# @file
#
# You might have known that abipkgdiff is a command line tool to compare two
@@ -260,8 +264,12 @@ def log_call(func):
func.__name__,
args if args else '',
kwargs if kwargs else '')
start = timer()
result = func(*args, **kwargs)
logger.debug('Result from %s: %s', func.__name__, result)
end = timer();
elapsed_type = timedelta(seconds=end-start)
logger.debug('Result from %s: %s, in: %s',
func.__name__, result, elapsed_type)
return result
return proxy
@@ -612,26 +620,18 @@ def generate_comparison_halves(rpm_col1, rpm_col2):
debuginfo_list1 = []
debuginfo_list2 = []
# If this is a *devel* package we are looking at, then get all
# the debug info packages associated to with the main package
# and stick them into the resulting comparison half.
if _rpm.is_devel:
debuginfo_list1 = rpm_col1.get_all_debuginfo_rpms(_rpm)
else:
debuginfo_list1.append(rpm_col1.get_matching_debuginfo(_rpm))
# Get all debug info packages associated to with the main
# package and stick them into the resulting comparison half.
debuginfo_list1 = rpm_col1.get_all_debuginfo_rpms(_rpm)
devel1 = rpm_col1.get_sibling_devel(_rpm)
if global_config.self_compare:
debuginfo_list2 = debuginfo_list1
devel2 = devel1
else:
if rpm2.is_devel:
debuginfo_list2 = rpm_col2.get_all_debuginfo_rpms(rpm2)
else:
debuginfo_list2.append(rpm_col2.get_matching_debuginfo(rpm2))
devel2 = rpm_col2.get_sibling_devel(rpm2)
debuginfo_list2 = rpm_col2.get_all_debuginfo_rpms(rpm2)
devel2 = rpm_col2.get_sibling_devel(rpm2)
yield (ComparisonHalf(subject=_rpm,
ancillary_debug=debuginfo_list1,
@@ -659,6 +659,11 @@ class Brew(object):
used to access koji XMLRPC APIs.
"""
self.session = koji.ClientSession(baseurl)
# I am instructing the http client to avoid verifying SSL
# certificates by default as some Koji server instance might
# use self-signed certficates that can't be easily verified.
if (hasattr(self.session, 'opts')):
self.session.opts.setdefault('no_ssl_verify', True)
@log_call
def listRPMs(self, buildID=None, arches=None, selector=None):
@@ -1182,25 +1187,8 @@ def abipkgdiff(cmp_half1, cmp_half2):
proc = subprocess.Popen(' '.join(cmd), shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
# So we could have done: stdout, stderr = proc.communicate()
# But then the documentatin of proc.communicate says:
#
# Note: The data read is buffered in memory, so do not use this
# method if the data size is large or unlimited. "
#
# In practice, we are seeing random cases where this
# proc.communicate() function does *NOT* terminate and seems to be
# in a deadlock state. So we are avoiding it altogether. We are
# then busy looping, waiting for the spawn process to finish, and
# then we get its output.
#
while True:
if proc.poll() != None:
break
stdout = ''.join(proc.stdout.readlines())
stderr = ''.join(proc.stderr.readlines())
stdout, stderr = proc.communicate()
is_ok = proc.returncode == ABIDIFF_OK
is_internal_error = proc.returncode & ABIDIFF_ERROR or proc.returncode & ABIDIFF_USAGE_ERROR
+30 -4
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2022 Red Hat, Inc.
// Copyright (C) 2017-2023 Red Hat, Inc.
//
// Author: Dodji Seketeli
@@ -56,6 +56,7 @@ struct options
bool display_version;
bool verbose;
bool missing_operand;
bool perform_change_categorization;
bool leaf_changes_only;
bool show_hexadecimal_values;
bool show_offsets_sizes_in_bits;
@@ -63,6 +64,9 @@ struct options
optional<bool> exported_interfaces_only;
#ifdef WITH_CTF
bool use_ctf;
#endif
#ifdef WITH_BTF
bool use_btf;
#endif
string wrong_option;
string kernel_dist_root1;
@@ -81,6 +85,7 @@ struct options
display_version(),
verbose(),
missing_operand(),
perform_change_categorization(true),
leaf_changes_only(true),
show_hexadecimal_values(true),
show_offsets_sizes_in_bits(false),
@@ -88,6 +93,10 @@ struct options
#ifdef WITH_CTF
,
use_ctf(false)
#endif
#ifdef WITH_BTF
,
use_btf(false)
#endif
{}
}; // end struct options.
@@ -118,6 +127,11 @@ 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
<< " --no-change-categorization | -x don't perform categorization "
"of changes, for speed purposes\n"
<< " --impacted-interfaces|-i show interfaces impacted by ABI changes\n"
<< " --full-impact|-f show the full impact of changes on top-most "
"interfaces\n"
@@ -260,6 +274,13 @@ 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
else if (!strcmp(argv[i], "--no-change-categorization")
|| !strcmp(argv[i], "-x"))
opts.perform_change_categorization = false;
else if (!strcmp(argv[i], "--impacted-interfaces")
|| !strcmp(argv[i], "-i"))
opts.show_impacted_interfaces = true;
@@ -330,6 +351,7 @@ set_diff_context(diff_context_sptr ctxt, const options& opts)
ctxt->show_linkage_names(false);
ctxt->show_symbols_unreferenced_by_debug_info
(true);
ctxt->perform_change_categorization(opts.perform_change_categorization);
ctxt->show_leaf_changes_only(opts.leaf_changes_only);
ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
ctxt->show_hex_values(opts.show_hexadecimal_values);
@@ -421,11 +443,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())
{