/* elfedit.c -- Update the ELF header of an ELF format file
- Copyright 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2010-2020 Free Software Foundation, Inc.
+ Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
This file is part of GNU Binutils.
#include "config.h"
#include "sysdep.h"
#include <assert.h>
-#include <sys/stat.h>
#if __GNUC__ >= 2
/* Define BFD64 here, even if our default architecture is 32 bit ELF
#endif
#include "bfd.h"
+#include "elfcomm.h"
#include "bucomm.h"
#include "elf/common.h"
#include "elf/external.h"
#include "elf/internal.h"
-
-#include "aout/ar.h"
-
#include "getopt.h"
#include "libiberty.h"
#include "safe-ctype.h"
static Elf64_External_Ehdr ehdr64;
static int input_elf_machine = -1;
static int output_elf_machine = -1;
-static int input_elf_class = -1;
+static int input_elf_type = -1;
+static int output_elf_type = -1;
+static int input_elf_osabi = -1;
+static int output_elf_osabi = -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;
-#define streq(a,b) (strcmp ((a), (b)) == 0)
-#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0)
-#define const_strneq(a,b) (strncmp ((a), (b), sizeof (b) - 1) == 0)
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
-void
-non_fatal (const char *message, ...)
-{
- va_list args;
+static unsigned int enable_x86_features;
+static unsigned int disable_x86_features;
- va_start (args, message);
- fprintf (stderr, _("%s: Error: "), program_name);
- vfprintf (stderr, message, args);
- va_end (args);
-}
+static int
+update_gnu_property (const char *file_name, FILE *file)
+{
+ char *map;
+ Elf_Internal_Phdr *phdrs;
+ struct stat st_buf;
+ unsigned int i;
+ int ret;
-#define BYTE_GET(field) byte_get (field, sizeof (field))
-#define BYTE_PUT(field, val) byte_put (field, val, sizeof (field))
+ if (!enable_x86_features && !disable_x86_features)
+ return 0;
-static bfd_vma (*byte_get) (unsigned char *, int);
-static void (*byte_put) (unsigned char *, bfd_vma, int);
+ if (elf_header.e_machine != EM_386
+ && elf_header.e_machine != EM_X86_64)
+ {
+ error (_("%s: Not an i386 nor x86-64 ELF file\n"), file_name);
+ return 0;
+ }
-static bfd_vma
-byte_get_little_endian (unsigned char *field, int size)
-{
- switch (size)
+ if (fstat (fileno (file), &st_buf) < 0)
{
- case 1:
- return *field;
-
- case 2:
- return ((unsigned int) (field[0]))
- | (((unsigned int) (field[1])) << 8);
-
- case 4:
- return ((unsigned long) (field[0]))
- | (((unsigned long) (field[1])) << 8)
- | (((unsigned long) (field[2])) << 16)
- | (((unsigned long) (field[3])) << 24);
-
- case 8:
- if (sizeof (bfd_vma) == 8)
- return ((bfd_vma) (field[0]))
- | (((bfd_vma) (field[1])) << 8)
- | (((bfd_vma) (field[2])) << 16)
- | (((bfd_vma) (field[3])) << 24)
- | (((bfd_vma) (field[4])) << 32)
- | (((bfd_vma) (field[5])) << 40)
- | (((bfd_vma) (field[6])) << 48)
- | (((bfd_vma) (field[7])) << 56);
- else if (sizeof (bfd_vma) == 4)
- /* We want to extract data from an 8 byte wide field and
- place it into a 4 byte wide field. Since this is a little
- endian source we can just use the 4 byte extraction code. */
- return ((unsigned long) (field[0]))
- | (((unsigned long) (field[1])) << 8)
- | (((unsigned long) (field[2])) << 16)
- | (((unsigned long) (field[3])) << 24);
+ error (_("%s: stat () failed\n"), file_name);
+ return 1;
+ }
- default:
- non_fatal (_("Unhandled data length: %d\n"), size);
- abort ();
+ 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;
}
-}
-static bfd_vma
-byte_get_big_endian (unsigned char *field, int size)
-{
- switch (size)
+ phdrs = xmalloc (elf_header.e_phnum * sizeof (*phdrs));
+
+ if (elf_header.e_ident[EI_CLASS] == ELFCLASS32)
{
- case 1:
- return *field;
-
- case 2:
- return ((unsigned int) (field[1])) | (((int) (field[0])) << 8);
-
- case 4:
- return ((unsigned long) (field[3]))
- | (((unsigned long) (field[2])) << 8)
- | (((unsigned long) (field[1])) << 16)
- | (((unsigned long) (field[0])) << 24);
-
- case 8:
- if (sizeof (bfd_vma) == 8)
- return ((bfd_vma) (field[7]))
- | (((bfd_vma) (field[6])) << 8)
- | (((bfd_vma) (field[5])) << 16)
- | (((bfd_vma) (field[4])) << 24)
- | (((bfd_vma) (field[3])) << 32)
- | (((bfd_vma) (field[2])) << 40)
- | (((bfd_vma) (field[1])) << 48)
- | (((bfd_vma) (field[0])) << 56);
- else if (sizeof (bfd_vma) == 4)
+ Elf32_External_Phdr *phdrs32
+ = (Elf32_External_Phdr *) (map + elf_header.e_phoff);
+ for (i = 0; i < elf_header.e_phnum; i++)
{
- /* Although we are extracing data from an 8 byte wide field,
- we are returning only 4 bytes of data. */
- field += 4;
- return ((unsigned long) (field[3]))
- | (((unsigned long) (field[2])) << 8)
- | (((unsigned long) (field[1])) << 16)
- | (((unsigned long) (field[0])) << 24);
+ 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);
}
-
- default:
- non_fatal (_("Unhandled data length: %d\n"), size);
- abort ();
}
+
+ 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;
}
-static void
-byte_put_little_endian (unsigned char * field, bfd_vma value, int size)
+/* Set enable_x86_features and disable_x86_features for a feature
+ string, FEATURE. */
+
+static int
+elf_x86_feature (const char *feature, int enable)
{
- switch (size)
+ 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
{
- case 8:
- field[7] = (((value >> 24) >> 24) >> 8) & 0xff;
- field[6] = ((value >> 24) >> 24) & 0xff;
- field[5] = ((value >> 24) >> 16) & 0xff;
- field[4] = ((value >> 24) >> 8) & 0xff;
- /* Fall through. */
- case 4:
- field[3] = (value >> 24) & 0xff;
- field[2] = (value >> 16) & 0xff;
- /* Fall through. */
- case 2:
- field[1] = (value >> 8) & 0xff;
- /* Fall through. */
- case 1:
- field[0] = value & 0xff;
- break;
+ error (_("Unknown x86 feature: %s\n"), feature);
+ return -1;
+ }
- default:
- non_fatal (_("Unhandled data length: %d\n"), size);
- abort ();
+ 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 void
-byte_put_big_endian (unsigned char * field, bfd_vma value, int size)
+static enum elfclass
+elf_class (int mach)
{
- switch (size)
+ switch (mach)
{
- case 8:
- field[7] = value & 0xff;
- field[6] = (value >> 8) & 0xff;
- field[5] = (value >> 16) & 0xff;
- field[4] = (value >> 24) & 0xff;
- value >>= 16;
- value >>= 16;
- /* Fall through. */
- case 4:
- field[3] = value & 0xff;
- field[2] = (value >> 8) & 0xff;
- value >>= 16;
- /* Fall through. */
- case 2:
- field[1] = value & 0xff;
- value >>= 8;
- /* Fall through. */
- case 1:
- field[0] = value & 0xff;
- break;
-
+ case EM_386:
+ case EM_IAMCU:
+ return ELF_CLASS_32;
+ case EM_L1OM:
+ case EM_K1OM:
+ case EM_AMDGPU:
+ return ELF_CLASS_64;
+ case EM_X86_64:
+ case EM_NONE:
+ return ELF_CLASS_BOTH;
default:
- non_fatal (_("Unhandled data length: %d\n"), size);
- abort ();
+ return ELF_CLASS_BOTH;
}
}
static int
update_elf_header (const char *file_name, FILE *file)
{
- int class, machine, status;
-
- 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)
- {
- non_fatal
- (_("%s: Not an ELF file - wrong magic bytes at the start\n"),
- file_name);
- return 0;
- }
+ int class, machine, type, status, osabi;
if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
{
- non_fatal
+ error
(_("%s: Unsupported EI_VERSION: %d is not %d\n"),
file_name, elf_header.e_ident[EI_VERSION],
EV_CURRENT);
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)
{
- non_fatal
- (_("%s: Unmatched EI_CLASS: %d is not %d\n"),
+ error
+ (_("%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)
{
- non_fatal
+ error
(_("%s: Unmatched e_machine: %d is not %d\n"),
file_name, machine, input_elf_machine);
return 0;
}
- /* Update e_machine. */
+ type = elf_header.e_type;
+
+ /* Skip if e_type doesn't match. */
+ if (input_elf_type != -1 && type != input_elf_type)
+ {
+ error
+ (_("%s: Unmatched e_type: %d is not %d\n"),
+ file_name, type, input_elf_type);
+ return 0;
+ }
+
+ osabi = elf_header.e_ident[EI_OSABI];
+
+ /* Skip if OSABI doesn't match. */
+ if (input_elf_osabi != -1 && osabi != input_elf_osabi)
+ {
+ error
+ (_("%s: Unmatched EI_OSABI: %d is not %d\n"),
+ file_name, osabi, input_elf_osabi);
+ return 0;
+ }
+
+ /* Update e_machine, e_type and EI_OSABI. */
switch (class)
{
default:
abort ();
break;
case ELFCLASS32:
- BYTE_PUT (ehdr32.e_machine, output_elf_machine);
+ if (output_elf_machine != -1)
+ BYTE_PUT (ehdr32.e_machine, output_elf_machine);
+ if (output_elf_type != -1)
+ BYTE_PUT (ehdr32.e_type, output_elf_type);
+ if (output_elf_osabi != -1)
+ ehdr32.e_ident[EI_OSABI] = output_elf_osabi;
status = fwrite (&ehdr32, sizeof (ehdr32), 1, file) == 1;
break;
case ELFCLASS64:
- BYTE_PUT (ehdr64.e_machine, output_elf_machine);
+ if (output_elf_machine != -1)
+ BYTE_PUT (ehdr64.e_machine, output_elf_machine);
+ if (output_elf_type != -1)
+ BYTE_PUT (ehdr64.e_type, output_elf_type);
+ if (output_elf_osabi != -1)
+ ehdr64.e_ident[EI_OSABI] = output_elf_osabi;
status = fwrite (&ehdr64, sizeof (ehdr64), 1, file) == 1;
break;
}
if (status != 1)
- non_fatal (_("%s: Failed to update ELF header: %s\n"),
+ error (_("%s: Failed to update ELF header: %s\n"),
file_name, strerror (errno));
return status;
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])
{
switch (elf_header.e_ident[EI_CLASS])
{
default:
- non_fatal (_("Unsupported EI_CLASS: %d\n"),
- elf_header.e_ident[EI_CLASS]);
return 0;
case ELFCLASS32:
overwriting things. */
if (sizeof (bfd_vma) < 8)
{
- non_fatal (_("This executable has been built without support for a\n\
+ error (_("This executable has been built without support for a\n\
64 bit data type and so it cannot process 64 bit ELF files.\n"));
return 0;
}
if (! get_file_header (file))
{
- non_fatal (_("%s: Failed to read ELF header\n"), file_name);
+ error (_("%s: Failed to read ELF header\n"), file_name);
return 1;
}
/* Go to the position of the ELF header. */
if (fseek (file, offset, SEEK_SET) != 0)
{
- non_fatal (_("%s: Failed to seek to ELF header\n"), file_name);
+ error (_("%s: Failed to seek to ELF header\n"), file_name);
}
if (! update_elf_header (file_name, file))
return 0;
}
-/* Return the path name for a proxy entry in a thin archive, adjusted relative
- to the path name of the thin archive itself if necessary. Always returns
- a pointer to malloc'ed memory. */
-
-static char *
-adjust_relative_path (const char *file_name, char * name, int name_len)
-{
- char * member_file_name;
- const char * base_name = lbasename (file_name);
-
- /* This is a proxy entry for a thin archive member.
- If the extended name table contains an absolute path
- name, or if the archive is in the current directory,
- use the path name as given. Otherwise, we need to
- find the member relative to the directory where the
- archive is located. */
- if (IS_ABSOLUTE_PATH (name) || base_name == file_name)
- {
- member_file_name = malloc (name_len + 1);
- if (member_file_name == NULL)
- {
- non_fatal (_("Out of memory\n"));
- return NULL;
- }
- memcpy (member_file_name, name, name_len);
- member_file_name[name_len] = '\0';
- }
- else
- {
- /* Concatenate the path components of the archive file name
- to the relative path name from the extended name table. */
- size_t prefix_len = base_name - file_name;
- member_file_name = malloc (prefix_len + name_len + 1);
- if (member_file_name == NULL)
- {
- non_fatal (_("Out of memory\n"));
- return NULL;
- }
- memcpy (member_file_name, file_name, prefix_len);
- memcpy (member_file_name + prefix_len, name, name_len);
- member_file_name[prefix_len + name_len] = '\0';
- }
- return member_file_name;
-}
-
-/* Structure to hold information about an archive file. */
-
-struct archive_info
-{
- char * file_name; /* Archive file name. */
- FILE * file; /* Open file descriptor. */
- unsigned long index_num; /* Number of symbols in table. */
- unsigned long * index_array; /* The array of member offsets. */
- char * sym_table; /* The symbol table. */
- unsigned long sym_size; /* Size of the symbol table. */
- char * longnames; /* The long file names table. */
- unsigned long longnames_size; /* Size of the long file names table. */
- unsigned long nested_member_origin; /* Origin in the nested archive of the current member. */
- unsigned long next_arhdr_offset; /* Offset of the next archive header. */
- bfd_boolean is_thin_archive; /* TRUE if this is a thin archive. */
- struct ar_hdr arhdr; /* Current archive header. */
-};
-
-/* Read the symbol table and long-name table from an archive. */
-
-static int
-setup_archive (struct archive_info * arch, const char * file_name,
- FILE * file, bfd_boolean is_thin_archive)
-{
- size_t got;
- unsigned long size;
-
- arch->file_name = strdup (file_name);
- arch->file = file;
- arch->index_num = 0;
- arch->index_array = NULL;
- arch->sym_table = NULL;
- arch->sym_size = 0;
- arch->longnames = NULL;
- arch->longnames_size = 0;
- arch->nested_member_origin = 0;
- arch->is_thin_archive = is_thin_archive;
- arch->next_arhdr_offset = SARMAG;
-
- /* Read the first archive member header. */
- if (fseek (file, SARMAG, SEEK_SET) != 0)
- {
- non_fatal (_("%s: failed to seek to first archive header\n"),
- file_name);
- return 1;
- }
- got = fread (&arch->arhdr, 1, sizeof arch->arhdr, file);
- if (got != sizeof arch->arhdr)
- {
- if (got == 0)
- return 0;
-
- non_fatal (_("%s: failed to read archive header\n"), file_name);
- return 1;
- }
-
- /* See if this is the archive symbol table. */
- if (const_strneq (arch->arhdr.ar_name, "/ ")
- || const_strneq (arch->arhdr.ar_name, "/SYM64/ "))
- {
- size = strtoul (arch->arhdr.ar_size, NULL, 10);
- size = size + (size & 1);
-
- arch->next_arhdr_offset += sizeof arch->arhdr + size;
-
- if (fseek (file, size, SEEK_CUR) != 0)
- {
- non_fatal (_("%s: failed to skip archive symbol table\n"),
- file_name);
- return 1;
- }
-
- /* Read the next archive header. */
- got = fread (&arch->arhdr, 1, sizeof arch->arhdr, file);
- if (got != sizeof arch->arhdr)
- {
- if (got == 0)
- return 0;
- non_fatal (_("%s: failed to read archive header following archive index\n"),
- file_name);
- return 1;
- }
- }
-
- if (const_strneq (arch->arhdr.ar_name, "// "))
- {
- /* This is the archive string table holding long member names. */
- arch->longnames_size = strtoul (arch->arhdr.ar_size, NULL, 10);
- arch->next_arhdr_offset += sizeof arch->arhdr + arch->longnames_size;
-
- arch->longnames = malloc (arch->longnames_size);
- if (arch->longnames == NULL)
- {
- non_fatal (_("Out of memory reading long symbol names in archive\n"));
- return 1;
- }
-
- if (fread (arch->longnames, arch->longnames_size, 1, file) != 1)
- {
- free (arch->longnames);
- arch->longnames = NULL;
- non_fatal (_("%s: failed to read long symbol name string table\n")
- , file_name);
- return 1;
- }
-
- if ((arch->longnames_size & 1) != 0)
- getc (file);
- }
-
- return 0;
-}
-
-/* Release the memory used for the archive information. */
-
-static void
-release_archive (struct archive_info * arch)
-{
- if (arch->file_name != NULL)
- free (arch->file_name);
- if (arch->index_array != NULL)
- free (arch->index_array);
- if (arch->sym_table != NULL)
- free (arch->sym_table);
- if (arch->longnames != NULL)
- free (arch->longnames);
-}
-
-/* Open and setup a nested archive, if not already open. */
-
-static int
-setup_nested_archive (struct archive_info * nested_arch, char * member_file_name)
-{
- FILE * member_file;
-
- /* Have we already setup this archive? */
- if (nested_arch->file_name != NULL
- && streq (nested_arch->file_name, member_file_name))
- return 0;
-
- /* Close previous file and discard cached information. */
- if (nested_arch->file != NULL)
- fclose (nested_arch->file);
- release_archive (nested_arch);
-
- member_file = fopen (member_file_name, "r+b");
- if (member_file == NULL)
- return 1;
- return setup_archive (nested_arch, member_file_name, member_file,
- FALSE);
-}
-
-static char *
-get_archive_member_name_at (struct archive_info * arch,
- unsigned long offset,
- struct archive_info * nested_arch);
-
-/* Get the name of an archive member from the current archive header.
- For simple names, this will modify the ar_name field of the current
- archive header. For long names, it will return a pointer to the
- longnames table. For nested archives, it will open the nested archive
- and get the name recursively. NESTED_ARCH is a single-entry cache so
- we don't keep rereading the same information from a nested archive. */
-
-static char *
-get_archive_member_name (struct archive_info * arch,
- struct archive_info * nested_arch)
-{
- unsigned long j, k;
-
- if (arch->arhdr.ar_name[0] == '/')
- {
- /* We have a long name. */
- char * endp;
- char * member_file_name;
- char * member_name;
-
- arch->nested_member_origin = 0;
- k = j = strtoul (arch->arhdr.ar_name + 1, &endp, 10);
- if (arch->is_thin_archive && endp != NULL && * endp == ':')
- arch->nested_member_origin = strtoul (endp + 1, NULL, 10);
-
- while ((j < arch->longnames_size)
- && (arch->longnames[j] != '\n')
- && (arch->longnames[j] != '\0'))
- j++;
- if (arch->longnames[j-1] == '/')
- j--;
- arch->longnames[j] = '\0';
-
- if (!arch->is_thin_archive || arch->nested_member_origin == 0)
- return arch->longnames + k;
-
- /* This is a proxy for a member of a nested archive.
- Find the name of the member in that archive. */
- member_file_name = adjust_relative_path (arch->file_name,
- arch->longnames + k,
- j - k);
- if (member_file_name != NULL
- && setup_nested_archive (nested_arch, member_file_name) == 0
- && (member_name = get_archive_member_name_at (nested_arch,
- arch->nested_member_origin,
- NULL)) != NULL)
- {
- free (member_file_name);
- return member_name;
- }
- free (member_file_name);
-
- /* Last resort: just return the name of the nested archive. */
- return arch->longnames + k;
- }
-
- /* We have a normal (short) name. */
- j = 0;
- while ((arch->arhdr.ar_name[j] != '/') && (j < 16))
- j++;
- arch->arhdr.ar_name[j] = '\0';
- return arch->arhdr.ar_name;
-}
-
-/* Get the name of an archive member at a given OFFSET within an
- archive ARCH. */
-
-static char *
-get_archive_member_name_at (struct archive_info * arch,
- unsigned long offset,
- struct archive_info * nested_arch)
-{
- size_t got;
-
- if (fseek (arch->file, offset, SEEK_SET) != 0)
- {
- non_fatal (_("%s: failed to seek to next file name\n"),
- arch->file_name);
- return NULL;
- }
- got = fread (&arch->arhdr, 1, sizeof arch->arhdr, arch->file);
- if (got != sizeof arch->arhdr)
- {
- non_fatal (_("%s: failed to read archive header\n"),
- arch->file_name);
- return NULL;
- }
- if (memcmp (arch->arhdr.ar_fmag, ARFMAG, 2) != 0)
- {
- non_fatal (_("%s: did not find a valid archive header\n"),
- arch->file_name);
- return NULL;
- }
-
- return get_archive_member_name (arch, nested_arch);
-}
-
-/* Construct a string showing the name of the archive member, qualified
- with the name of the containing archive file. For thin archives, we
- use square brackets to denote the indirection. For nested archives,
- we show the qualified name of the external member inside the square
- brackets (e.g., "thin.a[normal.a(foo.o)]"). */
-
-static char *
-make_qualified_name (struct archive_info * arch,
- struct archive_info * nested_arch,
- char * member_name)
-{
- size_t len;
- char * name;
-
- len = strlen (arch->file_name) + strlen (member_name) + 3;
- if (arch->is_thin_archive && arch->nested_member_origin != 0)
- len += strlen (nested_arch->file_name) + 2;
-
- name = malloc (len);
- if (name == NULL)
- {
- non_fatal (_("Out of memory\n"));
- return NULL;
- }
-
- if (arch->is_thin_archive && arch->nested_member_origin != 0)
- snprintf (name, len, "%s[%s(%s)]", arch->file_name, nested_arch->file_name, member_name);
- else if (arch->is_thin_archive)
- snprintf (name, len, "%s[%s]", arch->file_name, member_name);
- else
- snprintf (name, len, "%s(%s)", arch->file_name, member_name);
-
- return name;
-}
-
/* Process an ELF archive.
On entry the file is positioned just after the ARMAG string. */
struct archive_info arch;
struct archive_info nested_arch;
size_t got;
- size_t file_name_size;
int ret;
/* The ARCH structure is used to hold information about this archive. */
nested_arch.sym_table = NULL;
nested_arch.longnames = NULL;
- if (setup_archive (&arch, file_name, file, is_thin_archive) != 0)
+ if (setup_archive (&arch, file_name, file, is_thin_archive, FALSE) != 0)
{
ret = 1;
goto out;
}
- file_name_size = strlen (file_name);
ret = 0;
while (1)
/* Read the next archive header. */
if (fseek (file, arch.next_arhdr_offset, SEEK_SET) != 0)
{
- non_fatal (_("%s: failed to seek to next archive header\n"),
+ error (_("%s: failed to seek to next archive header\n"),
file_name);
return 1;
}
{
if (got == 0)
break;
- non_fatal (_("%s: failed to read archive header\n"),
+ error (_("%s: failed to read archive header\n"),
file_name);
ret = 1;
break;
}
if (memcmp (arch.arhdr.ar_fmag, ARFMAG, 2) != 0)
{
- non_fatal (_("%s: did not find a valid archive header\n"),
+ error (_("%s: did not find a valid archive header\n"),
arch.file_name);
ret = 1;
break;
name = get_archive_member_name (&arch, &nested_arch);
if (name == NULL)
{
- non_fatal (_("%s: bad archive file name\n"), file_name);
+ error (_("%s: bad archive file name\n"), file_name);
ret = 1;
break;
}
qualified_name = make_qualified_name (&arch, &nested_arch, name);
if (qualified_name == NULL)
{
- non_fatal (_("%s: bad archive file name\n"), file_name);
+ error (_("%s: bad archive file name\n"), file_name);
ret = 1;
break;
}
member_file = fopen (member_file_name, "r+b");
if (member_file == NULL)
{
- non_fatal (_("Input file '%s' is not readable\n"),
+ error (_("Input file '%s' is not readable\n"),
member_file_name);
free (member_file_name);
ret = 1;
if (fseek (nested_arch.file, archive_file_offset,
SEEK_SET) != 0)
{
- non_fatal (_("%s: failed to seek to archive member\n"),
+ error (_("%s: failed to seek to archive member\n"),
nested_arch.file_name);
ret = 1;
break;
if (stat (file_name, statbuf_p) < 0)
{
if (errno == ENOENT)
- non_fatal (_("'%s': No such file\n"), file_name);
+ error (_("'%s': No such file\n"), file_name);
else
- non_fatal (_("Could not locate '%s'. System error message: %s\n"),
+ error (_("Could not locate '%s'. System error message: %s\n"),
file_name, strerror (errno));
return 1;
}
if (! S_ISREG (statbuf_p->st_mode))
{
- non_fatal (_("'%s' is not an ordinary file\n"), file_name);
+ error (_("'%s' is not an ordinary file\n"), file_name);
return 1;
}
file = fopen (file_name, "r+b");
if (file == NULL)
{
- non_fatal (_("Input file '%s' is not readable\n"), file_name);
+ error (_("Input file '%s' is not readable\n"), file_name);
return 1;
}
if (fread (armag, SARMAG, 1, file) != 1)
{
- non_fatal (_("%s: Failed to read file's magic number\n"),
+ error (_("%s: Failed to read file's magic number\n"),
file_name);
fclose (file);
return 1;
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);
return ret;
}
+static const struct
+{
+ int osabi;
+ const char *name;
+}
+osabis[] =
+{
+ { ELFOSABI_NONE, "none" },
+ { ELFOSABI_HPUX, "HPUX" },
+ { ELFOSABI_NETBSD, "NetBSD" },
+ { ELFOSABI_GNU, "GNU" },
+ { ELFOSABI_GNU, "Linux" },
+ { ELFOSABI_SOLARIS, "Solaris" },
+ { ELFOSABI_AIX, "AIX" },
+ { ELFOSABI_IRIX, "Irix" },
+ { ELFOSABI_FREEBSD, "FreeBSD" },
+ { ELFOSABI_TRU64, "TRU64" },
+ { ELFOSABI_MODESTO, "Modesto" },
+ { ELFOSABI_OPENBSD, "OpenBSD" },
+ { ELFOSABI_OPENVMS, "OpenVMS" },
+ { ELFOSABI_NSK, "NSK" },
+ { ELFOSABI_AROS, "AROS" },
+ { ELFOSABI_FENIXOS, "FenixOS" }
+};
+
+/* Return ELFOSABI_XXX for an OSABI string, OSABI. */
+
+static int
+elf_osabi (const char *osabi)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (osabis); i++)
+ if (strcasecmp (osabi, osabis[i].name) == 0)
+ return osabis[i].osabi;
+
+ error (_("Unknown OSABI: %s\n"), osabi);
+
+ return -1;
+}
+
/* Return EM_XXX for a machine string, MACH. */
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)
+ return EM_K1OM;
if (strcasecmp (mach, "x86_64") == 0)
return EM_X86_64;
if (strcasecmp (mach, "x86-64") == 0)
return EM_X86_64;
+ if (strcasecmp (mach, "amdgcn") == 0)
+ return EM_AMDGPU;
if (strcasecmp (mach, "none") == 0)
return EM_NONE;
- non_fatal (_("Unknown machine type: %s\n"), mach);
+ error (_("Unknown machine type: %s\n"), mach);
return -1;
}
-/* Return ELF class for a machine type, MACH. */
+/* Return ET_XXX for a type string, TYPE. */
static int
-elf_class (int mach)
+elf_type (const char *type)
{
- switch (mach)
- {
- case EM_L1OM:
- case EM_X86_64:
- return ELFCLASS64;
- case EM_NONE:
- return ELFCLASSNONE;
- default:
- non_fatal (_("Unknown machine type: %d\n"), mach);
- return -1;
- }
+ if (strcasecmp (type, "rel") == 0)
+ return ET_REL;
+ if (strcasecmp (type, "exec") == 0)
+ return ET_EXEC;
+ if (strcasecmp (type, "dyn") == 0)
+ return ET_DYN;
+ if (strcasecmp (type, "none") == 0)
+ return ET_NONE;
+
+ error (_("Unknown type: %s\n"), type);
+
+ return -1;
}
enum command_line_switch
{
OPTION_INPUT_MACH = 150,
- OPTION_OUTPUT_MACH
+ OPTION_OUTPUT_MACH,
+ OPTION_INPUT_TYPE,
+ OPTION_OUTPUT_TYPE,
+ OPTION_INPUT_OSABI,
+ OPTION_OUTPUT_OSABI,
+#ifdef HAVE_MMAP
+ OPTION_ENABLE_X86_FEATURE,
+ OPTION_DISABLE_X86_FEATURE,
+#endif
};
static struct option options[] =
{
{"input-mach", required_argument, 0, OPTION_INPUT_MACH},
{"output-mach", required_argument, 0, OPTION_OUTPUT_MACH},
+ {"input-type", required_argument, 0, OPTION_INPUT_TYPE},
+ {"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)] --output-mach <machine> elffile(s)\n"),
+ fprintf (stream, _("Usage: %s <option(s)> elffile(s)\n"),
program_name);
fprintf (stream, _(" Update the ELF header of ELF files\n"));
fprintf (stream, _(" The options are:\n"));
fprintf (stream, _("\
--input-mach <machine> Set input machine type to <machine>\n\
--output-mach <machine> Set output machine type to <machine>\n\
+ --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"));
+#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\
"),
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;
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:
+ input_elf_type = elf_type (optarg);
+ if (input_elf_type < 0)
+ return 1;
break;
+ case OPTION_OUTPUT_TYPE:
+ output_elf_type = elf_type (optarg);
+ if (output_elf_type < 0)
+ return 1;
+ break;
+
+ case OPTION_INPUT_OSABI:
+ input_elf_osabi = elf_osabi (optarg);
+ if (input_elf_osabi < 0)
+ return 1;
+ break;
+
+ case OPTION_OUTPUT_OSABI:
+ output_elf_osabi = elf_osabi (optarg);
+ if (output_elf_osabi < 0)
+ 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);
}
}
- if (optind == argc || output_elf_machine == -1)
+ 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);
status = 0;