From ad4959f59f9a21522b0956226d7d7cffe2f63556 Mon Sep 17 00:00:00 2001
From: Sinny Kumari <sinny@redhat.com>
Date: Wed, 20 Apr 2016 00:01:56 +0530
Subject: [PATCH] Distinguish between PI executable and shared library

Both Position Independent Executable (aka PIE) and Shared
library are of type Dynamic Shared Object(DSO). Currently,
libabigail treats both as of type DSO. When we run
abipkgdiff with --dso-only option, ABI changes are looked up
in both PIEs and shared libraries. But, --dso-only option is intended
to run only on Shared libraries.
Bugzilla - https://sourceware.org/bugzilla/show_bug.cgi?id=19961

With this patch, we introduce a new type called ELF_TYPE_PI_EXEC
for PIE files. A file will be of type ELF_TYPE_DSO iff
is a shared library.

	* include/abg-dwarf-reader.h (elf_type): ELF_TYPE_PI_EXEC
	gets added in enum elf_type
	* src/abg-dwarf-reader.cc
	(lookup_data_tag_from_dynamic_segment): New function for
	data tag lookup in dynamic segment of an elf
	(elf_file_type): Returns ELF_TYPE_PI_EXEC file type for
	a PI executable
	(get_elf_file_type): Needs only elf handle
	(load_dt_soname_and_needed): code re-factorized and moved
	to lookup_data_tag_from_dynamic_segment().
	(get_type_of_elf_file): function call elf_file_type() takes
	elf handle now.
	* tests/test-diff-pkg.cc (create_maps_of_package_content):
	Also look for ELF_TYPE_PI_EXEC file type
	(compare): Also look for ELF_TYPE_PI_EXEC file type
	* tests/test-diff-pkg.cc (in_out_specs): Test case addition
	* tests/data/Makefile.am: Include test files
	* tests/data/test-diff-pkg/tarpkg-1-dir1.tar.gz: New test data
	* tests/data/test-diff-pkg/tarpkg-1-dir2.tar.gz: New test data
	* tests/data/test-diff-pkg/tarpkg-1-report-0.txt: New test result

Signed-off-by: Sinny Kumari <sinny@redhat.com>
---
 include/abg-dwarf-reader.h                     |   1 +
 src/abg-dwarf-reader.cc                        | 201 ++++++++++++++-----------
 tests/data/Makefile.am                         |   5 +-
 tests/data/test-diff-pkg/tarpkg-1-dir1.tar.gz  | Bin 0 -> 4788 bytes
 tests/data/test-diff-pkg/tarpkg-1-dir2.tar.gz  | Bin 0 -> 4835 bytes
 tests/data/test-diff-pkg/tarpkg-1-report-0.txt |  16 ++
 tests/test-diff-pkg.cc                         |  10 ++
 tools/abipkgdiff.cc                            |   9 +-
 8 files changed, 148 insertions(+), 94 deletions(-)
 create mode 100644 tests/data/test-diff-pkg/tarpkg-1-dir1.tar.gz
 create mode 100644 tests/data/test-diff-pkg/tarpkg-1-dir2.tar.gz
 create mode 100644 tests/data/test-diff-pkg/tarpkg-1-report-0.txt

