[ELF] Fix handling of FDE negative relative PC addr
authorAndrew Ng <anng.sw@gmail.com>
Mon, 23 Jul 2018 11:29:46 +0000 (11:29 +0000)
committerAndrew Ng <anng.sw@gmail.com>
Mon, 23 Jul 2018 11:29:46 +0000 (11:29 +0000)
Signed values for the FDE PC addr were not correctly handled in
readFdeAddr(). If the value is negative and the type of the value is
smaller than 64 bits, the FDE PC addr overflow error would be
incorrectly triggered.

Fixed readFdeAddr() to properly handle signed values by sign extending
where appropriate.

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

llvm-svn: 337683

lld/ELF/SyntheticSections.cpp
lld/test/ELF/eh-frame-negative-pcrel-sdata2.s [new file with mode: 0644]
lld/test/ELF/eh-frame-negative-pcrel-sdata4.s [new file with mode: 0644]
lld/test/ELF/eh-frame-negative-pcrel-sdata8.s [new file with mode: 0644]

index 4bf6de5ba6435f94d3e198362c88da430898a8f7..cbcf6bf39a1f6ab8323d4b955b0d38701aefbc3f 100644 (file)
@@ -534,9 +534,14 @@ static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
   switch (Size) {
   case DW_EH_PE_udata2:
     return read16(Buf);
+  case DW_EH_PE_sdata2:
+    return (int16_t)read16(Buf);
   case DW_EH_PE_udata4:
     return read32(Buf);
+  case DW_EH_PE_sdata4:
+    return (int32_t)read32(Buf);
   case DW_EH_PE_udata8:
+  case DW_EH_PE_sdata8:
     return read64(Buf);
   case DW_EH_PE_absptr:
     return readUint(Buf);
@@ -551,7 +556,7 @@ uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff,
   // The starting address to which this FDE applies is
   // stored at FDE + 8 byte.
   size_t Off = FdeOff + 8;
-  uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0x7);
+  uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf);
   if ((Enc & 0x70) == DW_EH_PE_absptr)
     return Addr;
   if ((Enc & 0x70) == DW_EH_PE_pcrel)
