Blob Blame History Raw
commit 5199e15870e05e5b0b9f98c20fc9b5427aa6dd6a
Author: Mark Wielaard <mark@klomp.org>
Date:   Mon Oct 15 23:35:47 2018 +0200

    Recognize and parse GNU Property notes.
    
    GNU Property notes are different from normal notes because they use
    variable alignment/padding of their fields. They are 8 byte aligned,
    but use 4 byte fields. The name is aligned at 4 bytes and padded so
    that, the desc is aligned at 8 bytes. The whole note is padded to
    8 bytes again. For normal notes all fields are both 4 bytes wide and
    4 bytes aligned.
    
    To recognize these new kind of ELF Notes a new Elf_Type is introduced,
    ELF_T_NHDR8. This type is used in the xlate functions to determine
    how to align and pad the various fields. Since the fields themselves
    can now have different alignments we will have to keep track of the
    current alignement and use either NOTE_ALIGN4 or NOTE_ALIGN8 to
    determine the padding.
    
    To set the correct Elf_Type on the Elf_Data we use either the section
    sh_addralign or the segment p_align values. Assuming 8 means the
    section or segment contains the new style notes, otherwise normal
    notes.
    
    When we cannot determine the "alignment" directly, like when parsing
    special kernel sys files, we check the name "GNU" and type
    "GNU_PROPERTY_TYPE_0" fields.
    
    ebl_object_note now parses the new NT_GNU_PROPERTY_TYPE_0 and can
    extract the GNU_PROPERTY_STACK_SIZE, GNU_PROPERTY_NO_COPY_ON_PROTECTED
    and GNU_PROPERTY_X86_FEATURE_1_AND types GNU_PROPERTY_X86_FEATURE_1_IBT
    and GNU_PROPERTY_X86_FEATURE_1_SHSTK.
    
    Tests are added for extracting the note from sections or segments
    as set by gcc -fcf-protection.
    
    Signed-off-by: Mark Wielaard <mark@klomp.org>

diff --git a/libdwelf/dwelf_elf_gnu_build_id.c b/libdwelf/dwelf_elf_gnu_build_id.c
index 8c78c70..dbcfc82 100644
--- a/libdwelf/dwelf_elf_gnu_build_id.c
+++ b/libdwelf/dwelf_elf_gnu_build_id.c
@@ -88,7 +88,9 @@ find_elf_build_id (Dwfl_Module *mod, int e_type, Elf *elf,
 	    result = check_notes (elf_getdata_rawchunk (elf,
 							phdr->p_offset,
 							phdr->p_filesz,
-							ELF_T_NHDR),
+							(phdr->p_align == 8
+							 ? ELF_T_NHDR8
+							 : ELF_T_NHDR)),
 				  phdr->p_vaddr,
 				  build_id_bits,
 				  build_id_elfaddr,
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index 84cb89a..01109f4 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -496,7 +496,9 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
       Elf_Data *notes = elf_getdata_rawchunk (elf,
 					      notes_phdr.p_offset,
 					      notes_phdr.p_filesz,
-					      ELF_T_NHDR);
+					      (notes_phdr.p_align == 8
+					       ? ELF_T_NHDR8
+					       : ELF_T_NHDR));
       if (likely (notes != NULL))
 	{
 	  size_t pos = 0;
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
index 8749884..0d633ff 100644
--- a/libdwfl/dwfl_segment_report_module.c
+++ b/libdwfl/dwfl_segment_report_module.c
@@ -27,7 +27,7 @@
    not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
-#include "../libelf/libelfP.h"	/* For NOTE_ALIGN.  */
+#include "../libelf/libelfP.h"	/* For NOTE_ALIGN4 and NOTE_ALIGN8.  */
 #undef	_
 #include "libdwflP.h"
 #include "common.h"
@@ -451,7 +451,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
   GElf_Addr build_id_vaddr = 0;
 
   /* Consider a PT_NOTE we've found in the image.  */
-  inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz)
+  inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz,
+			      GElf_Xword align)
   {
     /* If we have already seen a build ID, we don't care any more.  */
     if (build_id != NULL || filesz == 0)
@@ -478,7 +479,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 	notes = malloc (filesz);
 	if (unlikely (notes == NULL))
 	  return;
-	xlatefrom.d_type = xlateto.d_type = ELF_T_NHDR;
+	xlatefrom.d_type = xlateto.d_type = (align == 8
+					     ? ELF_T_NHDR8 : ELF_T_NHDR);
 	xlatefrom.d_buf = (void *) data;
 	xlatefrom.d_size = filesz;
 	xlateto.d_buf = notes;
@@ -489,15 +491,23 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
       }
 
     const GElf_Nhdr *nh = notes;
