From ea423e261fb45dfbebb7a1d73570dc60268cc979 Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Wed, 2 Mar 2016 05:38:42 +0000 Subject: [PATCH] [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 --- lld/ELF/OutputSections.cpp | 38 ++++++++++++++++++++++--------------- lld/test/ELF/eh-frame-hdr-abs-fde.s | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 lld/test/ELF/eh-frame-hdr-abs-fde.s 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 @@ template typename EhFrameHeader::uintX_t EhFrameHeader::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(F.PCRel); + PC = read16(F.PCRel); + break; case DW_EH_PE_udata4: - case DW_EH_PE_sdata4: - return FdeOff + read32(F.PCRel); + PC = read32(F.PCRel); + break; case DW_EH_PE_udata8: - case DW_EH_PE_sdata8: - return FdeOff + read64(F.PCRel); + PC = read64(F.PCRel); + break; + default: + fatal("unknown FDE size encoding"); + } + switch (F.Enc & 0x70) { case DW_EH_PE_absptr: - if (sizeof(uintX_t) == 8) - return FdeOff + read64(F.PCRel); - return FdeOff + read32(F.PCRel); + return PC; + case DW_EH_PE_pcrel: + return PC + EhVA + F.Off + 8; + default: + fatal("unknown FDE size relative encoding"); } - fatal("unknown FDE size encoding"); } template void EhFrameHeader::writeTo(uint8_t *Buf) { @@ -1084,8 +1090,10 @@ uint8_t EHOutputSection::getFdeEncoding(ArrayRef D) { skipAugP(D); continue; } - if (C == 'L') + if (C == 'L') { + readByte(D); continue; + } fatal("unknown .eh_frame augmentation string: " + Aug); } return DW_EH_PE_absptr; diff --git a/lld/test/ELF/eh-frame-hdr-abs-fde.s b/lld/test/ELF/eh-frame-hdr-abs-fde.s new file mode 100644 index 0000000..edc892a --- /dev/null +++ b/lld/test/ELF/eh-frame-hdr-abs-fde.s @@ -0,0 +1,33 @@ +# Check reading PC values of FDEs and writing lookup table in the .eh_frame_hdr +# if CIE augmentation string has 'L' token and PC values are encoded using +# absolute (not relative) format. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: ld.lld --eh-frame-hdr %t.o -o %t +# RUN: llvm-objdump -s -dwarf=frames %t | FileCheck %s + +# REQUIRES: mips + +# CHECK: Contents of section .eh_frame_hdr: +# CHECK-NEXT: 10158 011b033b ffffffcc 00000001 0000fea8 +# ^-- 0x20000 - 0x10158 +# .text - .eh_frame_hdr +# CHECK-NEXT: 10168 ffffffe8 +# CHECK-NEXT: Contents of section .text: +# CHECK-NEXT: 20000 00000000 + +# CHECK: Augmentation: "zLR" +# CHECK: Augmentation data: 00 0B +# ^-- DW_EH_PE_udata4 | DW_EH_PE_signed + + .text + .globl __start +__start: + .cfi_startproc + .cfi_lsda 0, _ex + nop + .cfi_endproc + + .data +_ex: + .word 0 -- 2.7.4