asan: Buffer overflow in evax_bfd_print_dst
[binutils-gdb.git] / bfd / elf-properties.c
index 5e48d75faa2694538cbbb74af0a86bfe85da3fd3..d67aadde16b778477b2d8f73dce0eac74edb5417 100644 (file)
@@ -1,5 +1,5 @@
 /* ELF program property support.
-   Copyright (C) 2017-2019 Free Software Foundation, Inc.
+   Copyright (C) 2017-2022 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -76,7 +76,7 @@ _bfd_elf_get_property (bfd *abfd, unsigned int type, unsigned int datasz)
 
 /* Parse GNU properties.  */
 
-bfd_boolean
+bool
 _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
 {
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
@@ -86,11 +86,11 @@ _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
 
   if (note->descsz < 8 || (note->descsz % align_size) != 0)
     {
-bad_size:
+    bad_size:
       _bfd_error_handler
        (_("warning: %pB: corrupt GNU_PROPERTY_TYPE (%ld) size: %#lx"),
         abfd, note->type, note->descsz);
-      return FALSE;
+      return false;
     }
 
   while (ptr != ptr_end)
@@ -113,7 +113,7 @@ bad_size:
             abfd, note->type, type, datasz);
          /* Clear all properties.  */
          elf_properties (abfd) = NULL;
-         return FALSE;
+         return false;
        }
 
       if (type >= GNU_PROPERTY_LOPROC)
@@ -134,7 +134,7 @@ bad_size:
                {
                  /* Clear all properties.  */
                  elf_properties (abfd) = NULL;
-                 return FALSE;
+                 return false;
                }
              else if (kind != property_ignored)
                goto next;
@@ -152,7 +152,7 @@ bad_size:
                     abfd, datasz);
                  /* Clear all properties.  */
                  elf_properties (abfd) = NULL;
-                 return FALSE;
+                 return false;
                }
              prop = _bfd_elf_get_property (abfd, type, datasz);
              if (datasz == 8)
@@ -170,14 +170,42 @@ bad_size:
                     abfd, datasz);
                  /* Clear all properties.  */
                  elf_properties (abfd) = NULL;
-                 return FALSE;
+                 return false;
                }
              prop = _bfd_elf_get_property (abfd, type, datasz);
-             elf_has_no_copy_on_protected (abfd) = TRUE;
+             elf_has_no_copy_on_protected (abfd) = true;
              prop->pr_kind = property_number;
              goto next;
 
            default:
+             if ((type >= GNU_PROPERTY_UINT32_AND_LO
+                  && type <= GNU_PROPERTY_UINT32_AND_HI)
+                 || (type >= GNU_PROPERTY_UINT32_OR_LO
+                     && type <= GNU_PROPERTY_UINT32_OR_HI))
+               {
+                 if (datasz != 4)
+                   {
+                     _bfd_error_handler
+                       (_("error: %pB: <corrupt property (0x%x) size: 0x%x>"),
+                        abfd, type, datasz);
+                     /* Clear all properties.  */
+                     elf_properties (abfd) = NULL;
+                     return false;
+                   }
+                 prop = _bfd_elf_get_property (abfd, type, datasz);
+                 prop->u.number |= bfd_h_get_32 (abfd, ptr);
+                 prop->pr_kind = property_number;
+                 if (type == GNU_PROPERTY_1_NEEDED
+                     && ((prop->u.number
+                          & GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS)
+                         != 0))
+                   {
+                     elf_has_indirect_extern_access (abfd) = true;
+                     /* GNU_PROPERTY_NO_COPY_ON_PROTECTED is implied.  */
+                     elf_has_no_copy_on_protected (abfd) = true;
+                   }
+                 goto next;
+               }
              break;
            }
        }
@@ -186,28 +214,30 @@ bad_size:
        (_("warning: %pB: unsupported GNU_PROPERTY_TYPE (%ld) type: 0x%x"),
         abfd, note->type, type);
 
-next:
+    next:
       ptr += (datasz + (align_size - 1)) & ~ (align_size - 1);
     }
 
-  return TRUE;
+  return true;
 }
 
 /* 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.  */
 
