Make abidiff and abidw support several --headers-dir{1, 2} options

Message ID 87d02jqo6y.fsf@redhat.com
State New
Headers
Series Make abidiff and abidw support several --headers-dir{1, 2} options |

Commit Message

Dodji Seketeli Sept. 18, 2020, 12:11 p.m. UTC
  Hello,

When handling a binary with abidiff or abidw it can be useful to
provide several different header files directories, for the cases
where the header files of the binary are scathered in several
different directories.

It thus becomes possible to invoke abidiff like this:

    abidiff --headers-dir1 first-header-dir1   \
	    --headers-dir1 second-header-dir1  \
	    --headers-dir2 first-header-dir2   \
	    --headers-dir2 second-header-dir2  \
	    binary1 binary2

This patch adds support for that.  It also modifies the
tests/test-abidiff-exit.cc test harness to make it take header
directories.  With that modification done, a new test is added in that
harness to exercise this new feature.

This should close the feature request over at
https://sourceware.org/bugzilla/show_bug.cgi?id=26565.

	* doc/manuals/abidiff.rst: Update documentation for the
	--headers-dir{1,2} options.
	* doc/manuals/abidw.rst: Likewise for the --header-dir option.
	* include/abg-tools-utils.h (gen_suppr_spec_from_headers): Add new
	overload that takes a vector of headers root dirs.
	* src/abg-tools-utils.cc (gen_suppr_spec_from_headers_root_dir):
	Define new function.
	(gen_suppr_spec_from_headers): Define a new overload that takes a
	vector of head_root_dir strings; it uses the new
	gen_suppr_spec_from_headers function.  Use the new overload in the
	previous one that takes just one head_root_dir string.
	* tools/abidiff.cc (options::headers_dirs{1,2}): Rename
	option::headers_dir{1,2} into this one and make it be a vector of
	strings rather than just a string.
	(parse_command_line): Support several --headers-dir{1,2} on the
	command line.
	(set_diff_context_from_opts, set_suppressions): Adjust.
	* tools/abidw.cc (options::headers_dirs): Renamed
	options::headers_dir into this and make it be a vector of strings
	rather than just a string.
	(parse_command_line): Support several --headers-dir on the command
	line.
	(set_suppressions): Adjust.
	* tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v{0,1}.h:
	Header files of new binary test input.
	* tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v{0,1}.h:
	Likewise.
	* tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v{0,1}.c:
	Source code of new binary test input.
	* tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-{1,2}.txt:
	Reference output of new binary test input.
	* tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v{0,1}.o:
	New binary test input.
	* tests/data/Makefile.am: Add the new files above to source
	distribution.
	* tests/test-abidiff-exit.cc (InOutSpec::in_elfv{0,1}_path): Add
	new data members.
	(in_out_specs): Adjust the content of this array as its type
	changed.  Also, add two new entries to run the test over the new
	binary test inputs above.
	(do_prefix_strings): Define new static function.
	(main): Use it the new do_prefix_strings here.  Make abidiff
	use the --header-dir{1,2} option whenever header directories are
	specified in an entry of the in_out_specs array.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>

Applied to master.

---
 doc/manuals/abidiff.rst                            |  23 +++-
 doc/manuals/abidw.rst                              |  15 ++-
 include/abg-tools-utils.h                          |   4 +
 src/abg-tools-utils.cc                             |  88 +++++++++++----
 tests/data/Makefile.am                             |  10 ++
 .../test-headers-dirs/headers-a/header-a-v0.h      |  26 +++++
 .../test-headers-dirs/headers-a/header-a-v1.h      |  26 +++++
 .../test-headers-dirs/headers-b/header-b-v0.h      |   8 ++
 .../test-headers-dirs/headers-b/header-b-v1.h      |   9 ++
 .../test-headers-dir-report-1.txt                  |   3 +
 .../test-headers-dir-report-2.txt                  |  13 +++
 .../test-headers-dirs/test-headers-dir-v0.c        |  33 ++++++
 .../test-headers-dirs/test-headers-dir-v0.o        | Bin 0 -> 3840 bytes
 .../test-headers-dirs/test-headers-dir-v1.c        |  36 ++++++
 .../test-headers-dirs/test-headers-dir-v1.o        | Bin 0 -> 3920 bytes
 tests/test-abidiff-exit.cc                         | 125 +++++++++++++++++++--
 tools/abidiff.cc                                   |  28 +++--
 tools/abidw.cc                                     |   6 +-
 18 files changed, 402 insertions(+), 51 deletions(-)
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v0.h
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v1.h
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v0.h
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v1.h
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.c
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.c
 create mode 100644 tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o
  

Patch

diff --git a/doc/manuals/abidiff.rst b/doc/manuals/abidiff.rst
index 6aac73b..74c3ebf 100644
--- a/doc/manuals/abidiff.rst
+++ b/doc/manuals/abidiff.rst
@@ -101,8 +101,18 @@  Options
   * ``--headers-dir1 | --hd1`` <headers-directory-path-1>
 
     Specifies where to find the public headers of the first shared
-    library that the tool has to consider.  The tool will thus filter
-    out ABI changes on types that are not defined in public headers.
+    library (or binary in general) that the tool has to consider.  The
+    tool will thus filter out ABI changes on types that are not
+    defined in public headers.
+
+    Note that several public header directories can be specified for
+    the first shared library.  In that case the ``--headers-dir1``
+    option should be present several times on the command line, like
+    in the following example: ::
+
+      $ abidiff --headers-dir1 /some/path       \
+                --headers-dir1 /some/other/path \
+		binary-version-1 binary-version-2
 
   * ``--header-file1 | --hf1`` <header-file-path-1>
 
