[lld] r305135 - Relax the overflow checking of R_386_PC16.

Rafael Espindola via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 9 18:56:59 PDT 2017


Author: rafael
Date: Fri Jun  9 20:56:58 2017
New Revision: 305135

URL: http://llvm.org/viewvc/llvm-project?rev=305135&view=rev
Log:
Relax the overflow checking of R_386_PC16.

Currently the freebsd early boot code fails to link. The issue reduces
to 16 bit code at position 0x7000 wanting to jump to position
0x9000. That is represented in the .o file as a relocation with no
symbol and an addend of 0x9000 - 2 (The -2 is because i386 uses the ip
after the current instruction for jumps).

If the addend is interpreted as signed (it should), it is -28674. In a
32 bit architecture, that is the address 0xffff8ffe. To get there from
0x7000 we have to add 4294909950 (too big) or subtract 57346 (too
small). We then produce an error.

What lld is missing is the fact that at runtime this will actually be
a 16 bit architecture and adding 0x1ffe produces 0x8ffe which is the
correct result in 16 bits (-28674).

Since we have a 16 bit addend and a 16 bit PC, the relocation can move
the PC to any 16 bit address and that is the only thing we really need
to check: if the address we are pointing to fits in 16 bits. This is
unfortunately hard to do since we have to delay subtracting the PC and
if we want to do that outside of Target.cpp, we have to move the
overflow check out too.  An incomplete patch that tries to do that is
at https://reviews.llvm.org/D34070

This patch instead just relaxes the check. Since the value we have is
the destination minus the PC and the PC is 16 bits, it should fit in
17 bits if the destination fits in 16 too.

bfd had a similar issue for some time and got a similar fix:
https://sourceware.org/ml/binutils/2005-08/msg00001.html

Added:
    lld/trunk/test/ELF/i386-reloc-large-addend.s
Modified:
    lld/trunk/ELF/Target.cpp
    lld/trunk/test/ELF/i386-reloc-range.s

Modified: lld/trunk/ELF/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.cpp?rev=305135&r1=305134&r2=305135&view=diff
==============================================================================
--- lld/trunk/ELF/Target.cpp (original)
+++ lld/trunk/ELF/Target.cpp Fri Jun  9 20:56:58 2017
@@ -537,7 +537,17 @@ void X86TargetInfo::relocateOne(uint8_t
     write16le(Loc, Val);
     break;
   case R_386_PC16:
-    checkInt<16>(Loc, Val, Type);
+    // R_386_PC16 is normally used with 16 bit code. In that situation
+    // the PC is 16 bits, just like the addend. This means that it can
+    // point from any 16 bit address to any other if the possibility
+    // of wrapping is included.
+    // The only restriction we have to check then is that the destination
+    // address fits in 16 bits. That is impossible to do here. The problem is
+    // that we are passed the final value, which already had the
+    // current location subtracted from it.
+    // We just check that Val fits in 17 bits. This misses some cases, but
+    // should have no false positives.
+    checkInt<17>(Loc, Val, Type);
     write16le(Loc, Val);
     break;
   default:

Added: lld/trunk/test/ELF/i386-reloc-large-addend.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/i386-reloc-large-addend.s?rev=305135&view=auto
==============================================================================
--- lld/trunk/test/ELF/i386-reloc-large-addend.s (added)
+++ lld/trunk/test/ELF/i386-reloc-large-addend.s Fri Jun  9 20:56:58 2017
@@ -0,0 +1,15 @@
+// RUN: llvm-mc %s -o %t.o -triple i386-pc-linux-code16 -filetype=obj
+
+// RUN: echo ".global foo; foo = 0x1" > %t1.s
+// RUN: llvm-mc %t1.s -o %t1.o -triple i386-pc-linux -filetype=obj
+
+// RUN: ld.lld -Ttext 0x7000 %t.o %t1.o -o %t
+// RUN: llvm-objdump -d -triple=i386-pc-linux-code16 %t | FileCheck %s
+
+// CHECK:        Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT:     7000:       e9 fe 1f        jmp     8190
+//                            0x1 + 0x9000 - 0x7003 == 8190
+        .global _start
+_start:
+jmp foo + 0x9000

Modified: lld/trunk/test/ELF/i386-reloc-range.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/i386-reloc-range.s?rev=305135&r1=305134&r2=305135&view=diff
==============================================================================
--- lld/trunk/test/ELF/i386-reloc-range.s (original)
+++ lld/trunk/test/ELF/i386-reloc-range.s Fri Jun  9 20:56:58 2017
@@ -1,8 +1,8 @@
 // RUN: llvm-mc %s -o %t.o -triple i386-pc-linux-code16 -filetype=obj
 
-// RUN: echo ".global foo; foo = 0x8202" > %t1.s
+// RUN: echo ".global foo; foo = 0x10202" > %t1.s
 // RUN: llvm-mc %t1.s -o %t1.o -triple i386-pc-linux -filetype=obj
-// RUN: echo ".global foo; foo = 0x8203" > %t2.s
+// RUN: echo ".global foo; foo = 0x10203" > %t2.s
 // RUN: llvm-mc %t2.s -o %t2.o -triple i386-pc-linux -filetype=obj
 
 // RUN: ld.lld -Ttext 0x200 %t.o %t1.o -o %t1
@@ -10,8 +10,8 @@
 
 // CHECK:        Disassembly of section .text:
 // CHECK-NEXT: _start:
-// CHECK-NEXT:      200:       e9 ff 7f        jmp     32767
-//                                   0x8202 - 0x203 == 32767
+// CHECK-NEXT:      200: {{.*}} jmp -1
+//              0x10202 - 0x203 == 0xffff
 
 // RUN: not ld.lld -Ttext 0x200 %t.o %t2.o -o %t2 2>&1 | FileCheck --check-prefix=ERR %s
 




More information about the llvm-commits mailing list