-    while ((const void *) nh < (const void *) notes + filesz)
-     {
-	const void *note_name = nh + 1;
-	const void *note_desc = note_name + NOTE_ALIGN (nh->n_namesz);
-	if (unlikely ((size_t) ((const void *) notes + filesz
-				- note_desc) < nh->n_descsz))
+    size_t len = 0;
+    while (filesz > len + sizeof (*nh))
+      {
+	const void *note_name;
+	const void *note_desc;
+
+	len += sizeof (*nh);
+	note_name = notes + len;
+
+	len += nh->n_namesz;
+	len = align == 8 ? NOTE_ALIGN8 (len) : NOTE_ALIGN4 (len);
+	note_desc = notes + len;
+
+	if (unlikely (filesz < len + nh->n_descsz))
 	  break;
 
-	if (nh->n_type == NT_GNU_BUILD_ID
+        if (nh->n_type == NT_GNU_BUILD_ID
 	    && nh->n_descsz > 0
 	    && nh->n_namesz == sizeof "GNU"
 	    && !memcmp (note_name, "GNU", sizeof "GNU"))
@@ -510,7 +520,9 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
 	    break;
 	  }
 
-	nh = note_desc + NOTE_ALIGN (nh->n_descsz);
+	len += nh->n_descsz;
+	len = align == 8 ? NOTE_ALIGN8 (len) : NOTE_ALIGN4 (len);
+	nh = (void *) notes + len;
       }
 
   done:
@@ -535,7 +547,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
       case PT_NOTE:
 	/* We calculate from the p_offset of the note segment,
 	   because we don't yet know the bias for its p_vaddr.  */
-	consider_notes (start + offset, filesz);
+	consider_notes (start + offset, filesz, align);
 	break;
 
       case PT_LOAD:
diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c
index 9f05f72..6c99b9e 100644
--- a/libdwfl/linux-core-attach.c
+++ b/libdwfl/linux-core-attach.c
@@ -355,7 +355,9 @@ dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
       if (phdr != NULL && phdr->p_type == PT_NOTE)
 	{
 	  note_data = elf_getdata_rawchunk (core, phdr->p_offset,
-					    phdr->p_filesz, ELF_T_NHDR);
+					    phdr->p_filesz, (phdr->p_align == 8
+							     ? ELF_T_NHDR8
+							     : ELF_T_NHDR));
 	  break;
 	}
     }
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index 9d0fef2..360e4ee 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -39,6 +39,7 @@
 #include <config.h>
 #include <system.h>
 
+#include "libelfP.h"
 #include "libdwflP.h"
 #include <inttypes.h>
 #include <errno.h>
