diff options
-rw-r--r-- | lld/ELF/OutputSections.cpp | 121 | ||||
-rw-r--r-- | lld/ELF/OutputSections.h | 13 | ||||
-rw-r--r-- | lld/test/ELF/eh-frame-hdr-icf.s | 2 |
3 files changed, 69 insertions, 67 deletions
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 03688d439ab..86bf3a38237 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -720,42 +720,6 @@ EhFrameHeader<ELFT>::EhFrameHeader() this->Header.sh_size = 12; } -// We have to get PC values of FDEs. They depend on relocations -// which are target specific, so we run this code after performing -// all relocations. We read the values from ouput buffer according to the -// encoding given for FDEs. Return value is an offset to the initial PC value -// for the FDE. -template <class ELFT> -typename EhFrameHeader<ELFT>::uintX_t -EhFrameHeader<ELFT>::getFdePc(uintX_t EhVA, const FdeData &F) { - const endianness E = ELFT::TargetEndianness; - 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: - PC = read16<E>(F.PCRel); - break; - case DW_EH_PE_udata4: - PC = read32<E>(F.PCRel); - break; - case DW_EH_PE_udata8: - PC = read64<E>(F.PCRel); - break; - default: - 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"); - } -} - // .eh_frame_hdr contains a binary search table of pointers to FDEs. // Each entry of the search table consists of two values, // the starting PC from where FDEs covers, and the FDE's address. @@ -763,36 +727,32 @@ EhFrameHeader<ELFT>::getFdePc(uintX_t EhVA, const FdeData &F) { template <class ELFT> void EhFrameHeader<ELFT>::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<uintX_t, size_t> PcToOffset; - for (const FdeData &F : FdeList) - PcToOffset[getFdePc(EhVA, F)] = F.Off; + // Sort the FDE list by their PC and uniqueify. Usually there is only + // one FDE for a PC (i.e. function), but if ICF merges two functions + // into one, there can be more than one FDEs pointing to the address. + auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; }; + std::stable_sort(Fdes.begin(), Fdes.end(), Less); + auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; }; + Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end()); - // Write a header. Buf[0] = 1; Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; Buf[2] = DW_EH_PE_udata4; Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; - uintX_t EhOff = EhVA - VA - 4; - write32<E>(Buf + 4, EhOff); - write32<E>(Buf + 8, PcToOffset.size()); + write32<E>(Buf + 4, Sec->getVA() - this->getVA() - 4); + write32<E>(Buf + 8, Fdes.size()); Buf += 12; - for (auto &I : PcToOffset) { - // The first four bytes are an offset to the initial PC value for the FDE. - write32<E>(Buf, I.first - VA); - // The last four bytes are an offset to the FDE data itself. - write32<E>(Buf + 4, EhVA + I.second - VA); + uintX_t VA = this->getVA(); + for (FdeData &Fde : Fdes) { + write32<E>(Buf, Fde.Pc - VA); + write32<E>(Buf + 4, Fde.FdeVA - VA); Buf += 8; } } template <class ELFT> -void EhFrameHeader<ELFT>::assignEhFrame(EHOutputSection<ELFT> *Sec) { +void EhFrameHeader<ELFT>::add(EHOutputSection<ELFT> *Sec) { assert((!this->Sec || this->Sec == Sec) && "multiple .eh_frame sections not supported for .eh_frame_hdr"); Live = Config->EhFrameHdr; @@ -800,10 +760,8 @@ void EhFrameHeader<ELFT>::assignEhFrame(EHOutputSection<ELFT> *Sec) { } template <class ELFT> -void EhFrameHeader<ELFT>::addFde(uint8_t Enc, size_t Off, uint8_t *PCRel) { - if (Live && (Enc & 0xF0) == DW_EH_PE_datarel) - fatal("DW_EH_PE_datarel encoding unsupported for FDEs by .eh_frame_hdr"); - FdeList.push_back(FdeData{Enc, Off, PCRel}); +void EhFrameHeader<ELFT>::addFde(uint32_t Pc, uint32_t FdeVA) { + Fdes.push_back({Pc, FdeVA}); } template <class ELFT> void EhFrameHeader<ELFT>::reserveFde() { @@ -966,7 +924,7 @@ template <class ELFT> EHOutputSection<ELFT>::EHOutputSection(StringRef Name, uint32_t Type, uintX_t Flags) : OutputSectionBase<ELFT>(Name, Type, Flags) { - Out<ELFT>::EhFrameHdr->assignEhFrame(this); + Out<ELFT>::EhFrameHdr->add(this); } template <class ELFT> @@ -1217,6 +1175,39 @@ template <class ELFT> void EHOutputSection<ELFT>::finalize() { this->Header.sh_size = Off; } +template <class ELFT> static uint64_t readFdeAddr(uint8_t *Buf, int Size) { + const endianness E = ELFT::TargetEndianness; + switch (Size) { + case DW_EH_PE_udata2: + return read16<E>(Buf); + case DW_EH_PE_udata4: + return read32<E>(Buf); + case DW_EH_PE_udata8: + return read64<E>(Buf); + case DW_EH_PE_absptr: + if (ELFT::Is64Bits) + return read64<E>(Buf); + return read32<E>(Buf); + } + fatal("unknown FDE size encoding"); +} + +// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to. +// We need it to create .eh_frame_hdr section. +template <class ELFT> +typename ELFT::uint EHOutputSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff, + uint8_t Enc) { + // The starting address to which this FDE applies to is + // stored at FDE + 8 byte. + size_t Off = FdeOff + 8; + uint64_t Addr = readFdeAddr<ELFT>(Buf + Off, Enc & 0x7); + if ((Enc & 0x70) == DW_EH_PE_absptr) + return Addr; + if ((Enc & 0x70) == DW_EH_PE_pcrel) + return Addr + this->getVA() + Off; + fatal("unknown FDE size relative encoding"); +} + template <class ELFT> void EHOutputSection<ELFT>::writeTo(uint8_t *Buf) { const endianness E = ELFT::TargetEndianness; for (CieRecord *Cie : Cies) { @@ -1230,12 +1221,22 @@ template <class ELFT> void EHOutputSection<ELFT>::writeTo(uint8_t *Buf) { // FDE's second word should have the offset to an associated CIE. // Write it. write32<E>(Buf + Off + 4, Off + 4 - CieOffset); - Out<ELFT>::EhFrameHdr->addFde(Cie->FdeEncoding, Off, Buf + Off + 8); } } for (EHInputSection<ELFT> *S : Sections) S->relocate(Buf, nullptr); + + // Construct .eh_frame_hdr. .eh_frame_hdr is a binary search table + // to get a FDE from an address to which FDE is applied to. So here + // we obtain two addresses and pass them to EhFrameHdr object. + for (CieRecord *Cie : Cies) { + for (SectionPiece *Fde : Cie->FdePieces) { + uintX_t Pc = getFdePc(Buf, Fde->OutputOff, Cie->FdeEncoding); + uintX_t FdeVA = this->getVA() + Fde->OutputOff; + Out<ELFT>::EhFrameHdr->addFde(Pc, FdeVA); + } + } } template <class ELFT> diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index 084c2c3d319..61330c77fde 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -363,6 +363,8 @@ private: uint8_t getFdeEncoding(ArrayRef<uint8_t> D); + uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc); + std::vector<EHInputSection<ELFT> *> Sections; std::vector<CieRecord *> Cies; @@ -536,8 +538,8 @@ public: EhFrameHeader(); void writeTo(uint8_t *Buf) override; - void addFde(uint8_t Enc, size_t Off, uint8_t *PCRel); - void assignEhFrame(EHOutputSection<ELFT> *Sec); + void addFde(uint32_t Pc, uint32_t FdeVA); + void add(EHOutputSection<ELFT> *Sec); void reserveFde(); bool Live = false; @@ -546,14 +548,13 @@ public: private: struct FdeData { - uint8_t Enc; - size_t Off; - uint8_t *PCRel; + uint32_t Pc; + uint32_t FdeVA; }; uintX_t getFdePc(uintX_t EhVA, const FdeData &F); - std::vector<FdeData> FdeList; + std::vector<FdeData> Fdes; }; template <class ELFT> class BuildIdSection : public OutputSectionBase<ELFT> { diff --git a/lld/test/ELF/eh-frame-hdr-icf.s b/lld/test/ELF/eh-frame-hdr-icf.s index 09b76458e53..2e7b335fb46 100644 --- a/lld/test/ELF/eh-frame-hdr-icf.s +++ b/lld/test/ELF/eh-frame-hdr-icf.s @@ -7,7 +7,7 @@ # CHECK: Contents of section .eh_frame_hdr: # CHECK-NEXT: 101a0 011b033b b4ffffff 01000000 600e0000 # ^ FDE count -# CHECK-NEXT: 101b0 e8ffffff 00000000 00000000 +# CHECK-NEXT: 101b0 d0ffffff 00000000 00000000 # ^ FDE for f2 .globl _start, f1, f2 |