[llvm-readobj] - Teach llvm-readobj to dump .note.gnu.property sections.

NT_GNU_PROPERTY_TYPE_0 is a recently added type of .note.gnu.property
section specified in Linux Extensions to gABI.
(https://github.com/hjl-tools/linux-abi/wiki/Linux-Extensions-to-gABI)

Patch teach tool to print such notes properly.

Differential revision: https://reviews.llvm.org/D44469

llvm-svn: 328078
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index d04aac9..1b811cc 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1440,6 +1440,13 @@
   NT_GNU_HWCAP = 2,
   NT_GNU_BUILD_ID = 3,
   NT_GNU_GOLD_VERSION = 4,
+  NT_GNU_PROPERTY_TYPE_0 = 5,
+};
+
+// Property types used in GNU_PROPERTY_TYPE_0 notes.
+enum {
+  GNU_PROPERTY_STACK_SIZE = 1,
+  GNU_PROPERTY_NO_COPY_ON_PROTECTED = 2,
 };
 
 // AMDGPU specific notes.
diff --git a/llvm/test/tools/llvm-readobj/note-gnu-property.s b/llvm/test/tools/llvm-readobj/note-gnu-property.s
new file mode 100644
index 0000000..6fd6908
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/note-gnu-property.s
@@ -0,0 +1,68 @@
+// REQUIRES: x86-registered-target
+// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t
+// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s
+
+// CHECK:      Displaying notes found at file offset 0x00000040 with length 0x00000070:
+// CHECK-NEXT:   Owner                 Data size       Description
+// CHECK-NEXT:   GNU                   0x00000060      NT_GNU_PROPERTY_TYPE_0 (property note)
+// CHECK-NEXT:     Properties:  stack size: 0x100
+// CHECK-NEXT:     stack size: 0x100
+// CHECK-NEXT:     no copy on protected
+// CHECK-NEXT:     <application-specific type 0xfefefefe>
+// CHECK-NEXT:     stack size: <corrupt length: 0x0>
+// CHECK-NEXT:     stack size: <corrupt length: 0x4> 
+// CHECK-NEXT:     no copy on protected <corrupt length: 0x1>
+// CHECK-NEXT:     <corrupt type (0x2) datasz: 0x1>
+
+.section ".note.gnu.property", "a"
+.align 4 
+  .long 4           /* Name length is always 4 ("GNU") */
+  .long end - begin /* Data length */
+  .long 5           /* Type: NT_GNU_PROPERTY_TYPE_0 */
+  .asciz "GNU"      /* Name */
+  .p2align 3
+begin:
+  .long 1           /* Type: GNU_PROPERTY_STACK_SIZE */
+  .long 8           /* Data size */
+  .quad 0x100       /* Data (stack size) */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* Test we handle alignment properly */
+  .long 1           /* Type: GNU_PROPERTY_STACK_SIZE */
+  .long 8           /* Data size */
+  .long 0x100       /* Data (stack size) */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  .long 2           /* Type: GNU_PROPERTY_NO_COPY_ON_PROTECTED */
+  .long 0           /* Data size */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* All notes below are broken. Test we are able to report them. */
+  
+  /* Broken note type */
+  .long 0xfefefefe  /* Invalid type for testing */
+  .long 0           /* Data size */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* GNU_PROPERTY_STACK_SIZE with zero stack size */
+  .long 1           /* Type: GNU_PROPERTY_STACK_SIZE */
+  .long 0           /* Data size */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* GNU_PROPERTY_STACK_SIZE with data size 4 (should be 8) */
+  .long 1           /* Type: GNU_PROPERTY_STACK_SIZE */
+  .long 4           /* Data size */
+  .long 0x100       /* Data (stack size) */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* GNU_PROPERTY_NO_COPY_ON_PROTECTED with pr_datasz and some data */
+  .long 2           /* Type: GNU_PROPERTY_NO_COPY_ON_PROTECTED */
+  .long 1           /* Data size (corrupted) */
+  .byte 1           /* Data */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+  
+  /* GNU_PROPERTY_NO_COPY_ON_PROTECTED with pr_datasz and without data */
+  .long 2           /* Type: GNU_PROPERTY_NO_COPY_ON_PROTECTED */
+  .long 1           /* Data size (corrupted) */
+  .p2align 3        /* Align to 8 byte for 64 bit */
+end:
diff --git a/llvm/test/tools/llvm-readobj/note-gnu-property2.s b/llvm/test/tools/llvm-readobj/note-gnu-property2.s
new file mode 100644
index 0000000..fe6be0c
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/note-gnu-property2.s
@@ -0,0 +1,22 @@
+// REQUIRES: x86-registered-target
+// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t
+// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s
+
+// CHECK:      Displaying notes found at file offset 0x00000040 with length 0x00000014:
+// CHECK-NEXT:   Owner                 Data size       Description
+// CHECK-NEXT:   GNU                   0x00000004      NT_GNU_PROPERTY_TYPE_0 (property note)
+// CHECK-NEXT:     Properties:
+// CHECK-NEXT:     <corrupted GNU_PROPERTY_TYPE_0>
+
+// Section below is broken, check we report that.
+
+.section ".note.gnu.property", "a"
+.align 4 
+  .long 4           /* Name length is always 4 ("GNU") */
+  .long end - begin /* Data length */
+  .long 5           /* Type: NT_GNU_PROPERTY_TYPE_0 */
+  .asciz "GNU"      /* Name */
+  .p2align 3
+begin:
+  .long 1           /* Type: GNU_PROPERTY_STACK_SIZE */
+end:
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 8d62864..b41d431 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -3412,6 +3412,7 @@
       {ELF::NT_GNU_HWCAP, "NT_GNU_HWCAP (DSO-supplied software HWCAP info)"},
       {ELF::NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID (unique build ID bitstring)"},
       {ELF::NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION (gold version)"},
+      {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"},
   };
 
   for (const auto &Note : Notes)
@@ -3476,8 +3477,35 @@
 }
 
 template <typename ELFT>
+static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize,
+                             ArrayRef<uint8_t> Data) {
+  switch (Type) {
+  default:
+    OS << format("    <application-specific type 0x%x>\n", Type);
+    return;
+  case GNU_PROPERTY_STACK_SIZE: {
+    OS << "    stack size: ";
+    if (DataSize == sizeof(typename ELFT::uint))
+      OS << format("0x%x\n",
+                   (uint64_t)(*(const typename ELFT::Addr *)Data.data()));
+    else
+      OS << format("<corrupt length: 0x%x>\n", DataSize);
+    break;
+  }
+  case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
+    OS << "    no copy on protected";
+    if (DataSize)
+      OS << format(" <corrupt length: 0x%x>", DataSize);
+    OS << "\n";
+    break;
+  }
+}
+
+template <typename ELFT>
 static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
                          ArrayRef<typename ELFT::Word> Words, size_t Size) {
+  using Elf_Word = typename ELFT::Word;
+
   switch (NoteType) {
   default:
     return;
@@ -3509,8 +3537,31 @@
     OS << "    Version: "
        << StringRef(reinterpret_cast<const char *>(Words.data()), Size);
     break;
-  }
+  case ELF::NT_GNU_PROPERTY_TYPE_0:
+    OS << "    Properties:";
 
+    ArrayRef<uint8_t> Arr(reinterpret_cast<const uint8_t *>(Words.data()),
+                          Size);
+    while (Arr.size() >= 8) {
+      uint32_t Type = *reinterpret_cast<const Elf_Word *>(Arr.data());
+      uint32_t DataSize = *reinterpret_cast<const Elf_Word *>(Arr.data() + 4);
+      Arr = Arr.drop_front(8);
+
+      // Take padding size into account if present.
+      uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint));
+      if (Arr.size() < PaddedSize) {
+        OS << format("    <corrupt type (0x%x) datasz: 0x%x>\n", Type,
+                     DataSize);
+        break;
+      }
+      printGNUProperty<ELFT>(OS, Type, DataSize, Arr.take_front(PaddedSize));
+      Arr = Arr.drop_front(PaddedSize);
+    }
+
+    if (!Arr.empty())
+      OS << "    <corrupted GNU_PROPERTY_TYPE_0>";
+    break;
+  }
   OS << '\n';
 }