@@ -554,15 +555,41 @@ check_notes (Dwfl_Module *mod, const char *notesfile,
     return 1;
 
   unsigned char *p = buf.data;
+  size_t len = 0;
   while (p < &buf.data[n])
     {
       /* No translation required since we are reading the native kernel.  */
       GElf_Nhdr *nhdr = (void *) p;
-      p += sizeof *nhdr;
+      len += sizeof *nhdr;
+      p += len;
       unsigned char *name = p;
-      p += (nhdr->n_namesz + 3) & -4U;
-      unsigned char *bits = p;
-      p += (nhdr->n_descsz + 3) & -4U;
+      unsigned char *bits;
+      /* This is somewhat ugly, GNU Property notes use different padding,
+	 but all we have is the file content, so we have to actually check
+	 the name and type.  */
+      if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0
+          && nhdr->n_namesz == sizeof "GNU"
+          && name + nhdr->n_namesz < &buf.data[n]
+          && !memcmp (name, "GNU", sizeof "GNU"))
+	{
+	  len += nhdr->n_namesz;
+	  len = NOTE_ALIGN8 (len);
+	  p = buf.data + len;
+	  bits = p;
+	  len += nhdr->n_descsz;
+	  len = NOTE_ALIGN8 (len);
+	  p = buf.data + len;
+	}
+      else
+	{
+	  len += nhdr->n_namesz;
+	  len = NOTE_ALIGN4 (len);
+	  p = buf.data + len;
+	  bits = p;
+	  len += nhdr->n_descsz;
+	  len = NOTE_ALIGN4 (len);
+	  p = buf.data + len;
+	}
 
       if (p <= &buf.data[n]
 	  && nhdr->n_type == NT_GNU_BUILD_ID
diff --git a/libebl/eblobjnote.c b/libebl/eblobjnote.c
index ca4f155..57e9f52 100644
--- a/libebl/eblobjnote.c
+++ b/libebl/eblobjnote.c
@@ -1,5 +1,5 @@
 /* Print contents of object file note.
-   Copyright (C) 2002, 2007, 2009, 2011, 2015, 2016 Red Hat, Inc.
+   Copyright (C) 2002, 2007, 2009, 2011, 2015, 2016, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -37,6 +37,8 @@
 #include <string.h>
 #include <libeblP.h>
 
+#include "libelfP.h"
+
 
 void
 ebl_object_note (Ebl *ebl, const char *name, uint32_t type,
@@ -153,6 +155,187 @@ ebl_object_note (Ebl *ebl, const char *name, uint32_t type,
 		    (int) descsz, desc);
 	  break;
 
+	case NT_GNU_PROPERTY_TYPE_0:
+	  if (strcmp (name, "GNU") == 0 && descsz > 0)
+	    {
+	      /* There are at least 2 words. type and datasz.  */
+	      while (descsz >= 8)
+		{
+		  struct pr_prop
+		  {
+		    GElf_Word pr_type;
+		    GElf_Word pr_datasz;
+		  } prop;
+
+		  Elf_Data in =
+		    {
+		      .d_version = EV_CURRENT,
+		      .d_type = ELF_T_WORD,
+		      .d_size = 8,
+		      .d_buf = (void *) desc
+		    };
+		  Elf_Data out =
+		    {
+		      .d_version = EV_CURRENT,
+		      .d_type = ELF_T_WORD,
+		      .d_size = descsz,
+		      .d_buf = (void *) &prop
+		    };
+
+		  if (gelf_xlatetom (ebl->elf, &out, &in,
+				     elf_getident (ebl->elf,
+						   NULL)[EI_DATA]) == NULL)
+		    {
+		      printf ("%s\n", elf_errmsg (-1));
+		      return;
+		    }
+
+		  desc += 8;
+		  descsz -= 8;
+
+		  int elfclass = gelf_getclass (ebl->elf);
+		  char *elfident = elf_getident (ebl->elf, NULL);
+		  GElf_Ehdr ehdr;
+		  gelf_getehdr (ebl->elf, &ehdr);
+
+		  /* Prefix.  */
+		  printf ("    ");
+		  if (prop.pr_type == GNU_PROPERTY_STACK_SIZE)
+		    {
+		      printf ("STACK_SIZE ");
+		      if (prop.pr_datasz == 4 || prop.pr_datasz == 8)
+			{
+			  GElf_Addr addr;
+			  in.d_type = ELF_T_ADDR;
+			  out.d_type = ELF_T_ADDR;
+			  in.d_size = prop.pr_datasz;
+			  out.d_size = sizeof (addr);
+			  in.d_buf = (void *) desc;
+			  out.d_buf = (void *) &addr;
+
+			  if (gelf_xlatetom (ebl->elf, &out, &in,
+					     elfident[EI_DATA]) == NULL)
+			    {
+			      printf ("%s\n", elf_errmsg (-1));
+			      return;
+			    }
+			  printf ("%#" PRIx64 "\n", addr);
+			}
+		      else
+			printf (" (garbage datasz: %" PRIx32 ")\n",
+				prop.pr_datasz);
+		    }
+		  else if (prop.pr_type == GNU_PROPERTY_NO_COPY_ON_PROTECTED)
+		    {
+		      printf ("NO_COPY_ON_PROTECTION");
+		      if (prop.pr_datasz == 0)
+			printf ("\n");
+		      else
+			printf (" (garbage datasz: %" PRIx32 ")\n",
+				prop.pr_datasz);
+		    }
+		  else if (prop.pr_type >= GNU_PROPERTY_LOPROC
+		      && prop.pr_type <= GNU_PROPERTY_HIPROC
+		      && (ehdr.e_machine == EM_386
+			  || ehdr.e_machine == EM_X86_64))
+		    {
+		      printf ("X86 ");
+		      if (prop.pr_type == GNU_PROPERTY_X86_FEATURE_1_AND)
+			{
+			  printf ("FEATURE_1_AND: ");
+
+			  if (prop.pr_datasz == 4)
+			    {
+			      GElf_Word data;
+			      in.d_type = ELF_T_WORD;
+			      out.d_type = ELF_T_WORD;
+			      in.d_size = 4;
+			      out.d_size = 4;
+			      in.d_buf = (void *) desc;
+			      out.d_buf = (void *) &data;
+
+			      if (gelf_xlatetom (ebl->elf, &out, &in,
+						 elfident[EI_DATA]) == NULL)
+				{
+				  printf ("%s\n", elf_errmsg (-1));
+				  return;
+				}
+			      printf ("%08" PRIx32 " ", data);
+
+			      if ((data & GNU_PROPERTY_X86_FEATURE_1_IBT)
+				  != 0)
+				{
+				  printf ("IBT");
+				  data &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
+				  if (data != 0)
+				    printf (" ");
+				}
+
+			      if ((data & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
+				  != 0)
+				{
+				  printf ("SHSTK");
+				  data &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+				  if (data != 0)
+				    printf (" ");
+				}
+
+			      if (data != 0)
+				printf ("UNKNOWN");
+			    }
+			  else
+			    printf ("<bad datasz: %" PRId32 ">",
+				    prop.pr_datasz);
+
+			  printf ("\n");
+			}
+		      else
+			{
+			  printf ("%#" PRIx32, prop.pr_type);
+			  if (prop.pr_datasz > 0)
+			    {
+			      printf (" data: ");
+			      size_t i;
+			      for (i = 0; i < prop.pr_datasz - 1; i++)
+				printf ("%02" PRIx8 " ", (uint8_t) desc[i]);
+			      printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
+			    }
+			}
+		    }
+		  else
+		    {
+		      if (prop.pr_type >= GNU_PROPERTY_LOPROC
+			  && prop.pr_type <= GNU_PROPERTY_HIPROC)
+			printf ("proc_type %#" PRIx32, prop.pr_type);
+		      else if (prop.pr_type >= GNU_PROPERTY_LOUSER
+			  && prop.pr_type <= GNU_PROPERTY_HIUSER)
+			printf ("app_type %#" PRIx32, prop.pr_type);
+		      else
+			printf ("unknown_type %#" PRIx32, prop.pr_type);
+
+		      if (prop.pr_datasz > 0)
+			{
+			  printf (" data: ");
+			  size_t i;
+			  for (i = 0; i < prop.pr_datasz - 1; i++)
+			    printf ("%02" PRIx8 " ", (uint8_t) desc[i]);
+			  printf ("%02" PRIx8 "\n", (uint8_t) desc[i]);
+			}
+		    }
+		  if (elfclass == ELFCLASS32)
+		    {
+		      desc += NOTE_ALIGN4 (prop.pr_datasz);
+		      descsz -= NOTE_ALIGN4 (prop.pr_datasz);
+		    }
+		  else
+		    {
+		      desc += NOTE_ALIGN8 (prop.pr_datasz);
+		      descsz -= NOTE_ALIGN8 (prop.pr_datasz);
+		    }
+		}
+	    }
+	  break;
+
 	case NT_GNU_ABI_TAG:
 	  if (strcmp (name, "GNU") == 0 && descsz >= 8 && descsz % 4 == 0)
 	    {
diff --git a/libebl/eblobjnotetypename.c b/libebl/eblobjnotetypename.c
index db040d2..af23cae 100644
--- a/libebl/eblobjnotetypename.c
+++ b/libebl/eblobjnotetypename.c
@@ -91,6 +91,7 @@ ebl_object_note_type_name (Ebl *ebl, const char *name, uint32_t type,
 	  KNOWNSTYPE (GNU_HWCAP),
 	  KNOWNSTYPE (GNU_BUILD_ID),
 	  KNOWNSTYPE (GNU_GOLD_VERSION),
+	  KNOWNSTYPE (GNU_PROPERTY_TYPE_0),
 	};
 
       /* Handle standard names.  */
diff --git a/libelf/elf32_xlatetom.c b/libelf/elf32_xlatetom.c
index 13cd485..3b94cac 100644
--- a/libelf/elf32_xlatetom.c
+++ b/libelf/elf32_xlatetom.c
@@ -60,7 +60,7 @@ elfw2(LIBELFBITS, xlatetom) (Elf_Data *dest, const Elf_Data *src,
   /* We shouldn't require integer number of records when processing
      notes.  Payload bytes follow the header immediately, it's not an
      array of records as is the case otherwise.  */
-  if (src->d_type != ELF_T_NHDR
+  if (src->d_type != ELF_T_NHDR && src->d_type != ELF_T_NHDR8
       && src->d_size % recsize != 0)
     {
       __libelf_seterrno (ELF_E_INVALID_DATA);
diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c
index 711be59..fd412e8 100644
--- a/libelf/elf_compress.c
+++ b/libelf/elf_compress.c
@@ -513,7 +513,8 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags)
 
       __libelf_reset_rawdata (scn, scn->zdata_base,
 			      scn->zdata_size, scn->zdata_align,
-			      __libelf_data_type (elf, sh_type));
+			      __libelf_data_type (elf, sh_type,
+						  scn->zdata_align));
 
       return 1;
     }
diff --git a/libelf/elf_compress_gnu.c b/libelf/elf_compress_gnu.c
index dfa7c57..198dc7d 100644
--- a/libelf/elf_compress_gnu.c
+++ b/libelf/elf_compress_gnu.c
@@ -196,7 +196,7 @@ elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags)
 	}
 
       __libelf_reset_rawdata (scn, buf_out, size, sh_addralign,
-			      __libelf_data_type (elf, sh_type));
+			      __libelf_data_type (elf, sh_type, sh_addralign));
 
       scn->zdata_base = buf_out;
 