diff --git a/include/abg-dwarf-reader.h b/include/abg-dwarf-reader.h
index d2c7209..bdc2120 100644
--- a/include/abg-dwarf-reader.h
+++ b/include/abg-dwarf-reader.h
@@ -74,6 +74,7 @@ operator&=(status&, status);
 enum elf_type
 {
   ELF_TYPE_EXEC,
+  ELF_TYPE_PI_EXEC,
   ELF_TYPE_DSO,
   ELF_TYPE_RELOCATABLE,
   ELF_TYPE_UNKNOWN
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index 4f92786..b785e16 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -1870,18 +1870,116 @@ lookup_public_variable_symbol_from_elf(const environment*		env,
   return found;
 }
 
+/// Get data tag information of an ELF file by looking up into its
+/// dynamic segment
+///
+/// @param elf the elf handle to use for the query.
+///
+/// @param dt_tag data tag to look for in dynamic segment
+/// @param dt_tag_data vector of found information for a given @p data_tag
+///
+/// @return true iff data tag @p data_tag was found
+
+bool
+lookup_data_tag_from_dynamic_segment(Elf*                       elf,
+                                     Elf64_Sxword               data_tag,
+                                     vector<string>&            dt_tag_data)
+{
+  size_t num_prog_headers = 0;
+  bool found = false;
+  if (elf_getphdrnum(elf, &num_prog_headers) < 0)
+    return found;
+
+  // Cycle through each program header.
+  for (size_t i = 0; i < num_prog_headers; ++i)
+    {
+      GElf_Phdr phdr_mem;
+      GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_mem);
+      if (phdr == NULL || phdr->p_type != PT_DYNAMIC)
+        continue;
+
+      // Poke at the dynamic segment like a section, so that we can
+      // get its section header information; also we'd like to read
+      // the data of the segment by using elf_getdata() but that
+      // function needs a Elf_Scn data structure to act on.
+      // Elfutils doesn't really have any particular function to
+      // access segment data, other than the functions used to
+      // access section data.
+      Elf_Scn *dynamic_section = gelf_offscn(elf, phdr->p_offset);
+      GElf_Shdr  shdr_mem;
+      GElf_Shdr *dynamic_section_header = gelf_getshdr(dynamic_section,
+                                                        &shdr_mem);
+      if (dynamic_section_header == NULL
+          || dynamic_section_header->sh_type != SHT_DYNAMIC)
+        continue;
+
+      // Get data of the dynamic segment (seen a a section).
+      Elf_Data *data = elf_getdata(dynamic_section, NULL);
+      if (data == NULL)
+        continue;
+
+      // Get the index of the section headers string table.
+      size_t string_table_index = 0;
+      assert (elf_getshdrstrndx(elf, &string_table_index) >= 0);
+
+      size_t dynamic_section_header_entry_size = gelf_fsize(elf,
+                                                            ELF_T_DYN, 1,
+                                                            EV_CURRENT);
+
+      GElf_Shdr link_mem;
+      GElf_Shdr *link =
+        gelf_getshdr(elf_getscn(elf,
+                                dynamic_section_header->sh_link),
+                      &link_mem);
+      assert(link != NULL);
+
+      size_t num_dynamic_section_entries =
+        dynamic_section_header->sh_size / dynamic_section_header_entry_size;
+
+      // Now walk through all the DT_* data tags that are in the
+      // segment/section
+      for (size_t j = 0; j < num_dynamic_section_entries; ++j)
+        {
+          GElf_Dyn dynamic_section_mem;
+          GElf_Dyn *dynamic_section = gelf_getdyn(data,
+                                                  j,
+                                                  &dynamic_section_mem);
+          if (dynamic_section->d_tag == data_tag)
+            {
+              dt_tag_data.push_back(elf_strptr(elf,
+                                               dynamic_section_header->sh_link,
+                                              dynamic_section->d_un.d_val));
+              found = true;
+            }
+        }
+    }
+  return found;
+}
+
 /// Convert the type of ELF file into @ref elf_type.
 ///
-/// @param header a elf header to get the ELF file type from.
+/// @param elf the elf handle to use for the query.
 ///
 /// @return the @ref elf_type for a given elf type.
 static elf_type
