summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--elf/dl-hwcaps.c24
-rw-r--r--elf/dl-load.c19
-rw-r--r--elf/readelflib.c19
-rw-r--r--include/elf.h16
5 files changed, 76 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 77092bbfa5..593811dcb4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2017-11-28  H.J. Lu  <hongjiu.lu@intel.com>
+
+	[BZ #22370]
+	* elf/dl-hwcaps.c (ROUND): Removed.
+	(_dl_important_hwcaps): Replace ROUND with ELF_NOTE_DESC_OFFSET
+	and ELF_NOTE_NEXT_OFFSET.
+	* elf/dl-load.c (ROUND): Removed.
+	(open_verify): Replace ROUND with ELF_NOTE_NEXT_OFFSET.
+	* elf/readelflib.c (ROUND): Removed.
+	(process_elf_file): Replace ROUND with ELF_NOTE_NEXT_OFFSET.
+	* include/elf.h [!_ISOMAC]: Include <libc-pointer-arith.h>.
+	[!_ISOMAC] (ELF_NOTE_DESC_OFFSET): New.
+	[!_ISOMAC] (ELF_NOTE_NEXT_OFFSET): Likewise.
+
 2017-11-28  Joseph Myers  <joseph@codesourcery.com>
 
 	* sysdeps/s390/fpu/s_fmaf.c: Include <libm-alias-float.h>.
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index 92f2eb45ce..27ba6122ff 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -67,6 +67,18 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
 	  {
 	    const ElfW(Addr) start = (phdr[i].p_vaddr
 				      + GLRO(dl_sysinfo_map)->l_addr);
+	    /* NB: Some PT_NOTE segment may have alignment value of 0
+	       or 1.  gABI specifies that PT_NOTE segments should be
+	       aligned to 4 bytes in 32-bit objects and to 8 bytes in
+	       64-bit objects.  As a Linux extension, we also support
+	       4 byte alignment in 64-bit objects.  If p_align is less
+	       than 4, we treate alignment as 4 bytes since some note
+	       segments have 0 or 1 byte alignment.   */
+	    ElfW(Addr) align = phdr[i].p_align;
+	    if (align < 4)
+	      align = 4;
+	    else if (align != 4 && align != 8)
+	      continue;
 	    /* The standard ELF note layout is exactly as the anonymous struct.
 	       The next element is a variable length vendor name of length
 	       VENDORLEN (with a real length rounded to ElfW(Word)), followed
@@ -80,7 +92,6 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
 	    } *note = (const void *) start;
 	    while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
 	      {
-#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
 		/* The layout of the type 2, vendor "GNU" note is as follows:
 		   .long <Number of capabilities enabled by this note>
 		   .long <Capabilities mask> (as mask >> _DL_FIRST_EXTRA).
@@ -91,17 +102,18 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
 		    && !memcmp ((note + 1), "GNU", sizeof "GNU")
 		    && note->datalen > 2 * sizeof (ElfW(Word)) + 2)
 		  {
-		    const ElfW(Word) *p = ((const void *) (note + 1)
-					   + ROUND (sizeof "GNU"));
+		    const ElfW(Word) *p
+		      = ((const void *) note
+			 + ELF_NOTE_DESC_OFFSET (sizeof "GNU", align));
 		    cnt += *p++;
 		    ++p;	/* Skip mask word.  */
 		    dsocaps = (const char *) p; /* Pseudo-string "<b>name"  */
 		    dsocapslen = note->datalen - sizeof *p * 2;
 		    break;
 		  }
-		note = ((const void *) (note + 1)
-			+ ROUND (note->vendorlen) + ROUND (note->datalen));
-#undef ROUND
+		note = ((const void *) note
+			+ ELF_NOTE_NEXT_OFFSET (note->vendorlen,
+						note->datalen, align));
 	      }
 	    if (dsocaps != NULL)
 	      break;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 1220183ce2..10d859bd35 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1683,6 +1683,18 @@ open_verify (const char *name, int fd,
 	if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
 	  {
 	    ElfW(Addr) size = ph->p_filesz;
+	    /* NB: Some PT_NOTE segment may have alignment value of 0
+	       or 1.  gABI specifies that PT_NOTE segments should be
+	       aligned to 4 bytes in 32-bit objects and to 8 bytes in
+	       64-bit objects.  As a Linux extension, we also support
+	       4 byte alignment in 64-bit objects.  If p_align is less
+	       than 4, we treate alignment as 4 bytes since some note
+	       segments have 0 or 1 byte alignment.   */
+	    ElfW(Addr) align = ph->p_align;
+	    if (align < 4)
+	      align = 4;
+	    else if (align != 4 && align != 8)
+	      continue;
 
 	    if (ph->p_offset + size <= (size_t) fbp->len)
 	      abi_note = (void *) (fbp->buf + ph->p_offset);
@@ -1696,10 +1708,9 @@ open_verify (const char *name, int fd,
 
 	    while (memcmp (abi_note, &expected_note, sizeof (expected_note)))
 	      {
-#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
-		ElfW(Addr) note_size = 3 * sizeof (ElfW(Word))
-				       + ROUND (abi_note[0])
-				       + ROUND (abi_note[1]);
+		ElfW(Addr) note_size
+		  = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
+					  align);
 
 		if (size - 32 < note_size)
 		  {
diff --git a/elf/readelflib.c b/elf/readelflib.c
index 9ad56dcc34..3a303fff5f 100644
--- a/elf/readelflib.c
+++ b/elf/readelflib.c
@@ -131,15 +131,26 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
 	      ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents
 						     + segment->p_offset);
 	      ElfW(Addr) size = segment->p_filesz;
+	      /* NB: Some PT_NOTE segment may have alignment value of 0
+		 or 1.  gABI specifies that PT_NOTE segments should be
+		 aligned to 4 bytes in 32-bit objects and to 8 bytes in
+		 64-bit objects.  As a Linux extension, we also support
+		 4 byte alignment in 64-bit objects.  If p_align is less
+		 than 4, we treate alignment as 4 bytes since some note
+		 segments have 0 or 1 byte alignment.   */
+	      ElfW(Addr) align = segment->p_align;
+	      if (align < 4)
+		align = 4;
+	      else if (align != 4 && align != 8)
+		continue;
 
 	      while (abi_note [0] != 4 || abi_note [1] != 16
 		     || abi_note [2] != 1
 		     || memcmp (abi_note + 3, "GNU", 4) != 0)
 		{
-#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
-		  ElfW(Addr) note_size = 3 * sizeof (ElfW(Word))
-					 + ROUND (abi_note[0])
-					 + ROUND (abi_note[1]);
+		  ElfW(Addr) note_size
+		    = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
+					    align);
 
 		  if (size - 32 < note_size || note_size == 0)
 		    {
diff --git a/include/elf.h b/include/elf.h
index f06a33f256..ab76aafb1e 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -1,7 +1,19 @@
 #ifndef _ELF_H
 #include <elf/elf.h>
 
-# ifndef _ISOMAC
+#ifndef _ISOMAC
+
+# include <libc-pointer-arith.h>
+
+/* Compute the offset of the note descriptor from size of note entry's
+   owner string and note alignment.  */
+# define ELF_NOTE_DESC_OFFSET(namesz, align) \
+  ALIGN_UP (sizeof (ElfW(Nhdr)) + (namesz), (align))
+
+/* Compute the offset of the next note entry from size of note entry's
+   owner string, size of the note descriptor and note alignment.  */
+# define ELF_NOTE_NEXT_OFFSET(namesz, descsz, align) \
+  ALIGN_UP (ELF_NOTE_DESC_OFFSET ((namesz), (align)) + (descsz), (align))
 
 /* Some information which is not meant for the public and therefore not
    in <elf.h>.  */
@@ -13,5 +25,5 @@
    (DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
     | DF_1_ORIGIN | DF_1_NODEFLIB)
 
-# endif /* !_ISOMAC */
+#endif /* !_ISOMAC */
 #endif /* elf.h */