diff --git a/libelf/elf_getdata.c b/libelf/elf_getdata.c
index 278dfa8..4f80aaf 100644
--- a/libelf/elf_getdata.c
+++ b/libelf/elf_getdata.c
@@ -65,7 +65,7 @@ static const Elf_Type shtype_map[EV_NUM - 1][TYPEIDX (SHT_HISUNW) + 1] =
       [SHT_PREINIT_ARRAY] = ELF_T_ADDR,
       [SHT_GROUP] = ELF_T_WORD,
       [SHT_SYMTAB_SHNDX] = ELF_T_WORD,
-      [SHT_NOTE] = ELF_T_NHDR,
+      [SHT_NOTE] = ELF_T_NHDR, /* Need alignment to guess ELF_T_NHDR8.  */
       [TYPEIDX (SHT_GNU_verdef)] = ELF_T_VDEF,
       [TYPEIDX (SHT_GNU_verneed)] = ELF_T_VNEED,
       [TYPEIDX (SHT_GNU_versym)] = ELF_T_HALF,
@@ -106,6 +106,7 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM]
       [ELF_T_GNUHASH] = __alignof__ (Elf32_Word),			      \
       [ELF_T_AUXV] = __alignof__ (ElfW2(Bits,auxv_t)),			      \
       [ELF_T_CHDR] = __alignof__ (ElfW2(Bits,Chdr)),			      \
