This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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 <sys/mman.h>.
	(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/doc/binutils.texi            |  34 +++-
 binutils/elfedit.c                    | 256 +++++++++++++++++++++++++-
 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 +
 9 files changed, 360 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/doc/binutils.texi b/binutils/doc/binutils.texi
index 6edd7b1ae8..6cfda45b27 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 5ad846eb1a..774be466ef 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"), 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"), 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"), 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
@@ -540,6 +752,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);
@@ -639,7 +857,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,6 +872,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}
@@ -668,7 +896,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\
 "),
@@ -741,6 +977,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 +1003,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/testsuite/config/default.exp b/ld/testsuite/config/default.exp
index 704ac300e4..fbc12eabf9 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 f6f5a80853..feec47ae0d 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" \
+	"" \
+	"" \
+	{ 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 0000000000..26b7ba5623
--- /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: <None>
+	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 0000000000..9c6ff77788
--- /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: <None>
+	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 0000000000..9c6ff77788
--- /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: <None>
+	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 0000000000..cca2076f21
--- /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: <None>
+	x86 ISA used: <None>
+	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 0000000000..391d3f723f
--- /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: <None>
+	x86 feature used: x86
-- 
2.17.2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]