Add .note.gnu.property runtime verification and merge support
[external/binutils.git] / bfd / elf-properties.c
index a0456f8..95e66ff 100644 (file)
@@ -69,6 +69,7 @@ _bfd_elf_get_property (bfd *abfd, unsigned int type, unsigned int datasz)
   memset (p, 0, sizeof (*p));
   p->property.pr_type = type;
   p->property.pr_datasz = datasz;
+  p->property.filename = abfd->filename;
   p->next = *lastp;
   *lastp = p;
   return &p->property;
@@ -163,6 +164,26 @@ bad_size:
              prop->pr_kind = property_number;
              goto next;
 
+           case GNU_PROPERTY_COMPILER_FLAGS:
+             /* Note that annobin uses 4-byte alignment even on 64-bit targets. */
+             if (!((datasz == 4) || (datasz == 8)))
+               {
+                 _bfd_error_handler
+                   (_("warning: %B: corrupt compiler flags: 0x%x\n"),
+                    abfd, datasz);
+                 /* Clear all properties.  */
+                 elf_properties (abfd) = NULL;
+                 return FALSE;
+               }
+             prop = _bfd_elf_get_property (abfd, type, datasz);
+             if (datasz == 8)
+               prop->u.number = bfd_h_get_64 (abfd, ptr);
+             else
+               prop->u.number = bfd_h_get_32 (abfd, ptr);
+
+             prop->pr_kind = property_number;
+             goto next;
+
            default:
              break;
            }
@@ -184,6 +205,110 @@ next:
   return TRUE;
 }
 
+/*  If validation bits are set - compare bit flags for equality.  */
+inline static bfd_boolean
+elf_gnu_property_validate_flag (bfd_vma anum,
+                               bfd_vma bnum,
+                               bfd_vma validation_bit_flag,
+                               bfd_vma validation_bit)
+{
+  if ((anum & validation_bit_flag) && (bnum & validation_bit_flag)
+      && ((anum & validation_bit) != (bnum & validation_bit)))
+    {
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static bfd_boolean
+elf_validate_compiler_flags_properties (elf_property *aprop,
+                                       elf_property *bprop)
+{
+  bfd_boolean is_flag_set;
+
+  /* Sanity check to verify correct usage.  */
+  BFD_ASSERT(aprop);
+  BFD_ASSERT(bprop);
+
+  if (aprop->pr_type != bprop->pr_type)
+    return TRUE;
+
+  BFD_ASSERT(aprop->pr_type == GNU_PROPERTY_COMPILER_FLAGS);
+
+  /* Return TRUE if compiler flags are identical (likely?).  */
+  if (aprop->u.number == bprop->u.number)
+    return TRUE;
+
+  if (!elf_gnu_property_validate_flag(aprop->u.number,
+                                     bprop->u.number,
+                                     GNU_PROPERTY_SANITIZE_VALIDATION,
+                                     GNU_PROPERTY_SANITIZE_ADDRESS))
+    {
+      is_flag_set = bprop->u.number & GNU_PROPERTY_SANITIZE_ADDRESS;
+      _bfd_error_handler
+       (_("%s: ERROR: Validation failed, linking %s object with previous %s"),
+        bprop->filename, is_flag_set ? "sanitized" : "unsanitized",
+        !is_flag_set ? "sanitized" : "unsanitized");
+      return FALSE;
+    }
+
+  if (!elf_gnu_property_validate_flag(aprop->u.number,
+                                     bprop->u.number,
+                                     GNU_PROPERTY_USECXX_VALIDATION,
+                                     GNU_PROPERTY_USECXX11_ABI))
+    {
+      is_flag_set = bprop->u.number & GNU_PROPERTY_USECXX11_ABI;
+      _bfd_error_handler
+       (_("ERROR: Validation failed, linking %s ABI object with %s ABI"),
+        bprop->filename, is_flag_set ? "pre-cxx11" : "cxx11",
+        !is_flag_set ? "pre-cxx11" : "cxx11");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+/* Merge bprop into aprop according compiler-flags properties, return TRUE if */
+/* aprop is updated. */
+static bfd_boolean
+elf_merge_gnu_properties_compiler_flags (elf_property *aprop,
+                                        elf_property *bprop)
+{
+  bfd_boolean is_updated = FALSE;
+  /* Likely that objects have the same properties.  */
+  if (aprop->u.number == bprop->u.number) {
+    return FALSE;
+  }
+
+  /* Validation bit + no-validation bit = validation bit.  */
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_SANITIZE_VALIDATION)
+    {
+      aprop->u.number |= GNU_PROPERTY_SANITIZE_VALIDATION;
+      is_updated = TRUE;
+    }
+  /* Sanitized object + unsanitized results = sanitized final object.  */
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_SANITIZE_ADDRESS)
+    {
+      aprop->u.number |= GNU_PROPERTY_SANITIZE_ADDRESS;
+      is_updated = TRUE;
+    }
+
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_USECXX_VALIDATION)
+    {
+      aprop->u.number |= GNU_PROPERTY_USECXX_VALIDATION;
+      is_updated = TRUE;
+    }
+
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_USECXX11_ABI)
+    {
+      aprop->u.number |= GNU_PROPERTY_USECXX11_ABI;
+      is_updated = TRUE;
+    }
+
+  return is_updated;
+}
+
 /* Merge GNU property BPROP with APROP.  If APROP isn't NULL, return TRUE
    if APROP is updated.  Otherwise, return TRUE if BPROP should be merged
    with ABFD.  */
@@ -218,6 +343,44 @@ elf_merge_gnu_properties (bfd *abfd, elf_property *aprop,
       /* Return TRUE if APROP is NULL to indicate that BPROP should
         be added to ABFD.  */
       return aprop == NULL;
+      /* FALLTHROUGH */
+
+    case GNU_PROPERTY_COMPILER_FLAGS:
+      {
+       bfd_boolean is_updated;
+       if (aprop != NULL && bprop != NULL)
+         {
+#ifdef ENABLE_ANNOBIN_VERIFICATION
+           if (!elf_validate_compiler_flags_properties (aprop, bprop))
+             {
+               _bfd_error_handler(_("ERROR: Linking failed due to incompatible "
+                                    "compilation flags used to generate objects\n"));
+               /* Strict mode, abort.  */
+#ifdef ENABLE_ANNOBIN_VERIFICATION_STRICT
+               _exit (EXIT_FAILURE);
+#endif
+             }
+#endif
+           is_updated = elf_merge_gnu_properties_compiler_flags (aprop, bprop);
+         }
+       else
+         {
+           is_updated = FALSE;
+           /* aprop or bprop is NULL, warn for missing GNU_PROPERTY_COMPILER_FLAGS.
+
+              This is too noisy since glibc's crt{i,n}.o are compiled with
+              predefined flags (without annobin support), so we'll have at
+              least 2x of the following warning for every linking. May be
+              enabled after we verified crt* compilation with plugins.  */
+         /*  if (bprop == NULL)
+             _bfd_error_handler
+               (_("WARNING: not found (generated by annobin gcc plugin), "
+                  "runtime verification may be incomplete"),
+                NOTE_GNU_PROPERTY_SECTION_NAME);  */
+         }
+       return is_updated;
+      }
+      /* FALLTHROUGH */
 
     default:
       /* Never should happen.  */
@@ -380,6 +543,9 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
       sec = bfd_get_section_by_name (first_pbfd,
                                     NOTE_GNU_PROPERTY_SECTION_NAME);
 
+      if (!sec)
+       return;
+
       /* Update stack size in .note.gnu.property with -z stack-size=N
         if N > 0.  */
       if (info->stacksize > 0)