+      [ELF_T_NHDR8] = 8 /* Special case for GNU Property note.  */	      \
     }
     [EV_CURRENT - 1] =
     {
@@ -118,7 +119,7 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM]
 
 Elf_Type
 internal_function
-__libelf_data_type (Elf *elf, int sh_type)
+__libelf_data_type (Elf *elf, int sh_type, GElf_Xword align)
 {
   /* Some broken ELF ABI for 64-bit machines use the wrong hash table
      entry size.  See elf-knowledge.h for more information.  */
@@ -129,7 +130,13 @@ __libelf_data_type (Elf *elf, int sh_type)
       return (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD);
     }
   else
-    return shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)];
+    {
+      Elf_Type t = shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)];
+      /* Special case for GNU Property notes.  */
+      if (t == ELF_T_NHDR && align == 8)
+	t = ELF_T_NHDR8;
+      return t;
+    }
 }
 
 /* Convert the data in the current section.  */
@@ -272,7 +279,9 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
       else
 	{
 	  Elf_Type t = shtype_map[LIBELF_EV_IDX][TYPEIDX (type)];
-	  if (t == ELF_T_VDEF || t == ELF_T_NHDR
+	  if (t == ELF_T_NHDR && align == 8)
+	    t = ELF_T_NHDR8;
+	  if (t == ELF_T_VDEF || t == ELF_T_NHDR || t == ELF_T_NHDR8
 	      || (t == ELF_T_GNUHASH && elf->class == ELFCLASS64))
 	    entsize = 1;
 	  else
@@ -357,7 +366,7 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn)
   if ((flags & SHF_COMPRESSED) != 0)
     scn->rawdata.d.d_type = ELF_T_CHDR;
   else
-    scn->rawdata.d.d_type = __libelf_data_type (elf, type);
+    scn->rawdata.d.d_type = __libelf_data_type (elf, type, align);
   scn->rawdata.d.d_off = 0;
 
   /* Make sure the alignment makes sense.  d_align should be aligned both
diff --git a/libelf/gelf_fsize.c b/libelf/gelf_fsize.c
index 0c50926..d04ec5d 100644
--- a/libelf/gelf_fsize.c
+++ b/libelf/gelf_fsize.c
@@ -64,6 +64,8 @@ const size_t __libelf_type_sizes[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] =
       [ELF_T_VNEED]	= sizeof (ElfW2(LIBELFBITS, Ext_Verneed)),	      \
       [ELF_T_VNAUX]	= sizeof (ElfW2(LIBELFBITS, Ext_Vernaux)),	      \
       [ELF_T_NHDR]	= sizeof (ElfW2(LIBELFBITS, Ext_Nhdr)),		      \
+      /* Note the header size is the same, but padding is different.  */      \
+      [ELF_T_NHDR8]	= sizeof (ElfW2(LIBELFBITS, Ext_Nhdr)),		      \
       [ELF_T_SYMINFO]	= sizeof (ElfW2(LIBELFBITS, Ext_Syminfo)),	      \
       [ELF_T_MOVE]	= sizeof (ElfW2(LIBELFBITS, Ext_Move)),		      \
       [ELF_T_LIB]	= sizeof (ElfW2(LIBELFBITS, Ext_Lib)),		      \
diff --git a/libelf/gelf_getnote.c b/libelf/gelf_getnote.c
index c75edda..6d33b35 100644
--- a/libelf/gelf_getnote.c
+++ b/libelf/gelf_getnote.c
@@ -1,5 +1,5 @@
 /* Get note information at the supplied offset.
-   Copyright (C) 2007, 2014, 2015 Red Hat, Inc.
+   Copyright (C) 2007, 2014, 2015, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -43,7 +43,7 @@ gelf_getnote (Elf_Data *data, size_t offset, GElf_Nhdr *result,
   if (data == NULL)
     return 0;
 
-  if (unlikely (data->d_type != ELF_T_NHDR))
+  if (unlikely (data->d_type != ELF_T_NHDR && data->d_type != ELF_T_NHDR8))
     {
       __libelf_seterrno (ELF_E_INVALID_HANDLE);
       return 0;
@@ -69,27 +69,42 @@ gelf_getnote (Elf_Data *data, size_t offset, GElf_Nhdr *result,
       const GElf_Nhdr *n = data->d_buf + offset;
       offset += sizeof *n;
 
-      /* Include padding.  Check below for overflow.  */
-      GElf_Word namesz = NOTE_ALIGN (n->n_namesz);
-      GElf_Word descsz = NOTE_ALIGN (n->n_descsz);
-
-      if (unlikely (offset > data->d_size
-		    || data->d_size - offset < namesz
-		    || (namesz == 0 && n->n_namesz != 0)))
+      if (offset > data->d_size)
 	offset = 0;
       else
 	{
+	  /* This is slightly tricky, offset is guaranteed to be 4
+	     byte aligned, which is what we need for the name_offset.
+	     And normally desc_offset is also 4 byte aligned, but not
+	     for GNU Property notes, then it should be 8.  So align
+	     the offset, after adding the namesz, and include padding
+	     in descsz to get to the end.  */
 	  *name_offset = offset;
-	  offset += namesz;
-	  if (unlikely (offset > data->d_size
-			|| data->d_size - offset < descsz
-			|| (descsz == 0 && n->n_descsz != 0)))
+	  offset += n->n_namesz;
+	  if (offset > data->d_size)
 	    offset = 0;
 	  else
 	    {
-	      *desc_offset = offset;
-	      offset += descsz;
-	      *result = *n;
+	      /* Include padding.  Check below for overflow.  */
+	      GElf_Word descsz = (data->d_type == ELF_T_NHDR8
+				  ? NOTE_ALIGN8 (n->n_descsz)
+				  : NOTE_ALIGN4 (n->n_descsz));
+
+	      if (data->d_type == ELF_T_NHDR8)
+		offset = NOTE_ALIGN8 (offset);
+	      else
+		offset = NOTE_ALIGN4 (offset);
+
+	      if (unlikely (offset > data->d_size
+			    || data->d_size - offset < descsz
+			    || (descsz == 0 && n->n_descsz != 0)))
+		offset = 0;
+	      else
+		{
+		  *desc_offset = offset;
+		  offset += descsz;
+		  *result = *n;
+		}
 	    }
 	}
     }
