diff --git a/elfcpp/x86_64.h b/elfcpp/x86_64.h
index 97a87ae424d..da6ac191734 100644
--- a/elfcpp/x86_64.h
+++ b/elfcpp/x86_64.h
@@ -102,6 +102,14 @@ enum
 				  // GOT if the instruction starts at 4
 				  // bytes before the relocation offset,
 				  // relaxable.
+  R_X86_64_CODE_4_GOTTPOFF = 44,  // 32 bit signed PC relative offset to
+				  // GOT entry for IE symbol if the
+				  // instruction starts at 4 bytes before
+				  // the relocation offset.
+  R_X86_64_CODE_4_GOTPC32_TLSDESC = 45, // 32-bit PC relative to TLS
+					// descriptor in GOT if the
+					// instruction starts at 4 bytes
+					// before the relocation offset.
   // GNU vtable garbage collection extensions.
   R_X86_64_GNU_VTINHERIT = 250,
   R_X86_64_GNU_VTENTRY = 251
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index df9405c1aac..0685e917d0e 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -1244,6 +1244,17 @@ x86_64_gd_to_le: x86_64_gd_to_le.o gcctestdir/ld
 x86_64_gd_to_le.stdout: x86_64_gd_to_le
 	$(TEST_OBJDUMP) -dw $< > $@
 
+check_SCRIPTS += x86_64_ie_to_le.sh
+check_DATA += x86_64_ie_to_le.stdout
+MOSTLYCLEANFILES += x86_64_ie_to_le
+
+x86_64_ie_to_le.o: x86_64_ie_to_le.s
+	$(TEST_AS) --64 -o $@ $<
+x86_64_ie_to_le: x86_64_ie_to_le.o gcctestdir/ld
+	gcctestdir/ld -o $@ $<
+x86_64_ie_to_le.stdout: x86_64_ie_to_le
+	$(TEST_OBJDUMP) -dw $< > $@
+
 check_SCRIPTS += x86_64_overflow_pc32.sh
 check_DATA += x86_64_overflow_pc32.err
 MOSTLYCLEANFILES += x86_64_overflow_pc32.err
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index fb0eb12553b..06be15051e5 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -296,6 +296,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_31 = x86_64_mov_to_lea.sh \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_indirect_call_to_direct.sh \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_gd_to_le.sh \
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_ie_to_le.sh \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_overflow_pc32.sh \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x32_overflow_pc32.sh \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	pr23016_1.sh \
@@ -319,6 +320,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_indirect_call_to_direct1.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_indirect_jump_to_direct1.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_gd_to_le.stdout \
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_ie_to_le.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_overflow_pc32.err \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x32_overflow_pc32.err \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	pr23016_1.stdout \
@@ -343,6 +345,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_indirect_call_to_direct1 \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_indirect_jump_to_direct1 \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_gd_to_le \
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_ie_to_le \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x86_64_overflow_pc32.err \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	x32_overflow_pc32.err
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_34 = pr17704a_test
@@ -5736,6 +5739,13 @@ x86_64_gd_to_le.sh.log: x86_64_gd_to_le.sh
 	--log-file $$b.log --trs-file $$b.trs \
 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
 	"$$tst" $(AM_TESTS_FD_REDIRECT)
+x86_64_ie_to_le.sh.log: x86_64_ie_to_le.sh
+	@p='x86_64_ie_to_le.sh'; \
+	b='x86_64_ie_to_le.sh'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
 x86_64_overflow_pc32.sh.log: x86_64_overflow_pc32.sh
 	@p='x86_64_overflow_pc32.sh'; \
 	b='x86_64_overflow_pc32.sh'; \
@@ -8474,6 +8484,13 @@ uninstall-am:
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	gcctestdir/ld -o $@ $<
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_gd_to_le.stdout: x86_64_gd_to_le
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	$(TEST_OBJDUMP) -dw $< > $@
+
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_ie_to_le.o: x86_64_ie_to_le.s
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	$(TEST_AS) --64 -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_ie_to_le: x86_64_ie_to_le.o gcctestdir/ld
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	gcctestdir/ld -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_ie_to_le.stdout: x86_64_ie_to_le
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	$(TEST_OBJDUMP) -dw $< > $@
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_overflow_pc32.o: x86_64_overflow_pc32.s
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@	$(TEST_AS) -o $@ $<
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@x86_64_overflow_pc32.err: x86_64_overflow_pc32.o gcctestdir/ld
diff --git a/gold/testsuite/x86_64_gd_to_le.s b/gold/testsuite/x86_64_gd_to_le.s
index dcdd303184d..79f9c48f526 100644
--- a/gold/testsuite/x86_64_gd_to_le.s
+++ b/gold/testsuite/x86_64_gd_to_le.s
@@ -7,6 +7,7 @@ _start:
 	subq	$8, %rsp
 	.cfi_def_cfa_offset 16
 	leaq	foo@TLSDESC(%rip), %r9