-elf_file_type(const GElf_Ehdr* header)
+elf_file_type(Elf* elf)
 {
+  GElf_Ehdr ehdr_mem;
+  GElf_Ehdr *header = gelf_getehdr (elf, &ehdr_mem);
+  vector<string> dt_debug;
+
   switch (header->e_type)
     {
     case ET_DYN:
-      return ELF_TYPE_DSO;
+
+      if (lookup_data_tag_from_dynamic_segment(elf, DT_DEBUG, dt_debug))
+        {
+          return ELF_TYPE_PI_EXEC;
+        }
+      else
+        {
+          return ELF_TYPE_DSO;
+        }
     case ET_EXEC:
       return ELF_TYPE_EXEC;
     case ET_REL:
@@ -3113,10 +3211,7 @@ public:
   elf_type
   get_elf_file_type()
   {
-    Elf* elf = elf_handle();
-    GElf_Ehdr eh_mem;
-    GElf_Ehdr* elf_header = gelf_getehdr(elf, &eh_mem);
-    return elf_file_type(elf_header);
+    return elf_file_type(elf_handle());
   }
 
   /// The section containing the symbol table from the current ELF
@@ -4087,88 +4182,17 @@ public:
 
   /// Load the DT_NEEDED and DT_SONAME elf TAGS.
   ///
-  /// @return true if the tags could be read, false otherwise.
   void
   load_dt_soname_and_needed()
   {
-    size_t num_prog_headers = 0;
-    if (elf_getphdrnum(elf_handle(), &num_prog_headers) < 0)
-      return;
-
-    unsigned found = 0;
-    // Cycle through each program header.
-    for (size_t i = 0; i < num_prog_headers; ++i)
-      {
-	GElf_Phdr phdr_mem;
-	GElf_Phdr *phdr = gelf_getphdr(elf_handle(), i, &phdr_mem);
-	if (phdr == NULL || phdr->p_type != PT_DYNAMIC)
-	  continue;
-
-	// Poke at the dynamic segment like a section, so that we can
-	// get its section header information; also we'd like to read
-	// the data of the segment by using elf_getdata() but that
-	// function needs a Elf_Scn data structure to act on.
-	// Elfutils doesn't really have any particular function to
-	// access segment data, other than the functions used to
-	// access section data.
-	Elf_Scn *dynamic_section = gelf_offscn(elf_handle(), phdr->p_offset);
-	GElf_Shdr  shdr_mem;
-	GElf_Shdr *dynamic_section_header = gelf_getshdr(dynamic_section,
-							 &shdr_mem);
-	if (dynamic_section_header == NULL
-	    || dynamic_section_header->sh_type != SHT_DYNAMIC)
-	  continue;
-
-	// Get data of the dynamic segment (seen a a section).
-	Elf_Data *data = elf_getdata(dynamic_section, NULL);
-	if (data == NULL)
-	  continue;
-
-	// Get the index of the section headers string table.
-	size_t string_table_index = 0;
-	assert (elf_getshdrstrndx(elf_handle(), &string_table_index) >= 0);
-
-	size_t dynamic_section_header_entry_size = gelf_fsize(elf_handle(),
-							      ELF_T_DYN, 1,
-							      EV_CURRENT);
-
-	GElf_Shdr link_mem;
-	GElf_Shdr *link =
-	  gelf_getshdr(elf_getscn(elf_handle(),
-				  dynamic_section_header->sh_link),
-		       &link_mem);
-	assert(link != NULL);
-
-	size_t num_dynamic_section_entries =
-	  dynamic_section_header->sh_size / dynamic_section_header_entry_size;
-
-	// Now walk through all the DT_* data tags that are in the
-	// segment/section
-	for (size_t j = 0; j < num_dynamic_section_entries; ++j)
-	  {
-	    GElf_Dyn dynamic_section_mem;
-	    GElf_Dyn *dynamic_section = gelf_getdyn(data,
-						    j,
-						    &dynamic_section_mem);
-	    if (dynamic_section == NULL)
-	      break;
-
-	    if (dynamic_section->d_tag == DT_NEEDED)
-	      {
-		string dt_needed = elf_strptr(elf_handle(),
-					      dynamic_section_header->sh_link,
-					      dynamic_section->d_un.d_val);
-		dt_needed_.push_back(dt_needed);
-		++found;
-	      }
-	    else if (dynamic_section->d_tag == DT_SONAME)
-	      {
-		dt_soname_ = elf_strptr(elf_handle(),
-					dynamic_section_header->sh_link,
-					dynamic_section->d_un.d_val);
-	      }
-	  }
-      }
+    vector<string> dt_tag_data;
+    if (lookup_data_tag_from_dynamic_segment(elf_handle(), DT_NEEDED,
+                                             dt_tag_data))
+      dt_needed_ = dt_tag_data;
+    dt_tag_data.clear();
+    if (lookup_data_tag_from_dynamic_segment(elf_handle(), DT_SONAME,
+                                             dt_tag_data))
+      dt_soname_ = dt_tag_data.at(0);
   }
 
   /// Read the string representing the architecture of the current ELF
@@ -9773,10 +9797,7 @@ get_type_of_elf_file(const string& path, elf_type& type)
 
   elf_version (EV_CURRENT);
   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
-  GElf_Ehdr ehdr_mem;
-  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
-  string soname;
-  type = elf_file_type(ehdr);
+  type = elf_file_type(elf);
   close(fd);
 
   return true;
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index a165168..b9df914 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1090,5 +1090,8 @@ test-diff-pkg/gtk2-debuginfo-2.24.22-5.el7.i686.rpm \
 test-diff-pkg/gtk2-debuginfo-2.24.28-8.el7.i686.rpm \
 test-diff-pkg/gtk2-immodule-xim-2.24.22-5.el7.i686--gtk2-immodule-xim-2.24.28-8.el7.i686-report-0.txt \
 test-diff-pkg/gtk2-immodule-xim-2.24.22-5.el7.i686.rpm \
-test-diff-pkg/gtk2-immodule-xim-2.24.28-8.el7.i686.rpm
+test-diff-pkg/gtk2-immodule-xim-2.24.28-8.el7.i686.rpm \
+test-diff-pkg/tarpkg-1-dir1.tar.gz \
+test-diff-pkg/tarpkg-1-dir2.tar.gz \
+test-diff-pkg/tarpkg-1-report-0.txt
 
diff --git a/tests/data/test-diff-pkg/tarpkg-1-dir1.tar.gz b/tests/data/test-diff-pkg/tarpkg-1-dir1.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..56d7a55346a6c42b2c8ffe3c14875af28ea9b127
GIT binary patch
diff --git a/tests/data/test-diff-pkg/tarpkg-1-dir2.tar.gz b/tests/data/test-diff-pkg/tarpkg-1-dir2.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..609bc7ee5ae74db05be9399939eb3341264da826
GIT binary patch
diff --git a/tests/data/test-diff-pkg/tarpkg-1-report-0.txt b/tests/data/test-diff-pkg/tarpkg-1-report-0.txt
new file mode 100644
index 0000000..3465da1
--- /dev/null
+++ b/tests/data/test-diff-pkg/tarpkg-1-report-0.txt
@@ -0,0 +1,16 @@
+================ changes of 'libobj-v0.so'===============
+  Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+  Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+  1 function with some indirect sub-type change:
+
+    [C]'function void bar(S&)' at obj-v0.cc:16:1 has some indirect sub-type changes:
+      parameter 1 of type 'S&' has sub-type changes:
+        in referenced type 'struct S' at obj-v0.cc:4:1:
+          type size changed from 32 to 64 bits
+          1 data member insertion:
+            'char S::mem1', at offset 32 (in bits) at obj-v0.cc:7:1
+
+
+================ end of changes of 'libobj-v0.so'===============
+
diff --git a/tests/test-diff-pkg.cc b/tests/test-diff-pkg.cc
index c47c5cc..79ae60d 100644
--- a/tests/test-diff-pkg.cc
+++ b/tests/test-diff-pkg.cc
@@ -195,6 +195,16 @@ static InOutSpec in_out_specs[] =
     "data/test-diff-pkg/tarpkg-0-report-0.txt",
     "output/test-diff-pkg/tarpkg-0-report-03.txt"
   },
+  {
+    "data/test-diff-pkg/tarpkg-1-dir1.tar.gz",
+    "data/test-diff-pkg/tarpkg-1-dir2.tar.gz",
+    "--dso-only",
+    "",
+    "",
+    "",
+    "data/test-diff-pkg/tarpkg-1-report-0.txt",
+    "output/test-diff-pkg/tarpkg-1-report-0.txt"
+  },
 #endif //WITH_TAR
 
 #ifdef WITH_RPM
diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc
index 0ed29dc..7bd1367 100644
--- a/tools/abipkgdiff.cc
+++ b/tools/abipkgdiff.cc
@@ -1164,7 +1164,8 @@ create_maps_of_package_content(package& package,
       else
 	{
 	  if (e->type != abigail::dwarf_reader::ELF_TYPE_DSO
-	      && e->type != abigail::dwarf_reader::ELF_TYPE_EXEC)
+	      && e->type != abigail::dwarf_reader::ELF_TYPE_EXEC
+              && e->type != abigail::dwarf_reader::ELF_TYPE_PI_EXEC)
 	    {
 	      if (verbose)
 		emit_prefix("abipkgdiff", cerr)
@@ -1404,7 +1405,8 @@ compare(package&	first_package,
 
       if (iter != second_package.path_elf_file_sptr_map().end()
 	  && (iter->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
-	      || iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC))
+	      || iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
+              || iter->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC))
 	{
 	  elf_pairs.push_back(compare_args_sptr(new compare_args(*it->second,
 						debug_dir1, *iter->second,
@@ -1456,7 +1458,8 @@ compare(package&	first_package,
 
       if (iter != second_package.path_elf_file_sptr_map().end()
 	  && (iter->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
-	      || iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC))
+	      || iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
+              || iter->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC))
 	{
 	  second_package.path_elf_file_sptr_map().erase(iter);
 	  while (true)
-- 
2.4.11

