From c98de13871b3376ba7c25dd33ca49a9712e6542b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Mon, 11 Apr 2016 16:40:08 +0000 Subject: [PATCH] ELF: Set FDE count in .eh_frame_hdr correctly. It is possible to have FDEs with duplicate PCs if ICF was able to merge functions with FDEs, or if the input files for some reason contained duplicate FDEs. We previously weren't handling this correctly when producing the contents of the .eh_frame_hdr section; we were dropping entries and leaving null entries at the end of the section, which confused consumers of unwind data, such as the backtrace() function. Fix the bug by setting the FDE count to the number of FDEs actually emitted into .eh_frame_hdr, rather than the number of FDEs in .eh_frame. Differential Revision: http://reviews.llvm.org/D18911 llvm-svn: 265957 --- lld/ELF/OutputSections.cpp | 18 ++++++++++-------- lld/test/ELF/eh-frame-hdr-icf.s | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 lld/test/ELF/eh-frame-hdr-icf.s diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 753960447fa9..89a9df0c44bb 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -727,23 +727,25 @@ EhFrameHeader::getFdePc(uintX_t EhVA, const FdeData &F) { template void EhFrameHeader::writeTo(uint8_t *Buf) { const endianness E = ELFT::TargetEndianness; + uintX_t EhVA = Sec->getVA(); + uintX_t VA = this->getVA(); + + // InitialPC -> Offset in .eh_frame, sorted by InitialPC, and deduplicate PCs. + // FIXME: Deduplication leaves unneeded null bytes at the end of the section. + std::map PcToOffset; + for (const FdeData &F : FdeList) + PcToOffset[getFdePc(EhVA, F)] = F.Off; + const uint8_t Header[] = {1, DW_EH_PE_pcrel | DW_EH_PE_sdata4, DW_EH_PE_udata4, DW_EH_PE_datarel | DW_EH_PE_sdata4}; memcpy(Buf, Header, sizeof(Header)); - uintX_t EhVA = Sec->getVA(); - uintX_t VA = this->getVA(); uintX_t EhOff = EhVA - VA - 4; write32(Buf + 4, EhOff); - write32(Buf + 8, this->FdeList.size()); + write32(Buf + 8, PcToOffset.size()); Buf += 12; - // InitialPC -> Offset in .eh_frame, sorted by InitialPC. - std::map PcToOffset; - for (const FdeData &F : FdeList) - PcToOffset[getFdePc(EhVA, F)] = F.Off; - for (auto &I : PcToOffset) { // The first four bytes are an offset to the initial PC value for the FDE. write32(Buf, I.first - VA); diff --git a/lld/test/ELF/eh-frame-hdr-icf.s b/lld/test/ELF/eh-frame-hdr-icf.s new file mode 100644 index 000000000000..09b76458e53e --- /dev/null +++ b/lld/test/ELF/eh-frame-hdr-icf.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --eh-frame-hdr +# RUN: llvm-objdump -s %t2 | FileCheck %s + +# CHECK: Contents of section .eh_frame_hdr: +# CHECK-NEXT: 101a0 011b033b b4ffffff 01000000 600e0000 +# ^ FDE count +# CHECK-NEXT: 101b0 e8ffffff 00000000 00000000 +# ^ FDE for f2 + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" +f1: + .cfi_startproc + ret + .cfi_endproc + +.section .text.f2, "ax" +f2: + .cfi_startproc + ret + .cfi_endproc