diff --git a/libelf/gelf_xlate.c b/libelf/gelf_xlate.c
index 479f143..b5d6ef3 100644
--- a/libelf/gelf_xlate.c
+++ b/libelf/gelf_xlate.c
@@ -195,7 +195,8 @@ const xfct_t __elf_xfctstom[EV_NUM - 1][EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM]
 	[ELF_T_VDAUX]	= elf_cvt_Verdef,				      \
 	[ELF_T_VNEED]	= elf_cvt_Verneed,				      \
 	[ELF_T_VNAUX]	= elf_cvt_Verneed,				      \
-	[ELF_T_NHDR]	= elf_cvt_note,					      \
+	[ELF_T_NHDR]	= elf_cvt_note4,				      \
+	[ELF_T_NHDR8]	= elf_cvt_note8,				      \
 	[ELF_T_SYMINFO] = ElfW2(Bits, cvt_Syminfo),			      \
 	[ELF_T_MOVE]	= ElfW2(Bits, cvt_Move),			      \
 	[ELF_T_LIB]	= ElfW2(Bits, cvt_Lib),				      \
diff --git a/libelf/libelf.h b/libelf/libelf.h
index d11358c..1ff11c9 100644
--- a/libelf/libelf.h
+++ b/libelf/libelf.h
@@ -117,6 +117,8 @@ typedef enum
   ELF_T_GNUHASH,		/* GNU-style hash section.  */
   ELF_T_AUXV,			/* Elf32_auxv_t, Elf64_auxv_t, ... */
   ELF_T_CHDR,			/* Compressed, Elf32_Chdr, Elf64_Chdr, ... */
