X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=binutils%2Felfedit.c;h=e33c340d758ce78f17706d6cc6a5ee81b6aac8d8;hb=b05a0fc79b9eed659cf52b4706f11700b92ae4af;hp=02b9c98f851348b5c338c2f1c0a7e4caebc2ed79;hpb=aa9f7286724c2e47077f2f5456d464a961f8e8a4;p=binutils-gdb.git diff --git a/binutils/elfedit.c b/binutils/elfedit.c index 02b9c98f851..e33c340d758 100644 --- a/binutils/elfedit.c +++ b/binutils/elfedit.c @@ -1,5 +1,5 @@ /* elfedit.c -- Update the ELF header of an ELF format file - Copyright (C) 2010-2015 Free Software Foundation, Inc. + Copyright (C) 2010-2021 Free Software Foundation, Inc. This file is part of GNU Binutils. @@ -18,7 +18,9 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "config.h" #include "sysdep.h" +#include "libiberty.h" #include #if __GNUC__ >= 2 @@ -65,6 +67,224 @@ enum elfclass static enum elfclass input_elf_class = ELF_CLASS_UNKNOWN; static enum elfclass output_elf_class = ELF_CLASS_BOTH; +#ifdef HAVE_MMAP +#include + +static unsigned int enable_x86_features; +static unsigned int disable_x86_features; + +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; + + 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 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 if (strcasecmp (feature, "lam_u48") == 0) + x86_feature = GNU_PROPERTY_X86_FEATURE_1_LAM_U48; + else if (strcasecmp (feature, "lam_u57") == 0) + x86_feature = GNU_PROPERTY_X86_FEATURE_1_LAM_U57; + 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 @@ -91,17 +311,6 @@ update_elf_header (const char *file_name, FILE *file) { int class, machine, type, status, osabi; - 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) - { - error - (_("%s: Not an ELF file - wrong magic bytes at the start\n"), - file_name); - return 0; - } - if (elf_header.e_ident[EI_VERSION] != EV_CURRENT) { error @@ -212,6 +421,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]) { @@ -232,8 +447,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: @@ -327,12 +540,13 @@ process_object (const char *file_name, FILE *file) static int process_archive (const char * file_name, FILE * file, - bfd_boolean is_thin_archive) + bool is_thin_archive) { struct archive_info arch; 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; @@ -350,7 +564,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; @@ -408,6 +624,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; } @@ -418,8 +635,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; } @@ -430,6 +649,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; } @@ -443,6 +663,8 @@ process_archive (const char * file_name, FILE * file, } 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; @@ -453,6 +675,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; } @@ -461,6 +684,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; @@ -497,6 +721,20 @@ check_file (const char *file_name, struct stat *statbuf_p) return 1; } +#if defined (_WIN32) && !defined (__CYGWIN__) + else if (statbuf_p->st_size == 0) + { + /* MS-Windows 'stat' reports the null device as a regular file; + fix that. */ + int fd = open (file_name, O_RDONLY | O_BINARY); + if (isatty (fd)) + { + statbuf_p->st_mode &= ~S_IFREG; + statbuf_p->st_mode |= S_IFCHR; + } + } +#endif + if (! S_ISREG (statbuf_p->st_mode)) { error (_("'%s' is not an ordinary file\n"), file_name); @@ -532,14 +770,20 @@ process_file (const char *file_name) } if (memcmp (armag, ARMAG, SARMAG) == 0) - ret = process_archive (file_name, file, FALSE); + ret = process_archive (file_name, file, false); else if (memcmp (armag, ARMAGT, SARMAG) == 0) - ret = process_archive (file_name, file, TRUE); + ret = process_archive (file_name, file, true); else { 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); @@ -639,7 +883,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[] = @@ -650,31 +898,59 @@ 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) { + unsigned int i; + char *osabi = concat (osabis[0].name, NULL); + + for (i = 1; i < ARRAY_SIZE (osabis); i++) + osabi = reconcat (osabi, osabi, "|", osabis[i].name, NULL); + 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\ + --input-mach [none|i386|iamcu|l1om|k1om|x86_64]\n\ + Set input machine type\n\ + --output-mach [none|i386|iamcu|l1om|k1om|x86_64]\n\ + Set output machine type\n\ + --input-type [none|rel|exec|dyn]\n\ + Set input file type\n\ + --output-type [none|rel|exec|dyn]\n\ + Set output file type\n\ + --input-osabi [%s]\n\ + Set input OSABI\n\ + --output-osabi [%s]\n\ + Set output OSABI\n"), + osabi, osabi); +#ifdef HAVE_MMAP + fprintf (stream, _("\ + --enable-x86-feature [ibt|shstk|lam_u48|lam_u57]\n\ + Enable x86 feature\n\ + --disable-x86-feature [ibt|shstk|lam_u48|lam_u57]\n\ + Disable x86 feature\n")); +#endif + fprintf (stream, _("\ -h --help Display this information\n\ -v --version Display the version number of %s\n\ "), program_name); if (REPORT_BUGS_TO[0] && exit_status == 0) fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); + free (osabi); exit (exit_status); } @@ -683,12 +959,10 @@ main (int argc, char ** argv) { int c, status; -#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) +#ifdef HAVE_LC_MESSAGES setlocale (LC_MESSAGES, ""); #endif -#if defined (HAVE_SETLOCALE) setlocale (LC_CTYPE, ""); -#endif bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); @@ -741,6 +1015,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); @@ -755,6 +1041,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);