[SystemZ] Sort relocs to avoid code corruption by linker optimization
authorUlrich Weigand <ulrich.weigand@de.ibm.com>
Wed, 16 Dec 2015 18:12:40 +0000 (18:12 +0000)
committerUlrich Weigand <ulrich.weigand@de.ibm.com>
Wed, 16 Dec 2015 18:12:40 +0000 (18:12 +0000)
The SystemZ linkers provide an optimization to transform a general-
or local-dynamic TLS sequence into an initial-exec sequence if possible.
Do do that, the compiler generates a function call to __tls_get_offset,
which is a brasl instruction annotated with *two* relocations:

- a R_390_PLT32DBL to install __tls_get_offset as branch target
- a R_390_TLS_GDCALL / R_390_TLS_LDCALL to inform the linker that
  the TLS optimization should be performed if possible

If the optimization is performed, the brasl is replaced by an ld load
instruction.

However, *both* relocs are processed independently by the linker.
Therefore it is crucial that the R_390_PLT32DBL is processed *first*
(installing the branch target for the brasl) and the R_390_TLS_GDCALL
is processed *second* (replacing the whole brasl with an ld).

If the relocs are swapped, the linker will first replace the brasl
with an ld, and *then* install the __tls_get_offset branch target
offset.  Since ld has a different layout than brasl, this may even
result in a completely different (or invalid) instruction; in any
case, the resulting code is corrupted.

Unfortunately, the way the MC common code sorts relocations causes
these two to *always* end up the wrong way around, resulting in
wrong code generation by the linker and crashes.

This patch overrides the sortRelocs routine to detect this particular
pair of relocs and enforce the required order.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@255787 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/SystemZ/MCTargetDesc/SystemZMCObjectWriter.cpp
test/MC/SystemZ/fixups.s

index ee1af02..76a00fc 100644 (file)
@@ -26,6 +26,8 @@ protected:
   // Override MCELFObjectTargetWriter.
   unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
                         bool IsPCRel) const override;
   // Override MCELFObjectTargetWriter.
   unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
                         bool IsPCRel) const override;
+  void sortRelocs(const MCAssembler &Asm,
+                  std::vector<ELFRelocationEntry> &Relocs) override;
 };
 } // end anonymous namespace
 
 };
 } // end anonymous namespace
 
@@ -152,6 +154,23 @@ unsigned SystemZObjectWriter::GetRelocType(const MCValue &Target,
   }
 }
 
   }
 }
 
+void SystemZObjectWriter::sortRelocs(const MCAssembler &Asm,
+                                     std::vector<ELFRelocationEntry> &Relocs) {
+  // The default function sorts entries by Offset in descending order.
+  MCELFObjectTargetWriter::sortRelocs(Asm, Relocs);
+
+  // This is OK for SystemZ, except for R_390_TLS_GDCALL/LDCALL relocs.
+  // There is typically another reloc, a R_390_PLT32DBL, on the same
+  // instruction.  This other reloc must come *before* the GDCALL reloc,
+  // or else the TLS linker optimization may generate incorrect code.
+  for (unsigned i = 0, e = Relocs.size(); i + 1 < e; ++i) {
+    if ((Relocs[i + 1].Type == ELF::R_390_TLS_GDCALL ||
+         Relocs[i + 1].Type == ELF::R_390_TLS_LDCALL) &&
+        Relocs[i].Offset == Relocs[i + 1].Offset + 2)
+      std::swap(Relocs[i], Relocs[i + 1]);
+  }
+}
+
 MCObjectWriter *llvm::createSystemZObjectWriter(raw_pwrite_stream &OS,
                                                 uint8_t OSABI) {
   MCELFObjectTargetWriter *MOTW = new SystemZObjectWriter(OSABI);
 MCObjectWriter *llvm::createSystemZObjectWriter(raw_pwrite_stream &OS,
                                                 uint8_t OSABI) {
   MCELFObjectTargetWriter *MOTW = new SystemZObjectWriter(OSABI);
index 8354121..ea3b690 100644 (file)
 # CHECK: brasl %r14, target@PLT:tls_gdcall:sym  # encoding: [0xc0,0xe5,A,A,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC32DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSGD, kind: FK_390_TLS_CALL
 # CHECK: brasl %r14, target@PLT:tls_gdcall:sym  # encoding: [0xc0,0xe5,A,A,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC32DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSGD, kind: FK_390_TLS_CALL
-# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_GDCALL sym 0x0
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT32DBL target 0x2
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT32DBL target 0x2
+# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_GDCALL sym 0x0
        .align 16
        brasl %r14, target@plt:tls_gdcall:sym
 
 # CHECK: brasl %r14, target@PLT:tls_ldcall:sym  # encoding: [0xc0,0xe5,A,A,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC32DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSLDM, kind: FK_390_TLS_CALL
        .align 16
        brasl %r14, target@plt:tls_gdcall:sym
 
 # CHECK: brasl %r14, target@PLT:tls_ldcall:sym  # encoding: [0xc0,0xe5,A,A,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC32DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSLDM, kind: FK_390_TLS_CALL
-# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_LDCALL sym 0x0
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT32DBL target 0x2
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT32DBL target 0x2
+# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_LDCALL sym 0x0
        .align 16
        brasl %r14, target@plt:tls_ldcall:sym
 
        .align 16
        brasl %r14, target@plt:tls_ldcall:sym
 
 # CHECK: bras %r14, target@PLT:tls_gdcall:sym   # encoding: [0xa7,0xe5,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC16DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSGD, kind: FK_390_TLS_CALL
 # CHECK: bras %r14, target@PLT:tls_gdcall:sym   # encoding: [0xa7,0xe5,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC16DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSGD, kind: FK_390_TLS_CALL
-# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_GDCALL sym 0x0
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT16DBL target 0x2
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT16DBL target 0x2
+# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_GDCALL sym 0x0
        .align 16
        bras %r14, target@plt:tls_gdcall:sym
 
 # CHECK: bras %r14, target@PLT:tls_ldcall:sym   # encoding: [0xa7,0xe5,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC16DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSLDM, kind: FK_390_TLS_CALL
        .align 16
        bras %r14, target@plt:tls_gdcall:sym
 
 # CHECK: bras %r14, target@PLT:tls_ldcall:sym   # encoding: [0xa7,0xe5,A,A]
 # CHECK-NEXT:                                   # fixup A - offset: 2, value: target@PLT+2, kind: FK_390_PC16DBL
 # CHECK-NEXT:                                   # fixup B - offset: 0, value: sym@TLSLDM, kind: FK_390_TLS_CALL
-# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_LDCALL sym 0x0
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT16DBL target 0x2
 # CHECK-REL:                                    0x{{[0-9A-F]*2}} R_390_PLT16DBL target 0x2
+# CHECK-REL:                                    0x{{[0-9A-F]*0}} R_390_TLS_LDCALL sym 0x0
        .align 16
        bras %r14, target@plt:tls_ldcall:sym
 
        .align 16
        bras %r14, target@plt:tls_ldcall:sym