diff options
Diffstat (limited to 'binutils/elfedit.c')
-rw-r--r-- | binutils/elfedit.c | 256 |
1 files changed, 254 insertions, 2 deletions
diff --git a/binutils/elfedit.c b/binutils/elfedit.c index a9f7c9d..98d4fd7 100644 --- a/binutils/elfedit.c +++ b/binutils/elfedit.c @@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "config.h" #include "sysdep.h" #include <assert.h> @@ -65,6 +66,217 @@ enum elfclass 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_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); + 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 + 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 @@ -533,6 +745,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); @@ -632,7 +850,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[] = @@ -643,6 +865,12 @@ 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} @@ -661,7 +889,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\ "), @@ -734,6 +970,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); @@ -748,6 +996,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); |