+  ELF_T_NHDR8,			/* Special GNU Properties note.  Same as Nhdr,
+				   except padding.  */
   /* Keep this the last entry.  */
   ELF_T_NUM
 } Elf_Type;
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index ed216c8..fa6d55d 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -452,7 +452,8 @@ extern const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_
 /* Given an Elf handle and a section type returns the Elf_Data d_type.
    Should not be called when SHF_COMPRESSED is set, the d_type should
    be ELF_T_BYTE.  */
-extern Elf_Type __libelf_data_type (Elf *elf, int sh_type) internal_function;
+extern Elf_Type __libelf_data_type (Elf *elf, int sh_type, GElf_Xword align)
+  internal_function;
 
 /* The libelf API does not have such a function but it is still useful.
    Get the memory size for the given type.
@@ -624,8 +625,13 @@ extern void __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size,
       }									      \
   } while (0)
 
-/* Align offset to 4 bytes as needed for note name and descriptor data.  */
-#define NOTE_ALIGN(n)	(((n) + 3) & -4U)
+/* Align offset to 4 bytes as needed for note name and descriptor data.
+   This is almost always used, except for GNU Property notes, which use
+   8 byte padding...  */
+#define NOTE_ALIGN4(n)	(((n) + 3) & -4U)
+
+/* Special note padding rule for GNU Property notes.  */
+#define NOTE_ALIGN8(n)	(((n) + 7) & -8U)
 
 /* Convenience macro.  */
 #define INVALID_NDX(ndx, type, data) \
diff --git a/libelf/note_xlate.h b/libelf/note_xlate.h
index 62c6f63..9bdc3e2 100644
--- a/libelf/note_xlate.h
+++ b/libelf/note_xlate.h
@@ -1,5 +1,5 @@
 /* Conversion functions for notes.
-   Copyright (C) 2007, 2009, 2014 Red Hat, Inc.
+   Copyright (C) 2007, 2009, 2014, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -27,38 +27,60 @@
    not, see <http://www.gnu.org/licenses/>.  */
 
 static void
