X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=binutils%2Felfedit.c;h=da1984e5b1d2a7a83470e1b89812bc6a5ba604bc;hb=fe56157f92879313e0a6f46eeaee06f71314cc04;hp=28e40be321502d814c66d5173f6fab130fc94963;hpb=30fd33bb933183e837b6709e189f1122333b95e2;p=deliverable%2Fbinutils-gdb.git diff --git a/binutils/elfedit.c b/binutils/elfedit.c index 28e40be321..da1984e5b1 100644 --- a/binutils/elfedit.c +++ b/binutils/elfedit.c @@ -1,6 +1,5 @@ /* elfedit.c -- Update the ELF header of an ELF format file - Copyright 2010 - Free Software Foundation, Inc. + Copyright (C) 2010-2019 Free Software Foundation, Inc. This file is part of GNU Binutils. @@ -22,7 +21,6 @@ #include "config.h" #include "sysdep.h" #include -#include #if __GNUC__ >= 2 /* Define BFD64 here, even if our default architecture is 32 bit ELF @@ -33,15 +31,13 @@ #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" @@ -55,248 +51,359 @@ static Elf32_External_Ehdr ehdr32; 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 -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: + 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 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 0; } + /* Return if e_machine is the same as output_elf_machine. */ + if (output_elf_machine == elf_header.e_machine) + 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) - input_elf_class = elf_header.e_ident[EI_CLASS]; - else - if (elf_header.e_ident[EI_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"), - file_name, elf_header.e_ident[EI_CLASS], - input_elf_class); + error + (_("%s: Unmatched input EI_CLASS: %d is not %d\n"), + file_name, class, input_elf_class); return 0; } - /* Return if e_machine is the same as output_elf_machine. */ - if (output_elf_machine == elf_header.e_machine) - return 1; + 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) - input_elf_machine = elf_header.e_machine; - else if (elf_header.e_machine != input_elf_machine) + if (input_elf_machine != -1 && machine != input_elf_machine) { - non_fatal + error (_("%s: Unmatched e_machine: %d is not %d\n"), - file_name, elf_header.e_machine, input_elf_machine); + file_name, machine, input_elf_machine); return 0; } - /* Update e_machine. */ - switch (input_elf_class) + 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: /* We should never get here. */ 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; @@ -309,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]) { @@ -329,8 +442,6 @@ get_file_header (FILE * file) 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: @@ -362,7 +473,7 @@ get_file_header (FILE * file) 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; } @@ -403,14 +514,14 @@ process_object (const char *file_name, FILE *file) 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)) @@ -419,340 +530,6 @@ process_object (const char *file_name, FILE *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. */ @@ -763,7 +540,6 @@ process_archive (const char * file_name, FILE * file, 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. */ @@ -782,13 +558,12 @@ 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) != 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) @@ -800,7 +575,7 @@ process_archive (const char * file_name, FILE * file, /* 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; } @@ -809,14 +584,14 @@ process_archive (const char * file_name, FILE * file, { 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; @@ -831,7 +606,7 @@ process_archive (const char * file_name, FILE * file, 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; } @@ -840,7 +615,7 @@ process_archive (const char * file_name, FILE * file, 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; } @@ -860,7 +635,7 @@ process_archive (const char * file_name, FILE * file, 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; @@ -884,7 +659,7 @@ process_archive (const char * file_name, FILE * file, 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; @@ -923,16 +698,16 @@ check_file (const char *file_name, struct stat *statbuf_p) 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; } @@ -952,13 +727,13 @@ process_file (const char *file_name) 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; @@ -973,6 +748,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); @@ -980,13 +761,60 @@ process_file (const char *file_name) 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) @@ -994,54 +822,85 @@ elf_machine (const char *mach) 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 elffile(s)\n"), + fprintf (stream, _("Usage: %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 Set input machine type to \n\ --output-mach Set output machine type to \n\ + --input-type Set input file type to \n\ + --output-type Set output file type to \n\ + --input-osabi Set input OSABI to \n\ + --output-osabi Set output OSABI to \n")); +#ifdef HAVE_MMAP + fprintf (stream, _("\ + --enable-x86-feature \n\ + Enable x86 feature \n\ + --disable-x86-feature \n\ + Disable x86 feature \n")); +#endif + fprintf (stream, _("\ -h --help Display this information\n\ -v --version Display the version number of %s\n\ "), @@ -1077,7 +936,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; @@ -1085,8 +944,47 @@ 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: + 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); @@ -1099,7 +997,14 @@ main (int argc, char ** argv) } } - 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;