@@ -116,6 +126,15 @@  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.
 
+    Note that several public header directories can be specified for
+    the second shared library.  In that case the ``--headers-dir2``
+    option should be present several times like in the following
+    example: ::
+
+      $ abidiff --headers-dir2 /some/path       \
+                --headers-dir2 /some/other/path \
+		binary-version-1 binary-version-2
+
   * ``--header-file2 | --hf2`` <header-file-path-2>
 
     Specifies where to find one public header of the second shared
diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst
index d0720a0..a67e5fa 100644
--- a/doc/manuals/abidw.rst
+++ b/doc/manuals/abidw.rst
@@ -128,9 +128,18 @@  Options
 
   * ``--headers-dir | --hd`` <headers-directory-path-1>
 
-    Specifies where to find the public headers of the first shared
-    library that the tool has to consider.  The tool will thus filter
-    out types that are not defined in public headers.
+    Specifies where to find the public headers of the binary that the
+    tool has to consider.  The tool will thus filter out types that
+    are not defined in public headers.
+
+    Note that several public header directories can be specified for
+    the binary to consider.  In that case the ``--header-dir`` option
+    should be present several times on the command line, like in the
+    following example: ::
+
+      $ abidw --header-dir /some/path       \
+              --header-dir /some/other/path \
+              binary > binary.abi
 
   * ``--header-file | --hf`` <header-file-path>
 
diff --git a/include/abg-tools-utils.h b/include/abg-tools-utils.h
index c47e3f4..12651fc 100644
--- a/include/abg-tools-utils.h
+++ b/include/abg-tools-utils.h
@@ -91,6 +91,10 @@  suppr::type_suppression_sptr
 gen_suppr_spec_from_headers(const string& hdrs_root_dir,
 			    const vector<string>& hdr_files);
 
+suppr::type_suppression_sptr
+gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
+			    const vector<string>& header_files);
+
 suppr::suppressions_type
 gen_suppr_spec_from_kernel_abi_whitelists
    (const vector<string>& abi_whitelist_paths);
diff --git a/src/abg-tools-utils.cc b/src/abg-tools-utils.cc
index f1dc736..bc3d587 100644
--- a/src/abg-tools-utils.cc
+++ b/src/abg-tools-utils.cc
@@ -1842,12 +1842,44 @@  handle_fts_entry(const FTSENT *entry,
     }
 }
 
+/// Populate a type_supression from header files found in a given
+/// directory tree.
+///
+/// The suppression suppresses types defined in source files that are
+/// *NOT* found in the directory tree.
+///
+/// This is a subroutine for gen_suppr_spect_from_headers.
+///
+/// @param headers_root_dir the directory tree to consider for header
+/// files.
+///
+/// @param result the type_supression to populate from the content of
+/// @p headers_root_dir.
+static void
+gen_suppr_spec_from_headers_root_dir(const string& headers_root_dir,
+				     type_suppression_sptr &result)
+{
+  if (!headers_root_dir.empty())
+    {
+      char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
+
+      if (FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL))
+	{
+	  FTSENT *entry;
+	  while ((entry = fts_read(file_hierarchy)))
+	    handle_fts_entry(entry, result);
+	  fts_close(file_hierarchy);
+	}
+    }
+}
+
 /// Generate a type suppression specification that suppresses ABI
 /// changes for types defined in source files that are neither in a
-/// given header root dir, not in a set of header files.
+/// given set of header root directories nor in a set of header
+/// files.
 ///
-/// @param headers_root_dir ABI changes in types defined in files
-/// *NOT* found in this directory tree are going be suppressed.
+/// @param headers_root_dirs ABI changes in types defined in files
+/// *NOT* found in these directory trees are going be suppressed.
 ///
 /// @param header_files a set of additional header files that define
 /// types that are to be kept (not supressed) by the returned type
