]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Recognize and parse GNU Property notes.
authorMark Wielaard <mark@klomp.org>
Mon, 15 Oct 2018 21:35:47 +0000 (23:35 +0200)
committerMark Wielaard <mark@klomp.org>
Sun, 28 Oct 2018 23:57:57 +0000 (00:57 +0100)
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>
29 files changed:
libdwelf/ChangeLog
libdwelf/dwelf_elf_gnu_build_id.c
libdwfl/ChangeLog
libdwfl/core-file.c
libdwfl/dwfl_segment_report_module.c
libdwfl/linux-core-attach.c
libdwfl/linux-kernel-modules.c
libebl/ChangeLog
libebl/eblobjnote.c
libebl/eblobjnotetypename.c
libelf/ChangeLog
libelf/elf32_xlatetom.c
libelf/elf_compress.c
libelf/elf_compress_gnu.c
libelf/elf_getdata.c
libelf/gelf_fsize.c
libelf/gelf_getnote.c
libelf/gelf_xlate.c
libelf/libelf.h
libelf/libelfP.h
libelf/note_xlate.h
src/ChangeLog
src/elflint.c
src/readelf.c
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-n.sh [new file with mode: 0755]
tests/testfile-gnu-property-note.bz2 [new file with mode: 0755]
tests/testfile-gnu-property-note.o.bz2 [new file with mode: 0644]

index a3326556d658a03f3f56346c4cbd41674ab660bf..ba921347b46384cbaeccfbbd89ba688ff78e8517 100644 (file)
@@ -1,3 +1,8 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * dwelf_elf_gnu_build_id.c (find_elf_build_id): Check p_align to
+       set ELF type.
+
 2015-10-11  Akihiko Odaki  <akihiko.odaki.4i@stu.hosei.ac.jp>
 
        * dwelf_strtab.c: Remove sys/param.h include.
index 8c78c70053fcab2c20c188dffce5103c118d0eef..dbcfc8292f094b2c5597d82ea1dc7882221d44f1 100644 (file)
@@ -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,
index 6c333d839d58cdab9f892e90671776c925aac55b..9e7bb316f34cb7c16e73f80bebbed0e823d3baa4 100644 (file)
@@ -1,3 +1,13 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * dwfl_segment_report_module.c (consider_note): Take align as new
+       argument.  Use align to set d_type and calculate padding.
+       (dwfl_segment_report_module): Pass align to consider_notes.
+       * core-file.c (dwfl_core_file_report): Check p_align to set ELF
+       type.
+       * linux-kernel-modules.c (check_notes): Check name and type of note
+       to determine padding.
+
 2018-10-19  Mark Wielaard  <mark@klomp.org>
 
        * dwfl_module_getdwarf.c (find_aux_sym): Check sh_entsize is not zero.
index 84cb89accb96a338bd23e405ad1442bc94d6366d..01109f4bd675a42b4d755bdc5139567f5c567236 100644 (file)
@@ -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;
index 87498846a8c87b3bac72528de48f8511b4eb3a8c..0d633ffee62128b97e7485fac828cdb13a5bd6fe 100644 (file)
@@ -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:
index 9f05f72a0695e834191eb66beaec86e089d96118..6c99b9e27fccb8232059c4d8b736df6d304c298d 100644 (file)
@@ -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;
        }
     }
index 9d0fef2cf260116267678c0d47ab7ba099e11c47..360e4ee9342a6b5808c384cc537df4afff8f5dc5 100644 (file)
@@ -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
index aec848b9cd8db6f339c4c508aaf3ef1f63769e42..120c84c06e6c48c2c40a9ef191ad4df7df060eca 100644 (file)
@@ -1,3 +1,9 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * eblobjnote.c (ebl_object_note): Handle NT_GNU_PROPERTY_TYPE_0.
+       * eblobjnotetypename.c (ebl_object_note_type_name): Add
+       GNU_PROPERTY_TYPE_0.
+
 2018-10-02  Andreas Schwab  <schwab@suse.de>
 
        * ebl-hooks.h (EBLHOOK(reloc_simple_type)): Add third parameter.