-static bfd_boolean
-elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
+static bool
+elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
                          elf_property *aprop, elf_property *bprop)
 {
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;
+  unsigned int number;
+  bool updated;
 
   if (bed->merge_gnu_properties != NULL
       && pr_type >= GNU_PROPERTY_LOPROC
       && pr_type < GNU_PROPERTY_LOUSER)
-    return bed->merge_gnu_properties (info, abfd, aprop, bprop);
+    return bed->merge_gnu_properties (info, abfd, bbfd, aprop, bprop);
 
   switch (pr_type)
     {
@@ -217,7 +247,7 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
          if (bprop->u.number > aprop->u.number)
            {
              aprop->u.number = bprop->u.number;
-             return TRUE;
+             return true;
            }
          break;
        }
@@ -229,19 +259,88 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
       return aprop == NULL;
 
     default:
+      updated = false;
+      if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
+         && pr_type <= GNU_PROPERTY_UINT32_OR_HI)
+       {
+         if (aprop != NULL && bprop != NULL)
+           {
+             number = aprop->u.number;
+             aprop->u.number = number | bprop->u.number;
+             /* Remove the property if all bits are empty.  */
+             if (aprop->u.number == 0)
+               {
+                 aprop->pr_kind = property_remove;
+                 updated = true;
+               }
+             else
+               updated = number != (unsigned int) aprop->u.number;
+           }
+         else
+           {
+             /* Only one of APROP and BPROP can be NULL.  */
+             if (aprop != NULL)
+               {
+                 if (aprop->u.number == 0)
+                   {
+                     /* Remove APROP if all bits are empty.  */
+                     aprop->pr_kind = property_remove;
+                     updated = true;
+                   }
+               }
+             else
+               {
+                 /* Return TRUE if APROP is NULL and all bits of BPROP
+                    aren't empty to indicate that BPROP should be added
+                    to ABFD.  */
+                 updated = bprop->u.number != 0;
+               }
+           }
+         return updated;
+       }
+      else if (pr_type >= GNU_PROPERTY_UINT32_AND_LO
+              && pr_type <= GNU_PROPERTY_UINT32_AND_HI)
+       {
+         /* Only one of APROP and BPROP can be NULL:
+            1. APROP & BPROP when both APROP and BPROP aren't NULL.
+            2. If APROP is NULL, remove x86 feature.
+            3. Otherwise, do nothing.
+            */
+         if (aprop != NULL && bprop != NULL)
+           {
+             number = aprop->u.number;
+             aprop->u.number = number & bprop->u.number;
+             updated = number != (unsigned int) aprop->u.number;
+             /* Remove the property if all feature bits are cleared.  */
+             if (aprop->u.number == 0)
+               aprop->pr_kind = property_remove;
+           }
+         else
+           {
+             /* There should be no AND properties since some input
+                doesn't have them.   */
+             if (aprop != NULL)
+               {
+                 aprop->pr_kind = property_remove;
+                 updated = true;
+               }
+           }
+         return updated;
+       }
+
       /* Never should happen.  */
       abort ();
     }
 
-  return FALSE;
+  return false;
 }
 
