[ELF] Fix reading of PC values of FDEs

The patch fixes two related problems:
- If CIE augmentation string has 'L' token the CIE contains a byte
  defines LSDA encoding. We should skip this byte in `getFdeEncoding`
  routine. Before this fix we do not skip it and if the next token
  is 'R' treat this byte as FDE encoding.
- FDE encoding format has separate flags e.g. DW_EH_PE_pcrel for
  definition of relative pointers. We should add .eh_frame address to
  the PC value iif the DW_EH_PE_pcrel is specified.

http://www.airs.com/blog/archives/460

There is one more not fixed problem in this code. If PC value is encoded
using signed relative format e.g. DW_EH_PE_sdata4 | DW_EH_PE_pcrel we
should sign extend result of read32 to perform calculation correctly.
I am going to fix that in a separate patch.

Differential Revision: http://reviews.llvm.org/D17733

llvm-svn: 262461
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index bec005e..3274ec0 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -668,25 +668,31 @@
 typename EhFrameHeader<ELFT>::uintX_t
 EhFrameHeader<ELFT>::getFdePc(uintX_t EhVA, const FdeData &F) {
   const endianness E = ELFT::TargetEndianness;
-  assert((F.Enc & 0xF0) != DW_EH_PE_datarel);
-
-  uintX_t FdeOff = EhVA + F.Off + 8;
-  switch (F.Enc & 0xF) {
+  uint8_t Size = F.Enc & 0x7;
+  if (Size == DW_EH_PE_absptr)
+    Size = sizeof(uintX_t) == 8 ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
+  uint64_t PC;
+  switch (Size) {
   case DW_EH_PE_udata2:
-  case DW_EH_PE_sdata2:
-    return FdeOff + read16<E>(F.PCRel);
+    PC = read16<E>(F.PCRel);
+    break;
   case DW_EH_PE_udata4:
-  case DW_EH_PE_sdata4:
-    return FdeOff + read32<E>(F.PCRel);
+    PC = read32<E>(F.PCRel);
+    break;
   case DW_EH_PE_udata8:
-  case DW_EH_PE_sdata8:
-    return FdeOff + read64<E>(F.PCRel);
-  case DW_EH_PE_absptr:
-    if (sizeof(uintX_t) == 8)
-      return FdeOff + read64<E>(F.PCRel);
-    return FdeOff + read32<E>(F.PCRel);
+    PC = read64<E>(F.PCRel);
+    break;
+  default:
+    fatal("unknown FDE size encoding");
   }
-  fatal("unknown FDE size encoding");
+  switch (F.Enc & 0x70) {
+  case DW_EH_PE_absptr:
+    return PC;
+  case DW_EH_PE_pcrel:
+    return PC + EhVA + F.Off + 8;
+  default:
+    fatal("unknown FDE size relative encoding");
+  }
 }
 
 template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
@@ -1084,8 +1090,10 @@
       skipAugP<ELFT>(D);
       continue;
     }
-    if (C == 'L')
+    if (C == 'L') {
+      readByte(D);
       continue;
+    }
     fatal("unknown .eh_frame augmentation string: " + Aug);
   }
   return DW_EH_PE_absptr;