-elf_cvt_note (void *dest, const void *src, size_t len, int encode)
+elf_cvt_note (void *dest, const void *src, size_t len, int encode,
+	      bool nhdr8)
 {
+  /* Note that the header is always the same size, but the padding
+     differs for GNU Property notes.  */
   assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
 
   while (len >= sizeof (Elf32_Nhdr))
     {
+      /* Convert the header.  */
       (1 ? Elf32_cvt_Nhdr : Elf64_cvt_Nhdr) (dest, src, sizeof (Elf32_Nhdr),
 					     encode);
       const Elf32_Nhdr *n = encode ? src : dest;
-      Elf32_Word namesz = NOTE_ALIGN (n->n_namesz);
-      Elf32_Word descsz = NOTE_ALIGN (n->n_descsz);
 
-      len -= sizeof *n;
-      src += sizeof *n;
-      dest += sizeof *n;
+      size_t note_len = sizeof *n;
 
-      if (namesz > len)
+      /* desc needs to be aligned.  */
+      note_len += n->n_namesz;
+      note_len = nhdr8 ? NOTE_ALIGN8 (note_len) : NOTE_ALIGN4 (note_len);
+      if (note_len > len || note_len < 8)
 	break;
-      len -= namesz;
-      if (descsz > len)
+
+      /* data as a whole needs to be aligned.  */
+      note_len += n->n_descsz;
+      note_len = nhdr8 ? NOTE_ALIGN8 (note_len) : NOTE_ALIGN4 (note_len);
+      if (note_len > len || note_len < 8)
 	break;
-      len -= descsz;
 
+      /* Copy or skip the note data.  */
+      size_t note_data_len = note_len - sizeof *n;
+      src += sizeof *n;
+      dest += sizeof *n;
       if (src != dest)
-	memcpy (dest, src, namesz + descsz);
+	memcpy (dest, src, note_data_len);
 
-      src += namesz + descsz;
-      dest += namesz + descsz;
+      src += note_data_len;
+      dest += note_data_len;
+      len -= note_len;
     }
 
-    /* Copy opver any leftover data unconcerted.  Probably part of
+    /* Copy over any leftover data unconverted.  Probably part of
        truncated name/desc data.  */
     if (unlikely (len > 0) && src != dest)
       memcpy (dest, src, len);
 }
+
+static void
+elf_cvt_note4 (void *dest, const void *src, size_t len, int encode)
+{
+  elf_cvt_note (dest, src, len, encode, false);
+}
+
+static void
+elf_cvt_note8 (void *dest, const void *src, size_t len, int encode)
+{
+  elf_cvt_note (dest, src, len, encode, true);
+}
diff --git a/src/elflint.c b/src/elflint.c
index 3d44595..fa3af4c 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -4331,6 +4331,7 @@ section [%2d] '%s': unknown core file note type %" PRIu32
 	  case NT_GNU_HWCAP:
 	  case NT_GNU_BUILD_ID:
 	  case NT_GNU_GOLD_VERSION:
+	  case NT_GNU_PROPERTY_TYPE_0:
 	    break;
 
 	  case 0:
@@ -4376,7 +4377,8 @@ phdr[%d]: no note entries defined for the type of file\n"),
   GElf_Off notes_size = 0;
   Elf_Data *data = elf_getdata_rawchunk (ebl->elf,
 					 phdr->p_offset, phdr->p_filesz,
-					 ELF_T_NHDR);
+					 (phdr->p_align == 8
+					  ? ELF_T_NHDR8 : ELF_T_NHDR));
   if (data != NULL && data->d_buf != NULL)
     notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset);
 
diff --git a/src/readelf.c b/src/readelf.c
index 72ae04e..ccd07eb 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -12300,7 +12300,8 @@ handle_notes (Ebl *ebl, GElf_Ehdr *ehdr)
       handle_notes_data (ebl, ehdr, phdr->p_offset,
 			 elf_getdata_rawchunk (ebl->elf,
 					       phdr->p_offset, phdr->p_filesz,
-					       ELF_T_NHDR));
+					       (phdr->p_align == 8
+						? ELF_T_NHDR8 : ELF_T_NHDR)));
     }
 }