-/* Return the property of TYPE on *LISTP and remove it from *LISTP.
-   Return NULL if not found.  */
+/* Return the property of TYPE on *LISTP and remove it from *LISTP if RM is
+   true.  Return NULL if not found.  */
 
 static elf_property *
 elf_find_and_remove_property (elf_property_list **listp,
-                             unsigned int type, bfd_boolean remove)
+                             unsigned int type, bool rm)
 {
   elf_property_list *list;
 
@@ -250,7 +349,7 @@ elf_find_and_remove_property (elf_property_list **listp,
       if (type == list->property.pr_type)
        {
          /* Remove this property.  */
-         if (remove)
+         if (rm)
            *listp = list->next;
          return &list->property;
        }
@@ -270,7 +369,7 @@ elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
 {
   elf_property_list *p, **lastp;
   elf_property *pr;
-  bfd_boolean number_p;
+  bool number_p;
   bfd_vma number = 0;
 
   /* Merge each GNU property in FIRST_PBFD with the one on *LISTP.  */
@@ -280,16 +379,16 @@ elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
       {
        if (p->property.pr_kind == property_number)
          {
-           number_p = TRUE;
+           number_p = true;
            number = p->property.u.number;
          }
        else
-         number_p = FALSE;
+         number_p = false;
        pr = elf_find_and_remove_property (listp, p->property.pr_type,
-                                          TRUE);
+                                          true);
        /* Pass NULL to elf_merge_gnu_properties for the property which
           isn't on *LISTP.  */
-       elf_merge_gnu_properties (info, first_pbfd, &p->property, pr);
+       elf_merge_gnu_properties (info, first_pbfd, abfd, &p->property, pr);
        if (p->property.pr_kind == property_remove)
          {
            if (info->has_map_file)
@@ -322,12 +421,10 @@ elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
                         (bfd_vma) p->property.pr_type, first_pbfd, abfd);
                  }
              }
-           else
-             {
-               /* Remove this property.  */
-               *lastp = p->next;
-               continue;
-             }
+
+           /* Remove this property.  */
+           *lastp = p->next;
+           continue;
          }
        else if (number_p)
          {
@@ -359,16 +456,16 @@ elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
     {
       if (p->property.pr_kind == property_number)
        {
-         number_p = TRUE;
+         number_p = true;
          number = p->property.u.number;
        }
       else
-       number_p = FALSE;
+       number_p = false;
 
-      if (elf_merge_gnu_properties (info, first_pbfd, NULL, &p->property))
+      if (elf_merge_gnu_properties (info, first_pbfd, abfd, NULL, &p->property))
        {
          if (p->property.pr_type == GNU_PROPERTY_NO_COPY_ON_PROTECTED)
-           elf_has_no_copy_on_protected (first_pbfd) = TRUE;
+           elf_has_no_copy_on_protected (first_pbfd) = true;
 
          pr = _bfd_elf_get_property (first_pbfd, p->property.pr_type,
                                      p->property.pr_datasz);
@@ -382,7 +479,7 @@ elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
        {
          pr = elf_find_and_remove_property (&elf_properties (first_pbfd),
                                             p->property.pr_type,
-                                            FALSE);
+                                            false);
          if (pr == NULL)
            {
              if (number_p)
@@ -437,7 +534,8 @@ elf_get_gnu_property_section_size (elf_property_list *list,
 /* Write GNU properties.  */
 
 static void
-elf_write_gnu_properties (bfd *abfd, bfd_byte *contents,
+elf_write_gnu_properties (struct bfd_link_info *info,
+                         bfd *abfd, bfd_byte *contents,
                          elf_property_list *list, unsigned int size,
                          unsigned int align_size)
 {
@@ -482,6 +580,11 @@ elf_write_gnu_properties (bfd *abfd, bfd_byte *contents,
              break;
 
            case 4:
+             /* Save the pointer to GNU_PROPERTY_1_NEEDED so that it
+                can be updated later if needed.  */
+             if (info != NULL
+                 && list->property.pr_type == GNU_PROPERTY_1_NEEDED)
+               info->needed_1_p = contents + size;
              bfd_h_put_32 (abfd, list->property.u.number,
                            contents + size);
              break;
@@ -510,40 +613,83 @@ elf_write_gnu_properties (bfd *abfd, bfd_byte *contents,
 bfd *
 _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 {
-  bfd *abfd, *first_pbfd = NULL;
+  bfd *abfd, *first_pbfd = NULL, *elf_bfd = NULL;
   elf_property_list *list;
   asection *sec;
-  bfd_boolean has_properties = FALSE;
+  bool has_properties = false;
   const struct elf_backend_data *bed
     = get_elf_backend_data (info->output_bfd);
   unsigned int elfclass = bed->s->elfclass;
   int elf_machine_code = bed->elf_machine_code;
+  elf_property *p;
 
   /* Find the first relocatable ELF input with GNU properties.  */
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
        && (abfd->flags & DYNAMIC) == 0
-       && elf_properties (abfd) != NULL)
+       && (elf_machine_code
+           == get_elf_backend_data (abfd)->elf_machine_code)
+       && (elfclass == get_elf_backend_data (abfd)->s->elfclass))
       {
-       has_properties = TRUE;
-
        /* Ignore GNU properties from ELF objects with different machine
           code or class.  Also skip objects without a GNU_PROPERTY note
           section.  */
-       if ((elf_machine_code
-            == get_elf_backend_data (abfd)->elf_machine_code)
-           && (elfclass
-               == get_elf_backend_data (abfd)->s->elfclass)
-           && bfd_get_section_by_name (abfd,
-                                       NOTE_GNU_PROPERTY_SECTION_NAME) != NULL
-           )
+       elf_bfd = abfd;
+
+       if (elf_properties (abfd) != NULL)
          {
-           /* Keep .note.gnu.property section in FIRST_PBFD.  */
-           first_pbfd = abfd;
-           break;
+           has_properties = true;
+
+           if (bfd_get_section_by_name (abfd,
+                                        NOTE_GNU_PROPERTY_SECTION_NAME)
+               != NULL)
+             {
+               /* Keep .note.gnu.property section in FIRST_PBFD.  */
+               first_pbfd = abfd;
+               break;
+             }
          }
       }
 
+  if (info->indirect_extern_access > 0 && elf_bfd != NULL)
+    {
+      /* Support -z indirect-extern-access.  */
+      if (first_pbfd == NULL)
+       {
+         sec = bfd_make_section_with_flags (elf_bfd,
+                                            NOTE_GNU_PROPERTY_SECTION_NAME,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_IN_MEMORY
+                                             | SEC_READONLY
+                                             | SEC_HAS_CONTENTS
+                                             | SEC_DATA));
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
+
+         if (!bfd_set_section_alignment (sec,
+                                         elfclass == ELFCLASS64 ? 3 : 2))
+           info->callbacks->einfo (_("%F%pA: failed to align section\n"),
+                                   sec);
+
+         elf_section_type (sec) = SHT_NOTE;
+         first_pbfd = elf_bfd;
+         has_properties = true;
+       }
+
+      p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_1_NEEDED, 4);
+      if (p->pr_kind == property_unknown)
+       {
+         /* Create GNU_PROPERTY_1_NEEDED.  */
+         p->u.number
+           = GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
+         p->pr_kind = property_number;
+       }
+      else
+       p->u.number
+         |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
+    }
+
   /* Do nothing if there is no .note.gnu.property section.  */
   if (!has_properties)
     return NULL;
@@ -555,7 +701,7 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     if (abfd != first_pbfd
-       && (abfd->flags & (DYNAMIC | BFD_PLUGIN)) == 0)
+       && (abfd->flags & (DYNAMIC | BFD_PLUGIN | BFD_LINKER_CREATED)) == 0)
       {
        elf_property_list *null_ptr = NULL;
        elf_property_list **listp = &null_ptr;
@@ -607,7 +753,6 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
         if N > 0.  */
       if (info->stacksize > 0)
        {
-         elf_property *p;
          bfd_vma stacksize = info->stacksize;
 
          p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_STACK_SIZE,
@@ -649,7 +794,30 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
       sec->size = size;
       contents = (bfd_byte *) bfd_zalloc (first_pbfd, size);
 
-      elf_write_gnu_properties (first_pbfd, contents, list, size,
+      if (info->indirect_extern_access <= 0)
+       {
+         /* Get GNU_PROPERTY_1_NEEDED properties.  */
+         p = elf_find_and_remove_property (&elf_properties (first_pbfd),
+                                           GNU_PROPERTY_1_NEEDED, false);
+         if (p != NULL)
+           {
+             if (info->indirect_extern_access < 0)
+               {
+                 /* Set indirect_extern_access to 1 to indicate that
+                    it is turned on by input properties.  */
+                 if ((p->u.number
+                      & GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS)
+                     != 0)
+                   info->indirect_extern_access = 1;
+               }
+             else
+               /* Turn off indirect external access.  */
+               p->u.number
+                 &= ~GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
+           }
+       }
+
+      elf_write_gnu_properties (info, first_pbfd, contents, list, size,
                                align_size);
 
       /* Cache the section contents for elf_link_input_bfd.  */
@@ -658,7 +826,16 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
       /* If GNU_PROPERTY_NO_COPY_ON_PROTECTED is set, protected data
         symbol is defined in the shared object.  */
       if (elf_has_no_copy_on_protected (first_pbfd))
-       info->extern_protected_data = FALSE;
+       info->extern_protected_data = false;
+
+      if (info->indirect_extern_access > 0)
+       {
+         /* For indirect external access, don't generate copy
+            relocations.  NB: Set to nocopyreloc to 2 to indicate
+            that it is implied by indirect_extern_access.  */
+         info->nocopyreloc = 2;
+         info->extern_protected_data = false;
+       }
     }
 
   return first_pbfd;
@@ -682,7 +859,7 @@ _bfd_elf_convert_gnu_property_size (bfd *ibfd, bfd *obfd)
 
 /* Convert GNU properties.  */
 
-bfd_boolean
+bool
 _bfd_elf_convert_gnu_properties (bfd *ibfd, asection *isec,
                                 bfd *obfd, bfd_byte **ptr,
                                 bfd_size_type *ptr_size)
@@ -697,14 +874,16 @@ _bfd_elf_convert_gnu_properties (bfd *ibfd, asection *isec,
   align_shift = bed->s->elfclass == ELFCLASS64 ? 3 : 2;
 
   /* Get the output .note.gnu.property section size.  */
-  size = bfd_get_section_size (isec->output_section);
+  size = bfd_section_size (isec->output_section);
 
   /* Update the output .note.gnu.property section alignment.  */
-  bfd_set_section_alignment (obfd, isec->output_section, align_shift);
+  bfd_set_section_alignment (isec->output_section, align_shift);
 
-  if (size > bfd_get_section_size (isec))
+  if (size > bfd_section_size (isec))
     {
       contents = (bfd_byte *) bfd_malloc (size);
+      if (contents == NULL)
+       return false;
       free (*ptr);
       *ptr = contents;
     }
@@ -714,7 +893,8 @@ _bfd_elf_convert_gnu_properties (bfd *ibfd, asection *isec,
   *ptr_size = size;
 
   /* Generate the output .note.gnu.property section.  */
-  elf_write_gnu_properties (ibfd, contents, list, size, 1 << align_shift);
+  elf_write_gnu_properties (NULL, ibfd, contents, list, size,
+                           1 << align_shift);
 
-  return TRUE;
+  return true;
 }