+	leaq	foo@TLSDESC(%rip), %r29
 	movq	%r9, %rax
 	call	*foo@TLSCALL(%rax)
 	addq	%fs:0, %rax
diff --git a/gold/testsuite/x86_64_gd_to_le.sh b/gold/testsuite/x86_64_gd_to_le.sh
index 82a6f985b88..5b0a043e0a8 100755
--- a/gold/testsuite/x86_64_gd_to_le.sh
+++ b/gold/testsuite/x86_64_gd_to_le.sh
@@ -24,3 +24,4 @@
 set -e
 
 grep -q "mov[ \t]\+\$0x[a-f0-9]\+,%r9" x86_64_gd_to_le.stdout
+grep -q "mov[ \t]\+\$0x[a-f0-9]\+,%r29" x86_64_gd_to_le.stdout
diff --git a/gold/testsuite/x86_64_ie_to_le.s b/gold/testsuite/x86_64_ie_to_le.s
new file mode 100644
index 00000000000..c5752068866
--- /dev/null
+++ b/gold/testsuite/x86_64_ie_to_le.s
@@ -0,0 +1,17 @@
+	.text
+	.p2align 4
+	.globl	_start
+	.type	_start, @function
+_start:
+	addq	foo@gottpoff(%rip), %r12
+	movq	foo@gottpoff(%rip), %rax
+	addq	foo@gottpoff(%rip), %r16
+	movq	foo@gottpoff(%rip), %r20
+	.size	_start, .-_start
+	.section	.tdata,"awT",@progbits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.long	30
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gold/testsuite/x86_64_ie_to_le.sh b/gold/testsuite/x86_64_ie_to_le.sh
new file mode 100755
index 00000000000..417f0bfc636
--- /dev/null
+++ b/gold/testsuite/x86_64_ie_to_le.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# x86_64_ie_to_le.sh -- a test for IE -> LE conversion.
+
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+set -e
+
+grep -q "add[ \t]\+\$0x[a-f0-9]\+,%r12" x86_64_ie_to_le.stdout
+grep -q "mov[ \t]\+\$0x[a-f0-9]\+,%rax" x86_64_ie_to_le.stdout
+grep -q "add[ \t]\+\$0x[a-f0-9]\+,%r16" x86_64_ie_to_le.stdout
+grep -q "mov[ \t]\+\$0x[a-f0-9]\+,%r20" x86_64_ie_to_le.stdout
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index b7be9bf0d48..bc4260db5f3 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -1110,7 +1110,8 @@ class Target_x86_64 : public Sized_target<size, false>
   // Adjust TLS relocation type based on the options and whether this
   // is a local symbol.
   static tls::Tls_optimization
-  optimize_tls_reloc(bool is_final, int r_type);
+  optimize_tls_reloc(bool is_final, int r_type, size_t r_offset,
+		     const unsigned char* reloc_view);
 
   // Get the GOT section, creating it if necessary.
   Output_data_got<64, false>*
