diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -31,21 +31,16 @@ /// Partial address range. Besides an offset, only the /// HighPC is stored. The structure is stored in a map where the LowPC is the /// key. -struct ObjFileAddressRange { +struct AddressHighPC { /// Function HighPC. - uint64_t HighPC; + uint64_t HighPC = 0; /// Offset to apply to the linked address. /// should be 0 for not-linked object file. - int64_t Offset; - - ObjFileAddressRange(uint64_t EndPC, int64_t Offset) - : HighPC(EndPC), Offset(Offset) {} - - ObjFileAddressRange() : HighPC(0), Offset(0) {} + int64_t Offset = 0; }; -/// Map LowPC to ObjFileAddressRange. -using RangesTy = std::map; +/// Map LowPC to AddressHighPC. +using RangesTy = std::map; /// AddressesMap represents information about valid addresses used /// by debug information. Valid addresses are those which points to diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h @@ -18,12 +18,24 @@ class DeclContext; +template <> struct DenseMapInfo { + static object::SectionedAddress getEmptyKey(); + + static object::SectionedAddress getTombstoneKey(); + + static unsigned getHashValue(const object::SectionedAddress &val); + + static bool isEqual(const object::SectionedAddress &lhs, + const object::SectionedAddress &rhs); +}; + template using HalfOpenIntervalMap = IntervalMap::LeafSize, IntervalMapHalfOpenInfo>; -using FunctionIntervals = HalfOpenIntervalMap; +using FunctionIntervals = + HalfOpenIntervalMap; // FIXME: Delete this structure. struct PatchLocation { @@ -80,6 +92,7 @@ StringRef ClangModuleName) : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), ClangModuleName(ClangModuleName) { + Info.resize(OrigUnit.getNumDIEs()); auto CUDie = OrigUnit.getUnitDIE(false); @@ -129,7 +142,9 @@ uint64_t getLowPc() const { return LowPc; } uint64_t getHighPc() const { return HighPc; } - bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + bool hasLabelAt(object::SectionedAddress Addr) const { + return Labels.count(Addr); + } Optional getUnitRangesAttribute() const { return UnitRangeAttribute; @@ -171,11 +186,12 @@ /// Add the low_pc of a label that is relocated by applying /// offset \p PCOffset. - void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); + void addLabelLowPc(object::SectionedAddress LabelLowPc, int64_t PcOffset); /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying /// offset \p PCOffset. - void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + void addFunctionRange(object::SectionedAddress LowPC, + object::SectionedAddress HighPC, int64_t PCOffset); /// Keep track of a DW_AT_range attribute that we will need to patch up later. void noteRangeAttribute(const DIE &Die, PatchLocation Attr); @@ -284,7 +300,7 @@ FunctionIntervals Ranges; /// The DW_AT_low_pc of each DW_TAG_label. - SmallDenseMap Labels; + SmallDenseMap Labels; /// DW_AT_ranges attributes to patch after we have gathered /// all the unit's function addresses. diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h --- a/llvm/include/llvm/Object/ObjectFile.h +++ b/llvm/include/llvm/Object/ObjectFile.h @@ -149,12 +149,24 @@ std::tie(RHS.SectionIndex, RHS.Address); } +inline bool operator<=(const SectionedAddress &LHS, + const SectionedAddress &RHS) { + return std::tie(LHS.SectionIndex, LHS.Address) <= + std::tie(RHS.SectionIndex, RHS.Address); +} + inline bool operator==(const SectionedAddress &LHS, const SectionedAddress &RHS) { return std::tie(LHS.SectionIndex, LHS.Address) == std::tie(RHS.SectionIndex, RHS.Address); } +inline bool operator!=(const SectionedAddress &LHS, + const SectionedAddress &RHS) { + return std::tie(LHS.SectionIndex, LHS.Address) != + std::tie(RHS.SectionIndex, RHS.Address); +} + raw_ostream &operator<<(raw_ostream &OS, const SectionedAddress &Addr); /// This is a value type class that represents a single symbol in the list of diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp --- a/llvm/lib/DWARFLinker/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -418,7 +418,7 @@ std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); - auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); + auto LowPc = dwarf::toSectionedAddress(DIE.find(dwarf::DW_AT_low_pc)); assert(LowPc.hasValue() && "low_pc attribute is not an address."); if (!LowPc || !RelocMgr.hasValidRelocationAt(LowPcOffset, LowPcEndOffset, MyInfo)) @@ -440,7 +440,7 @@ // generation bugs aside, this is really wrong in the case of labels, where // a label marking the end of a function will have a PC == CU's high_pc. if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) - .getValueOr(UINT64_MAX) <= LowPc) + .getValueOr(UINT64_MAX) <= LowPc->Address) return Flags; Unit.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); return Flags | TF_Keep; @@ -448,7 +448,7 @@ Flags |= TF_Keep; - Optional HighPc = DIE.getHighPC(*LowPc); + Optional HighPc = DIE.getHighPC(LowPc->Address); if (!HighPc) { reportWarning("Function without high_pc. Range will be discarded.\n", File, &DIE); @@ -456,8 +456,9 @@ } // Replace the debug map range with a more accurate one. - Ranges[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust); - Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); + Ranges[*LowPc] = {*HighPc, MyInfo.AddrAdjust}; + Unit.addFunctionRange(*LowPc, {*HighPc, LowPc->SectionIndex}, + MyInfo.AddrAdjust); return Flags; } @@ -1035,17 +1036,17 @@ : Addr) + Info.PCOffset; else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - Addr = Unit.getLowPc(); - if (Addr == std::numeric_limits::max()) + const auto &pc = Unit.getLowPc(); + + if (pc != std::numeric_limits::max()) + Addr = pc; + else return 0; } Info.HasLowPc = true; } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (uint64_t HighPc = Unit.getHighPc()) - Addr = HighPc; - else - return 0; + Addr = Unit.getHighPc(); } else // If we have a high_pc recorded for the input DIE, use // it. Otherwise (when no relocations where applied) just use the @@ -1095,10 +1096,11 @@ if (AttrSpec.Attr == dwarf::DW_AT_high_pc && Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (Unit.getLowPc() == -1ULL) + if (Unit.getLowPc() != std::numeric_limits::max()) + // Dwarf >= 4 high_pc is an size, not an address. + Value = Unit.getHighPc() - Unit.getLowPc(); + else return 0; - // Dwarf >= 4 high_pc is an size, not an address. - Value = Unit.getHighPc() - Unit.getLowPc(); } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) Value = *Val.getAsSectionOffset(); else if (AttrSpec.Form == dwarf::DW_FORM_sdata) @@ -1472,12 +1474,12 @@ auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; DWARFUnit &OrigUnit = Unit.getOrigUnit(); auto OrigUnitDie = OrigUnit.getUnitDIE(false); - uint64_t OrigLowPc = - dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); + uint64_t OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), + std::numeric_limits::max()); // Ranges addresses are based on the unit's low_pc. Compute the // offset we need to apply to adapt to the new unit's low_pc. int64_t UnitPcOffset = 0; - if (OrigLowPc != -1ULL) + if (OrigLowPc != std::numeric_limits::max()) UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); for (const auto &RangeAttribute : Unit.getRangesAttributes()) { @@ -1493,11 +1495,14 @@ const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); if (CurrRange == InvalidRange || - First.StartAddress + OrigLowPc < CurrRange.start() || - First.StartAddress + OrigLowPc >= CurrRange.stop()) { - CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); - if (CurrRange == InvalidRange || - CurrRange.start() > First.StartAddress + OrigLowPc) { + First.SectionIndex != CurrRange.start().SectionIndex || + First.StartAddress + OrigLowPc < CurrRange.start().Address || + First.StartAddress + OrigLowPc >= CurrRange.stop().Address) { + CurrRange = FunctionRanges.find( + {First.StartAddress + OrigLowPc, First.SectionIndex}); + if (CurrRange == InvalidRange || CurrRange == FunctionRanges.end() || + CurrRange.start().SectionIndex != First.SectionIndex || + CurrRange.start().Address > First.StartAddress + OrigLowPc) { reportWarning("no mapping for range.", File); continue; } @@ -1623,37 +1628,43 @@ // it is marked as end_sequence in the input (because in that // case, the relocation offset is accurate and that entry won't // serve as the start of another function). - if (CurrRange == InvalidRange || Row.Address.Address < CurrRange.start() || - Row.Address.Address > CurrRange.stop() || - (Row.Address.Address == CurrRange.stop() && !Row.EndSequence)) { + if (CurrRange == InvalidRange || + Row.Address.SectionIndex != CurrRange.start().SectionIndex || + Row.Address.Address < CurrRange.start().Address || + Row.Address.Address > CurrRange.stop().Address || + (Row.Address.Address == CurrRange.stop().Address && !Row.EndSequence)) { // We just stepped out of a known range. Insert a end_sequence // corresponding to the end of the range. uint64_t StopAddress = CurrRange != InvalidRange - ? CurrRange.stop() + CurrRange.value() - : -1ULL; - CurrRange = FunctionRanges.find(Row.Address.Address); + ? CurrRange.stop().Address + CurrRange.value() + : std::numeric_limits::max(); + CurrRange = FunctionRanges.find(Row.Address); bool CurrRangeValid = - CurrRange != InvalidRange && CurrRange.start() <= Row.Address.Address; + CurrRange != InvalidRange && + CurrRange.start().SectionIndex == Row.Address.SectionIndex && + CurrRange.start().Address <= Row.Address.Address; if (!CurrRangeValid) { CurrRange = InvalidRange; - if (StopAddress != -1ULL) { + if (StopAddress != std::numeric_limits::max()) { // Try harder by looking in the Address ranges map. // There are corner cases where this finds a // valid entry. It's unclear if this is right or wrong, but // for now do as dsymutil. // FIXME: Understand exactly what cases this addresses and // potentially remove it along with the Ranges map. - auto Range = Ranges.lower_bound(Row.Address.Address); + auto Range = Ranges.lower_bound(Row.Address); if (Range != Ranges.begin() && Range != Ranges.end()) --Range; - if (Range != Ranges.end() && Range->first <= Row.Address.Address && + if (Range != Ranges.end() && + Range->first.SectionIndex == Row.Address.SectionIndex && + Range->first.Address <= Row.Address.Address && Range->second.HighPC >= Row.Address.Address) { StopAddress = Row.Address.Address + Range->second.Offset; } } } - if (StopAddress != -1ULL && !Seq.empty()) { + if (StopAddress != std::numeric_limits::max() && !Seq.empty()) { // Insert end sequence row with the computed end address, but // the same line as the previous one. auto NextLine = Seq.back(); @@ -1806,10 +1817,11 @@ // the function entry point, thus we can't just lookup the address // in the debug map. Use the AddressInfo's range map to see if the FDE // describes something that we can relocate. - auto Range = Ranges.upper_bound(Loc); + auto Range = + Ranges.upper_bound({Loc, object::SectionedAddress::UndefSection}); if (Range != Ranges.begin()) --Range; - if (Range == Ranges.end() || Range->first > Loc || + if (Range == Ranges.end() || Range->first.Address > Loc || Range->second.HighPC <= Loc) { // The +4 is to account for the size of the InitialLength field itself. InputOffset = EntryOffset + InitialLength + 4; diff --git a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp --- a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp @@ -11,6 +11,27 @@ namespace llvm { +object::SectionedAddress DenseMapInfo::getEmptyKey() { + return {object::SectionedAddress::UndefSection, + object::SectionedAddress::UndefSection - 1}; +} + +object::SectionedAddress +DenseMapInfo::getTombstoneKey() { + return {object::SectionedAddress::UndefSection, + object::SectionedAddress::UndefSection - 2}; +} + +unsigned DenseMapInfo::getHashValue( + const object::SectionedAddress &val) { + return llvm::hash_value(std::make_pair(val.Address, val.SectionIndex)); +} + +bool DenseMapInfo::isEqual( + const object::SectionedAddress &lhs, const object::SectionedAddress &rhs) { + return lhs == rhs; +} + /// Check if the DIE at \p Idx is in the scope of a function. static bool inFunctionScope(CompileUnit &U, unsigned Idx) { while (Idx) { @@ -99,19 +120,23 @@ } } -void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { +void CompileUnit::addLabelLowPc(object::SectionedAddress LabelLowPc, + int64_t PcOffset) { Labels.insert({LabelLowPc, PcOffset}); } -void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, +void CompileUnit::addFunctionRange(object::SectionedAddress FuncLowPc, + object::SectionedAddress FuncHighPc, int64_t PcOffset) { + assert(FuncLowPc.SectionIndex == FuncHighPc.SectionIndex); + // Don't add empty ranges to the interval map. They are a problem because // the interval map expects half open intervals. This is safe because they // are empty anyway. if (FuncHighPc != FuncLowPc) Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); - this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); - this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); + LowPc = std::min(LowPc, FuncLowPc.Address + PcOffset); + HighPc = std::max(HighPc, FuncHighPc.Address + PcOffset); } void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.h b/llvm/tools/dsymutil/DwarfLinkerForBinary.h --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.h @@ -112,9 +112,10 @@ for (const auto &Entry : DMO.symbols()) { const auto &Mapping = Entry.getValue(); if (Mapping.Size && Mapping.ObjectAddress) - AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( + AddressRanges[{*Mapping.ObjectAddress, + object::SectionedAddress::UndefSection}] = { *Mapping.ObjectAddress + Mapping.Size, - int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + int64_t(Mapping.BinaryAddress - *Mapping.ObjectAddress)}; } } virtual ~AddressManager() override { clear(); } diff --git a/llvm/tools/dsymutil/DwarfStreamer.cpp b/llvm/tools/dsymutil/DwarfStreamer.cpp --- a/llvm/tools/dsymutil/DwarfStreamer.cpp +++ b/llvm/tools/dsymutil/DwarfStreamer.cpp @@ -305,8 +305,9 @@ continue; // All range entries should lie in the function range. - if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && - Range.EndAddress + OrigLowPc <= FuncRange.stop())) + if (!(Range.SectionIndex == FuncRange.start().SectionIndex && + Range.StartAddress + OrigLowPc >= FuncRange.start().Address && + Range.EndAddress + OrigLowPc <= FuncRange.stop().Address)) warn("inconsistent range data.", "emitting debug_ranges"); MS->emitIntValue(Range.StartAddress + PcOffset, AddressSize); MS->emitIntValue(Range.EndAddress + PcOffset, AddressSize); @@ -334,8 +335,8 @@ const auto &FunctionRanges = Unit.getFunctionRanges(); for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); Range != End; ++Range) - Ranges.push_back(std::make_pair(Range.start() + Range.value(), - Range.stop() + Range.value())); + Ranges.push_back(std::make_pair(Range.start().Address + Range.value(), + Range.stop().Address + Range.value())); // The object addresses where sorted, but again, the linked // addresses might end up in a different order. @@ -386,7 +387,8 @@ MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); // Offset each range by the right amount. - int64_t PcOffset = -Unit.getLowPc(); + int64_t PcOffset = 0; + PcOffset = -Unit.getLowPc(); // Emit coalesced ranges. for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { MS->emitIntValue(Range->first + PcOffset, AddressSize); @@ -425,7 +427,6 @@ int64_t UnitPcOffset = 0; if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); - SmallVector Buffer; for (const auto &Attr : Attributes) { uint64_t Offset = Attr.first.get(); diff --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -2,6 +2,7 @@ ${LLVM_TARGETS_TO_BUILD} AsmPrinter DebugInfoDWARF + DWARFLinker MC Object ObjectYAML @@ -20,6 +21,7 @@ DWARFDieTest.cpp DWARFFormValueTest.cpp DWARFLocationExpressionTest.cpp + DWARFLinkerCompileUnit.cpp ) target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFLinkerCompileUnit.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFLinkerCompileUnit.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFLinkerCompileUnit.cpp @@ -0,0 +1,165 @@ +//===- llvm/unittest/DebugInfo/DWARFLinkerCompileUnit.cpp -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::dwarf; +using object::SectionedAddress; + +namespace { + +TEST(DWARFLinkerCompileUnit, sectionedAddressRanges) { + const char *yamldata = "debug_abbrev:\n" + " - Code: 0x00000001\n" + " Tag: DW_TAG_compile_unit\n" + " Children: DW_CHILDREN_yes\n" + " Attributes:\n" + "debug_info:\n" + " - Length:\n" + " TotalLength: 0\n" + " Version: 4\n" + " AbbrOffset: 0\n" + " AddrSize: 8\n" + " Entries:\n" + " - AbbrCode: 0x00000001\n" + " Values:\n" + " - AbbrCode: 0x00000000\n" + " Values:\n"; + + auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata), true); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = cast(DwarfContext->getUnitAtIndex(0)); + + // Check the compile unit DIE is valid. + auto DieDG = U->getUnitDIE(false); + EXPECT_TRUE(DieDG.isValid()); + + // Create DWARFLinker compile unit. + CompileUnit CU(*U, 1, false, ""); + + // Check for adding zero address range from undefined section. + CU.addFunctionRange({0x10, object::SectionedAddress::UndefSection}, + {0x10, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(CU.getFunctionRanges().empty()); + + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x4010); + + // Check for adding zero address range from first section. + CU.addFunctionRange({0x10, 1}, {0x10, 1}, 0x4500); + EXPECT_TRUE(CU.getFunctionRanges().empty()); + + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x4510); + + // Check for adding valid address range from undefined section. + CU.addFunctionRange({0x10, object::SectionedAddress::UndefSection}, + {0x20, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x10, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x10, object::SectionedAddress::UndefSection}) == 0x4000); + + // Check for adding second valid address range from undefined section. + CU.addFunctionRange({0x20, object::SectionedAddress::UndefSection}, + {0x40, object::SectionedAddress::UndefSection}, 0x5000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x20, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x20, object::SectionedAddress::UndefSection}) == 0x5000); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x19, object::SectionedAddress::UndefSection}) == 0x4000); + + // Check for adding third valid address range from undefined section. + CU.addFunctionRange({0x100, object::SectionedAddress::UndefSection}, + {0x200, object::SectionedAddress::UndefSection}, 0x6000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x100, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + + // Check that function ranges does not contain values which were not put there + // previously. + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x5, object::SectionedAddress::UndefSection}, -1LL) == -1LL); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x100, object::SectionedAddress::UndefSection}) == 0x6000); + + // Check compile unit low/high pc values. + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x6200); + + // Check for adding valid address range from first section. + CU.addFunctionRange({0x10, 1}, {0x20, 1}, 0x1000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 1}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 1}) == 0x1000); + + // Check for adding second valid address range from first section. + CU.addFunctionRange({0x20, 1}, {0x40, 1}, 0x2000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x20, 1}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x20, 1}) == 0x2000); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x19, 1}) == 0x1000); + + // Check for adding valid address range from second section. + CU.addFunctionRange({0x10, 2}, {0x20, 2}, 0x3000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 2}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 2}) == 0x3000); + + // Check that function ranges does not contain values which were not put there + // previously. + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 3}, -1LL) == -1LL); + EXPECT_TRUE(CU.getFunctionRanges().lookup({UINT64_MAX, 2}, -1LL) == -1LL); + + EXPECT_TRUE(CU.getLowPc() == 0x1010); + EXPECT_TRUE(CU.getHighPc() == 0x6200); + + // Check for adding valid address range from four section. + CU.addFunctionRange({0x0, 4}, {0x200, 4}, 0x8000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 4}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 4}) == 0x8000); + + EXPECT_TRUE(CU.getLowPc() == 0x1010); + EXPECT_TRUE(CU.getHighPc() == 0x8200); + + // Check adding labels. + CU.addLabelLowPc({0x10, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(CU.hasLabelAt({0x10, object::SectionedAddress::UndefSection})); + EXPECT_TRUE(!CU.hasLabelAt({0x0, object::SectionedAddress::UndefSection})); + + CU.addLabelLowPc({0x10, 1}, 0x4000); + EXPECT_TRUE(CU.hasLabelAt({0x10, 1})); + EXPECT_TRUE(!CU.hasLabelAt({0x0, 1})); + + EXPECT_TRUE(!CU.hasLabelAt({0x10, 2})); +} + +} // end anonymous namespace