index ca4f155d7821194a3fd33751a723b9e89d8962de..57e9f52f45b16bf7c2ed066d0bae76af9a95e5f6 100644 (file)
@@ -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)
            {
index db040d29367a7b696842452cf8ec00c2b63536c9..af23caea8274fb1a259c3d64b6c34963f8813a8f 100644 (file)
@@ -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.  */
index be37ab6081ecc947605edd03d2533236783c3e98..af565036078f6c142a6ac561048a7d54b1a58728 100644 (file)
@@ -1,3 +1,31 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * libelf.h (Elf_Type): Add ELF_T_NHDR8.
+       * libelfP.h (__libelf_data_type): Add align argument.
+       (NOTE_ALIGN): Split into...
+       (NOTE_ALIGN4): ... and ...
+       (NOTE_ALIGN8): this.
+       * elf32_xlatetom.c (xlatetom): Recognize both ELF_T_NHDR and
+       ELF_T_NHDR8.
+       * elf_compress.c (elf_compress): Pass zdata_align to
+       __libelf_data_type.
+       * elf_compress_gnu.c (elf_compress_gnu): Pass sh_addralign to
+       __libelf_data_type.
+       * elf_getdata.c (shtype_map): Add ELF_T_NHDR8.
+       (__libelf_data_type): Take align as extra argument, use it to
+       determine Elf_Type.
+       (__libelf_set_rawdata_wrlock): Recognize ELF_T_NHDR8. Pass align to
+       __libelf_data_type.
+       * gelf_fsize.c (__libelf_type_sizes): Add ELF_T_NHDR8.
+       * gelf_getnote.c (gelf_getnote): Use Elf_Type of Elf_Data to calculate
+       padding.
+       * gelf_xlate.c (__elf_xfctstom): Set ELF_T_NHDR to elf_cvt_note4,
+       add ELF_T_NHDR8.
+       * note_xlate.h (elf_cvt_note): Take nhdr8 argument and use it to
+       determine padding.
+       (elf_cvt_note4): New function.
+       (elf_cvt_note8): Likewise.
+
 2018-09-13  Mark Wielaard  <mark@klomp.org>
 
        * elf32_updatefile.c (updatemmap): Use shnum, not ehdr->e_shnum.
index 13cd485dba0c579c7ccaa7733e493c36084ff779..3b94cac73409fcdaa10a778b411acda91cd6bcd6 100644 (file)
@@ -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);
index 711be591b5e466ec207351ca1d31cc0bcbef59a6..fd412e8a64a6a393b6a427091df2f0f8fa9e38ba 100644 (file)
@@ -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;
     }
index dfa7c571d5ee1b26e7cba528809074a8cc079bfa..198dc7d5b47a08351d9cc345d0ffb12df0c26634 100644 (file)
@@ -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;
 
index 278dfa8f21b72888f367223f6b72be955ac86fce..4f80aaf2debc62df5df547824c6889d948a13da1 100644 (file)
@@ -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
index 0c509265cb11390613d0e2316e95a7b984f1ceca..d04ec5d54dbade0ea973b3dc0d17f2cb4e18b8ae 100644 (file)
@@ -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)),                \
index c75eddabb501324d1f6dd3967993edad913551f0..6d33b3558b746fc499d9d20a0c1ba5b48bf4ccae 100644 (file)
@@ -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;
+               }
            }
        }
     }
index 479f1436e333a3636abe5d2f8cd5629d421aa1fe..b5d6ef3de843f855b8d042e55ef4bdbfd65fb312 100644 (file)
@@ -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),                               \
index d11358cc3a7f0d755e2a9d129b9fddec47f5fc88..1ff11c95055e81b90b7b9c060243060cee4072e7 100644 (file)
@@ -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;
index ed216c8ca3d7e1c983fd0d9cd30077fdc8580a0f..fa6d55d8a7045a8616cef1624a979d66c1df5d50 100644 (file)
@@ -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) \
index 62c6f63de18c478c5980135119cd8e50f6b344f0..9bdc3e2caaa4c782c2473f6c564f802fb728847c 100644 (file)
@@ -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
    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);
+}
index 5061cc11b165edd34beaedb9bc4daa68965b5f44..758534de162513cbcde439f29bab8252fffa79e0 100644 (file)
@@ -1,3 +1,10 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * elflint.c (check_note_data): Recognize NT_GNU_PROPERTY_TYPE_0.
+       (check_note): Use p_align to pass either ELF_T_NHDR or ELF_T_NHDR8 to
+       elf_getdata_rawchunk.
+       * readelf (handle_notes): Likewise.
+
 2018-10-24  Mark Wielaard  <mark@klomp.org>
 
        * addr2line.c (print_addrsym): Use elf_getshdrstrndx instead of
