diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 1bc69f791bd84..3bcc1248bf826 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1139,6 +1139,7 @@ enum : unsigned { SHT_LLVM_OFFLOADING = 0x6fff4c0b, // LLVM device offloading data. SHT_LLVM_LTO = 0x6fff4c0c, // .llvm.lto for fat LTO. SHT_LLVM_JT_SIZES = 0x6fff4c0d, // LLVM jump tables sizes. + SHT_LLVM_FUNC_ADDR_MAP = 0x6fff4c0e, // LLVM function address map. // Android's experimental support for SHT_RELR sections. // https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512 SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets. diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 5291369b3b9f1..82ebf0361c13d 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -414,6 +414,8 @@ class AsmPrinter : public MachineFunctionPass { void emitBBAddrMapSection(const MachineFunction &MF); + void emitFuncAddrMapSection(const MachineFunction &MF); + void emitKCFITrapEntry(const MachineFunction &MF, const MCSymbol *Symbol); virtual void emitKCFITypeId(const MachineFunction &MF); diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h index 57ba40f7ac26f..8ba1c30d8a499 100644 --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -177,6 +177,9 @@ class MCContext { /// LLVM_BB_ADDR_MAP version to emit. uint8_t BBAddrMapVersion = 2; + /// LLVM_FUNC_ADDR_MAP version to emit. + uint8_t FuncAddrMapVersion = 1; + /// The file name of the log file from the environment variable /// AS_SECURE_LOG_FILE. Which must be set before the .secure_log_unique /// directive is used or it is an error. @@ -656,6 +659,8 @@ class MCContext { uint8_t getBBAddrMapVersion() const { return BBAddrMapVersion; } + uint8_t getFuncAddrMapVersion() const { return FuncAddrMapVersion; } + /// @} /// \name Dwarf Management diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index fb575fe721015..4bd34947762c0 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -364,6 +364,8 @@ class MCObjectFileInfo { MCSection *getBBAddrMapSection(const MCSection &TextSec) const; + MCSection *getFuncAddrMapSection(const MCSection &TextSec) const; + MCSection *getKCFITrapSection(const MCSection &TextSec) const; MCSection *getPseudoProbeSection(const MCSection &TextSec) const; diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h index 3aa1d7864fcb7..b179889d60125 100644 --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -513,6 +513,10 @@ class ELFFile { decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr, std::vector *PGOAnalyses = nullptr) const; + Expected> + decodeFuncAddrMap(const Elf_Shdr &Sec, + const Elf_Shdr *RelaSec = nullptr) const; + /// Returns a map from every section matching \p IsMatch to its relocation /// section, or \p nullptr if it has no relocation section. This function /// returns an error if any of the \p IsMatch calls fail or if it fails to diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 87e4dbe448091..7d79fb82c046f 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -1027,6 +1027,44 @@ struct PGOAnalysisMap { } }; +// Struct representing the FuncAddrMap for one function. +struct FuncAddrMap { + + // Bitfield of optional features to control the extra information + // emitted/encoded in the the section. + struct Features { + bool DynamicInstCount : 1; + + // Encodes to minimum bit width representation. + uint8_t encode() const { + return (static_cast(DynamicInstCount) << 0); + } + + // Decodes from minimum bit width representation and validates no + // unnecessary bits are used. + static Expected decode(uint8_t Val) { + Features Feat{static_cast(Val & (1 << 0))}; + if (Feat.encode() != Val) + return createStringError( + std::error_code(), + "invalid encoding for FuncAddrMap::Features: 0x%x", Val); + return Feat; + } + + bool operator==(const Features &Other) const { + return DynamicInstCount == Other.DynamicInstCount; + } + }; + + uint64_t FunctionAddress = 0; // Function entry address. + uint64_t DynamicInstCount = 0; // Dynamic instruction count for this function + + // Flags to indicate if each feature was enabled in this function + Features FeatEnable; + + uint64_t getFunctionAddress() const { return FunctionAddress; } +}; + } // end namespace object. } // end namespace llvm. diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index dfdfa055d65fa..80d6a9ff83439 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -195,6 +195,13 @@ struct PGOAnalysisMapEntry { std::optional> PGOBBEntries; }; +struct FuncAddrMapEntry { + uint8_t Version; + llvm::yaml::Hex8 Feature; + llvm::yaml::Hex64 Address; + llvm::yaml::Hex64 DynamicInstCount; +}; + struct StackSizeEntry { llvm::yaml::Hex64 Address; llvm::yaml::Hex64 Size; @@ -229,6 +236,7 @@ struct Chunk { DependentLibraries, CallGraphProfile, BBAddrMap, + FuncAddrMap, // Special chunks. SpecialChunksStart, @@ -355,6 +363,20 @@ struct BBAddrMapSection : Section { } }; +struct FuncAddrMapSection : Section { + std::optional> Entries; + + FuncAddrMapSection() : Section(ChunkKind::FuncAddrMap) {} + + std::vector> getEntries() const override { + return {{"Entries", Entries.has_value()}}; + }; + + static bool classof(const Chunk *S) { + return S->Kind == ChunkKind::FuncAddrMap; + } +}; + struct StackSizesSection : Section { std::optional> Entries; @@ -762,6 +784,7 @@ bool shouldAllocateFileSpace(ArrayRef Phdrs, } // end namespace llvm LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::FuncAddrMapEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBRangeEntry) @@ -929,6 +952,10 @@ template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::FuncAddrMapEntry &E); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &E); }; diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index b2a4721f37b26..4c56c306b5fc3 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -147,6 +147,11 @@ enum class PGOMapFeaturesEnum { BrProb, All, }; + +enum class FuncAddrMapFeaturesEnum { + DynamicInstCount, +}; + static cl::bits PgoAnalysisMapFeatures( "pgo-analysis-map", cl::Hidden, cl::CommaSeparated, cl::values( @@ -173,6 +178,13 @@ static cl::opt EmitJumpTableSizesSection( cl::desc("Emit a section containing jump table addresses and sizes"), cl::Hidden, cl::init(false)); +static cl::bits FuncAddrMapFeatures( + "func-addr-map", cl::Hidden, cl::CommaSeparated, + cl::values(clEnumValN(FuncAddrMapFeaturesEnum::DynamicInstCount, + "dyn-inst-count", "Dynamic instruction count")), + cl::desc("Emit features of function address map in SHT_LLVM_FUNC_ADDR_MAP " + "section")); + // This isn't turned on by default, since several of the scheduling models are // not completely accurate, and we don't want to be misleading. static cl::opt PrintLatency( @@ -1390,6 +1402,14 @@ static uint32_t getBBAddrMapMetadata(const MachineBasicBlock &MBB) { .encode(); } +static llvm::object::FuncAddrMap::Features getFuncAddrMapFeature() { + return {FuncAddrMapFeatures.isSet(FuncAddrMapFeaturesEnum::DynamicInstCount)}; +} + +static bool isAnyFuncAddrMapFeature() { + return FuncAddrMapFeatures.getBits() != 0; +} + static llvm::object::BBAddrMap::Features getBBAddrMapFeature(const MachineFunction &MF, int NumMBBSectionRanges) { // Ensure that the user has not passed in additional options while also @@ -1424,6 +1444,39 @@ getBBAddrMapFeature(const MachineFunction &MF, int NumMBBSectionRanges) { static_cast(BBAddrMapSkipEmitBBEntries)}; } +void AsmPrinter::emitFuncAddrMapSection(const MachineFunction &MF) { + if (!isAnyFuncAddrMapFeature()) + return; + + MCSection *FuncAddrMapSection = + getObjFileLowering().getFuncAddrMapSection(*MF.getSection()); + assert(FuncAddrMapSection && + ".llvm_func_addr_map section is not initialized."); + const MCSymbol *FunctionSymbol = getFunctionBegin(); + OutStreamer->pushSection(); + OutStreamer->switchSection(FuncAddrMapSection); + OutStreamer->AddComment("version"); + uint8_t FuncAddrMapVersion = + OutStreamer->getContext().getFuncAddrMapVersion(); + OutStreamer->emitInt8(FuncAddrMapVersion); + OutStreamer->AddComment("feature"); + auto Features = getFuncAddrMapFeature(); + OutStreamer->emitInt8(Features.encode()); + + OutStreamer->AddComment("function address"); + OutStreamer->emitSymbolValue(FunctionSymbol, getPointerSize()); + if (Features.DynamicInstCount) { + const MachineBlockFrequencyInfo *MBFI = + &getAnalysis().getBFI(); + uint64_t DynInstCount = 0; + for (const MachineBasicBlock &MBB : MF) + DynInstCount += MBFI->getBlockProfileCount(&MBB).value_or(0) * MBB.size(); + OutStreamer->AddComment("Dynamic instruction count"); + OutStreamer->emitULEB128IntValue(DynInstCount); + } + OutStreamer->popSection(); +} + void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) { MCSection *BBAddrMapSection = getObjFileLowering().getBBAddrMapSection(*MF.getSection()); @@ -2119,6 +2172,8 @@ void AsmPrinter::emitFunctionBody() { MF->getContext().reportWarning( SMLoc(), "pgo-analysis-map is enabled for function " + MF->getName() + " but it does not have labels"); + + emitFuncAddrMapSection(*MF); } // Emit sections containing instruction and function PCs. @@ -2749,7 +2804,7 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { F.hasFnAttribute("xray-instruction-threshold") || needFuncLabels(MF, *this) || NeedsLocalForSize || MF.getTarget().Options.EmitStackSizeSection || - MF.getTarget().Options.BBAddrMap) { + MF.getTarget().Options.BBAddrMap || isAnyFuncAddrMapFeature()) { CurrentFnBegin = createTempSymbol("func_begin"); if (NeedsLocalForSize) CurrentFnSymForSize = CurrentFnBegin; diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 150e38a94db6a..47ed5d91c8084 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -1120,6 +1120,24 @@ MCObjectFileInfo::getBBAddrMapSection(const MCSection &TextSec) const { cast(TextSec.getBeginSymbol())); } +MCSection * +MCObjectFileInfo::getFuncAddrMapSection(const MCSection &TextSec) const { + if (Ctx->getObjectFileType() != MCContext::IsELF) + return nullptr; + + const MCSectionELF &ElfSec = static_cast(TextSec); + unsigned Flags = ELF::SHF_LINK_ORDER; + StringRef GroupName; + if (const MCSymbol *Group = ElfSec.getGroup()) { + GroupName = Group->getName(); + Flags |= ELF::SHF_GROUP; + } + + return Ctx->getELFSection(".llvm_func_addr_map", ELF::SHT_LLVM_FUNC_ADDR_MAP, + Flags, 0, GroupName, true, ElfSec.getUniqueID(), + cast(TextSec.getBeginSymbol())); +} + MCSection * MCObjectFileInfo::getKCFITrapSection(const MCSection &TextSec) const { if (Ctx->getObjectFileType() != MCContext::IsELF) diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp index b58210b3c268e..044e9c19f4b77 100644 --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -679,6 +679,8 @@ bool ELFAsmParser::parseSectionArguments(bool IsPush, SMLoc loc) { Type = ELF::SHT_LLVM_LTO; else if (TypeName == "llvm_jt_sizes") Type = ELF::SHT_LLVM_JT_SIZES; + else if (TypeName == "llvm_func_addr_map") + Type = ELF::SHT_LLVM_FUNC_ADDR_MAP; else if (TypeName.getAsInteger(0, Type)) return TokError("unknown section type"); } diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp index 25e62b70b5e2a..8603ea43ede86 100644 --- a/llvm/lib/MC/MCSectionELF.cpp +++ b/llvm/lib/MC/MCSectionELF.cpp @@ -174,6 +174,8 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T, OS << "llvm_lto"; else if (Type == ELF::SHT_LLVM_JT_SIZES) OS << "llvm_jt_sizes"; + else if (Type == ELF::SHT_LLVM_FUNC_ADDR_MAP) + OS << "llvm_func_addr_map"; else OS << "0x" << Twine::utohexstr(Type); diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index b6d0699ee4fe0..bb5e6fa597012 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -320,7 +320,8 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) { STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_BB_ADDR_MAP); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_OFFLOADING); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LTO); - STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES) + STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES); + STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_FUNC_ADDR_MAP); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef); @@ -731,6 +732,101 @@ static IntTy readULEB128As(DataExtractor &Data, DataExtractor::Cursor &Cur, return static_cast(Value); } +template +static Expected> +decodeFuncAddrMapImpl(const ELFFile &EF, + const typename ELFFile::Elf_Shdr &Sec, + const typename ELFFile::Elf_Shdr *RelaSec) { + bool IsRelocatable = EF.getHeader().e_type == ELF::ET_REL; + + // This DenseMap maps the offset of each function (the location of the + // reference to the function in the SHT_LLVM_FUNC_ADDR_MAP section) to the + // addend (the location of the function in the text section). + llvm::DenseMap FunctionOffsetTranslations; + if (IsRelocatable && RelaSec) { + assert(RelaSec && + "Can't read a SHT_LLVM_FUNC_ADDR_MAP section in a relocatable " + "object file without providing a relocation section."); + Expected::Elf_Rela_Range> Relas = EF.relas(*RelaSec); + if (!Relas) + return createError("unable to read relocations for section " + + describe(EF, Sec) + ": " + + toString(Relas.takeError())); + for (typename ELFFile::Elf_Rela Rela : *Relas) + FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend; + } + auto GetAddressForRelocation = + [&](unsigned RelocationOffsetInSection) -> Expected { + auto FOTIterator = + FunctionOffsetTranslations.find(RelocationOffsetInSection); + if (FOTIterator == FunctionOffsetTranslations.end()) { + return createError("failed to get relocation data for offset: " + + Twine::utohexstr(RelocationOffsetInSection) + + " in section " + describe(EF, Sec)); + } + return FOTIterator->second; + }; + Expected> ContentsOrErr = EF.getSectionContents(Sec); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + ArrayRef Content = *ContentsOrErr; + DataExtractor Data(Content, EF.isLE(), ELFT::Is64Bits ? 8 : 4); + std::vector FunctionEntries; + + DataExtractor::Cursor Cur(0); + Error ULEBSizeErr = Error::success(); + + // Helper lampda to extract the (possiblly relocatable) address stored at Cur. + auto ExtractAddress = [&]() -> Expected::uintX_t> { + uint64_t RelocationOffsetInSection = Cur.tell(); + auto Address = + static_cast::uintX_t>(Data.getAddress(Cur)); + if (!Cur) + return Cur.takeError(); + if (!IsRelocatable) + return Address; + assert(Address == 0); + Expected AddressOrErr = + GetAddressForRelocation(RelocationOffsetInSection); + if (!AddressOrErr) + return AddressOrErr.takeError(); + return *AddressOrErr; + }; + + uint8_t Version = 0; + uint8_t Feature = 0; + FuncAddrMap::Features FeatEnable{}; + while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) { + if (Sec.sh_type == ELF::SHT_LLVM_FUNC_ADDR_MAP) { + Version = Data.getU8(Cur); + if (!Cur) + break; + Feature = Data.getU8(Cur); // Feature byte + if (!Cur) + break; + auto FeatEnableOrErr = FuncAddrMap::Features::decode(Feature); + if (!FeatEnableOrErr) + return FeatEnableOrErr.takeError(); + FeatEnable = *FeatEnableOrErr; + } + typename ELFFile::uintX_t FunctionAddress = 0; + auto AddressOrErr = ExtractAddress(); + if (!AddressOrErr) + return AddressOrErr.takeError(); + FunctionAddress = *AddressOrErr; + uint64_t DynamicInstCount = + FeatEnable.DynamicInstCount + ? readULEB128As(Data, Cur, ULEBSizeErr) + : 0; + FunctionEntries.push_back({FunctionAddress, DynamicInstCount, FeatEnable}); + } + // Either Cur is in the error state, or we have an error in ULEBSizeErr, but + // we join all errors here to be safe. + if (!Cur || ULEBSizeErr) + return joinErrors(Cur.takeError(), std::move(ULEBSizeErr)); + return FunctionEntries; +} + template static Expected> decodeBBAddrMapImpl(const ELFFile &EF, @@ -939,6 +1035,14 @@ ELFFile::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec, return std::move(AddrMapsOrErr); } +template +Expected> +ELFFile::decodeFuncAddrMap(const Elf_Shdr &Sec, + const Elf_Shdr *RelaSec) const { + auto AddrMapsOrErr = decodeFuncAddrMapImpl(*this, Sec, RelaSec); + return std::move(AddrMapsOrErr); +} + template Expected< MapVector> diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index cc41bbe6bbde2..2a923515f7ef9 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -287,6 +287,9 @@ template class ELFState { void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::BBAddrMapSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::FuncAddrMapSection &Section, + ContiguousBlobAccumulator &CBA); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::HashSection &Section, ContiguousBlobAccumulator &CBA); @@ -898,6 +901,8 @@ void ELFState::initSectionHeaders(std::vector &SHeaders, writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else { llvm_unreachable("Unknown section type"); } @@ -1541,6 +1546,24 @@ void ELFState::writeSectionContent( } } +template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::FuncAddrMapSection &Section, + ContiguousBlobAccumulator &CBA) { + for (const auto &[Idx, E] : llvm::enumerate(*Section.Entries)) { + // Write version and feature values. + if (Section.Type == llvm::ELF::SHT_LLVM_FUNC_ADDR_MAP) { + CBA.write(E.Version); + CBA.write(E.Feature); + SHeader.sh_size += 2; + } + CBA.write(E.Address, ELFT::Endianness); + SHeader.sh_size += sizeof(uintX_t); + if (E.DynamicInstCount) + SHeader.sh_size += CBA.writeULEB128(E.DynamicInstCount); + } +} + template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section, diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 7e94d01a97153..1c7bbc5239a1b 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -723,6 +723,7 @@ void ScalarEnumerationTraits::enumeration( ECase(SHT_LLVM_PART_PHDR); ECase(SHT_LLVM_BB_ADDR_MAP_V0); ECase(SHT_LLVM_BB_ADDR_MAP); + ECase(SHT_LLVM_FUNC_ADDR_MAP); ECase(SHT_LLVM_OFFLOADING); ECase(SHT_LLVM_LTO); ECase(SHT_GNU_ATTRIBUTES); @@ -1425,6 +1426,12 @@ static void sectionMapping(IO &IO, ELFYAML::RawContentSection &Section) { IO.mapOptional("Info", Section.Info); } +static void sectionMapping(IO &IO, ELFYAML::FuncAddrMapSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Entries", Section.Entries); +} + static void sectionMapping(IO &IO, ELFYAML::BBAddrMapSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Content", Section.Content); @@ -1725,6 +1732,11 @@ void MappingTraits>::mapping( Section.reset(new ELFYAML::BBAddrMapSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_LLVM_FUNC_ADDR_MAP: + if (!IO.outputting()) + Section.reset(new ELFYAML::FuncAddrMapSection()); + sectionMapping(IO, *cast(Section.get())); + break; default: if (!IO.outputting()) { StringRef Name; @@ -1846,6 +1858,15 @@ void MappingTraits::mapping( IO.mapRequired("Size", E.Size); } +void MappingTraits::mapping( + IO &IO, ELFYAML::FuncAddrMapEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapRequired("Version", E.Version); + IO.mapOptional("Feature", E.Feature, Hex8(0)); + IO.mapOptional("Address", E.Address, Hex64(0)); + IO.mapOptional("DynInstCnt", E.DynamicInstCount, Hex64(0)); +} + void MappingTraits::mapping( IO &IO, ELFYAML::BBAddrMapEntry &E) { assert(IO.getContext() && "The IO context is not initialized"); diff --git a/llvm/test/CodeGen/X86/function-address-map-function-sections.ll b/llvm/test/CodeGen/X86/function-address-map-function-sections.ll new file mode 100644 index 0000000000000..4590dfd5da2ff --- /dev/null +++ b/llvm/test/CodeGen/X86/function-address-map-function-sections.ll @@ -0,0 +1,41 @@ +; RUN: llc < %s -mtriple=x86_64 -function-sections -func-addr-map=dyn-inst-count| FileCheck %s + +$_Z4fooTIiET_v = comdat any + +define dso_local i32 @_Z3barv() { + ret i32 0 +} +;; Check we add SHF_LINK_ORDER for .llvm_func_addr_map and link it with the corresponding .text sections. +; CHECK: .section .text._Z3barv,"ax",@progbits +; CHECK-LABEL: _Z3barv: +; CHECK-NEXT: [[BAR_BEGIN:.Lfunc_begin[0-9]+]]: +; CHECK: .section .llvm_func_addr_map,"o",@llvm_func_addr_map,.text._Z3barv{{$}} +; CHECK-NEXT: .byte 1 # version +; CHECK-NEXT: .byte 1 # feature +; CHECK-NEXT: .quad [[BAR_BEGIN]] # function address + + +define dso_local i32 @_Z3foov() { + %1 = call i32 @_Z4fooTIiET_v() + ret i32 %1 +} +; CHECK: .section .text._Z3foov,"ax",@progbits +; CHECK-LABEL: _Z3foov: +; CHECK-NEXT: [[FOO_BEGIN:.Lfunc_begin[0-9]+]]: +; CHECK: .section .llvm_func_addr_map,"o",@llvm_func_addr_map,.text._Z3foov{{$}} +; CHECK-NEXT: .byte 1 # version +; CHECK-NEXT: .byte 1 # feature +; CHECK-NEXT: .quad [[FOO_BEGIN]] # function address + + +define linkonce_odr dso_local i32 @_Z4fooTIiET_v() comdat { + ret i32 0 +} +;; Check we add .llvm_func_addr_map section to a COMDAT group with the corresponding .text section if such a COMDAT exists. +; CHECK: .section .text._Z4fooTIiET_v,"axG",@progbits,_Z4fooTIiET_v,comdat +; CHECK-LABEL: _Z4fooTIiET_v: +; CHECK-NEXT: [[FOOCOMDAT_BEGIN:.Lfunc_begin[0-9]+]]: +; CHECK: .section .llvm_func_addr_map,"oG",@llvm_func_addr_map,.text._Z4fooTIiET_v,_Z4fooTIiET_v,comdat{{$}} +; CHECK-NEXT: .byte 1 # version +; CHECK-NEXT: .byte 1 # feature +; CHECK-NEXT: .quad [[FOOCOMDAT_BEGIN]] # function address diff --git a/llvm/test/MC/AsmParser/llvm_section_types.s b/llvm/test/MC/AsmParser/llvm_section_types.s index 147b1499d2b88..ab30a7565104e 100644 --- a/llvm/test/MC/AsmParser/llvm_section_types.s +++ b/llvm/test/MC/AsmParser/llvm_section_types.s @@ -17,6 +17,9 @@ .byte 1 .section .section8,"",@llvm_lto .byte 1 +.section .section9,"",@llvm_func_addr_map +.byte 1 + # CHECK: Name: .section1 # CHECK-NEXT: Type: SHT_LLVM_BB_ADDR_MAP @@ -34,3 +37,5 @@ # CHECK-NEXT: Type: SHT_LLVM_OFFLOADING # CHECK: Name: .section8 # CHECK-NEXT: Type: SHT_LLVM_LTO +# CHECK: Name: .section9 +# CHECK-NEXT: Type: SHT_LLVM_FUNC_ADDR_MAP diff --git a/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test b/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test new file mode 100644 index 0000000000000..cf88a5c2dfb62 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/func-addr-map.test @@ -0,0 +1,96 @@ +## This test checks how we handle the --func-addr-map option. + +## Check 64-bit: +# RUN: yaml2obj --docnum=1 %s -DBITS=64 -DADDR=0x999999999 -o %t1.x64.o +# RUN: llvm-readobj %t1.x64.o --func-addr-map 2>&1 | FileCheck %s -DADDR=0x999999999 -DFILE=%t1.x64.o --check-prefix=CHECK +# RUN: llvm-readelf %t1.x64.o --func-addr-map | FileCheck %s --check-prefix=GNU + +## Check 32-bit: +# RUN: yaml2obj --docnum=1 %s -DBITS=32 -o %t1.x32.o +# RUN: llvm-readobj %t1.x32.o --func-addr-map 2>&1 | FileCheck -DADDR=0x11111 %s -DFILE=%t1.x32.o --check-prefix=CHECK +# RUN: llvm-readelf %t1.x32.o --func-addr-map | FileCheck %s --check-prefix=GNU + +## Check that a malformed section can be handled. +# RUN: yaml2obj --docnum=1 %s -DBITS=32 -DSIZE=3 -o %t2.o +# RUN: llvm-readobj %t2.o --func-addr-map 2>&1 | FileCheck %s -DOFFSET=0x3 -DFILE=%t2.o --check-prefix=TRUNCATED + +# CHECK: FuncAddrMap [ +# CHECK-NEXT: Function { +# CHECK-NEXT: At: [[ADDR]] +# CHECK-NEXT: warning: '[[FILE]]': could not identify function symbol for address ([[ADDR]]) in SHT_LLVM_FUNC_ADDR_MAP section with index 3 +# CHECK-NEXT: Name: +# CHECK-NEXT: DynamicInstCount: 10 +# CHECK-NEXT: } +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x22222 +# CHECK-NEXT: Name: foo +# CHECK-NEXT: DynamicInstCount: 16 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: FuncAddrMap [ +# CHECK-NEXT: Function { +# CHECK-NEXT: At: 0x33333 +# CHECK-NEXT: Name: bar +# CHECK-NEXT: DynamicInstCount: 10 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# GNU: GNUStyle::printFuncAddrMaps not implemented + +# TRUNCATED: FuncAddrMap [ +# TRUNCATED-NEXT: warning: '[[FILE]]': unable to dump SHT_LLVM_FUNC_ADDR_MAP section with index 3: unexpected end of data at offset [[OFFSET]] +# TRUNCATED-NEXT: ] +## Check that the other valid section is properly dumped. +# TRUNCATED-NEXT: FuncAddrMap [ +# TRUNCATED-NEXT: Function { +# TRUNCATED-NEXT: At: 0x33333 +# TRUNCATED-NEXT: Name: bar +# TRUNCATED-NEXT: DynamicInstCount: 10 +# TRUNCATED-NEXT: } +# TRUNCATED-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + - Name: .text.bar + Type: SHT_PROGBITS + Flags: [SHF_ALLOC] + - Name: .llvm_func_addr_map + Type: SHT_LLVM_FUNC_ADDR_MAP + ShSize: [[SIZE=]] + Link: .text + Entries: + - Version: 1 + Feature: 0x1 + Address: [[ADDR=0x11111]] + DynInstCnt: 0xA + - Version: 1 + Feature: 0x1 + Address: 0x22222 + DynInstCnt: 0x10 + - Name: dummy_section + Type: SHT_PROGBITS + Size: 16 + - Name: '.llvm_func_addr_map_2' + Type: SHT_LLVM_FUNC_ADDR_MAP + Link: .text.bar + Entries: + - Version: 1 + Feature: 0x1 + Address: 0x33333 + DynInstCnt: 0xA +Symbols: + - Name: foo + Section: .text + Type: STT_FUNC + Value: 0x22222 + - Name: bar + Section: .text.bar + Type: STT_FUNC + Value: 0x33333 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index bfca65aad52b4..26b2073392805 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -606,6 +606,7 @@ template class GNUELFDumper : public ELFDumper { void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; + void printFuncAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -717,6 +718,7 @@ template class LLVMELFDumper : public ELFDumper { void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; + void printFuncAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -5199,6 +5201,10 @@ void GNUELFDumper::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) { OS << "GNUStyle::printBBAddrMaps not implemented\n"; } +template void GNUELFDumper::printFuncAddrMaps() { + OS << "GNUStyle::printFuncAddrMaps not implemented\n"; +} + static Expected> toULEB128Array(ArrayRef Data) { std::vector Ret; const uint8_t *Cur = Data.begin(); @@ -7784,6 +7790,61 @@ template void LLVMELFDumper::printCGProfile() { } } +template void LLVMELFDumper::printFuncAddrMaps() { + bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; + using Elf_Shdr = typename ELFT::Shdr; + auto IsMatch = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_FUNC_ADDR_MAP; + }; + Expected> SecRelocMapOrErr = + this->Obj.getSectionAndRelocations(IsMatch); + if (!SecRelocMapOrErr) { + this->reportUniqueWarning( + "failed to get SHT_LLVM_FUNC_ADDR_MAP section(s): " + + toString(SecRelocMapOrErr.takeError())); + return; + } + + for (auto const &[Sec, RelocSec] : *SecRelocMapOrErr) { + std::optional FunctionSec; + if (IsRelocatable) + FunctionSec = + unwrapOrError(this->FileName, this->Obj.getSection(Sec->sh_link)); + ListScope L(W, "FuncAddrMap"); + if (IsRelocatable && !RelocSec) { + this->reportUniqueWarning("unable to get relocation section for " + + this->describe(*Sec)); + continue; + } + Expected> FuncAddrMapOrErr = + this->Obj.decodeFuncAddrMap(*Sec, RelocSec); + if (!FuncAddrMapOrErr) { + this->reportUniqueWarning("unable to dump " + this->describe(*Sec) + + ": " + toString(FuncAddrMapOrErr.takeError())); + continue; + } + for (const auto &AM : *FuncAddrMapOrErr) { + DictScope D(W, "Function"); + W.printHex("At", AM.getFunctionAddress()); + SmallVector FuncSymIndex = + this->getSymbolIndexesForFunctionAddress(AM.getFunctionAddress(), + FunctionSec); + std::string FuncName = ""; + if (FuncSymIndex.empty()) + this->reportUniqueWarning( + "could not identify function symbol for address (0x" + + Twine::utohexstr(AM.getFunctionAddress()) + ") in " + + this->describe(*Sec)); + else + FuncName = this->getStaticSymbolName(FuncSymIndex.front()); + + W.printString("Name", FuncName); + if (AM.FeatEnable.DynamicInstCount) + W.printNumber("DynamicInstCount", AM.DynamicInstCount); + } + } +} + template void LLVMELFDumper::printBBAddrMaps(bool PrettyPGOAnalysis) { bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index cd744e3bbfb71..28e239f86db66 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -132,6 +132,7 @@ class ObjDumper { // If PrettyPGOAnalysis is true, prints BFI as relative frequency and BPI as // percentage. Otherwise raw values are displayed. virtual void printBBAddrMaps(bool PrettyPGOAnalysis) {} + virtual void printFuncAddrMaps() {} virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index 7d574d875d22e..1cd806d40f89c 100644 --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -18,6 +18,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, -- "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, " "--section-groups and --histogram">; def arch_specific : FF<"arch-specific", "Display architecture-specific information">; +def func_addr_map : FF<"func-addr-map", "Display the function address map section">; def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">; def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">; def cg_profile : FF<"cg-profile", "Display call graph profile section">; diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 2f77e5d350553..588744dc8bf69 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -98,6 +98,7 @@ static bool All; static bool ArchSpecificInfo; static bool BBAddrMap; static bool PrettyPGOAnalysisMap; +static bool FuncAddrMap; bool ExpandRelocs; static bool CGProfile; static bool Decompress; @@ -219,6 +220,7 @@ static void parseOptions(const opt::InputArgList &Args) { WithColor::warning(errs(), ToolName) << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to " "have an effect\n"; + opts::FuncAddrMap = Args.hasArg(OPT_func_addr_map); opts::CGProfile = Args.hasArg(OPT_cg_profile); opts::Decompress = Args.hasArg(OPT_decompress); opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); @@ -473,6 +475,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printCGProfile(); if (opts::BBAddrMap) Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap); + if (opts::FuncAddrMap) + Dumper->printFuncAddrMaps(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::Notes) diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index b1c8032ea2192..9534335138544 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -97,6 +97,8 @@ class ELFDumper { dumpStackSizesSection(const Elf_Shdr *Shdr); Expected dumpBBAddrMapSection(const Elf_Shdr *Shdr); + Expected + dumpFuncAddrMapSection(const Elf_Shdr *Shdr); Expected dumpPlaceholderSection(const Elf_Shdr *Shdr); @@ -629,6 +631,8 @@ ELFDumper::dumpSections() { [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); }; case ELF::SHT_LLVM_BB_ADDR_MAP: return [this](const Elf_Shdr *S) { return dumpBBAddrMapSection(S); }; + case ELF::SHT_LLVM_FUNC_ADDR_MAP: + return [this](const Elf_Shdr *S) { return dumpFuncAddrMapSection(S); }; case ELF::SHT_STRTAB: case ELF::SHT_SYMTAB: case ELF::SHT_DYNSYM: @@ -989,6 +993,55 @@ ELFDumper::dumpBBAddrMapSection(const Elf_Shdr *Shdr) { return S.release(); } +template +Expected +ELFDumper::dumpFuncAddrMapSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef Content = *ContentOrErr; + if (Content.empty()) + return S.release(); + + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector Entries; + DataExtractor::Cursor Cur(0); + uint8_t Version = 0; + uint8_t Feature = 0; + uint64_t Address = 0; + while (Cur && Cur.tell() < Content.size()) { + if (Shdr->sh_type == ELF::SHT_LLVM_FUNC_ADDR_MAP) { + Version = Data.getU8(Cur); + Feature = Data.getU8(Cur); + } + auto FeatureOrErr = llvm::object::FuncAddrMap::Features::decode(Feature); + if (!FeatureOrErr) + return FeatureOrErr.takeError(); + + Address = Data.getAddress(Cur); + + uint64_t DynamicInstCount = + FeatureOrErr->DynamicInstCount ? Data.getULEB128(Cur) : 0; + Entries.push_back({Version, Feature, Address, DynamicInstCount}); + } + + if (!Cur) { + // If the section cannot be decoded, we dump it as an array of bytes. + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + } else { + S->Entries = std::move(Entries); + } + + return S.release(); +} + template Expected ELFDumper::dumpAddrsigSection(const Elf_Shdr *Shdr) {