@@ -1856,29 +1888,15 @@  handle_fts_entry(const FTSENT *entry,
 /// @return the resulting type suppression generated, if any file was
 /// found in the directory tree @p headers_root_dir.
 type_suppression_sptr
-gen_suppr_spec_from_headers(const string& headers_root_dir,
+gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
 			    const vector<string>& header_files)
 {
   type_suppression_sptr result;
 
-  if (headers_root_dir.empty() && header_files.empty())
-    // We were given no headers root dir and no header files
-    // so the resulting suppression specification shall be empty.
-    return result;
-
-  if (!headers_root_dir.empty())
-    {
-      char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
-
-      FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
-      if (!file_hierarchy)
-	return result;
-
-      FTSENT *entry;
-      while ((entry = fts_read(file_hierarchy)))
-	handle_fts_entry(entry, result);
-      fts_close(file_hierarchy);
-    }
+  for (vector<string>::const_iterator root_dir = headers_root_dirs.begin();
+       root_dir != headers_root_dirs.end();
+       ++root_dir)
+    gen_suppr_spec_from_headers_root_dir(*root_dir, result);
 
   for (vector<string>::const_iterator file = header_files.begin();
        file != header_files.end();
@@ -1889,6 +1907,32 @@  gen_suppr_spec_from_headers(const string& headers_root_dir,
 }
 
 /// Generate a type suppression specification that suppresses ABI
+/// changes for types defined in source files that are neither in a
+/// given header root dir, not in a set of header files.
+///
+/// @param headers_root_dir ABI changes in types defined in files
+/// *NOT* found in this directory tree are going be suppressed.
+///
+/// @param header_files a set of additional header files that define
+/// types that are to be kept (not supressed) by the returned type
+/// suppression.
+///
+/// @return the resulting type suppression generated, if any file was
+/// found in the directory tree @p headers_root_dir.
+type_suppression_sptr
+gen_suppr_spec_from_headers(const string& headers_root_dir,
+			    const vector<string>& header_files)
+{
+  type_suppression_sptr result;
+  vector<string> root_dirs;
+
+  if (!headers_root_dir.empty())
+    root_dirs.push_back(headers_root_dir);
+
+  return gen_suppr_spec_from_headers(root_dirs, header_files);
+}
+
+/// Generate a type suppression specification that suppresses ABI
 /// changes for types defined in source files that are not in a given
 /// header root dir.
 ///
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 7334db2..8712b0c 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -174,6 +174,16 @@  test-abidiff-exit/test-net-change-report0.txt \
 test-abidiff-exit/test-net-change-report1.txt \
 test-abidiff-exit/test-net-change-report2.txt \
 test-abidiff-exit/test-net-change-report3.txt \
+test-abidiff-exit/test-headers-dirs/headers-a/header-a-v0.h \
+test-abidiff-exit/test-headers-dirs/headers-a/header-a-v1.h \
+test-abidiff-exit/test-headers-dirs/headers-b/header-b-v0.h \
+test-abidiff-exit/test-headers-dirs/headers-b/header-b-v1.h \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt  \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.c \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.c \
+test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o \
 \
 test-diff-dwarf/test0-v0.cc		\
 test-diff-dwarf/test0-v0.o			\
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v0.h b/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v0.h
new file mode 100644
index 0000000..49f956f
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v0.h
@@ -0,0 +1,26 @@ 
+typedef struct opaque_struct * opaque_struct_pointer_type;
+
+typedef struct public_struct_type *public_struct_pointer_type;
+typedef struct public_struct_type2 *public_struct_pointer_type2;
+
+typedef void (*FuncPointerType0) (public_struct_pointer_type,
+				  public_struct_pointer_type);
+
+typedef void (*FuncPointerType1) (public_struct_pointer_type, int);
+
+typedef struct public_struct_type2
+{
+  FuncPointerType0 m0;
+  FuncPointerType1 m1;
+} public_struct_type2;
+
+typedef struct public_struct_type
+{
+  opaque_struct_pointer_type m0;
+  public_struct_type2 *m1;
+} public_struct_type;
+
+void foo(public_struct_pointer_type p1);
+
+void bar(second_public_struct_pointer_type p1,
+	 second_opaque_struct_pointer_type p2);
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v1.h b/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v1.h
new file mode 100644
index 0000000..49f956f
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/headers-a/header-a-v1.h
@@ -0,0 +1,26 @@ 
+typedef struct opaque_struct * opaque_struct_pointer_type;
+
+typedef struct public_struct_type *public_struct_pointer_type;
+typedef struct public_struct_type2 *public_struct_pointer_type2;
+
+typedef void (*FuncPointerType0) (public_struct_pointer_type,
+				  public_struct_pointer_type);
+
+typedef void (*FuncPointerType1) (public_struct_pointer_type, int);
+
+typedef struct public_struct_type2
+{
+  FuncPointerType0 m0;
+  FuncPointerType1 m1;
+} public_struct_type2;
+
+typedef struct public_struct_type
+{
+  opaque_struct_pointer_type m0;
+  public_struct_type2 *m1;
+} public_struct_type;
+
+void foo(public_struct_pointer_type p1);
+
+void bar(second_public_struct_pointer_type p1,
+	 second_opaque_struct_pointer_type p2);
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v0.h b/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v0.h
new file mode 100644
index 0000000..1fe1b37
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v0.h
@@ -0,0 +1,8 @@ 
+typedef struct second_opaque_struct *second_opaque_struct_pointer_type;
+
+typedef struct second_public_struct * second_public_struct_pointer_type;
+
+struct second_public_struct
+{
+  int m1;
+};
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v1.h b/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v1.h
new file mode 100644
index 0000000..cd7802d
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/headers-b/header-b-v1.h
@@ -0,0 +1,9 @@ 
+typedef struct second_opaque_struct *second_opaque_struct_pointer_type;
+
+typedef struct second_public_struct * second_public_struct_pointer_type;
+
+struct second_public_struct
+{
+  int m1;
+  char m2;
+};
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt
new file mode 100644
index 0000000..41fc64d
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt
@@ -0,0 +1,3 @@ 
+Functions changes summary: 0 Removed, 0 Changed (2 filtered out), 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt
new file mode 100644
index 0000000..75d5a61
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt
@@ -0,0 +1,13 @@ 
+Functions changes summary: 0 Removed, 1 Changed (1 filtered out), 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function void bar(second_public_struct_pointer_type, second_opaque_struct_pointer_type)' at test-headers-dir-v1.c:33:1 has some indirect sub-type changes:
+    parameter 1 of type 'typedef second_public_struct_pointer_type' has sub-type changes:
+      underlying type 'second_public_struct*' changed:
+        in pointed to type 'struct second_public_struct' at header-b-v1.h:5:1:
+          type size changed from 32 to 64 (in bits)
+          1 data member insertion:
+            'char second_public_struct::m2', at offset 32 (in bits) at header-b-v1.h:8:1
+
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.c b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.c
new file mode 100644
index 0000000..0784f53
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.c
@@ -0,0 +1,33 @@ 
+/*
+ * Compile this test with:
+ *   gcc -c -g test-headers-dir-v0.c
+ *
+ *   This test exhibits changes that are redundant in a weird way.
+ *   The redundant path through the diff graph involves typedefs,
+ *   function types and function parameter diff nodes.
+ *
+ */
+#include "headers-b/header-b-v0.h"
+#include "headers-a/header-a-v0.h"
+
+struct opaque_struct
+{
+  int m0;
+  int m1;
+  struct public_struct_type *m2;
+};
+
+struct second_opaque_struct
+{
+  int m0;
+};
+
+void foo(public_struct_pointer_type p __attribute__((unused)))
+{
+}
+
+void
+bar(second_public_struct_pointer_type p1 __attribute__((unused)),
+    second_opaque_struct_pointer_type p2 __attribute__((unused)))
+{
+}
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..477198bc15a428381a62586991281d6a87c269dc
GIT binary patch
literal 3840
zcmbtWU1%It6ux(FHko9a&8A6fLu$K%ng%+vo1_h~nsjYwo1&CZtcnn~GrN=B(fxCF
zHl+R)R4RQD3qo5cSkPC&N5L1N=tCdG2hmqweAgGz`k?3Bxo10jGohdt_TF>8^PQi2
z=gyhEa{Bo*Db5)U&Q{n1jZnslBQaTuMK;0qvWsiieqFov&Yv51Nm{$s{cYp!JqR)&
zd4>$PBj$kz@mLDw6kmc_7%MPK&jCL9BFO1Rf${xUKmv}OgaeB(sxU{yR2t3c#vDrx
zMa02@wBHOPzTnp-{|521eDqt%hrnZ!kFGq6;y}7F&-nNcAR5YDpdyT)&<a1672|)w
zf^~_BtB9bxnDk+f22sp|ph?OoN)aWqEGcpqVsHi&VOTH^NgQi3a8h}P*CgRQ5?+N#
z+6-B56k!a@5C+<YH(M>nr_dP(JuH6v78pm`V8Xzu>osP&PQdsfNrNe)1b%y@4QFr&
zPaJ3?Z8(L)mQ*e=I1v6sX2KMi>C8CaJCuC{Nc)+{oG_m@&rLuPW6YGozyuc=(?pyD
zo*sqzBGQt{*x=(pTQP+JJKD!*rjTN!8Acek`N^q>J2QnjiU)3-l7x|TFGdP3jhRS<
zLXTpx@WY1?AMuM8$KM4d#ax&<|2s1ZvwRKUym-xXDqhgBTt<?^Sm<>^i}Z>gSX%|V
zi~%FeauKNE%cB4%0%T3YQDwjuQ~dpfuTv?=m#?)NUcS<*yyoYteweTOuH*Vu$FJub
z-Fh!s9y53H6(@A0XaT9>Z*E%NHaO{Jb=cYS7jvt*WA>7LJU3sMJ6>3vU&tM~;8k*K
zP8i|V!c((3s}Xja-g4DzdVyaC)d|YA<?W?KYw=jls<Kwwd86x<I$_W)hpgRo>wZ~j
zrSNjwOMvEC$1As*l~SK?sojE?(F-E4BnCUvZI;&~)(a52paXLhxUUSYg1$8RxkQia
z;dS*T=&zieU0pqqI|AoB`|#t%n*n1oS{Q%x0^{3RKD}qi_!#cULBR2cL%yxgX0lgP
ztD}RbfdClVo<SoF`HqtPTx5?dJ7)+DE$H`uc3R--C3beid$+UCO@Jk7h^Wc!FbOue
z14BTtp<VFdU2y0X2}7{a1Qg>_7wb0*wx=He4LTQmweXapL7j3MRK!W|MNGa&*lw|r
z_I8YZ@!eBq#BrA|!4JoHw8%V&Z>UoCX}D4uwREJ#pVDlMAMbZ|49ECCD*dE}|DklM
zi|YJH)l<{>_?g1-VYKw>?<oAHhJUK`?=<|B;{UASUnu>)hJT~<N0lGdX)1k1!{x^o
zd`cQlakdnWOZ+_VXgKD<LC+NRv_zsQt7JV+YZpAHaLgIcThwsM%k0kOM(DV(hC#H}
zND95}klFY>V7A-oFgx(-j$QHGZnfk$H(Ownvg5jex7BBM0;gH^`d#XN(<2v1!R&IY
z(eU8klih5Eo(=8J>V!`Dbrr~~l{N#X;i<y^=YsDYo*|txg)1sS|9Y#(L^SpoNQmj3
z>g67zU(o$$z>M$DfAyaOUp*#fr2OA&MopFfTJT?G5uIYAs9NSP$^q{i^5|Wsb6EyV
zkBMQPRF~{?RANKtu7gd-kbY137h=9B>fGl5OR7JI%?AegWBs@#z5WVddQ5=WeU<+`
zRANKtmcW*je_iFLbHe-x>G|7$VN9wML^<A87vf7TA*r;!1?(jMqpDd@KUQITdnNx5
zlt0;NeI?#(2u$GKQkXif9`lm&r+Y{S+CS-^fRJ>4wS?z~-$e4KbH#n=xiR)vDt}FF
o6u*1;_166{jsETd#B%EXA4VlM=-ddxxJmvuRsEk4QLVcFzx`ZyR{#J2

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.c b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.c
new file mode 100644
index 0000000..a600757
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.c
@@ -0,0 +1,36 @@ 
+/*
+ * Compile this test with:
+ *   gcc -c -g test-headers-dir-v1.c
+ *
+ *   This test exhibits changes that are redundant in a weird way.
+ *   The redundant path through the diff graph involves typedefs,
+ *   function types and function parameter diff nodes.
+ *
+ */
+
+#include "headers-b/header-b-v1.h"
+#include "headers-a/header-a-v1.h"
+
+struct opaque_struct
+{
+  int m0;
+  int m1;
+  struct public_struct_type *m2;
+  char m3;
+};
+
+struct second_opaque_struct
+{
+  int m0;
+  char m1;
+};
+
+void foo(public_struct_pointer_type p __attribute__((unused)))
+{
+}
+
+void
+bar(second_public_struct_pointer_type p1 __attribute__((unused)),
+    second_opaque_struct_pointer_type p2 __attribute__((unused)))
+{
+}
diff --git a/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o b/tests/data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..a7978e2e78677acdde0a47d75735ce8359661483
GIT binary patch
literal 3920
zcmbtW-D@0G6hC)wHkovi?Ivk!LJMw+(iWWAe6%H2+eTyC6s4A8F)wAE*_~up_siMY
zk}4{wRQe*VAE;1NsIS@=3xY3|zW4|DF9^OU)rS%gKhC*x?q>F8s-PG4-gAEEcRudt
z>>DSZIhhdxbQ5q4HZ_d`T-~-QFDh{nCSV-SFJJj_`O0e>7w%|j`AYZhg*(4tkO9TB
zRJbiMCpY6khUAP`z+PI%uveaz*yW*|ZOnj}{17D+XF*I|SA^I{)=`mGs37dDOqQ5L
z%nY&59AvE6XK)}}Yeq0629XnImEtQDrS&!TD(j@k{fx58n%~SiM&dxWF$ZFN14S#_
zm<KUIaq}EW5-zPjiHJYQ%du&kHbs&XHwdA!<c@0yTAE@%r75E%#gts2C~Asw@L5!(
zb)F!?hp9Z(ao*v!B3vZG<sLzm9!J<tWk_Y)w%Tfe*h9ro)0--;e}@#ijV6koxmJY{
z-wQ!ZD;jn=7Q}VCjhis-#WcH(3$oWy$_oHuOgx;Muw`y{ZcL01<sZOUyFliS*pJz#
zC$Na^U@Ku@LdcwL6D|<Xj$(Z=X~6~<d<bdBY-s@uv55Jtonqe<v4^uj!M3^L_*Cq+
zZ;H|_PJZ;HBG}npxD1}NkyIkq{0P;JKiZFEL!Z?4I-(iyu@~a^+$i?y=HSwaYT#9Z
zu;cikC0wB>=tPdzE49$MIO~=va*R1X0adzh40ti1Y8s9!L$=%_-kSe5lfit&YO4_x
zE3L|lwc=VWD%NYh=hxP}TD{ol)_ci{l(|!^c#)?>2T7IM>Z%j0qmx-y2Tq+iUsx&}
zbQj!1g}Is8Lo<ix<_r7J1(m|G7sa?U|LB2&(}=pw;OJV=48mF&RWB@8kFGBqb`Bpb
zIBU>qdoOo`QYQ+#<p|nczg{b|R*EjQgA`~EIzhSBtd#nEOYIik)F6z#%!1=}O!I@2
z-Ddf0?E4%hoH2n}ovyDqqprT>|4>)Cig$#|=!@51KAc)wI#Sq=i+teU*O-n6wjJ>R
z;+1nC*7IWb$dGjv4+>pfeSOvKeIl2?oLL$jJb?tZJ<h)B&T9MV*v_Uc1BS*Y>DRxu
z7!Z)DUj)K?N3_pPq9v`#_|e;9lAzw>6ntn4Mu3Fj`{4L?q%;YmDJaF!uUfxZ!bm@W
z8op%swWC#vn|?WWqe{5edlA#mEIq&Uu=Zw5e#zPE%!Jb{pT{4yv3O8<2*1brP6KBZ
zrdXhgle=^v!B0MVZUU$HKd^q>z(;rqd;@=<@s5EnG5(o>zsvX+2Hs)(mVs;kUkrSO
z`TF4Mb^DTed)cn>+l-$uaQZ}2TQ%@G*55Jk+lmeFDdRLG*ZF$`r~K6PUXz|xNZjRF
zHsiGJgBKX5oXNb42Cnmh+qu+;JRip>jK`{$qF_A&mp&HY`kfBAVNmznO5k_bO10){
z3vEjFd_N2>_L;rVYpw<TF7;Y7&@Pw)+;XeY2=ITG+iXRFi_f6biM;Yl94M%kRzt55
zaN+-Rp!-N`sMlGCTV%ofwDUG;8cd@^nE8UO^ceku>Aw%nbg%xa|1A2NF)5?Yf6p))
zs`^Vq|2`MVDbd#XDcxWPI&Z|&Z-8ExqllR?Da;u@WH(7kG?RN4Z6-$aKiGdJ;Y*^)
zeHyW}`rGs<VblIpKTXN3zk-+<6CrSTL*67M(M)a$ZE5*G=lpt|C_h1F{x)J1Q`d>2
zYBxFmEhC{;_4q2X)BG#^Sm<|}!}jK?{oiJPZP(*_$;U>6DcnyO<L&A(KW2Y@4z)qg
zU+W*BkhXr`r0gGkE49B~SDJ^Jn_}PP{8gSPeGloYZu(Oi^X?%8D}4SRBqh=Gx@m~w
QruqNG_21A$9!>wh0TY6SUH||9

literal 0
HcmV?d00001

diff --git a/tests/test-abidiff-exit.cc b/tests/test-abidiff-exit.cc
index 936d1d6..6ebe2a0 100644
--- a/tests/test-abidiff-exit.cc
+++ b/tests/test-abidiff-exit.cc
@@ -34,6 +34,7 @@ 
 #include <sys/wait.h>
 #include <cstring>
 #include <string>
+#include <vector>
 #include <fstream>
 #include <iostream>
 #include <cstdlib>
@@ -47,6 +48,8 @@  struct InOutSpec
   const char*	in_elfv0_path;
   const char*	in_elfv1_path;
   const char*	in_suppr_path;
+  const char*   in_elfv0_headers_dirs;
+  const char*   in_elfv1_headers_dirs;
   const char*	abidiff_options;
   abidiff_status status;
   const char*	in_report_path;
@@ -59,6 +62,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test1-voffset-change-v0.o",
     "data/test-abidiff-exit/test1-voffset-change-v1.o",
     "",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -69,6 +74,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test1-voffset-change-v0.o",
     "data/test-abidiff-exit/test1-voffset-change-v1.o",
     "data/test-abidiff-exit/test1-voffset-change.abignore",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test1-voffset-change-report1.txt",
@@ -78,6 +85,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test2-filtered-removed-fns-v0.o",
     "data/test-abidiff-exit/test2-filtered-removed-fns-v1.o",
     "",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -88,6 +97,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test2-filtered-removed-fns-v0.o",
     "data/test-abidiff-exit/test2-filtered-removed-fns-v1.o",
     "data/test-abidiff-exit/test2-filtered-removed-fns.abignore",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test2-filtered-removed-fns-report1.txt",
@@ -98,6 +109,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-loc-v1.bi",
     "",
     "",
+    "",
+    "",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-loc-with-locs-report.txt",
     "output/test-abidiff-exit/test-loc-with-locs-report.txt"
@@ -106,6 +119,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-loc-v0.bi",
     "data/test-abidiff-exit/test-loc-v1.bi",
     "",
+    "",
+    "",
     "--no-show-locs",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-loc-without-locs-report.txt",
@@ -115,6 +130,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-no-stray-comma-v0.o",
     "data/test-abidiff-exit/test-no-stray-comma-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-no-stray-comma-report.txt",
@@ -124,6 +141,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-stats-v0.o",
     "data/test-abidiff-exit/test-leaf-stats-v1.o",
     "",
+    "",
+    "",
     "--no-show-locs --leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-leaf-stats-report.txt",
@@ -133,6 +152,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-more-v0.o",
     "data/test-abidiff-exit/test-leaf-more-v1.o",
     "",
+    "",
+    "",
     "--no-show-locs --leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -143,6 +164,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-fun-type-v0.o",
     "data/test-abidiff-exit/test-leaf-fun-type-v1.o",
     "",
+    "",
+    "",
     "--no-show-locs --leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-leaf-fun-type-report.txt",
@@ -152,6 +175,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-redundant-v0.o",
     "data/test-abidiff-exit/test-leaf-redundant-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-leaf-redundant-report.txt",
@@ -161,6 +186,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-peeling-v0.o",
     "data/test-abidiff-exit/test-leaf-peeling-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-leaf-peeling-report.txt",
@@ -170,6 +197,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf-cxx-members-v0.o",
     "data/test-abidiff-exit/test-leaf-cxx-members-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -181,6 +210,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-member-size-v1.o",
     "",
     "",
+    "",
+    "",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-member-size-report0.txt",
     "output/test-abidiff-exit/test-member-size-report0.txt"
@@ -189,6 +220,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-member-size-v0.o",
     "data/test-abidiff-exit/test-member-size-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-member-size-report1.txt",
@@ -198,6 +231,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-decl-struct-v0.o",
     "data/test-abidiff-exit/test-decl-struct-v1.o",
     "",
+    "",
+    "",
     "--harmless",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-decl-struct-report.txt",
@@ -208,6 +243,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-fun-param-v1.abi",
     "",
     "",
+    "",
+    "",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-fun-param-report.txt",
     "output/test-abidiff-exit/test-fun-param-report.txt"
@@ -216,6 +253,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-decl-enum-v0.o",
     "data/test-abidiff-exit/test-decl-enum-v1.o",
     "",
+    "",
+    "",
     "--harmless",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE,
     "data/test-abidiff-exit/test-decl-enum-report.txt",
@@ -226,6 +265,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-decl-enum-v1.o",
     "",
     "",
+    "",
+    "",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test-decl-enum-report-2.txt",
     "output/test-abidiff-exit/test-decl-enum-report-2.txt"
@@ -234,6 +275,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-decl-enum-v0.o",
     "data/test-abidiff-exit/test-decl-enum-v1.o",
     "",
+    "",
+    "",
     "--leaf-changes-only",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test-decl-enum-report-3.txt",
@@ -243,6 +286,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-net-change-v0.o",
     "data/test-abidiff-exit/test-net-change-v1.o",
     "",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -253,6 +298,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-net-change-v0.o",
     "data/test-abidiff-exit/test-net-change-v1.o",
     "data/test-abidiff-exit/test-net-change.abignore",
+    "",
+    "",
     "--no-default-suppression --no-show-locs",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test-net-change-report1.txt",
@@ -262,6 +309,8 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-net-change-v0.o",
     "data/test-abidiff-exit/test-net-change-v1.o",
     "",
+    "",
+    "",
     "--no-default-suppression --no-show-locs --leaf-changes-only",
     abigail::tools_utils::ABIDIFF_ABI_CHANGE
     | abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE,
@@ -272,44 +321,90 @@  InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-net-change-v0.o",
     "data/test-abidiff-exit/test-net-change-v1.o",
     "data/test-abidiff-exit/test-net-change.abignore",
+    "",
+    "",
     "--no-default-suppression --no-show-locs --leaf-changes-only",
     abigail::tools_utils::ABIDIFF_OK,
     "data/test-abidiff-exit/test-net-change-report3.txt",
     "output/test-abidiff-exit/test-net-change-report3.txt"
   },
-  {0, 0, 0 ,0,  abigail::tools_utils::ABIDIFF_OK, 0, 0}
+  {
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o",
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o",
+    "",
+    "data/test-abidiff-exit/test-headers-dirs/headers-a",
+    "data/test-abidiff-exit/test-headers-dirs/headers-a",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_OK,
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt",
+    "output/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v0.o",
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-v1.o",
+    "",
+    "data/test-abidiff-exit/test-headers-dirs/headers-a, "
+    "data/test-abidiff-exit/test-headers-dirs/headers-b",
+    "data/test-abidiff-exit/test-headers-dirs/headers-a, "
+    "data/test-abidiff-exit/test-headers-dirs/headers-b",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt",
+    "output/test-abidiff-exit/test-headers-dirs/test-headers-dir-report-2.txt"
+  },
+  {0, 0, 0 ,0, 0, 0, abigail::tools_utils::ABIDIFF_OK, 0, 0}
 };
 
+/// Prefix the strings in a vector of string.
+///
+/// @param strings the strings to prefix.
+///
+/// @param prefix the prefix to use.
+static void
+do_prefix_strings(std::vector<std::string> &strings,
+		  const std::string& prefix)
+{
+  for (std::vector<std::string>::size_type i = 0; i < strings.size(); ++i)
+    strings[i] = prefix + strings[i];
+}
+
 int
 main()
 {
   using std::string;
+  using std::vector;
   using std::cerr;
   using abigail::tests::get_src_dir;
   using abigail::tests::get_build_dir;
   using abigail::tools_utils::ensure_parent_dir_created;
+  using abigail::tools_utils::split_string;
   using abigail::tools_utils::abidiff_status;
 
   bool is_ok = true;
   string in_elfv0_path, in_elfv1_path,
     in_suppression_path, abidiff_options, abidiff, cmd,
     ref_diff_report_path, out_diff_report_path;
+  vector<string> in_elfv0_headers_dirs, in_elfv1_headers_dirs;
+  string source_dir_prefix = string(get_src_dir()) + "/tests/";
+  string build_dir_prefix = string(get_build_dir()) + "/tests/";
 
     for (InOutSpec* s = in_out_specs; s->in_elfv0_path; ++s)
       {
-	in_elfv0_path = string(get_src_dir()) + "/tests/" + s->in_elfv0_path;
-	in_elfv1_path = string(get_src_dir()) + "/tests/" + s->in_elfv1_path;
+	in_elfv0_path = source_dir_prefix + s->in_elfv0_path;
+	in_elfv1_path = source_dir_prefix + s->in_elfv1_path;
+	split_string(s->in_elfv0_headers_dirs, ",", in_elfv0_headers_dirs);
+	split_string(s->in_elfv1_headers_dirs, ",", in_elfv1_headers_dirs);
+	do_prefix_strings(in_elfv0_headers_dirs, source_dir_prefix);
+	do_prefix_strings(in_elfv1_headers_dirs, source_dir_prefix);
+
 	if (s->in_suppr_path && strcmp(s->in_suppr_path, ""))
-	  in_suppression_path =
-	    string(get_src_dir()) + "/tests/" + s->in_suppr_path;
+	  in_suppression_path = source_dir_prefix + s->in_suppr_path;
 	else
 	  in_suppression_path.clear();
 
 	abidiff_options = s->abidiff_options;
-	ref_diff_report_path =
-	  string(get_src_dir()) + "/tests/" + s->in_report_path;
-	out_diff_report_path =
-	  string(get_build_dir()) + "/tests/" + s->out_report_path;
+	ref_diff_report_path = source_dir_prefix + s->in_report_path;
+	out_diff_report_path = build_dir_prefix + s->out_report_path;
 
 	if (!ensure_parent_dir_created(out_diff_report_path))
 	  {
@@ -323,6 +418,18 @@  main()
 	if (!abidiff_options.empty())
 	  abidiff += " " + abidiff_options;
 
+	if (!in_elfv0_headers_dirs.empty())
+	  for (vector<string>::const_iterator s = in_elfv0_headers_dirs.begin();
+	       s != in_elfv0_headers_dirs.end();
+	       ++s)
+	    abidiff += " --headers-dir1 " + *s;
+
+	if (!in_elfv1_headers_dirs.empty())
+	  for (vector<string>::const_iterator s = in_elfv1_headers_dirs.begin();
+	       s != in_elfv1_headers_dirs.end();
+	       ++s)
+	    abidiff += " --headers-dir2 " + *s;
+
 	if (!in_suppression_path.empty())
 	  abidiff += " --suppressions " + in_suppression_path;
 
diff --git a/tools/abidiff.cc b/tools/abidiff.cc
index 162d5eb..6ac81ee 100644
--- a/tools/abidiff.cc
+++ b/tools/abidiff.cc
@@ -78,9 +78,9 @@  struct options
   vector<string>	drop_var_regex_patterns;
   vector<string>	keep_fn_regex_patterns;
   vector<string>	keep_var_regex_patterns;
-  string		headers_dir1;
+  vector<string>	headers_dirs1;
   vector<string>        header_files1;
-  string		headers_dir2;
+  vector<string>	headers_dirs2;
   vector<string>        header_files2;
   bool			drop_private_types;
   bool			linux_kernel_mode;
@@ -318,7 +318,9 @@  parse_command_line(int argc, char* argv[], options& opts)
 	      opts.wrong_option = argv[i];
 	      return true;
 	    }
-	  opts.headers_dir1 = argv[j];
+	  // The user can specify several header files directories for
+	  // the first binary.
+	  opts.headers_dirs1.push_back(argv[j]);
 	  ++i;
 	}
       else if (!strcmp(argv[i], "--header-file1")
@@ -344,7 +346,9 @@  parse_command_line(int argc, char* argv[], options& opts)
 	      opts.wrong_option = argv[i];
 	      return true;
 	    }
-	  opts.headers_dir2 = argv[j];
+	  // The user can specify several header files directories for
+	  // the first binary.
+	  opts.headers_dirs2.push_back(argv[j]);
 	  ++i;
 	}
       else if (!strcmp(argv[i], "--header-file2")
@@ -720,22 +724,22 @@  set_diff_context_from_opts(diff_context_sptr ctxt,
       load_default_user_suppressions(supprs);
     }
 
-  if (!opts.headers_dir1.empty() || !opts.header_files1.empty())
+  if (!opts.headers_dirs1.empty() || !opts.header_files1.empty())
     {
       // Generate suppression specification to avoid showing ABI
       // changes on types that are not defined in public headers.
       suppression_sptr suppr =
-	gen_suppr_spec_from_headers(opts.headers_dir1, opts.header_files1);
+	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
       if (suppr)
 	ctxt->add_suppression(suppr);
     }
 
-  if (!opts.headers_dir2.empty() || !opts.header_files2.empty())
+  if (!opts.headers_dirs2.empty() || !opts.header_files2.empty())
     {
       // Generate suppression specification to avoid showing ABI
       // changes on types that are not defined in public headers.
       suppression_sptr suppr =
-	gen_suppr_spec_from_headers(opts.headers_dir2, opts.header_files2);
+	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
       if (suppr)
 	ctxt->add_suppression(suppr);
     }
@@ -770,7 +774,7 @@  set_suppressions(ReadContextType& read_ctxt, const options& opts)
     read_suppressions(*i, supprs);
 
   if (read_context_get_path(read_ctxt) == opts.file1
-      && (!opts.headers_dir1.empty() || !opts.header_files1.empty()))
+      && (!opts.headers_dirs1.empty() || !opts.header_files1.empty()))
     {
       // Generate suppression specification to avoid showing ABI
       // changes on types that are not defined in public headers for
@@ -780,7 +784,7 @@  set_suppressions(ReadContextType& read_ctxt, const options& opts)
       // corpus loading, they are going to be dropped from the
       // internal representation altogether.
       suppression_sptr suppr =
-	gen_suppr_spec_from_headers(opts.headers_dir1, opts.header_files1);
+	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
       if (suppr)
 	{
 	  if (opts.drop_private_types)
@@ -790,7 +794,7 @@  set_suppressions(ReadContextType& read_ctxt, const options& opts)
     }
 
   if (read_context_get_path(read_ctxt) == opts.file2
-      && (!opts.headers_dir2.empty() || !opts.header_files2.empty()))
+      && (!opts.headers_dirs2.empty() || !opts.header_files2.empty()))
     {
       // Generate suppression specification to avoid showing ABI
       // changes on types that are not defined in public headers for
@@ -800,7 +804,7 @@  set_suppressions(ReadContextType& read_ctxt, const options& opts)
       // corpus loading, they are going to be dropped from the
       // internal representation altogether.
       suppression_sptr suppr =
-	gen_suppr_spec_from_headers(opts.headers_dir2, opts.header_files2);
+	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
       if (suppr)
 	{
 	  if (opts.drop_private_types)
diff --git a/tools/abidw.cc b/tools/abidw.cc
index 27a9e48..756a29e 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -90,7 +90,7 @@  struct options
   string		out_file_path;
   vector<char*>	di_root_paths;
   vector<char**>	prepared_di_root_paths;
-  string		headers_dir;
+  vector<string>	headers_dirs;
   vector<string>	header_files;
   string		vmlinux;
   vector<string>	suppression_paths;
@@ -239,7 +239,7 @@  parse_command_line(int argc, char* argv[], options& opts)
 	  int j = i + 1;
 	  if (j >= argc)
 	    return false;
-	  opts.headers_dir = argv[j];
+	  opts.headers_dirs.push_back(argv[j]);
 	  ++i;
 	}
       else if (!strcmp(argv[i], "--header-file")
@@ -445,7 +445,7 @@  set_suppressions(read_context& read_ctxt, options& opts)
     read_suppressions(*i, supprs);
 
   suppression_sptr suppr =
-    abigail::tools_utils::gen_suppr_spec_from_headers(opts.headers_dir,
+    abigail::tools_utils::gen_suppr_spec_from_headers(opts.headers_dirs,
 						      opts.header_files);
   if (suppr)
     {