gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / binutils / elfedit.c
index 5f213823bd8aec8ee38411f2aa3cbd608d065f09..d0e4e95f4d6ebdfbb88cba87434306ccd8a420bd 100644 (file)
@@ -1,5 +1,5 @@
 /* elfedit.c -- Update the ELF header of an ELF format file
-   Copyright (C) 2010-2014 Free Software Foundation, Inc.
+   Copyright (C) 2010-2020 Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
 
@@ -18,6 +18,7 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 \f
+#include "config.h"
 #include "sysdep.h"
 #include <assert.h>
 
@@ -54,24 +55,257 @@ static int input_elf_type = -1;
 static int output_elf_type = -1;
 static int input_elf_osabi = -1;
 static int output_elf_osabi = -1;
-static int input_elf_class = -1;
+enum elfclass
+  {
+    ELF_CLASS_UNKNOWN = -1,
+    ELF_CLASS_NONE = ELFCLASSNONE,
+    ELF_CLASS_32 = ELFCLASS32,
+    ELF_CLASS_64 = ELFCLASS64,
+    ELF_CLASS_BOTH
+  };
+static enum elfclass input_elf_class = ELF_CLASS_UNKNOWN;
+static enum elfclass output_elf_class = ELF_CLASS_BOTH;
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+
+static unsigned int enable_x86_features;
+static unsigned int disable_x86_features;
 
 static int
-update_elf_header (const char *file_name, FILE *file)
+update_gnu_property (const char *file_name, FILE *file)
 {
-  int class, machine, type, status, osabi;
+  char *map;
+  Elf_Internal_Phdr *phdrs;
+  struct stat st_buf;
+  unsigned int i;
+  int ret;
 
-  if (elf_header.e_ident[EI_MAG0] != ELFMAG0
-      || elf_header.e_ident[EI_MAG1] != ELFMAG1
-      || elf_header.e_ident[EI_MAG2] != ELFMAG2
-      || elf_header.e_ident[EI_MAG3] != ELFMAG3)
+  if (!enable_x86_features && !disable_x86_features)
+    return 0;
+
+  if (elf_header.e_machine != EM_386
+      && elf_header.e_machine != EM_X86_64)
     {
-      error
-       (_("%s: Not an ELF file - wrong magic bytes at the start\n"),
-        file_name);
+      error (_("%s: Not an i386 nor x86-64 ELF file\n"), file_name);
+      return 0;
+    }
+
+  if (fstat (fileno (file), &st_buf) < 0)
+    {
+      error (_("%s: stat () failed\n"), file_name);
+      return 1;
+    }
+
+  map = mmap (NULL, st_buf.st_size, PROT_READ | PROT_WRITE,
+             MAP_SHARED, fileno (file), 0);
+  if (map == MAP_FAILED)
+    {
+      error (_("%s: mmap () failed\n"), file_name);
       return 0;
     }
 
+  phdrs = xmalloc (elf_header.e_phnum * sizeof (*phdrs));
+
+  if (elf_header.e_ident[EI_CLASS] == ELFCLASS32)
+    {
+      Elf32_External_Phdr *phdrs32
+       = (Elf32_External_Phdr *) (map + elf_header.e_phoff);
+      for (i = 0; i < elf_header.e_phnum; i++)
+       {
+         phdrs[i].p_type = BYTE_GET (phdrs32[i].p_type);
+         phdrs[i].p_offset = BYTE_GET (phdrs32[i].p_offset);
+         phdrs[i].p_vaddr = BYTE_GET (phdrs32[i].p_vaddr);
+         phdrs[i].p_paddr = BYTE_GET (phdrs32[i].p_paddr);
+         phdrs[i].p_filesz = BYTE_GET (phdrs32[i].p_filesz);
+         phdrs[i].p_memsz = BYTE_GET (phdrs32[i].p_memsz);
+         phdrs[i].p_flags = BYTE_GET (phdrs32[i].p_flags);
+         phdrs[i].p_align = BYTE_GET (phdrs32[i].p_align);
+       }
+    }
+  else
+    {
+      Elf64_External_Phdr *phdrs64
+       = (Elf64_External_Phdr *) (map + elf_header.e_phoff);
+      for (i = 0; i < elf_header.e_phnum; i++)
+       {
+         phdrs[i].p_type = BYTE_GET (phdrs64[i].p_type);
+         phdrs[i].p_offset = BYTE_GET (phdrs64[i].p_offset);
+         phdrs[i].p_vaddr = BYTE_GET (phdrs64[i].p_vaddr);
+         phdrs[i].p_paddr = BYTE_GET (phdrs64[i].p_paddr);
+         phdrs[i].p_filesz = BYTE_GET (phdrs64[i].p_filesz);
+         phdrs[i].p_memsz = BYTE_GET (phdrs64[i].p_memsz);
+         phdrs[i].p_flags = BYTE_GET (phdrs64[i].p_flags);
+         phdrs[i].p_align = BYTE_GET (phdrs64[i].p_align);
+       }
+    }
+
+  ret = 0;
+  for (i = 0; i < elf_header.e_phnum; i++)
+    if (phdrs[i].p_type == PT_NOTE)
+      {
+       size_t offset = phdrs[i].p_offset;
+       size_t size = phdrs[i].p_filesz;
+       size_t align = phdrs[i].p_align;
+       char *buf = map + offset;
+       char *p = buf;
+
+       while (p < buf + size)
+         {
+           Elf_External_Note *xnp = (Elf_External_Note *) p;
+           Elf_Internal_Note in;
+
+           if (offsetof (Elf_External_Note, name) > buf - p + size)
+             {
+               ret = 1;
+               goto out;
+             }
+
+           in.type = BYTE_GET (xnp->type);
+           in.namesz = BYTE_GET (xnp->namesz);
+           in.namedata = xnp->name;
+           if (in.namesz > buf - in.namedata + size)
+             {
+               ret = 1;
+               goto out;
+             }
+
+           in.descsz = BYTE_GET (xnp->descsz);
+           in.descdata = p + ELF_NOTE_DESC_OFFSET (in.namesz, align);
+           in.descpos = offset + (in.descdata - buf);
+           if (in.descsz != 0
+               && (in.descdata >= buf + size
+                   || in.descsz > buf - in.descdata + size))
+             {
+               ret = 1;
+               goto out;
+             }
+
+           if (in.namesz == sizeof "GNU"
+               && strcmp (in.namedata, "GNU") == 0
+               && in.type == NT_GNU_PROPERTY_TYPE_0)
+             {
+               unsigned char *ptr;
+               unsigned char *ptr_end;
+
+               if (in.descsz < 8 || (in.descsz % align) != 0)
+                 {
+                   ret = 1;
+                   goto out;
+                 }
+
+               ptr = (unsigned char *) in.descdata;
+               ptr_end = ptr + in.descsz;
+
+               do
+                 {
+                   unsigned int type = byte_get (ptr, 4);
+                   unsigned int datasz = byte_get (ptr + 4, 4);
+                   unsigned int bitmask, old_bitmask;
+
+                   ptr += 8;
+                   if ((ptr + datasz) > ptr_end)
+                     {
+                       ret = 1;
+                       goto out;
+                     }
+
+                   if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
+                     {
+                       if (datasz != 4)
+                         {
+                           ret = 1;
+                           goto out;
+                         }
+
+                       old_bitmask = byte_get (ptr, 4);
+                       bitmask = old_bitmask;
+                       if (enable_x86_features)
+                         bitmask |= enable_x86_features;
+                       if (disable_x86_features)
+                         bitmask &= ~disable_x86_features;
+                       if (old_bitmask != bitmask)
+                         byte_put (ptr, bitmask, 4);
+                       goto out;
+                     }
+
+                   ptr += ELF_ALIGN_UP (datasz, align);
+                 }
+               while ((ptr_end - ptr) >= 8);
+             }
+
+           p += ELF_NOTE_NEXT_OFFSET (in.namesz, in.descsz, align);
+         }
+      }
+
+ out:
+  if (ret != 0)
+    error (_("%s: Invalid PT_NOTE segment\n"), file_name);
+
+  free (phdrs);
+  munmap (map, st_buf.st_size);
+
+  return ret;
+}
+
+/* Set enable_x86_features and disable_x86_features for a feature
+   string, FEATURE.  */
+
+static int
+elf_x86_feature (const char *feature, int enable)
+{
+  unsigned int x86_feature;
+  if (strcasecmp (feature, "ibt") == 0)
+    x86_feature = GNU_PROPERTY_X86_FEATURE_1_IBT;
+  else if (strcasecmp (feature, "shstk") == 0)
+    x86_feature = GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+  else
+    {
+      error (_("Unknown x86 feature: %s\n"), feature);
+      return -1;
+    }
+
+  if (enable)
+    {
+      enable_x86_features |= x86_feature;
+      disable_x86_features &= ~x86_feature;
+    }
+  else
+    {
+      disable_x86_features |= x86_feature;
+      enable_x86_features &= ~x86_feature;
+    }
+
+  return 0;
+}
+#endif
+
+/* Return ELF class for a machine type, MACH.  */
+
+static enum elfclass
+elf_class (int mach)
+{
+  switch (mach)
+    {
+    case EM_386:
+    case EM_IAMCU:
+      return ELF_CLASS_32;
+    case EM_L1OM:
+    case EM_K1OM:
+      return ELF_CLASS_64;
+    case EM_X86_64:
+    case EM_NONE:
+      return ELF_CLASS_BOTH;
+    default:
+      return ELF_CLASS_BOTH;
+    }
+}
+
+static int
+update_elf_header (const char *file_name, FILE *file)
+{
+  int class, machine, type, status, osabi;
+
   if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
     {
       error
@@ -86,17 +320,29 @@ update_elf_header (const char *file_name, FILE *file)
     return 1;
 
   class = elf_header.e_ident[EI_CLASS];
+  machine = elf_header.e_machine;
 
   /* Skip if class doesn't match. */
-  if (input_elf_class != -1 && class != input_elf_class)
+  if (input_elf_class == ELF_CLASS_UNKNOWN)
+    input_elf_class = elf_class (machine);
+
+  if (input_elf_class != ELF_CLASS_BOTH
+      && (int) input_elf_class != class)
     {
       error
-       (_("%s: Unmatched EI_CLASS: %d is not %d\n"),
+       (_("%s: Unmatched input EI_CLASS: %d is not %d\n"),
         file_name, class, input_elf_class);
       return 0;
     }
 
-  machine = elf_header.e_machine;
+  if (output_elf_class != ELF_CLASS_BOTH
+      && (int) output_elf_class != class)
+    {
+      error
+       (_("%s: Unmatched output EI_CLASS: %d is not %d\n"),
+        file_name, class, output_elf_class);
+      return 0;
+    }
 
   /* Skip if e_machine doesn't match. */
   if (input_elf_machine != -1 && machine != input_elf_machine)
@@ -170,6 +416,12 @@ get_file_header (FILE * file)
   if (fread (elf_header.e_ident, EI_NIDENT, 1, file) != 1)
     return 0;
 
+  if (elf_header.e_ident[EI_MAG0] != ELFMAG0
+      || elf_header.e_ident[EI_MAG1] != ELFMAG1
+      || elf_header.e_ident[EI_MAG2] != ELFMAG2
+      || elf_header.e_ident[EI_MAG3] != ELFMAG3)
+    return 0;
+
   /* Determine how to read the rest of the header.  */
   switch (elf_header.e_ident[EI_DATA])
     {
@@ -190,8 +442,6 @@ get_file_header (FILE * file)
   switch (elf_header.e_ident[EI_CLASS])
     {
     default:
-      error (_("Unsupported EI_CLASS: %d\n"),
-                elf_header.e_ident[EI_CLASS]);
       return 0;
 
     case ELFCLASS32:
@@ -291,6 +541,7 @@ process_archive (const char * file_name, FILE * file,
   struct archive_info nested_arch;
   size_t got;
   int ret;
+  struct stat statbuf;
 
   /* The ARCH structure is used to hold information about this archive.  */
   arch.file_name = NULL;
@@ -308,7 +559,9 @@ process_archive (const char * file_name, FILE * file,
   nested_arch.sym_table = NULL;
   nested_arch.longnames = NULL;
 
-  if (setup_archive (&arch, file_name, file, is_thin_archive, FALSE) != 0)
+  if (fstat (fileno (file), &statbuf) < 0
+      || setup_archive (&arch, file_name, file, statbuf.st_size,
+                       is_thin_archive, FALSE) != 0)
     {
       ret = 1;
       goto out;
@@ -366,6 +619,7 @@ process_archive (const char * file_name, FILE * file,
       if (qualified_name == NULL)
        {
          error (_("%s: bad archive file name\n"), file_name);
+         free (name);
          ret = 1;
          break;
        }
@@ -376,8 +630,10 @@ process_archive (const char * file_name, FILE * file,
           FILE *member_file;
           char *member_file_name = adjust_relative_path (file_name,
                                                         name, namelen);
+         free (name);
           if (member_file_name == NULL)
             {
+             free (qualified_name);
               ret = 1;
               break;
             }
@@ -388,6 +644,7 @@ process_archive (const char * file_name, FILE * file,
               error (_("Input file '%s' is not readable\n"),
                         member_file_name);
               free (member_file_name);
+             free (qualified_name);
               ret = 1;
               break;
             }
@@ -398,9 +655,12 @@ process_archive (const char * file_name, FILE * file,
 
           fclose (member_file);
           free (member_file_name);
+         free (qualified_name);
         }
       else if (is_thin_archive)
         {
+         free (name);
+
           /* This is a proxy for a member of a nested archive.  */
           archive_file_offset = arch.nested_member_origin + sizeof arch.arhdr;
 
@@ -411,6 +671,7 @@ process_archive (const char * file_name, FILE * file,
             {
               error (_("%s: failed to seek to archive member\n"),
                         nested_arch.file_name);
+             free (qualified_name);
               ret = 1;
               break;
             }
@@ -419,6 +680,7 @@ process_archive (const char * file_name, FILE * file,
         }
       else
         {
+         free (name);
           archive_file_offset = arch.next_arhdr_offset;
           arch.next_arhdr_offset += archive_file_size;
 
@@ -498,6 +760,12 @@ process_file (const char *file_name)
       rewind (file);
       archive_file_size = archive_file_offset = 0;
       ret = process_object (file_name, file);
+#ifdef HAVE_MMAP
+      if (!ret
+         && (elf_header.e_type == ET_EXEC
+             || elf_header.e_type == ET_DYN))
+       ret = update_gnu_property (file_name, file);
+#endif
     }
 
   fclose (file);
@@ -551,6 +819,10 @@ elf_osabi (const char *osabi)
 static int
 elf_machine (const char *mach)
 {
+  if (strcasecmp (mach, "i386") == 0)
+    return EM_386;
+  if (strcasecmp (mach, "iamcu") == 0)
+    return EM_IAMCU;
   if (strcasecmp (mach, "l1om") == 0)
     return EM_L1OM;
   if (strcasecmp (mach, "k1om") == 0)
@@ -567,25 +839,6 @@ elf_machine (const char *mach)
   return -1;
 }
 
-/* Return ELF class for a machine type, MACH.  */
-
-static int
-elf_class (int mach)
-{
-  switch (mach)
-    {
-    case EM_L1OM:
-    case EM_K1OM:
-    case EM_X86_64:
-      return ELFCLASS64;
-    case EM_NONE:
-      return ELFCLASSNONE;
-    default:
-      error (_("Unknown machine type: %d\n"), mach);
-      return -1;
-    }
-}
-
 /* Return ET_XXX for a type string, TYPE.  */
 
 static int
@@ -612,7 +865,11 @@ enum command_line_switch
     OPTION_INPUT_TYPE,
     OPTION_OUTPUT_TYPE,
     OPTION_INPUT_OSABI,
-    OPTION_OUTPUT_OSABI
+    OPTION_OUTPUT_OSABI,
+#ifdef HAVE_MMAP
+    OPTION_ENABLE_X86_FEATURE,
+    OPTION_DISABLE_X86_FEATURE,
+#endif
   };
 
 static struct option options[] =
@@ -623,12 +880,18 @@ static struct option options[] =
   {"output-type",      required_argument, 0, OPTION_OUTPUT_TYPE},
   {"input-osabi",      required_argument, 0, OPTION_INPUT_OSABI},
   {"output-osabi",     required_argument, 0, OPTION_OUTPUT_OSABI},
+#ifdef HAVE_MMAP
+  {"enable-x86-feature",
+                       required_argument, 0, OPTION_ENABLE_X86_FEATURE},
+  {"disable-x86-feature",
+                       required_argument, 0, OPTION_DISABLE_X86_FEATURE},
+#endif
   {"version",          no_argument, 0, 'v'},
   {"help",             no_argument, 0, 'h'},
   {0,                  no_argument, 0, 0}
 };
 
-static void
+ATTRIBUTE_NORETURN static void
 usage (FILE *stream, int exit_status)
 {
   fprintf (stream, _("Usage: %s <option(s)> elffile(s)\n"),
@@ -641,7 +904,15 @@ usage (FILE *stream, int exit_status)
   --input-type <type>         Set input file type to <type>\n\
   --output-type <type>        Set output file type to <type>\n\
   --input-osabi <osabi>       Set input OSABI to <osabi>\n\
-  --output-osabi <osabi>      Set output OSABI to <osabi>\n\
+  --output-osabi <osabi>      Set output OSABI to <osabi>\n"));
+#ifdef HAVE_MMAP
+  fprintf (stream, _("\
+  --enable-x86-feature <feature>\n\
+                              Enable x86 feature <feature>\n\
+  --disable-x86-feature <feature>\n\
+                              Disable x86 feature <feature>\n"));
+#endif
+  fprintf (stream, _("\
   -h --help                   Display this information\n\
   -v --version                Display the version number of %s\n\
 "),
@@ -677,7 +948,7 @@ main (int argc, char ** argv)
          if (input_elf_machine < 0)
            return 1;
          input_elf_class = elf_class (input_elf_machine);
-         if (input_elf_class < 0)
+         if (input_elf_class == ELF_CLASS_UNKNOWN)
            return 1;
          break;
 
@@ -685,6 +956,9 @@ main (int argc, char ** argv)
          output_elf_machine = elf_machine (optarg);
          if (output_elf_machine < 0)
            return 1;
+         output_elf_class = elf_class (output_elf_machine);
+         if (output_elf_class == ELF_CLASS_UNKNOWN)
+           return 1;
          break;
 
        case OPTION_INPUT_TYPE:
@@ -711,6 +985,18 @@ main (int argc, char ** argv)
            return 1;
          break;
 
+#ifdef HAVE_MMAP
+       case OPTION_ENABLE_X86_FEATURE:
+         if (elf_x86_feature (optarg, 1) < 0)
+           return 1;
+         break;
+
+       case OPTION_DISABLE_X86_FEATURE:
+         if (elf_x86_feature (optarg, 0) < 0)
+           return 1;
+         break;
+#endif
+
        case 'h':
          usage (stdout, 0);
 
@@ -725,6 +1011,10 @@ main (int argc, char ** argv)
 
   if (optind == argc
       || (output_elf_machine == -1
+#ifdef HAVE_MMAP
+        && ! enable_x86_features
+        && ! disable_x86_features
+#endif
          && output_elf_type == -1
          && output_elf_osabi == -1))
     usage (stderr, 1);
This page took 0.029906 seconds and 4 git commands to generate.