diff --git a/include/eld/Target/ARMEXIDXSection.h b/include/eld/Target/ARMEXIDXSection.h deleted file mode 100644 index fb78399df..000000000 --- a/include/eld/Target/ARMEXIDXSection.h +++ /dev/null @@ -1,39 +0,0 @@ -//===- ARMEXIDXSection.h---------------------------------------------------===// -// Part of the eld Project, under the BSD License -// See https://github.com/qualcomm/eld/LICENSE.txt for license information. -// SPDX-License-Identifier: BSD-3-Clause -//===----------------------------------------------------------------------===// - -#ifndef ELD_READERS_ARM_EXIDX_SECTION_H -#define ELD_READERS_ARM_EXIDX_SECTION_H - -#include "eld/Readers/ELFSection.h" -#include - -namespace eld { - -class DiagnosticEngine; -class RegionFragment; - -struct EXIDXEntry { - uint32_t InputOffset = -1; - Fragment *Frag = nullptr; -}; - -class ARMEXIDXSection : public ELFSection { - llvm::SmallVector Entries; - -public: - explicit ARMEXIDXSection(const std::string &Name, uint32_t Flag, - uint32_t EntSize, uint32_t Size, uint64_t PAddr) - : ELFSection(LDFileFormat::Target, Name, Flag, EntSize, /*AddrAlign=*/0, - llvm::ELF::SHT_ARM_EXIDX, /*Info=*/0, /*Link=*/nullptr, Size, - PAddr) {} - - void addEntry(EXIDXEntry E) { Entries.push_back(E); } - EXIDXEntry getEntry(uint32_t Offset) const; - - static bool classof(const ELFSection *S) { return S->isEXIDX(); } -}; -} // namespace eld -#endif diff --git a/lib/Object/SectionMap.cpp b/lib/Object/SectionMap.cpp index bc8bbce6f..5b7434714 100644 --- a/lib/Object/SectionMap.cpp +++ b/lib/Object/SectionMap.cpp @@ -29,7 +29,6 @@ #include "eld/Script/WildcardPattern.h" #include "eld/Support/Memory.h" #include "eld/Support/MsgHandling.h" -#include "eld/Target/ARMEXIDXSection.h" #include "eld/Target/ELFSegment.h" #include "eld/Target/GNULDBackend.h" #include "llvm/ADT/Hashing.h" @@ -537,8 +536,6 @@ ELFSection *SectionMap::createOutputSectionEntry(std::string Section, ELFSection *SectionMap::createELFSection(const std::string &Name, LDFileFormat::Kind K, uint32_t Type, uint32_t Flags, uint32_t EntSize) { - if (Type == llvm::ELF::SHT_ARM_EXIDX && K == LDFileFormat::Target) - return make(Name, Flags, EntSize, /*Size=*/0, /*PAddr=*/0); return make(K, Name, Flags, EntSize, /*AddrAlign=*/0, Type, /*Info=*/0, /*Link=*/nullptr, diff --git a/lib/Target/ARM/ARMEXIDXFragment.cpp b/lib/Target/ARM/ARMEXIDXFragment.cpp new file mode 100644 index 000000000..5298f8f61 --- /dev/null +++ b/lib/Target/ARM/ARMEXIDXFragment.cpp @@ -0,0 +1,124 @@ +//===- ARMEXIDXFragment.cpp------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "ARMEXIDXFragment.h" +#include "eld/Diagnostics/MsgHandler.h" +#include "eld/Readers/ELFSection.h" +#include "eld/Readers/Relocation.h" +#include "eld/SymbolResolver/ResolveInfo.h" +#include + +using namespace eld; + +EXIDXPiece EXIDXFragment::getPiece(uint32_t Offset) const { + ASSERT(getOwningSection(), "EXIDX fragment must have an owning section"); + ASSERT(Offset < getOwningSection()->size(), + "Exception on .ARM.exidx relocation handling"); + for (const EXIDXPiece &P : Pieces) + if (P.InputOffset <= Offset && Offset < P.InputOffset + P.Size) + return P; + ASSERT(false, "Unable to map relocation to EXIDX piece range"); + return {}; +} + +uint32_t EXIDXFragment::translateInputOffset(uint32_t InputOffset) const { + uint32_t OutputOffset = 0; + for (const EXIDXPiece &P : Pieces) { + if (P.InputOffset <= InputOffset && InputOffset < P.InputOffset + P.Size) + return OutputOffset + (InputOffset - P.InputOffset); + OutputOffset += P.Size; + } + ASSERT(false, "Unable to translate EXIDX relocation offset to piece"); + return 0; +} + +size_t EXIDXFragment::size() const { + size_t Total = 0; + for (const EXIDXPiece &P : Pieces) + Total += P.Size; + return Total; +} + +void EXIDXFragment::dump(llvm::raw_ostream &OS) { + if (Pieces.empty()) + return; + + ELFSection *S = getOwningSection(); + const bool IsGC = S && (S->isIgnore() || S->isDiscard()); + ELFSection *OutSection = getOutputELFSection(); + const uint64_t SectionAddr = OutSection ? OutSection->addr() : 0; + const uint64_t FragmentOutputOffset = hasOffset() ? getOffset() : 0; + const uint64_t FragmentOutputAddr = SectionAddr + FragmentOutputOffset; + + OS << "#EXIDX"; + OS << "\tsec=0x"; + OS.write_hex(FragmentOutputAddr); + OS << "\tsz=0x"; + OS.write_hex(size()); + OS << "\tn=" << Pieces.size(); + if (IsGC) + OS << "\t"; + OS << "\n"; + + for (size_t I = 0; I < Pieces.size(); ++I) { + const EXIDXPiece &Piece = Pieces[I]; + OS << "#P[" << I << "]"; + OS << "\toff=0x"; + OS.write_hex(Piece.InputOffset); + OS << "\taddr=0x"; + OS.write_hex(FragmentOutputAddr + Piece.InputOffset); + OS << "\tsz=0x"; + OS.write_hex(Piece.Size); + if (IsGC) + OS << "\t"; + OS << "\n"; + + if (!S) + continue; + + for (Relocation *R : S->getRelocations()) { + if (!R || !R->targetRef() || R->targetRef()->isNull()) + continue; + if (R->targetRef()->frag() != this) + continue; + + const uint32_t RelInputOffset = R->targetRef()->offset(); + if (RelInputOffset < Piece.InputOffset || + RelInputOffset >= Piece.InputOffset + Piece.Size) + continue; + + OS << "#R"; + if (ResolveInfo *Sym = R->symInfo()) + OS << "\tsym=" << Sym->name(); + OS << "\tadd=0x"; + OS.write_hex(R->addend()); + if (IsGC) + OS << "\t"; + OS << "\n"; + } + } +} + +eld::Expected EXIDXFragment::emit(MemoryRegion &Mr, Module &) { + if (Pieces.empty()) + return {}; + + llvm::StringRef Region = getRegion(); + uint32_t BaseOffset = getOffset(); + uint32_t OutOffset = 0; + for (const EXIDXPiece &Piece : Pieces) { + const uint32_t Begin = Piece.InputOffset; + const uint32_t PieceSize = Piece.Size; + ASSERT(Begin + PieceSize <= Region.size(), + "Invalid EXIDX piece boundaries"); + if (!PieceSize) + continue; + std::memcpy(Mr.begin() + BaseOffset + OutOffset, Region.data() + Begin, + PieceSize); + OutOffset += PieceSize; + } + return {}; +} diff --git a/lib/Target/ARM/ARMEXIDXFragment.h b/lib/Target/ARM/ARMEXIDXFragment.h new file mode 100644 index 000000000..211ae3633 --- /dev/null +++ b/lib/Target/ARM/ARMEXIDXFragment.h @@ -0,0 +1,45 @@ +//===- ARMEXIDXFragment.h--------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#ifndef TARGET_ARM_ARMEXIDXFRAGMENT_H +#define TARGET_ARM_ARMEXIDXFRAGMENT_H + +#include "eld/Fragment/RegionFragment.h" +#include "llvm/ADT/SmallVector.h" +#include + +namespace eld { + +struct EXIDXPiece { + uint32_t InputOffset = UINT32_MAX; + uint32_t Size = 0; +}; + +class EXIDXFragment : public RegionFragment { +public: + EXIDXFragment(llvm::StringRef Region, ELFSection *O, uint32_t Align = 1) + : RegionFragment(Region, O, Fragment::Type::Region, Align) {} + + ~EXIDXFragment() override = default; + + void addPiece(EXIDXPiece P) { Pieces.push_back(P); } + + llvm::SmallVectorImpl &getPieces() { return Pieces; } + const llvm::SmallVectorImpl &getPieces() const { return Pieces; } + + EXIDXPiece getPiece(uint32_t Offset) const; + uint32_t translateInputOffset(uint32_t InputOffset) const; + size_t size() const override; + void dump(llvm::raw_ostream &OS) override; + eld::Expected emit(MemoryRegion &Mr, Module &M) override; + +private: + llvm::SmallVector Pieces; +}; + +} // namespace eld + +#endif diff --git a/lib/Target/ARM/ARMEXIDXSection.cpp b/lib/Target/ARM/ARMEXIDXSection.cpp deleted file mode 100644 index e2da9845e..000000000 --- a/lib/Target/ARM/ARMEXIDXSection.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//===- ARMEXIDXSection.cpp-------------------------------------------------===// -// Part of the eld Project, under the BSD License -// See https://github.com/qualcomm/eld/LICENSE.txt for license information. -// SPDX-License-Identifier: BSD-3-Clause -//===----------------------------------------------------------------------===// -// -// The MCLinker Project -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "eld/Target/ARMEXIDXSection.h" -#include "eld/Diagnostics/MsgHandler.h" -#include "eld/Fragment/RegionFragment.h" - -using namespace eld; - -EXIDXEntry ARMEXIDXSection::getEntry(uint32_t Offset) const { - ASSERT(Offset < size(), "Exception on .ARM.exidx relocation handling " + - getInputFile()->getInput()->decoratedPath()); - return llvm::partition_point(Entries, [Offset](const EXIDXEntry &E) { - return E.InputOffset <= Offset; - })[-1]; -} diff --git a/lib/Target/ARM/ARMLDBackend.cpp b/lib/Target/ARM/ARMLDBackend.cpp index 4a8a7743f..1c7b79c09 100644 --- a/lib/Target/ARM/ARMLDBackend.cpp +++ b/lib/Target/ARM/ARMLDBackend.cpp @@ -14,6 +14,7 @@ #include "ARM.h" #include "ARMAttributeFragment.h" #include "ARMELFDynamic.h" +#include "ARMEXIDXFragment.h" #include "ARMInfo.h" #include "ARMRelocator.h" #include "ARMToARMStub.h" @@ -35,12 +36,12 @@ #include "eld/Support/RegisterTimer.h" #include "eld/Support/TargetRegistry.h" #include "eld/SymbolResolver/IRBuilder.h" -#include "eld/Target/ARMEXIDXSection.h" #include "eld/Target/ELFFileFormat.h" #include "eld/Target/ELFSegment.h" #include "eld/Target/ELFSegmentFactory.h" #include "eld/Target/GNULDBackend.h" #include "eld/Target/TargetInfo.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELFTypes.h" @@ -48,7 +49,9 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" +#include #include +#include using namespace eld; using namespace llvm; @@ -157,7 +160,7 @@ void ARMGNULDBackend::initTargetSymbols() { m_Module.getIRBuilder() ->addSymbol( m_Module.getInternalInput(Module::Script), SymbolName, - ResolveInfo::NoType, ResolveInfo::Define, ResolveInfo::Global, + ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Global, 0x0, // size 0x0, // value FragmentRef::null(), ResolveInfo::Visibility::Hidden); @@ -175,7 +178,7 @@ void ARMGNULDBackend::initTargetSymbols() { m_Module.getIRBuilder() ->addSymbol( m_Module.getInternalInput(Module::Script), SymbolName, - ResolveInfo::NoType, ResolveInfo::Define, ResolveInfo::Global, + ResolveInfo::Object, ResolveInfo::Define, ResolveInfo::Global, 0x0, // size 0x0, // value FragmentRef::null(), ResolveInfo::Visibility::Hidden); @@ -224,54 +227,6 @@ void ARMGNULDBackend::doPreLayout() { return; } - ELFSection *exidx = - m_Module.getScript().sectionMap().find(llvm::ELF::SHT_ARM_EXIDX); - if (exidx && exidx->size()) { - OutputSectionEntry *O = exidx->getOutputSection(); - Fragment *Start = nullptr; - Fragment *End = nullptr; - ELFSection *Last = nullptr; - for (auto &In : *O) { - ELFSection *S = In->getSection(); - if (S->size() == 0) - continue; - Last = S; - if (!Start) { - Start = Last->getFrontFragment(); - } - if (Last) - End = Last->getBackFragment(); - - FragmentRef *exidx_start = make(*Start, 0); - - FragmentRef *exidx_end = make(*End, End->size()); - - ResolveInfo oldStart, oldEnd; - - // FIXME: need real provide support. This will fail if trampoline inserted - // inside the EXIDX section - if (m_pEXIDXStart) { - m_pEXIDXStart->setValue(exidx->addr() + - exidx_start->getOutputOffset(m_Module)); - oldStart.override(*m_pEXIDXStart->resolveInfo()); - } - if (m_pEXIDXEnd) { - m_pEXIDXEnd->setValue(exidx->addr() + - exidx_end->getOutputOffset(m_Module)); - oldEnd.override(*m_pEXIDXEnd->resolveInfo()); - } - - if (m_pEXIDXEnd) { - m_pEXIDXEnd->setFragmentRef(exidx_end); - m_pEXIDXEnd->resolveInfo()->setType(ResolveInfo::Object); - } - if (m_pEXIDXStart) { - m_pEXIDXStart->setFragmentRef(exidx_start); - m_pEXIDXStart->resolveInfo()->setType(ResolveInfo::Object); - } - } - } - // initialize .dynamic data if ((!config().isCodeStatic() || config().options().forceDynamic()) && nullptr == m_pDynamic) @@ -335,177 +290,189 @@ void ARMGNULDBackend::doPreLayout() { output_sect->setLink(output_link); } } - - // check if entry is missing - LDSymbol *entry_sym = m_Module.getNamePool().findSymbol(getEntry().str()); - Relocation *entry_reloc = nullptr; - ELFSection *Last = nullptr; - if (exidx && (entry_sym != nullptr) && (entry_sym->hasFragRef())) { - for (auto &In : *exidx->getOutputSection()) { - ELFSection *S = In->getSection(); - if (S->size()) - Last = S; - for (auto &F : S->getFragmentList()) { - for (auto &relocation : F->getOwningSection()->getRelocations()) { - // bypass the reloc if the symbol is in the discarded input section - ResolveInfo *info = relocation->symInfo(); - - if (ResolveInfo::Section == info->type() && - ResolveInfo::Undefined == info->desc()) - continue; - - // bypass the reloc if the section where it sits will be discarded. - if (relocation->targetRef()->frag()->getOwningSection()->isIgnore()) - continue; - - if (relocation->targetRef()->frag()->getOwningSection()->isDiscard()) - continue; - - uint64_t reloc_offset = relocation->targetRef()->offset(); - if (!reloc_offset) { - Fragment *region_frag = - relocation->symInfo()->outSymbol()->fragRef()->frag(); - if (region_frag == entry_sym->fragRef()->frag()) { - entry_reloc = relocation; - break; - } - } - } - } - } - - LayoutInfo *layoutInfo = getModule().getLayoutInfo(); - if (!entry_reloc) { - static const char raw_data[] = "\x00\x00\x00\x00\x01\x00\x00\x00"; - StringRef entry_data(raw_data, 8); - size_t align = entry_sym->fragRef()->frag()->alignment(); - Fragment *frag = - make(entry_data, Last, Fragment::Type::Region, align); - Last->addFragmentAndUpdateSize(frag); - if (layoutInfo) - layoutInfo->recordFragment(Last->getInputFile(), Last, frag); - // create the relocation against this entry - entry_reloc = Relocation::Create(llvm::ELF::R_ARM_PREL31, 32, - make(*frag, 0), 0); - entry_reloc->setSymInfo(entry_sym->resolveInfo()); - Last->addRelocation(entry_reloc); - m_InternalRelocs.push_back(entry_reloc); - } - } } void ARMGNULDBackend::sortEXIDX() { - // Mark all fragments. + // ARM EHABI requires .ARM.exidx entries to be sorted by the address of + // the function each entry describes. ELFSection *E = m_Module.getScript().sectionMap().find(llvm::ELF::SHT_ARM_EXIDX); if (!E) return; - if (!E->size()) + OutputSectionEntry *O = E->getOutputSection(); + if (!O) return; - OutputSectionEntry *O = E->getOutputSection(); - llvm::SmallVector Frags; - ELFSection *exidx = nullptr; - // Scan relocation to the fragment. + const uint64_t MaxSortKey = std::numeric_limits::max(); + llvm::DenseSet EXIDXFragSet; + EXIDXFragSet.reserve(m_EXIDXFragments.size()); + for (const auto &KV : m_EXIDXFragments) + EXIDXFragSet.insert(KV.second); + + DiagnosticEngine *Diag = config().getDiagEngine(); for (auto &In : *O) { ELFSection *S = In->getSection(); - if (!exidx) - exidx = In->getSection(); - for (auto &F : S->getFragmentList()) - Frags.push_back(F); - S->clearFragments(); - } + if (!S) + continue; - if (O->getLastRule()) { - exidx = O->getLastRule()->getSection(); - exidx->setMatchedLinkerScriptRule(O->getLastRule()); - } - for (auto &F : Frags) - F->getOwningSection()->setMatchedLinkerScriptRule( - exidx->getMatchedLinkerScriptRule()); - exidx->splice(exidx->getFragmentList().end(), Frags); - - for (auto &F : exidx->getFragmentList()) { - for (auto &relocation : F->getOwningSection()->getRelocations()) { - // bypass the reloc if the symbol is in the discarded input section - ResolveInfo *info = relocation->symInfo(); - - if (ResolveInfo::Section == info->type() && - ResolveInfo::Undefined == info->desc()) - continue; + llvm::DenseMap FragSortKeys; + llvm::DenseMap OriginalFragOffsets; + bool SawEXIDXFrag = false; + for (Fragment *F : S->getFragmentList()) + OriginalFragOffsets[F] = F->getOffset(Diag); - // bypass the reloc if the section where it sits will be discarded. - if (relocation->targetRef()->frag()->getOwningSection()->isIgnore()) + for (Fragment *F : S->getFragmentList()) { + if (!EXIDXFragSet.contains(F)) continue; + SawEXIDXFrag = true; + FragSortKeys[F] = MaxSortKey; - if (relocation->targetRef()->frag()->getOwningSection()->isDiscard()) + EXIDXFragment *EXIDX = static_cast(F); + ELFSection *Owning = EXIDX->getOwningSection(); + if (!Owning) continue; - - if (0x0 == relocation->type()) + auto &Pieces = EXIDX->getPieces(); + if (Pieces.empty()) continue; - uint64_t reloc_offset = relocation->targetRef()->offset(); - if (!reloc_offset) { - // this is key - Fragment *region_frag = relocation->targetRef()->frag(); - region_frag->setFragmentKind(Fragment::Region); + llvm::DenseMap SortKeys; + llvm::DenseMap OriginalOrder; + for (uint32_t I = 0, N = Pieces.size(); I != N; ++I) { + const uint32_t InputOffset = Pieces[I].InputOffset; + SortKeys[InputOffset] = MaxSortKey; + OriginalOrder[InputOffset] = I; + } + + // The first word of an EXIDX entry anchors the described function. + for (Relocation *R : Owning->getRelocations()) { + if (!R || !R->targetRef() || R->targetRef()->isNull()) + continue; + if (R->targetRef()->frag() != EXIDX) + continue; + + const uint32_t RelocOffset = R->targetRef()->offset(); + EXIDXPiece Piece = EXIDX->getPiece(RelocOffset); + if (RelocOffset != Piece.InputOffset) + continue; + + int64_t Candidate = static_cast(R->symValue(m_Module)) + + static_cast(R->addend()); + uint64_t &Key = SortKeys[Piece.InputOffset]; + Key = std::min(Key, static_cast(Candidate)); + } + + const ELFSection *Linked = Owning->getLink(); + const uint64_t FallbackKey = Linked ? Linked->addr() : MaxSortKey; + std::stable_sort(Pieces.begin(), Pieces.end(), + [&](const EXIDXPiece &A, const EXIDXPiece &B) { + uint64_t KeyA = SortKeys.lookup(A.InputOffset); + uint64_t KeyB = SortKeys.lookup(B.InputOffset); + if (KeyA == MaxSortKey) + KeyA = FallbackKey; + if (KeyB == MaxSortKey) + KeyB = FallbackKey; + if (KeyA != KeyB) + return KeyA < KeyB; + return OriginalOrder.lookup(A.InputOffset) < + OriginalOrder.lookup(B.InputOffset); + }); + + // Relocations are created with input offsets; convert them to + // piece-layout offsets after sorting/compaction. + for (Relocation *R : Owning->getRelocations()) { + if (!R || !R->targetRef() || R->targetRef()->isNull()) + continue; + if (R->targetRef()->frag() != EXIDX) + continue; + uint32_t NewOffset = + EXIDX->translateInputOffset(R->targetRef()->offset()); + R->targetRef()->setOffset(NewOffset); + } - Relocator::Address S = getRelocator()->getSymValue(relocation); - Relocator::Address key = S + (relocation->target() & 0xFFFFFFFF); - region_frag->setOffset(key); // key is used for sorting + uint64_t FragKey = MaxSortKey; + for (const EXIDXPiece &P : Pieces) + FragKey = std::min(FragKey, SortKeys.lookup(P.InputOffset)); + if (FragKey == MaxSortKey) { + const ELFSection *Linked = Owning->getLink(); + FragKey = Linked ? Linked->addr() : MaxSortKey; } - } // for all relocations - } // for all relocation section - - // Sort fragments by key value stored in offset - std::sort(exidx->getFragmentList().begin(), exidx->getFragmentList().end(), - [this](Fragment *i, Fragment *j) { - Relocator::Address i_offset = - i->getOffset(config().getDiagEngine()); - Relocator::Address j_offset = - j->getOffset(config().getDiagEngine()); - return (i_offset < j_offset); - }); - - // Reset offset to real offset - uint64_t offset = 0; - for (auto &Frag : exidx->getFragmentList()) { - if (Frag->isNull()) + FragSortKeys[F] = FragKey; + } + + if (!SawEXIDXFrag) continue; - Frag->setOffset(offset); - offset += 8; + + std::stable_sort(S->getFragmentList().begin(), S->getFragmentList().end(), + [&](Fragment *A, Fragment *B) { + const bool AIsEXIDX = EXIDXFragSet.contains(A); + const bool BIsEXIDX = EXIDXFragSet.contains(B); + if (AIsEXIDX && BIsEXIDX) { + const uint64_t KeyA = FragSortKeys.lookup(A); + const uint64_t KeyB = FragSortKeys.lookup(B); + if (KeyA != KeyB) + return KeyA < KeyB; + } + return OriginalFragOffsets.lookup(A) < + OriginalFragOffsets.lookup(B); + }); + + uint32_t NextOffset = 0; + for (Fragment *F : S->getFragmentList()) { + F->setOffset(NextOffset); + NextOffset = F->getOffset(Diag) + F->size(); + } + S->setSize(NextOffset); } - O->setFirstNonEmptyRule(exidx->getMatchedLinkerScriptRule()); + Fragment *FirstEXIDXFrag = nullptr; + Fragment *LastEXIDXFrag = nullptr; + uint64_t TotalSize = 0; + for (auto &In : *O) { + ELFSection *S = In->getSection(); + if (!S) + continue; - // Reset EXIDX symbols. - if (m_pEXIDXStart) { - m_pEXIDXStart->fragRef()->setFragment(exidx->getFrontFragment()); - m_pEXIDXStart->fragRef()->setOffset(0); + uint64_t SectionSize = 0; + for (Fragment *F : S->getFragmentList()) { + if (!EXIDXFragSet.contains(F)) + continue; + SectionSize += F->size(); + if (!F->size()) + continue; + if (!FirstEXIDXFrag) + FirstEXIDXFrag = F; + LastEXIDXFrag = F; + } + TotalSize += SectionSize; + } + + if (m_pEXIDXStart && FirstEXIDXFrag) { + m_pEXIDXStart->setFragmentRef(make(*FirstEXIDXFrag, 0)); } - if (m_pEXIDXEnd) { - m_pEXIDXEnd->fragRef()->setFragment(exidx->getBackFragment()); - m_pEXIDXEnd->fragRef()->setOffset(8); + if (m_pEXIDXEnd && LastEXIDXFrag) { + m_pEXIDXEnd->setFragmentRef( + make(*LastEXIDXFrag, LastEXIDXFrag->size())); } + + E->setSize(TotalSize); } bool ARMGNULDBackend::readSection(InputFile &pInput, ELFSection *S) { - // We need break them down to individual entry - if (auto *EXIDX = llvm::dyn_cast(S)) { - uint32_t Offset = 0; + // Keep one EXIDX fragment per section and track entry offsets as pieces. + if (S->isEXIDX()) { + llvm::StringRef Region = pInput.getSlice(S->offset(), S->size()); + EXIDXFragment *EXIDX = make(Region, S, S->getAddrAlign()); + m_EXIDXFragments[S] = EXIDX; LayoutInfo *layoutInfo = getModule().getLayoutInfo(); + S->addFragment(EXIDX); for (uint32_t i = 0; i < S->size(); i += 8) { - llvm::StringRef region = pInput.getSlice(S->offset() + i, 8); - Fragment *frag = make(region, S, Fragment::Type::Region, - S->getAddrAlign()); - if (layoutInfo) - layoutInfo->recordFragment(&pInput, S, frag); - EXIDX->addFragment(frag); - EXIDX->addEntry({Offset, frag}); - Offset += 8; + const uint32_t PieceSize = (S->size() - i >= 8) ? 8 : (S->size() - i); + EXIDX->addPiece({i, PieceSize}); } + if (layoutInfo) + layoutInfo->recordFragment(&pInput, S, EXIDX); return true; } if (S->getType() == llvm::ELF::SHT_ARM_ATTRIBUTES) { @@ -532,7 +499,7 @@ void ARMGNULDBackend::doPostLayout() { m_Module.getConfig().options().printTimingStats()); ELFSection *exidx = m_Module.getScript().sectionMap().find(llvm::ELF::SHT_ARM_EXIDX); - if (exidx && exidx->size()) + if (exidx) sortEXIDX(); } @@ -1237,11 +1204,12 @@ bool ARMGNULDBackend::handleRelocation(ELFSection *Section, uint32_t Offset, Relocation::Address Addend, bool LastVisit) { - if (auto *EXIDX = llvm::dyn_cast(Section)) { - EXIDXEntry Entry = EXIDX->getEntry(Offset); - Relocation *R = eld::IRBuilder::addRelocation( - getRelocator(), *Entry.Frag, Type, Sym, Offset - Entry.InputOffset); - EXIDX->addRelocation(R); + auto It = m_EXIDXFragments.find(Section); + if (It != m_EXIDXFragments.end()) { + (void)It->second->getPiece(Offset); + Relocation *R = eld::IRBuilder::addRelocation(getRelocator(), *It->second, + Type, Sym, Offset); + Section->addRelocation(R); return true; } return false; diff --git a/lib/Target/ARM/ARMLDBackend.h b/lib/Target/ARM/ARMLDBackend.h index 37946a3ce..2c476d32d 100644 --- a/lib/Target/ARM/ARMLDBackend.h +++ b/lib/Target/ARM/ARMLDBackend.h @@ -30,6 +30,7 @@ namespace eld { class LinkerConfig; class TargetInfo; class ARMAttributeFragment; +class EXIDXFragment; //===----------------------------------------------------------------------===// /// ARMGNULDBackend - linker backend of ARM target of GNU ELF format @@ -268,6 +269,7 @@ class ARMGNULDBackend : public GNULDBackend { /// ARM Attribute Fragment ARMAttributeFragment *AttributeFragment; bool m_bEmitRegionTable; + llvm::DenseMap m_EXIDXFragments; llvm::DenseMap m_GOTMap; llvm::DenseMap m_GOTPLTMap; llvm::DenseMap m_PLTMap; diff --git a/lib/Target/ARM/CMakeLists.txt b/lib/Target/ARM/CMakeLists.txt index 53d663d0e..4f9029311 100644 --- a/lib/Target/ARM/CMakeLists.txt +++ b/lib/Target/ARM/CMakeLists.txt @@ -4,7 +4,7 @@ llvm_add_library( ARMAttributeFragment.cpp ARMELFDynamic.cpp ARMEmulation.cpp - ARMEXIDXSection.cpp + ARMEXIDXFragment.cpp ARMInfo.cpp ARMGOT.cpp ARMLDBackend.cpp diff --git a/test/ARM/standalone/EXIDXSorting/EXIDXGCAnnotations.test b/test/ARM/standalone/EXIDXSorting/EXIDXGCAnnotations.test new file mode 100644 index 000000000..ed6355b3b --- /dev/null +++ b/test/ARM/standalone/EXIDXSorting/EXIDXGCAnnotations.test @@ -0,0 +1,20 @@ +#---EXIDXGCAnnotations.test--------------------- Executable ------------------# +#BEGIN_COMMENT +# This tests that EXIDX dump records are annotated with when the owning +# EXIDX section is garbage collected. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -target arm -c %p/Inputs/exidx.s -o %t1.1.o +RUN: %link -MapStyle txt %linkopts %t1.1.o -T %p/Inputs/script.t -e _start --gc-sections -Map %t2.map -MapDetail show-header-details -o %t2.out +RUN: %filecheck %s < %t2.map + +#CHECK: # .ARM.exidx.text.f1{{.*}} +#CHECK-NEXT: #EXIDX{{.*}} +#CHECK-NEXT: #P[0]{{.*}} +#CHECK-NEXT: #R{{.*}}sym=.text.f1{{.*}} + +#CHECK: .ARM.exidx.text.start +#CHECK-NEXT: #EXIDX{{.*}}n=1 +#CHECK-NEXT: #P[0]{{.*}} +#CHECK-NEXT: #R{{.*}}sym=.text.start{{.*}} +#END_TEST diff --git a/test/ARM/standalone/EXIDXSorting/EXIDXSorting.test b/test/ARM/standalone/EXIDXSorting/EXIDXSorting.test index 1fe57eecb..fc608b7f3 100644 --- a/test/ARM/standalone/EXIDXSorting/EXIDXSorting.test +++ b/test/ARM/standalone/EXIDXSorting/EXIDXSorting.test @@ -7,8 +7,7 @@ RUN: %clang %clangopts -target arm -c %p/Inputs/exidx.s -o %t1.1.o RUN: %link -MapStyle txt %linkopts %t1.1.o -T %p/Inputs/script.t -o %t2.out -Map %t2.map RUN: %filecheck %s < %t2.map -#CHECK: .ARM.exidx.text.f2 0x11010 0x8 -#CHECK: .ARM.exidx.text.f2 0x11018 0x8 +#CHECK: .ARM.exidx.text.f2 0x11010 0x10 #CHECK: .ARM.exidx.text.f1 0x11020 0x8 #CHECK: .ARM.exidx.text.start 0x11028 0x8 diff --git a/test/ARM/standalone/EXIDXSorting/EXIDXSortingDiscard.test b/test/ARM/standalone/EXIDXSorting/EXIDXSortingDiscard.test new file mode 100644 index 000000000..0c57462fb --- /dev/null +++ b/test/ARM/standalone/EXIDXSorting/EXIDXSortingDiscard.test @@ -0,0 +1,21 @@ +#---EXIDXSortingDiscard.test------------------- Executable,LS ----------------# +#BEGIN_COMMENT +# This tests EXIDX piece sorting when linker script DISCARD rules remove one +# EXIDX input section. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -target arm -c %p/Inputs/exidx.s -o %t1.1.o +RUN: %link -MapStyle txt %linkopts %t1.1.o -T %p/Inputs/script_discard_exidx_f1.t -o %t2.out -Map %t2.map +RUN: %filecheck %s --check-prefix=MAP < %t2.map +RUN: %readelf -u %t2.out | %filecheck %s --check-prefix=UNW + +#MAP: # .ARM.exidx.text.f1{{[[:space:]]+}} +#MAP: .ARM.exidx.text.f2{{[[:space:]]+}}0x11010{{[[:space:]]+}}0x10 +#MAP: .ARM.exidx.text.start{{[[:space:]]+}}0x11020{{[[:space:]]+}}0x8 +#MAP-NOT: .ARM.exidx.text.f1{{[[:space:]]+}}0x + +#UNW: FunctionAddress: 0x11000 +#UNW: FunctionAddress: 0x11004 +#UNW: FunctionAddress: 0x1100C +#UNW-NOT: FunctionAddress: 0x11008 +#END_TEST diff --git a/test/ARM/standalone/EXIDXSorting/EXIDXSortingPartialLink.test b/test/ARM/standalone/EXIDXSorting/EXIDXSortingPartialLink.test new file mode 100644 index 000000000..b98e4a47d --- /dev/null +++ b/test/ARM/standalone/EXIDXSorting/EXIDXSortingPartialLink.test @@ -0,0 +1,17 @@ +#---EXIDXSortingPartialLink.test--------------- PartialLink ------------------# +#BEGIN_COMMENT +# This tests EXIDX piece sorting with partial linking. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -target arm -c %p/Inputs/exidx.s -o %t1.1.o +RUN: %link -MapStyle txt %linkopts -r %t1.1.o -T %p/Inputs/script.t -o %t2.r.o -Map %t2.map +RUN: %filecheck %s --check-prefix=MAP < %t2.map +RUN: %readelf -S -W %t2.r.o | %filecheck %s --check-prefix=REL + +#MAP: .ARM.exidx.text.f2{{[[:space:]]+}}0x0{{[[:space:]]+}}0x10 +#MAP: .ARM.exidx.text.f1{{[[:space:]]+}}0x10{{[[:space:]]+}}0x8 +#MAP: .ARM.exidx.text.start{{[[:space:]]+}}0x18{{[[:space:]]+}}0x8 + +#REL: .ARM.exidx{{[[:space:]]+}}ARM_EXIDX{{[[:space:]]+}}00000000{{[[:space:]]+}}{{[0-9a-f]+}}{{[[:space:]]+}}000020 +#REL: .rel.ARM.exidx{{[[:space:]]+}}REL +#END_TEST diff --git a/test/ARM/standalone/EXIDXSorting/EXIDXSortingShared.test b/test/ARM/standalone/EXIDXSorting/EXIDXSortingShared.test new file mode 100644 index 000000000..28eda3892 --- /dev/null +++ b/test/ARM/standalone/EXIDXSorting/EXIDXSortingShared.test @@ -0,0 +1,19 @@ +#---EXIDXSortingShared.test-------------------- Shared -----------------------# +#BEGIN_COMMENT +# This tests EXIDX piece sorting when producing shared libraries. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -target arm -c %p/Inputs/exidx.s -o %t1.1.o +RUN: %link -MapStyle txt %linkopts -shared %t1.1.o -T %p/Inputs/script.t -o %t2.so -Map %t2.map +RUN: %filecheck %s --check-prefix=MAP < %t2.map +RUN: %readelf -u %t2.so | %filecheck %s --check-prefix=UNW + +#MAP: .ARM.exidx.text.f2{{[[:space:]]+}}0x11010{{[[:space:]]+}}0x10 +#MAP: .ARM.exidx.text.f1{{[[:space:]]+}}0x11020{{[[:space:]]+}}0x8 +#MAP: .ARM.exidx.text.start{{[[:space:]]+}}0x11028{{[[:space:]]+}}0x8 + +#UNW: FunctionAddress: 0x11000 +#UNW: FunctionAddress: 0x11004 +#UNW: FunctionAddress: 0x11008 +#UNW: FunctionAddress: 0x1100C +#END_TEST diff --git a/test/ARM/standalone/EXIDXSorting/Inputs/script_discard_exidx_f1.t b/test/ARM/standalone/EXIDXSorting/Inputs/script_discard_exidx_f1.t new file mode 100644 index 000000000..7ae40d92e --- /dev/null +++ b/test/ARM/standalone/EXIDXSorting/Inputs/script_discard_exidx_f1.t @@ -0,0 +1,5 @@ +SECTIONS { + .text (0x11000) : { *(.text.f2) *(.text.f1) *(.text.start) } + /DISCARD/ : { *(.ARM.exidx.text.f1) } + .ARM.exidx : { *(.ARM.exidx*) } +}