[ELF] Fix X86 & X86_64 PLT retpoline padding
authorAndrew Ng <anng.sw@gmail.com>
Thu, 29 Mar 2018 14:03:01 +0000 (14:03 +0000)
committerAndrew Ng <anng.sw@gmail.com>
Thu, 29 Mar 2018 14:03:01 +0000 (14:03 +0000)
The PLT retpoline support for X86 and X86_64 did not include the padding
when writing the header and entries. This issue was revealed when linker
scripts were used, as this disables the built-in behaviour of filling
the last page of executable segments with trap instructions. This
particular behaviour was hiding the missing padding.

Added retpoline tests with linker scripts.

Differential Revision: https://reviews.llvm.org/D44682

llvm-svn: 328777

lld/ELF/Arch/X86.cpp
lld/ELF/Arch/X86_64.cpp
lld/test/ELF/i386-retpoline-nopic-linkerscript.s [new file with mode: 0644]
lld/test/ELF/i386-retpoline-pic-linkerscript.s [new file with mode: 0644]
lld/test/ELF/x86-64-retpoline-linkerscript.s [new file with mode: 0644]
lld/test/ELF/x86-64-retpoline-znow-linkerscript.s [new file with mode: 0644]

index e638feb..e98f0b6 100644 (file)
@@ -443,6 +443,7 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const {
       0x89, 0xc8,                               // 2b:   mov %ecx, %eax
       0x59,                                     // 2d:   pop %ecx
       0xc3,                                     // 2e:   ret
+      0xcc,                                     // 2f:   int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
@@ -456,12 +457,13 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
                             uint64_t PltEntryAddr, int32_t Index,
                             unsigned RelOff) const {
   const uint8_t Insn[] = {
-      0x50,                   // pushl %eax
-      0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
-      0xe8, 0,    0, 0, 0,    // call plt+0x20
-      0xe9, 0,    0, 0, 0,    // jmp plt+0x12
-      0x68, 0,    0, 0, 0,    // pushl $reloc_offset
-      0xe9, 0,    0, 0, 0,    // jmp plt+0
+      0x50,                            // pushl %eax
+      0x8b, 0x83, 0,    0,    0,    0, // mov foo@GOT(%ebx), %eax
+      0xe8, 0,    0,    0,    0,       // call plt+0x20
+      0xe9, 0,    0,    0,    0,       // jmp plt+0x12
+      0x68, 0,    0,    0,    0,       // pushl $reloc_offset
+      0xe9, 0,    0,    0,    0,       // jmp plt+0
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc,    // int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
@@ -484,7 +486,7 @@ void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
 }
 
 void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
-  const uint8_t PltData[] = {
+  const uint8_t Insn[] = {
       0xff, 0x35, 0,    0,    0,    0, // 0:    pushl GOTPLT+4
       0x50,                            // 6:    pushl %eax
       0xa1, 0,    0,    0,    0,       // 7:    mov GOTPLT+8, %eax
@@ -500,8 +502,9 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
       0x89, 0xc8,                      // 2b:   mov %ecx, %eax
       0x59,                            // 2d:   pop %ecx
       0xc3,                            // 2e:   ret
+      0xcc,                            // 2f:   int3; padding
   };
-  memcpy(Buf, PltData, sizeof(PltData));
+  memcpy(Buf, Insn, sizeof(Insn));
 
   uint32_t GotPlt = InX::GotPlt->getVA();
   write32le(Buf + 2, GotPlt + 4);
@@ -512,12 +515,14 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
                               uint64_t PltEntryAddr, int32_t Index,
                               unsigned RelOff) const {
   const uint8_t Insn[] = {
-      0x50,             // 0:  pushl %eax
-      0xa1, 0, 0, 0, 0, // 1:  mov foo_in_GOT, %eax
-      0xe8, 0, 0, 0, 0, // 6:  call plt+0x20
-      0xe9, 0, 0, 0, 0, // b:  jmp plt+0x11
-      0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
-      0xe9, 0, 0, 0, 0, // 15: jmp plt+0
+      0x50,                         // 0:  pushl %eax
+      0xa1, 0,    0,    0,    0,    // 1:  mov foo_in_GOT, %eax
+      0xe8, 0,    0,    0,    0,    // 6:  call plt+0x20
+      0xe9, 0,    0,    0,    0,    // b:  jmp plt+0x11
+      0x68, 0,    0,    0,    0,    // 10: pushl $reloc_offset
+      0xe9, 0,    0,    0,    0,    // 15: jmp plt+0
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
+      0xcc,                         // 1f: int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
index 512b1ff..2427d15 100644 (file)
@@ -500,6 +500,8 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
       0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19:   int3; .align 16
       0x4c, 0x89, 0x1c, 0x24,                   // 20: next: mov %r11, (%rsp)
       0xc3,                                     // 24:   ret
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25:   int3; padding
+      0xcc, 0xcc, 0xcc, 0xcc,                   // 2c:   int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
@@ -515,10 +517,11 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
                                unsigned RelOff) const {
   const uint8_t Insn[] = {
       0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0:  mov foo@GOTPLT(%rip), %r11
-      0xe8, 0,    0,    0, 0,       // 7:  callq plt+0x20
-      0xe9, 0,    0,    0, 0,       // c:  jmp plt+0x12
-      0x68, 0,    0,    0, 0,       // 11: pushq <relocation index>
-      0xe9, 0,    0,    0, 0,       // 16: jmp plt+0
+      0xe8, 0,    0,    0,    0,    // 7:  callq plt+0x20
+      0xe9, 0,    0,    0,    0,    // c:  jmp plt+0x12
+      0x68, 0,    0,    0,    0,    // 11: pushq <relocation index>
+      0xe9, 0,    0,    0,    0,    // 16: jmp plt+0
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
@@ -546,6 +549,9 @@ void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
       0xcc, 0xcc, 0xcc, 0xcc,       // c:    int3; .align 16
       0x4c, 0x89, 0x1c, 0x24,       // 10: next: mov %r11, (%rsp)
       0xc3,                         // 14:   ret
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15:   int3; padding
+      0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a:   int3; padding
+      0xcc,                         // 1f:   int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 }
@@ -555,8 +561,9 @@ void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
                                    uint64_t PltEntryAddr, int32_t Index,
                                    unsigned RelOff) const {
   const uint8_t Insn[] = {
-      0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
-      0xe9, 0,    0,    0, 0,       // jmp plt+0
+      0x4c, 0x8b, 0x1d, 0,    0, 0, 0, // mov foo@GOTPLT(%rip), %r11
+      0xe9, 0,    0,    0,    0,       // jmp plt+0
+      0xcc, 0xcc, 0xcc, 0xcc,          // int3; padding
   };
   memcpy(Buf, Insn, sizeof(Insn));
 
diff --git a/lld/test/ELF/i386-retpoline-nopic-linkerscript.s b/lld/test/ELF/i386-retpoline-nopic-linkerscript.s
new file mode 100644 (file)
index 0000000..0c7d1fc
--- /dev/null
@@ -0,0 +1,67 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN:   .text : { *(.text) } \
+// RUN:   .plt : { *(.plt) } \
+// RUN:   .got.plt : { *(.got.plt) } \
+// RUN:   .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld %t1.o %t2.so -o %t.exe -z retpolineplt --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK:      Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10:       ff 35 fc 00 00 00       pushl   252
+// CHECK-NEXT: 16:       50      pushl   %eax
+// CHECK-NEXT: 17:       a1 00 01 00 00  movl    256, %eax
+// CHECK-NEXT: 1c:       e8 0f 00 00 00  calll   15 <.plt+0x20>
+// CHECK-NEXT: 21:       f3 90   pause
+// CHECK-NEXT: 23:       0f ae e8        lfence
+// CHECK-NEXT: 26:       eb f9   jmp     -7 <.plt+0x11>
+// CHECK-NEXT: 28:       cc      int3
+// CHECK-NEXT: 29:       cc      int3
+// CHECK-NEXT: 2a:       cc      int3
+// CHECK-NEXT: 2b:       cc      int3
+// CHECK-NEXT: 2c:       cc      int3
+// CHECK-NEXT: 2d:       cc      int3
+// CHECK-NEXT: 2e:       cc      int3
+// CHECK-NEXT: 2f:       cc      int3
+// CHECK-NEXT: 30:       89 0c 24        movl    %ecx, (%esp)
+// CHECK-NEXT: 33:       8b 4c 24 04     movl    4(%esp), %ecx
+// CHECK-NEXT: 37:       89 44 24 04     movl    %eax, 4(%esp)
+// CHECK-NEXT: 3b:       89 c8   movl    %ecx, %eax
+// CHECK-NEXT: 3d:       59      popl    %ecx
+// CHECK-NEXT: 3e:       c3      retl
+// CHECK-NEXT: 3f:       cc      int3
+// CHECK-NEXT: 40:       50      pushl   %eax
+// CHECK-NEXT: 41:       a1 04 01 00 00  movl    260, %eax
+// CHECK-NEXT: 46:       e8 e5 ff ff ff  calll   -27 <.plt+0x20>
+// CHECK-NEXT: 4b:       e9 d1 ff ff ff  jmp     -47 <.plt+0x11>
+// CHECK-NEXT: 50:       68 00 00 00 00  pushl   $0
+// CHECK-NEXT: 55:       e9 b6 ff ff ff  jmp     -74 <.plt>
+// CHECK-NEXT: 5a:       cc      int3
+// CHECK-NEXT: 5b:       cc      int3
+// CHECK-NEXT: 5c:       cc      int3
+// CHECK-NEXT: 5d:       cc      int3
+// CHECK-NEXT: 5e:       cc      int3
+// CHECK-NEXT: 5f:       cc      int3
+// CHECK-NEXT: 60:       50      pushl   %eax
+// CHECK-NEXT: 61:       a1 08 01 00 00  movl    264, %eax
+// CHECK-NEXT: 66:       e8 c5 ff ff ff  calll   -59 <.plt+0x20>
+// CHECK-NEXT: 6b:       e9 b1 ff ff ff  jmp     -79 <.plt+0x11>
+// CHECK-NEXT: 70:       68 08 00 00 00  pushl   $8
+// CHECK-NEXT: 75:       e9 96 ff ff ff  jmp     -106 <.plt>
+// CHECK-NEXT: 7a:       cc      int3
+// CHECK-NEXT: 7b:       cc      int3
+// CHECK-NEXT: 7c:       cc      int3
+// CHECK-NEXT: 7d:       cc      int3
+// CHECK-NEXT: 7e:       cc      int3
+// CHECK-NEXT: 7f:       cc      int3
+
+.global _start
+_start:
+  jmp bar@PLT
+  jmp zed@PLT
diff --git a/lld/test/ELF/i386-retpoline-pic-linkerscript.s b/lld/test/ELF/i386-retpoline-pic-linkerscript.s
new file mode 100644 (file)
index 0000000..049e53f
--- /dev/null
@@ -0,0 +1,64 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux -position-independent %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux -position-independent %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN:   .text : { *(.text) } \
+// RUN:   .plt : { *(.plt) } \
+// RUN:   .got.plt : { *(.got.plt) } \
+// RUN:   .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld %t1.o %t2.so -o %t.exe -z retpolineplt -pie --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK:      Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10:       ff b3 fc 00 00 00       pushl   252(%ebx)
+// CHECK-NEXT: 16:       50      pushl   %eax
+// CHECK-NEXT: 17:       8b 83 00 01 00 00       movl    256(%ebx), %eax
+// CHECK-NEXT: 1d:       e8 0e 00 00 00  calll   14 <.plt+0x20>
+// CHECK-NEXT: 22:       f3 90   pause
+// CHECK-NEXT: 24:       0f ae e8        lfence
+// CHECK-NEXT: 27:       eb f9   jmp     -7 <.plt+0x12>
+// CHECK-NEXT: 29:       cc      int3
+// CHECK-NEXT: 2a:       cc      int3
+// CHECK-NEXT: 2b:       cc      int3
+// CHECK-NEXT: 2c:       cc      int3
+// CHECK-NEXT: 2d:       cc      int3
+// CHECK-NEXT: 2e:       cc      int3
+// CHECK-NEXT: 2f:       cc      int3
+// CHECK-NEXT: 30:       89 0c 24        movl    %ecx, (%esp)
+// CHECK-NEXT: 33:       8b 4c 24 04     movl    4(%esp), %ecx
+// CHECK-NEXT: 37:       89 44 24 04     movl    %eax, 4(%esp)
+// CHECK-NEXT: 3b:       89 c8   movl    %ecx, %eax
+// CHECK-NEXT: 3d:       59      popl    %ecx
+// CHECK-NEXT: 3e:       c3      retl
+// CHECK-NEXT: 3f:       cc      int3
+// CHECK-NEXT: 40:       50      pushl   %eax
+// CHECK-NEXT: 41:       8b 83 04 01 00 00       movl    260(%ebx), %eax
+// CHECK-NEXT: 47:       e8 e4 ff ff ff  calll   -28 <.plt+0x20>
+// CHECK-NEXT: 4c:       e9 d1 ff ff ff  jmp     -47 <.plt+0x12>
+// CHECK-NEXT: 51:       68 00 00 00 00  pushl   $0
+// CHECK-NEXT: 56:       e9 b5 ff ff ff  jmp     -75 <.plt>
+// CHECK-NEXT: 5b:       cc      int3
+// CHECK-NEXT: 5c:       cc      int3
+// CHECK-NEXT: 5d:       cc      int3
+// CHECK-NEXT: 5e:       cc      int3
+// CHECK-NEXT: 5f:       cc      int3
+// CHECK-NEXT: 60:       50      pushl   %eax
+// CHECK-NEXT: 61:       8b 83 08 01 00 00       movl    264(%ebx), %eax
+// CHECK-NEXT: 67:       e8 c4 ff ff ff  calll   -60 <.plt+0x20>
+// CHECK-NEXT: 6c:       e9 b1 ff ff ff  jmp     -79 <.plt+0x12>
+// CHECK-NEXT: 71:       68 08 00 00 00  pushl   $8
+// CHECK-NEXT: 76:       e9 95 ff ff ff  jmp     -107 <.plt>
+// CHECK-NEXT: 7b:       cc      int3
+// CHECK-NEXT: 7c:       cc      int3
+// CHECK-NEXT: 7d:       cc      int3
+// CHECK-NEXT: 7e:       cc      int3
+// CHECK-NEXT: 7f:       cc      int3
+
+.global _start
+_start:
+  jmp bar@PLT
+  jmp zed@PLT
diff --git a/lld/test/ELF/x86-64-retpoline-linkerscript.s b/lld/test/ELF/x86-64-retpoline-linkerscript.s
new file mode 100644 (file)
index 0000000..d173cf5
--- /dev/null
@@ -0,0 +1,67 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN:   .text : { *(.text) } \
+// RUN:   .plt : { *(.plt) } \
+// RUN:   .got.plt : { *(.got.plt) } \
+// RUN:   .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld -shared %t1.o %t2.so -o %t.exe -z retpolineplt --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK:      Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10:       ff 35 4a 01 00 00       pushq   330(%rip)
+// CHECK-NEXT: 16:       4c 8b 1d 4b 01 00 00    movq    331(%rip), %r11
+// CHECK-NEXT: 1d:       e8 0e 00 00 00  callq   14 <.plt+0x20>
+// CHECK-NEXT: 22:       f3 90   pause
+// CHECK-NEXT: 24:       0f ae e8        lfence
+// CHECK-NEXT: 27:       eb f9   jmp     -7 <.plt+0x12>
+// CHECK-NEXT: 29:       cc      int3
+// CHECK-NEXT: 2a:       cc      int3
+// CHECK-NEXT: 2b:       cc      int3
+// CHECK-NEXT: 2c:       cc      int3
+// CHECK-NEXT: 2d:       cc      int3
+// CHECK-NEXT: 2e:       cc      int3
+// CHECK-NEXT: 2f:       cc      int3
+// CHECK-NEXT: 30:       4c 89 1c 24     movq    %r11, (%rsp)
+// CHECK-NEXT: 34:       c3      retq
+// CHECK-NEXT: 35:       cc      int3
+// CHECK-NEXT: 36:       cc      int3
+// CHECK-NEXT: 37:       cc      int3
+// CHECK-NEXT: 38:       cc      int3
+// CHECK-NEXT: 39:       cc      int3
+// CHECK-NEXT: 3a:       cc      int3
+// CHECK-NEXT: 3b:       cc      int3
+// CHECK-NEXT: 3c:       cc      int3
+// CHECK-NEXT: 3d:       cc      int3
+// CHECK-NEXT: 3e:       cc      int3
+// CHECK-NEXT: 3f:       cc      int3
+// CHECK-NEXT: 40:       4c 8b 1d 29 01 00 00    movq    297(%rip), %r11
+// CHECK-NEXT: 47:       e8 e4 ff ff ff  callq   -28 <.plt+0x20>
+// CHECK-NEXT: 4c:       e9 d1 ff ff ff  jmp     -47 <.plt+0x12>
+// CHECK-NEXT: 51:       68 00 00 00 00  pushq   $0
+// CHECK-NEXT: 56:       e9 b5 ff ff ff  jmp     -75 <.plt>
+// CHECK-NEXT: 5b:       cc      int3
+// CHECK-NEXT: 5c:       cc      int3
+// CHECK-NEXT: 5d:       cc      int3
+// CHECK-NEXT: 5e:       cc      int3
+// CHECK-NEXT: 5f:       cc      int3
+// CHECK-NEXT: 60:       4c 8b 1d 11 01 00 00    movq    273(%rip), %r11
+// CHECK-NEXT: 67:       e8 c4 ff ff ff  callq   -60 <.plt+0x20>
+// CHECK-NEXT: 6c:       e9 b1 ff ff ff  jmp     -79 <.plt+0x12>
+// CHECK-NEXT: 71:       68 01 00 00 00  pushq   $1
+// CHECK-NEXT: 76:       e9 95 ff ff ff  jmp     -107 <.plt>
+// CHECK-NEXT: 7b:       cc      int3
+// CHECK-NEXT: 7c:       cc      int3
+// CHECK-NEXT: 7d:       cc      int3
+// CHECK-NEXT: 7e:       cc      int3
+// CHECK-NEXT: 7f:       cc      int3
+
+.global _start
+_start:
+  jmp bar@PLT
+  jmp zed@PLT
diff --git a/lld/test/ELF/x86-64-retpoline-znow-linkerscript.s b/lld/test/ELF/x86-64-retpoline-znow-linkerscript.s
new file mode 100644 (file)
index 0000000..27737b8
--- /dev/null
@@ -0,0 +1,54 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN:   .text : { *(.text) } \
+// RUN:   .plt : { *(.plt) } \
+// RUN:   .got.plt : { *(.got.plt) } \
+// RUN:   .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld -shared %t1.o %t2.so -o %t.exe -z retpolineplt -z now --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK:      Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10:     e8 0b 00 00 00  callq   11 <.plt+0x10>
+// CHECK-NEXT: 15:     f3 90   pause
+// CHECK-NEXT: 17:     0f ae e8        lfence
+// CHECK-NEXT: 1a:     eb f9   jmp     -7 <.plt+0x5>
+// CHECK-NEXT: 1c:     cc      int3
+// CHECK-NEXT: 1d:     cc      int3
+// CHECK-NEXT: 1e:     cc      int3
+// CHECK-NEXT: 1f:     cc      int3
+// CHECK-NEXT: 20:     4c 89 1c 24     movq    %r11, (%rsp)
+// CHECK-NEXT: 24:     c3      retq
+// CHECK-NEXT: 25:     cc      int3
+// CHECK-NEXT: 26:     cc      int3
+// CHECK-NEXT: 27:     cc      int3
+// CHECK-NEXT: 28:     cc      int3
+// CHECK-NEXT: 29:     cc      int3
+// CHECK-NEXT: 2a:     cc      int3
+// CHECK-NEXT: 2b:     cc      int3
+// CHECK-NEXT: 2c:     cc      int3
+// CHECK-NEXT: 2d:     cc      int3
+// CHECK-NEXT: 2e:     cc      int3
+// CHECK-NEXT: 2f:     cc      int3
+// CHECK-NEXT: 30:     4c 8b 1d 09 01 00 00    movq    265(%rip), %r11
+// CHECK-NEXT: 37:     e9 d4 ff ff ff  jmp     -44 <.plt>
+// CHECK-NEXT: 3c:     cc      int3
+// CHECK-NEXT: 3d:     cc      int3
+// CHECK-NEXT: 3e:     cc      int3
+// CHECK-NEXT: 3f:     cc      int3
+// CHECK-NEXT: 40: 4c 8b 1d 01 01 00 00        movq    257(%rip), %r11
+// CHECK-NEXT: 47:     e9 c4 ff ff ff  jmp     -60 <.plt>
+// CHECK-NEXT: 4c:     cc      int3
+// CHECK-NEXT: 4d:     cc      int3
+// CHECK-NEXT: 4e:     cc      int3
+// CHECK-NEXT: 4f:     cc      int3
+
+.global _start
+_start:
+  jmp bar@PLT
+  jmp zed@PLT