Gold: Handle R_X86_64_CODE_4_GOTPC32_TLSDESC/R_X86_64_CODE_4_GOTTPOFF
authorH.J. Lu <hjl.tools@gmail.com>
Sun, 2 Jul 2023 14:46:21 +0000 (07:46 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Thu, 28 Dec 2023 16:47:17 +0000 (08:47 -0800)
Handle R_X86_64_CODE_4_GOTTPOFF and R_X86_64_CODE_4_GOTPC32_TLSDESC.
Convert

add name@gottpoff(%rip), %reg
mov name@gottpoff(%rip), %reg

to

add $name@tpoff, %reg
mov $name@tpoff, %reg

and

lea name@tlsdesc(%rip), %reg

to

mov     $name@tpoff, %reg
mov name@gottpoff(%rip), %reg

if the instruction is encoded with the REX2 prefix when possible.

elfcpp/

* x86_64.h (R_X86_64_CODE_4_GOTTPOFF): New.
(R_X86_64_CODE_4_GOTPC32_TLSDESC): Likewise.

gold/

* x86_64.cc (Target_x86_64::optimize_tls_reloc): Handle
R_X86_64_CODE_4_GOTPC32_TLSDESC and R_X86_64_CODE_4_GOTTPOFF.
(Target_x86_64::Scan::get_reference_flags): Likewise.
(Target_x86_64::Scan::local): Likewise.
(Target_x86_64::Scan::global): Likewise.
(Target_x86_64::Relocate::relocate): Likewise.
(Target_x86_64::Relocate::relocate_tls): Likewise.
(Target_x86_64::Relocate::tls_desc_gd_to_ie): Handle
R_X86_64_CODE_4_GOTPC32_TLSDESC.
(Target_x86_64::Relocate::tls_desc_gd_to_le): Likewise.
(Target_x86_64::Relocate::tls_ie_to_le): Handle.
R_X86_64_CODE_4_GOTTPOFF.
* testsuite/Makefile.am: Add x86_64_ie_to_le test.
* testsuite/Makefile.in: Regenerated.
* testsuite/x86_64_gd_to_le.s: Add R_X86_64_CODE_4_GOTPC32_TLSDESC
test.
* testsuite/x86_64_gd_to_le.sh: Check GDesc to LE conversion.
* testsuite/x86_64_ie_to_le.s: New file.
* testsuite/x86_64_ie_to_le.sh: Likewise.

elfcpp/x86_64.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/x86_64_gd_to_le.s
gold/testsuite/x86_64_gd_to_le.sh
gold/testsuite/x86_64_ie_to_le.s [new file with mode: 0644]
gold/testsuite/x86_64_ie_to_le.sh [new file with mode: 0755]
gold/x86_64.cc

index 97a87ae..da6ac19 100644 (file)
@@ -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
index df9405c..0685e91 100644 (file)
@@ -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
index ab48f07..ffd4821 100644 (file)
@@ -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
@@ -5747,6 +5750,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'; \
@@ -8485,6 +8495,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
index dcdd303..79f9c48 100644 (file)
@@ -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
index 82a6f98..5b0a043 100755 (executable)
@@ -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 (file)
index 0000000..c575206
--- /dev/null
@@ -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 (executable)
index 0000000..417f0bf
--- /dev/null
@@ -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
index b7be9bf..bc4260d 100644 (file)
@@ -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)