From 8a6b075bc07f66678ed0176f895847df3ea7fcef Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 6 Nov 2018 09:38:33 -0800 Subject: [PATCH] elfedit: Add --enable-x86-feature/--disable-x86-feature Add --enable-x86-feature and --disable-x86-feature options to elfedit to set and clear the IBT and SHSTK bits in program property in ELF executables and shared objects. binutils/ * doc/binutils.texi: Document --enable-x86-feature and --disable-x86-feature options for elfedit. * elfedit.c: Include "config.h" and . (enable_x86_features): New. (disable_x86_features): Likewise. (update_gnu_property): Likewise. (elf_x86_feature): Likewise. (process_file): Call update_gnu_property on ET_EXEC or ET_DYN file. (command_line_switch): Add OPTION_ENABLE_X86_FEATURE and OPTION_DISABLE_X86_FEATURE. (options): Add--enable-x86-feature and --disable-x86-feature. (usage): Likewise. (main): Handle OPTION_ENABLE_X86_FEATURE and OPTION_DISABLE_X86_FEATURE. ld/ * testsuite/config/default.exp (ELFEDIT): New. * testsuite/ld-elf/linux-x86.exp (elfedit_test): New proc. Run elfedit tests. * testsuite/ld-elf/x86-feature-1a.rd: New file. * testsuite/ld-elf/x86-feature-1b.rd: Likewise. * testsuite/ld-elf/x86-feature-1c.rd: Likewise. * testsuite/ld-elf/x86-feature-1d.rd: Likewise. * testsuite/ld-elf/x86-feature-1e.rd: Likewise. --- binutils/ChangeLog | 18 +++ binutils/doc/binutils.texi | 34 +++-- binutils/elfedit.c | 256 +++++++++++++++++++++++++++++++++- ld/ChangeLog | 11 ++ ld/testsuite/config/default.exp | 4 + ld/testsuite/ld-elf/linux-x86.exp | 46 ++++++ ld/testsuite/ld-elf/x86-feature-1a.rd | 6 + ld/testsuite/ld-elf/x86-feature-1b.rd | 6 + ld/testsuite/ld-elf/x86-feature-1c.rd | 6 + ld/testsuite/ld-elf/x86-feature-1d.rd | 6 + ld/testsuite/ld-elf/x86-feature-1e.rd | 6 + 11 files changed, 389 insertions(+), 10 deletions(-) create mode 100644 ld/testsuite/ld-elf/x86-feature-1a.rd create mode 100644 ld/testsuite/ld-elf/x86-feature-1b.rd create mode 100644 ld/testsuite/ld-elf/x86-feature-1c.rd create mode 100644 ld/testsuite/ld-elf/x86-feature-1d.rd create mode 100644 ld/testsuite/ld-elf/x86-feature-1e.rd diff --git a/binutils/ChangeLog b/binutils/ChangeLog index bdbee0f..f484f92 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,21 @@ +2018-11-06 H.J. Lu + + * doc/binutils.texi: Document --enable-x86-feature and + --disable-x86-feature options for elfedit. + * elfedit.c: Include "config.h" and . + (enable_x86_features): New. + (disable_x86_features): Likewise. + (update_gnu_property): Likewise. + (elf_x86_feature): Likewise. + (process_file): Call update_gnu_property on ET_EXEC or ET_DYN + file. + (command_line_switch): Add OPTION_ENABLE_X86_FEATURE and + OPTION_DISABLE_X86_FEATURE. + (options): Add--enable-x86-feature and --disable-x86-feature. + (usage): Likewise. + (main): Handle OPTION_ENABLE_X86_FEATURE and + OPTION_DISABLE_X86_FEATURE. + 2018-11-03 H.J. Lu * elfedit.c (update_elf_header): Move EI_MAG? check to ... diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index 6edd7b1..6cfda45 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -42,7 +42,7 @@ section entitled ``GNU Free Documentation License''. * size: (binutils)size. List section sizes and total size. * strings: (binutils)strings. List printable strings from files. * strip: (binutils)strip. Discard symbols. -* elfedit: (binutils)elfedit. Update the ELF header of ELF files. +* elfedit: (binutils)elfedit. Update ELF header and property of ELF files. * windmc: (binutils)windmc. Generator for Windows message resources. * windres: (binutils)windres. Manipulate Windows resources. @end direntry @@ -111,7 +111,7 @@ List printable strings from files Discard symbols @item elfedit -Update the ELF header of ELF files. +Update the ELF header and program property of ELF files. @item c++filt Demangle encoded C++ symbols (on MS-DOS, this program is named @@ -151,7 +151,7 @@ in the section entitled ``GNU Free Documentation License''. * windres:: Manipulate Windows resources * dlltool:: Create files needed to build and use DLLs * readelf:: Display the contents of ELF format files -* elfedit:: Update the ELF header of ELF files +* elfedit:: Update ELF header and property of ELF files * Common Options:: Command-line options for all utilities * Selecting the Target System:: How these utilities determine the target * Reporting Bugs:: Reporting Bugs @@ -4725,7 +4725,7 @@ objdump(1), and the Info entries for @file{binutils}. @cindex Update ELF header @kindex elfedit -@c man title elfedit Update the ELF header of ELF files. +@c man title elfedit Update ELF header and program property of ELF files. @smallexample @c man begin SYNOPSIS elfedit @@ -4735,6 +4735,8 @@ elfedit [@option{--input-mach=}@var{machine}] @option{--output-mach=}@var{machine} @option{--output-type=}@var{type} @option{--output-osabi=}@var{osabi} + @option{--enable-x86-feature=}@var{feature} + @option{--disable-x86-feature=}@var{feature} [@option{-v}|@option{--version}] [@option{-h}|@option{--help}] @var{elffile}@dots{} @@ -4743,9 +4745,10 @@ elfedit [@option{--input-mach=}@var{machine}] @c man begin DESCRIPTION elfedit -@command{elfedit} updates the ELF header of ELF files which have -the matching ELF machine and file types. The options control how and -which fields in the ELF header should be updated. +@command{elfedit} updates the ELF header and program property of ELF +files which have the matching ELF machine and file types. The options +control how and which fields in the ELF header and program property +should be updated. @var{elffile}@dots{} are the ELF files to be updated. 32-bit and 64-bit ELF files are supported, as are archives containing ELF files. @@ -4755,7 +4758,9 @@ which fields in the ELF header should be updated. The long and short forms of options, shown here as alternatives, are equivalent. At least one of the @option{--output-mach}, -@option{--output-type} and @option{--output-osabi} options must be given. +@option{--output-type}, @option{--output-osabi}, +@option{--enable-x86-feature} and @option{--disable-x86-feature} +options must be given. @table @env @@ -4795,6 +4800,19 @@ The supported ELF OSABIs are, @var{none}, @var{HPUX}, @var{NetBSD}, Change the ELF OSABI in the ELF header to @var{osabi}. The supported ELF OSABI are the same as @option{--input-osabi}. +@item --enable-x86-feature=@var{feature} +Set the @var{feature} bit in program property in @var{exec} or @var{dyn} +ELF files with machine types of @var{i386} or @var{x86-64}. The +supported features are, @var{ibt} and @var{shstk}. + +@item --disable-x86-feature=@var{feature} +Clear the @var{feature} bit in program property in @var{exec} or +@var{dyn} ELF files with machine types of @var{i386} or @var{x86-64}. +The supported features are the same as @option{--enable-x86-feature}. + +Note: @option{--enable-x86-feature} and @option{--disable-x86-feature} +are available only on hosts with @samp{mmap} support. + @item -v @itemx --version Display the version number of @command{elfedit}. 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 @@ -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 + +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 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\ + --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\ "), @@ -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); diff --git a/ld/ChangeLog b/ld/ChangeLog index 17f42dc..0346ebb 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,14 @@ +2018-11-06 H.J. Lu + + * testsuite/config/default.exp (ELFEDIT): New. + * testsuite/ld-elf/linux-x86.exp (elfedit_test): New proc. + Run elfedit tests. + * testsuite/ld-elf/x86-feature-1a.rd: New file. + * testsuite/ld-elf/x86-feature-1b.rd: Likewise. + * testsuite/ld-elf/x86-feature-1c.rd: Likewise. + * testsuite/ld-elf/x86-feature-1d.rd: Likewise. + * testsuite/ld-elf/x86-feature-1e.rd: Likewise. + 2018-11-05 H.J. Lu PR gas/23854 diff --git a/ld/testsuite/config/default.exp b/ld/testsuite/config/default.exp index 704ac30..fbc12ea 100644 --- a/ld/testsuite/config/default.exp +++ b/ld/testsuite/config/default.exp @@ -243,6 +243,10 @@ if ![info exists READELFFLAGS] then { set READELFFLAGS {} } +if ![info exists ELFEDIT] then { + set ELFEDIT [findfile $base_dir/../binutils/elfedit] +} + if ![info exists LD] then { set LD [findfile $base_dir/ld-new ./ld-new [transform ld]] } diff --git a/ld/testsuite/ld-elf/linux-x86.exp b/ld/testsuite/ld-elf/linux-x86.exp index f6f5a80..a8e82eb 100644 --- a/ld/testsuite/ld-elf/linux-x86.exp +++ b/ld/testsuite/ld-elf/linux-x86.exp @@ -44,3 +44,49 @@ run_ld_link_exec_tests [list \ "asm" \ ] \ ] + +run_ld_link_tests [list \ + [list \ + "Build x86-feature-1" \ + "-z separate-code -z shstk" \ + "" \ + "-mx86-used-note=yes" \ + { start.s } \ + {{readelf -n x86-feature-1a.rd}} \ + "x86-feature-1" \ + ] \ +] + +proc elfedit_test { options test output } { + global ELFEDIT + global READELF + global srcdir + global subdir + + set test_name "elfedit $options" + send_log "$ELFEDIT $options tmpdir/$test\n" + set got [remote_exec host "$ELFEDIT $options tmpdir/$test" "" "/dev/null"] + if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { + send_log "$got\n" + unresolved "$test_name" + } + send_log "$READELF -n $options tmpdir/$test > tmpdir/$output.out\n" + set got [remote_exec host "$READELF -n tmpdir/$test" "" "/dev/null" "tmpdir/$output.out"] + if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { + send_log "$got\n" + unresolved "$test_name" +} + if { [regexp_diff tmpdir/$output.out $srcdir/$subdir/$output.rd] } then { + fail "$test_name" + } else { + pass "$test_name" + } +} + +elfedit_test "--enable-x86-feature ibt --disable-x86-feature shstk" \ + x86-feature-1 x86-feature-1b +elfedit_test "--enable-x86-feature ibt" x86-feature-1 x86-feature-1b +elfedit_test "--disable-x86-feature shstk" x86-feature-1 x86-feature-1c +elfedit_test "--disable-x86-feature ibt" x86-feature-1 x86-feature-1d +elfedit_test "--enable-x86-feature ibt --enable-x86-feature shstk" \ + x86-feature-1 x86-feature-1e diff --git a/ld/testsuite/ld-elf/x86-feature-1a.rd b/ld/testsuite/ld-elf/x86-feature-1a.rd new file mode 100644 index 0000000..26b7ba5 --- /dev/null +++ b/ld/testsuite/ld-elf/x86-feature-1a.rd @@ -0,0 +1,6 @@ +Displaying notes found in: .note.gnu.property + Owner Data size Description + GNU 0x[0-9a-f]+ NT_GNU_PROPERTY_TYPE_0 + Properties: x86 feature: SHSTK + x86 ISA used: + x86 feature used: x86 diff --git a/ld/testsuite/ld-elf/x86-feature-1b.rd b/ld/testsuite/ld-elf/x86-feature-1b.rd new file mode 100644 index 0000000..9c6ff77 --- /dev/null +++ b/ld/testsuite/ld-elf/x86-feature-1b.rd @@ -0,0 +1,6 @@ +Displaying notes found in: .note.gnu.property + Owner Data size Description + GNU 0x[0-9a-f]+ NT_GNU_PROPERTY_TYPE_0 + Properties: x86 feature: IBT + x86 ISA used: + x86 feature used: x86 diff --git a/ld/testsuite/ld-elf/x86-feature-1c.rd b/ld/testsuite/ld-elf/x86-feature-1c.rd new file mode 100644 index 0000000..9c6ff77 --- /dev/null +++ b/ld/testsuite/ld-elf/x86-feature-1c.rd @@ -0,0 +1,6 @@ +Displaying notes found in: .note.gnu.property + Owner Data size Description + GNU 0x[0-9a-f]+ NT_GNU_PROPERTY_TYPE_0 + Properties: x86 feature: IBT + x86 ISA used: + x86 feature used: x86 diff --git a/ld/testsuite/ld-elf/x86-feature-1d.rd b/ld/testsuite/ld-elf/x86-feature-1d.rd new file mode 100644 index 0000000..cca2076 --- /dev/null +++ b/ld/testsuite/ld-elf/x86-feature-1d.rd @@ -0,0 +1,6 @@ +Displaying notes found in: .note.gnu.property + Owner Data size Description + GNU 0x[0-9a-f]+ NT_GNU_PROPERTY_TYPE_0 + Properties: x86 feature: + x86 ISA used: + x86 feature used: x86 diff --git a/ld/testsuite/ld-elf/x86-feature-1e.rd b/ld/testsuite/ld-elf/x86-feature-1e.rd new file mode 100644 index 0000000..391d3f7 --- /dev/null +++ b/ld/testsuite/ld-elf/x86-feature-1e.rd @@ -0,0 +1,6 @@ +Displaying notes found in: .note.gnu.property + Owner Data size Description + GNU 0x[0-9a-f]+ NT_GNU_PROPERTY_TYPE_0 + Properties: x86 feature: IBT, SHSTK + x86 ISA used: + x86 feature used: x86 -- 2.7.4