@@ -2878,11 +2879,13 @@ Target_x86_64<size>::got_mod_index_entry(Symbol_table* symtab, Layout* layout,
 
 // Optimize the TLS relocation type based on what we know about the
 // symbol.  IS_FINAL is true if the final address of this symbol is
-// known at link time.
+// known at link time.  RELOC_VIEW points to the relocation offset.
 
 template<int size>
 tls::Tls_optimization
-Target_x86_64<size>::optimize_tls_reloc(bool is_final, int r_type)
+Target_x86_64<size>::optimize_tls_reloc(bool is_final, int r_type,
+					size_t r_offset,
+					const unsigned char* reloc_view)
 {
   // If we are generating a shared library, then we can't do anything
   // in the linker.
@@ -2891,6 +2894,10 @@ Target_x86_64<size>::optimize_tls_reloc(bool is_final, int r_type)
 
   switch (r_type)
     {
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
+      if (r_offset <= 4 || *(reloc_view - 4) != 0xd5)
+	return tls::TLSOPT_NONE;
+      // Fall through.
     case elfcpp::R_X86_64_TLSGD:
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:
     case elfcpp::R_X86_64_TLSDESC_CALL:
@@ -2913,6 +2920,10 @@ Target_x86_64<size>::optimize_tls_reloc(bool is_final, int r_type)
       // Another Local-Dynamic reloc.
       return tls::TLSOPT_TO_LE;
 
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
+      if (r_offset <= 4 || *(reloc_view - 4) != 0xd5)
+	return tls::TLSOPT_NONE;
+      // Fall through.
     case elfcpp::R_X86_64_GOTTPOFF:
       // These are Initial-Exec relocs which get the thread offset
       // from the GOT.  If we know that we are linking against the
@@ -2979,11 +2990,13 @@ Target_x86_64<size>::Scan::get_reference_flags(unsigned int r_type)
 
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
     case elfcpp::R_X86_64_TLSDESC_CALL:
     case elfcpp::R_X86_64_TLSLD:            // Local-dynamic
     case elfcpp::R_X86_64_DTPOFF32:
     case elfcpp::R_X86_64_DTPOFF64:
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
     case elfcpp::R_X86_64_TPOFF32:          // Local-exec
       return Symbol::TLS_REF;
 
@@ -3148,6 +3161,8 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
       target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
     }
 
+  const unsigned char* reloc_view = NULL;
+
   switch (r_type)
     {
     case elfcpp::R_X86_64_NONE:
@@ -3345,6 +3360,13 @@ need_got:
       break;
 
       // These are initial tls relocs, which are expected when linking
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
+      {
+	section_size_type stype;
+	reloc_view = object->section_contents(data_shndx, &stype, true);
+      }
+      // Fall through.
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
     case elfcpp::R_X86_64_TLSDESC_CALL:
@@ -3355,9 +3377,11 @@ need_got:
     case elfcpp::R_X86_64_TPOFF32:          // Local-exec
       {
 	bool output_is_shared = parameters->options().shared();
+	size_t r_offset = reloc.get_r_offset();
 	const tls::Tls_optimization optimized_type
 	    = Target_x86_64<size>::optimize_tls_reloc(!output_is_shared,
-						      r_type);
+						      r_type, r_offset,
+						      reloc_view + r_offset);
 	switch (r_type)
 	  {
 	  case elfcpp::R_X86_64_TLSGD:       // General-dynamic
@@ -3386,6 +3410,7 @@ need_got:
 	    break;
 
 	  case elfcpp::R_X86_64_GOTPC32_TLSDESC:
+	  case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
 	    target->define_tls_base_symbol(symtab, layout);
 	    if (optimized_type == tls::TLSOPT_NONE)
 	      {
@@ -3438,6 +3463,7 @@ need_got:
 	    break;
 
 	  case elfcpp::R_X86_64_GOTTPOFF:    // Initial-exec
+	  case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
 	    layout->set_has_static_tls();
 	    if (optimized_type == tls::TLSOPT_NONE)
 	      {
@@ -3615,6 +3641,8 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
       && this->reloc_needs_plt_for_ifunc(object, r_type))
     target->make_plt_entry(symtab, layout, gsym);
 
+  const unsigned char *reloc_view = NULL;
+
   switch (r_type)
     {
     case elfcpp::R_X86_64_NONE:
@@ -3872,6 +3900,13 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
       break;
 
       // These are initial tls relocs, which are expected for global()
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
+      {
+	section_size_type stype;
+	reloc_view = object->section_contents(data_shndx, &stype, true);
+      }
+      // Fall through.
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
     case elfcpp::R_X86_64_TLSDESC_CALL:
@@ -3884,11 +3919,15 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
 	// For the Initial-Exec model, we can treat undef symbols as final
 	// when building an executable.
 	const bool is_final = (gsym->final_value_is_known() ||
-			       (r_type == elfcpp::R_X86_64_GOTTPOFF &&
+			       ((r_type == elfcpp::R_X86_64_GOTTPOFF ||
+				 r_type == elfcpp::R_X86_64_CODE_4_GOTTPOFF) &&
 			        gsym->is_undefined() &&
 				parameters->options().output_is_executable()));
+	size_t r_offset = reloc.get_r_offset();
 	const tls::Tls_optimization optimized_type
-	    = Target_x86_64<size>::optimize_tls_reloc(is_final, r_type);
+	    = Target_x86_64<size>::optimize_tls_reloc(is_final, r_type,
+						      r_offset,
+						      reloc_view + r_offset);
 	switch (r_type)
 	  {
 	  case elfcpp::R_X86_64_TLSGD:       // General-dynamic
@@ -3917,6 +3956,7 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
 	    break;
 
 	  case elfcpp::R_X86_64_GOTPC32_TLSDESC:
+	  case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
 	    target->define_tls_base_symbol(symtab, layout);
 	    if (optimized_type == tls::TLSOPT_NONE)
 	      {
@@ -3965,6 +4005,7 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
 	    break;
 
 	  case elfcpp::R_X86_64_GOTTPOFF:    // Initial-exec
+	  case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
 	    layout->set_has_static_tls();
 	    if (optimized_type == tls::TLSOPT_NONE)
 	      {
@@ -4560,11 +4601,13 @@ Target_x86_64<size>::Relocate::relocate(
       // These are initial tls relocs, which are expected when linking
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
     case elfcpp::R_X86_64_TLSDESC_CALL:
     case elfcpp::R_X86_64_TLSLD:            // Local-dynamic
     case elfcpp::R_X86_64_DTPOFF32:
     case elfcpp::R_X86_64_DTPOFF64:
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
     case elfcpp::R_X86_64_TPOFF32:          // Local-exec
       this->relocate_tls(relinfo, target, relnum, rela, r_type, gsym, psymval,
 			 view, address, view_size);
@@ -4636,8 +4679,10 @@ Target_x86_64<size>::Relocate::relocate_tls(
   const bool is_final = (gsym == NULL
 			 ? !parameters->options().shared()
 			 : gsym->final_value_is_known());
+  size_t r_offset = rela.get_r_offset();
   tls::Tls_optimization optimized_type
-      = Target_x86_64<size>::optimize_tls_reloc(is_final, r_type);
+      = Target_x86_64<size>::optimize_tls_reloc(is_final, r_type,
+						r_offset, view);
   switch (r_type)
     {
     case elfcpp::R_X86_64_TLSGD:            // Global-dynamic
@@ -4704,6 +4749,7 @@ Target_x86_64<size>::Relocate::relocate_tls(
       break;
 
     case elfcpp::R_X86_64_GOTPC32_TLSDESC:  // Global-dynamic (from ~oliva url)
+    case elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC:
     case elfcpp::R_X86_64_TLSDESC_CALL:
       if (!is_executable && optimized_type == tls::TLSOPT_TO_LE)
 	{
@@ -4729,7 +4775,8 @@ Target_x86_64<size>::Relocate::relocate_tls(
 				   ? GOT_TYPE_TLS_OFFSET
 				   : GOT_TYPE_TLS_DESC);
 	  unsigned int got_offset = 0;
-	  if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC
+	  if ((r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC
+	       || r_type == elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC)
 	      && optimized_type == tls::TLSOPT_NONE)
 	    {
 	      // We created GOT entries in the .got.tlsdesc portion of
@@ -4760,7 +4807,8 @@ Target_x86_64<size>::Relocate::relocate_tls(
 	    }
 	  else if (optimized_type == tls::TLSOPT_NONE)
 	    {
-	      if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC)
+	      if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC
+		  || r_type == elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC)
 		{
 		  // Relocate the field with the offset of the pair of GOT
 		  // entries.
@@ -4845,6 +4893,7 @@ Target_x86_64<size>::Relocate::relocate_tls(
       break;
 
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
+    case elfcpp::R_X86_64_CODE_4_GOTTPOFF:
       if (gsym != NULL
 	  && gsym->is_undefined()
 	  && parameters->options().output_is_executable())
@@ -5051,7 +5100,8 @@ Target_x86_64<size>::Relocate::tls_desc_gd_to_ie(
     typename elfcpp::Elf_types<size>::Elf_Addr address,
     section_size_type view_size)
 {
-  if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC)
+  if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC
+      || r_type == elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC)
     {
       // LP64: leaq foo@tlsdesc(%rip), %rax
       //       ==> movq foo@gottpoff(%rip), %rax
@@ -5060,7 +5110,8 @@ Target_x86_64<size>::Relocate::tls_desc_gd_to_ie(
       tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
       tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4);
       tls::check_tls(relinfo, relnum, rela.get_r_offset(),
-		     (((view[-3] & 0xfb) == 0x48
+		     ((r_type == elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC
+		       || (view[-3] & 0xfb) == 0x48
 		       || (size == 32 && (view[-3] & 0xfb) == 0x40))
 		      && view[-2] == 0x8d
 		      && (view[-1] & 0xc7) == 0x05));
@@ -5132,6 +5183,22 @@ Target_x86_64<size>::Relocate::tls_desc_gd_to_le(
       value -= tls_segment->memsz();
       Relocate_functions<size, false>::rela32(view, value, 0);
     }
+  else if (r_type == elfcpp::R_X86_64_CODE_4_GOTPC32_TLSDESC)
+    {
+      // REX2: lea foo@tlsdesc(%rip), %reg
+      //       ==> mov foo@tpoff, %reg
+      tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
+      tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4);
+      tls::check_tls(relinfo, relnum, rela.get_r_offset(),
+		     (view[-2] == 0x8d
+		      && (view[-1] & 0xc7) == 0x05));
+      unsigned char rex2_mask = 4 | 4 << 4;
+      view[-3] = (view[-3] & ~rex2_mask) | ((view[-3] & rex2_mask) >> 2);
+      view[-2] = 0xc7;
+      view[-1] = 0xc0 | ((view[-1] >> 3) & 7);
+      value -= tls_segment->memsz();
+      Relocate_functions<size, false>::rela32(view, value, 0);
+    }
   else
     {
       // LP64: call *foo@tlscall(%rax)
@@ -5231,7 +5298,7 @@ Target_x86_64<size>::Relocate::tls_ie_to_le(
     size_t relnum,
     Output_segment* tls_segment,
     const elfcpp::Rela<size, false>& rela,
-    unsigned int,
+    unsigned int r_type,
     typename elfcpp::Elf_types<size>::Elf_Addr value,
     unsigned char* view,
     section_size_type view_size)
@@ -5250,35 +5317,50 @@ Target_x86_64<size>::Relocate::tls_ie_to_le(
   unsigned char op3 = view[-1];
   unsigned char reg = op3 >> 3;
 
-  if (op2 == 0x8b)
+  if (r_type == elfcpp::R_X86_64_GOTTPOFF)
     {
-      // movq
-      if (op1 == 0x4c)
-	view[-3] = 0x49;
-      else if (size == 32 && op1 == 0x44)
-	view[-3] = 0x41;
-      view[-2] = 0xc7;
-      view[-1] = 0xc0 | reg;
-    }
-  else if (reg == 4)
-    {
-      // Special handling for %rsp.
-      if (op1 == 0x4c)
-	view[-3] = 0x49;
-      else if (size == 32 && op1 == 0x44)
-	view[-3] = 0x41;
-      view[-2] = 0x81;
-      view[-1] = 0xc0 | reg;
+      if (op2 == 0x8b)
+	{
+	  // movq
+	  if (op1 == 0x4c)
+	    view[-3] = 0x49;
+	  else if (size == 32 && op1 == 0x44)
+	    view[-3] = 0x41;
+	  view[-2] = 0xc7;
+	  view[-1] = 0xc0 | reg;
+	}
+      else if (reg == 4)
+	{
+	  // Special handling for %rsp.
+	  if (op1 == 0x4c)
+	    view[-3] = 0x49;
+	  else if (size == 32 && op1 == 0x44)
+	    view[-3] = 0x41;
+	  view[-2] = 0x81;
+	  view[-1] = 0xc0 | reg;
+	}
+      else
+	{
+	  // addq
+	  if (op1 == 0x4c)
+	    view[-3] = 0x4d;
+	  else if (size == 32 && op1 == 0x44)
+	    view[-3] = 0x45;
+	  view[-2] = 0x8d;
+	  view[-1] = 0x80 | reg | (reg << 3);
+	}
     }
   else
     {
-      // addq
-      if (op1 == 0x4c)
-	view[-3] = 0x4d;
-      else if (size == 32 && op1 == 0x44)
-	view[-3] = 0x45;
-      view[-2] = 0x8d;
-      view[-1] = 0x80 | reg | (reg << 3);
+      if (op2 == 0x8b)
+	op2 = 0xc7;
+      else
+	op2 = 0x81;
+
+      unsigned char rex2_mask = 4 | 4 << 4;
+      view[-3] = (view[-3] & ~rex2_mask) | ((view[-3] & rex2_mask) >> 2);
+      view[-2] = op2;
+      view[-1] = 0xc0 | reg;
     }
 
   if (tls_segment != NULL)