diff --git a/lld/test/ELF/eh-frame-negative-pcrel-sdata2.s b/lld/test/ELF/eh-frame-negative-pcrel-sdata2.s
new file mode 100644 (file)
index 0000000..30ee856
--- /dev/null
@@ -0,0 +1,85 @@
+# REQUIRES: x86
+
+# Test handling of FDE pc negative relative addressing with DW_EH_PE_sdata2.
+# This situation can arise when .eh_frame is placed after .text.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text : { *(.text) } .eh_frame : { *(.eh_frame) } }" > %t.script
+# RUN: ld.lld --eh-frame-hdr --script %t.script --section-start .text=0x1000 %t.o -o %t
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1001
+# CHECK-NEXT:   Offset: 0x1001
+# CHECK-NEXT:   Size:
+# CHECK-NEXT:   Link:
+# CHECK-NEXT:   Info:
+# CHECK-NEXT:   AddressAlignment:
+# CHECK-NEXT:   EntrySize:
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 14000000 00000000 017A5200 01010101
+# CHECK-NEXT:     0010: 1A000000 00000000 0C000000 1C000000
+# CHECK-NEXT:     0020: DFFFFFFF
+#                       ^
+#   DFFFFFFF = _start(0x1000) - PC(.eh_frame(0x1001) + 0x20)
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame_hdr
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1030
+# CHECK-NEXT:   Offset: 0x1030
+# CHECK-NEXT:   Size: 20
+# CHECK-NEXT:   Link: 0
+# CHECK-NEXT:   Info: 0
+# CHECK-NEXT:   AddressAlignment: 4
+# CHECK-NEXT:   EntrySize: 0
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 011B033B CDFFFFFF 01000000 D0FFFFFF
+# CHECK-NEXT:     0010: E9FFFFFF
+#   Header (always 4 bytes): 011B033B
+#   CDFFFFFF = .eh_frame(0x1001) - .eh_frame_hdr(0x1030) - 4
+#   01000000 = 1 = the number of FDE pointers in the table.
+#   D0FFFFFF = _start(0x1000) - .eh_frame_hdr(0x1030)
+#   E9FFFFFF = FDE(.eh_frame(0x1001) + 0x18) - .eh_frame_hdr(0x1030)
+
+.text
+.global _start
+_start:
+ nop
+
+.section .eh_frame, "a"
+  .long 16   # Size
+  .long 0x00 # ID
+  .byte 0x01 # Version.
+
+  .byte 0x7A # Augmentation string: "zR"
+  .byte 0x52
+  .byte 0x00
+
+  .byte 0x01
+
+  .byte 0x01 # LEB128
+  .byte 0x01 # LEB128
+
+  .byte 0x01 # LEB128
+  .byte 0x1A # DW_EH_PE_pcrel | DW_EH_PE_sdata2
+
+  .byte 0x00
+  .byte 0x00
+  .byte 0x00
+
+  .long 10   # Size
+  .long 24   # ID
+fde:
+  .long _start - fde
+  .word 0
diff --git a/lld/test/ELF/eh-frame-negative-pcrel-sdata4.s b/lld/test/ELF/eh-frame-negative-pcrel-sdata4.s
new file mode 100644 (file)
index 0000000..71b91cf
--- /dev/null
@@ -0,0 +1,85 @@
+# REQUIRES: x86
+
+# Test handling of FDE pc negative relative addressing with DW_EH_PE_sdata4.
+# This situation can arise when .eh_frame is placed after .text.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text : { *(.text) } .eh_frame : { *(.eh_frame) } }" > %t.script
+# RUN: ld.lld --eh-frame-hdr --script %t.script --section-start .text=0x1000 %t.o -o %t
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1001
+# CHECK-NEXT:   Offset: 0x1001
+# CHECK-NEXT:   Size:
+# CHECK-NEXT:   Link:
+# CHECK-NEXT:   Info:
+# CHECK-NEXT:   AddressAlignment:
+# CHECK-NEXT:   EntrySize:
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 14000000 00000000 017A5200 01010101
+# CHECK-NEXT:     0010: 1B000000 00000000 0C000000 1C000000
+# CHECK-NEXT:     0020: DFFFFFFF
+#                       ^
+#   DFFFFFFF = _start(0x1000) - PC(.eh_frame(0x1001) + 0x20)
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame_hdr
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1030
+# CHECK-NEXT:   Offset: 0x1030
+# CHECK-NEXT:   Size: 20
+# CHECK-NEXT:   Link: 0
+# CHECK-NEXT:   Info: 0
+# CHECK-NEXT:   AddressAlignment: 4
+# CHECK-NEXT:   EntrySize: 0
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 011B033B CDFFFFFF 01000000 D0FFFFFF
+# CHECK-NEXT:     0010: E9FFFFFF
+#   Header (always 4 bytes): 011B033B
+#   CDFFFFFF = .eh_frame(0x1001) - .eh_frame_hdr(0x1030) - 4
+#   01000000 = 1 = the number of FDE pointers in the table.
+#   D0FFFFFF = _start(0x1000) - .eh_frame_hdr(0x1030)
+#   E9FFFFFF = FDE(.eh_frame(0x1001) + 0x18) - .eh_frame_hdr(0x1030)
+
+.text
+.global _start
+_start:
+ nop
+
+.section .eh_frame, "a"
+  .long 16   # Size
+  .long 0x00 # ID
+  .byte 0x01 # Version.
+
+  .byte 0x7A # Augmentation string: "zR"
+  .byte 0x52
+  .byte 0x00
+
+  .byte 0x01
+
+  .byte 0x01 # LEB128
+  .byte 0x01 # LEB128
+
+  .byte 0x01 # LEB128
+  .byte 0x1B # DW_EH_PE_pcrel | DW_EH_PE_sdata4
+
+  .byte 0x00
+  .byte 0x00
+  .byte 0x00
+
+  .long 12   # Size
+  .long 24   # ID
+fde:
+  .long _start - fde
+  .long 0
diff --git a/lld/test/ELF/eh-frame-negative-pcrel-sdata8.s b/lld/test/ELF/eh-frame-negative-pcrel-sdata8.s
new file mode 100644 (file)
index 0000000..2c6c9f2
--- /dev/null
@@ -0,0 +1,85 @@
+# REQUIRES: x86
+
+# Test handling of FDE pc negative relative addressing with DW_EH_PE_sdata8.
+# This situation can arise when .eh_frame is placed after .text.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text : { *(.text) } .eh_frame : { *(.eh_frame) } }" > %t.script
+# RUN: ld.lld --eh-frame-hdr --script %t.script --section-start .text=0x1000 %t.o -o %t
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1001
+# CHECK-NEXT:   Offset: 0x1001
+# CHECK-NEXT:   Size:
+# CHECK-NEXT:   Link:
+# CHECK-NEXT:   Info:
+# CHECK-NEXT:   AddressAlignment:
+# CHECK-NEXT:   EntrySize:
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 14000000 00000000 017A5200 01010101
+# CHECK-NEXT:     0010: 1C000000 00000000 14000000 1C000000
+# CHECK-NEXT:     0020: DFFFFFFF FFFFFFFF
+#                       ^
+#   DFFFFFFF FFFFFFFF = _start(0x1000) - PC(.eh_frame(0x1001) + 0x20)
+
+# CHECK:      Section {
+# CHECK:        Index:
+# CHECK:        Name: .eh_frame_hdr
+# CHECK-NEXT:   Type: SHT_PROGBITS
+# CHECK-NEXT:   Flags [
+# CHECK-NEXT:     SHF_ALLOC
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   Address: 0x1038
+# CHECK-NEXT:   Offset: 0x1038
+# CHECK-NEXT:   Size: 20
+# CHECK-NEXT:   Link: 0
+# CHECK-NEXT:   Info: 0
+# CHECK-NEXT:   AddressAlignment: 4
+# CHECK-NEXT:   EntrySize: 0
+# CHECK-NEXT:   SectionData (
+# CHECK-NEXT:     0000: 011B033B C5FFFFFF 01000000 C8FFFFFF
+# CHECK-NEXT:     0010: E1FFFFFF
+#   Header (always 4 bytes): 011B033B
+#   C5FFFFFF = .eh_frame(0x1001) - .eh_frame_hdr(0x1038) - 4
+#   01000000 = 1 = the number of FDE pointers in the table.
+#   C8FFFFFF = _start(0x1000) - .eh_frame_hdr(0x1038)
+#   E1FFFFFF = FDE(.eh_frame(0x1001) + 0x18) - .eh_frame_hdr(0x1038)
+
+.text
+.global _start
+_start:
+ nop
+
+.section .eh_frame, "a"
+  .long 16   # Size
+  .long 0x00 # ID
+  .byte 0x01 # Version.
+
+  .byte 0x7A # Augmentation string: "zR"
+  .byte 0x52
+  .byte 0x00
+
+  .byte 0x01
+
+  .byte 0x01 # LEB128
+  .byte 0x01 # LEB128
+
+  .byte 0x01 # LEB128
+  .byte 0x1C # DW_EH_PE_pcrel | DW_EH_PE_sdata8
+
+  .byte 0x00
+  .byte 0x00
+  .byte 0x00
+
+  .long 16   # Size
+  .long 24   # ID
+fde:
+  .quad _start - fde
+  .long 0