index 3d445954d666dbcff6d9440593e41f6c1d9f15c5..fa3af4c56fd6f331500294d04570b1b0a9f302b6 100644 (file)
@@ -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);
 
index 72ae04ec8447ba0904598bfcc5106f0fc86926f4..ccd07eb7b7c2cff8a2195b36742b3c6c0e8c6b4d 100644 (file)
@@ -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)));
     }
 }
 
index 751a0810d0ff0a743b3c61572aa42012cdf96986..d5a065639d46b23e83f039b5d29e992c04f3a7cb 100644 (file)
@@ -1,3 +1,12 @@
+2018-10-18  Mark Wielaard  <mark@klomp.org>
+
+       * run-readelf-n.sh: New test.
+       * testfile-gnu-property-note.bz2: New testfile.
+       * testfile-gnu-property-note.o.bz2: Likewise.
+       * Makefile.am (TESTS): Add run-readelf-n.sh.
+       (EXTRA_DIST): Likewise and testfile-gnu-property-note.bz2,
+       testfile-gnu-property-note.o.bz2.
+
 2018-10-12  Mark Wielaard  <mark@klomp.org>
 
        * run-readelf-zdebug.sh: Adjust flags output.
index 15b429b792e7b99874fab86777ce0a738b460ce3..a2a381ac899480701d3f8ea480486d6cbbc2cc4f 100644 (file)
@@ -106,6 +106,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
        run-readelf-test4.sh run-readelf-twofiles.sh \
        run-readelf-macro.sh run-readelf-loc.sh run-readelf-ranges.sh \
        run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \
+       run-readelf-n.sh \
        run-native-test.sh run-bug1-test.sh \
        run-debuglink.sh run-debugaltlink.sh run-buildid.sh \
        dwfl-bug-addr-overflow run-addrname-test.sh \
@@ -272,6 +273,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \
             run-readelf-addr.sh run-readelf-str.sh \
             run-readelf-types.sh \
+            run-readelf-n.sh \
+            testfile-gnu-property-note.bz2 testfile-gnu-property-note.o.bz2 \
             run-allfcts-multi.sh \
             test-offset-loop.bz2 test-offset-loop.alt.bz2 \
             run-prelink-addr-test.sh \
diff --git a/tests/run-readelf-n.sh b/tests/run-readelf-n.sh
new file mode 100755 (executable)
index 0000000..3ae7cf0
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright (C) 2018 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# - testfile-gnu-property-note.c
+# int
+# main ()
+# {
+#   return 0;
+# }
+#
+# gcc -fcf-protection -c testfile-gnu-property-note.c
+# gcc -o testfile-gnu-property-note testfile-gnu-property-note.o
+# eu-strip --strip-sections testfile-gnu-property-note
+
+testfiles testfile-gnu-property-note.o testfile-gnu-property-note
+
+# Test reading notes through sections
+testrun_compare ${abs_top_builddir}/src/readelf -n testfile-gnu-property-note.o << EOF
+
+Note section [ 6] '.note.gnu.property' of 32 bytes at offset 0x80:
+  Owner          Data size  Type
+  GNU                   16  GNU_PROPERTY_TYPE_0
+    X86 FEATURE_1_AND: 00000003 IBT SHSTK
+EOF
+
+# Test reading notes through segments
+testrun_compare ${abs_top_builddir}/src/readelf -n testfile-gnu-property-note << EOF
+
+Note segment of 32 bytes at offset 0x300:
+  Owner          Data size  Type
+  GNU                   16  GNU_PROPERTY_TYPE_0
+    X86 FEATURE_1_AND: 00000003 IBT SHSTK
+
+Note segment of 68 bytes at offset 0x320:
+  Owner          Data size  Type
+  GNU                   16  VERSION
+    OS: Linux, ABI: 3.2.0
+  GNU                   20  GNU_BUILD_ID
+    Build ID: 83cb2229fabd2065d1361f5b46424cd75270f94b
+EOF
diff --git a/tests/testfile-gnu-property-note.bz2 b/tests/testfile-gnu-property-note.bz2
new file mode 100755 (executable)
index 0000000..c03bd9b
Binary files /dev/null and b/tests/testfile-gnu-property-note.bz2 differ
diff --git a/tests/testfile-gnu-property-note.o.bz2 b/tests/testfile-gnu-property-note.o.bz2
new file mode 100644 (file)
index 0000000..5147c64
Binary files /dev/null and b/tests/testfile-gnu-property-note.o.bz2 differ