diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -126,6 +126,16 @@ llvm::yaml::Hex64 Val; }; +struct BBAddrMapEntry { + struct BBEntry { + llvm::yaml::Hex32 AddressOffset; + llvm::yaml::Hex32 Size; + llvm::yaml::Hex32 Metadata; + }; + llvm::yaml::Hex64 Address; + Optional> BBEntries; +}; + struct StackSizeEntry { llvm::yaml::Hex64 Address; llvm::yaml::Hex64 Size; @@ -159,7 +169,8 @@ Fill, LinkerOptions, DependentLibraries, - CallGraphProfile + CallGraphProfile, + BBAddrMap }; ChunkKind Kind; @@ -237,6 +248,16 @@ static bool classof(const Chunk *S) { return S->Kind == ChunkKind::Fill; } }; +struct BBAddrMapSection : Section { + Optional> Entries; + + BBAddrMapSection() : Section(ChunkKind::BBAddrMap) {} + + static bool classof(const Chunk *S) { + return S->Kind == ChunkKind::BBAddrMap; + } +}; + struct StackSizesSection : Section { Optional> Entries; @@ -637,6 +658,8 @@ } // end namespace llvm LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::LinkerOption) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntry) @@ -798,6 +821,14 @@ static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &Rel); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::BBAddrMapEntry::BBEntry &Rel); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::GnuHashHeader &Rel); }; diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -272,6 +272,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::BBAddrMapSection &Section, + ContiguousBlobAccumulator &CBA); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::HashSection &Section, ContiguousBlobAccumulator &CBA); @@ -755,6 +758,8 @@ 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"); } @@ -1313,6 +1318,29 @@ } } +template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::BBAddrMapSection &Section, + ContiguousBlobAccumulator &CBA) { + if (!Section.Entries) + return; + + for (const ELFYAML::BBAddrMapEntry &E : *Section.Entries) { + // Write the address of the function. + CBA.write(E.Address, ELFT::TargetEndianness); + // Write number of BBEntries (number of basic blocks in the function). + size_t NumBlocks = E.BBEntries ? E.BBEntries->size() : 0; + SHeader.sh_size += sizeof(uintX_t) + CBA.writeULEB128(NumBlocks); + if (!NumBlocks) + continue; + // Write all BBEntries. + for (const ELFYAML::BBAddrMapEntry::BBEntry &BBE : *E.BBEntries) + SHeader.sh_size += CBA.writeULEB128(BBE.AddressOffset) + + CBA.writeULEB128(BBE.Size) + + CBA.writeULEB128(BBE.Metadata); + } +} + 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 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -490,6 +490,7 @@ ECase(SHT_LLVM_SYMPART); ECase(SHT_LLVM_PART_EHDR); ECase(SHT_LLVM_PART_PHDR); + ECase(SHT_LLVM_BB_ADDR_MAP); ECase(SHT_GNU_ATTRIBUTES); ECase(SHT_GNU_HASH); ECase(SHT_GNU_verdef); @@ -1144,6 +1145,12 @@ IO.mapOptional("Info", Section.Info); } +static void sectionMapping(IO &IO, ELFYAML::BBAddrMapSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Entries", Section.Entries); +} + static void sectionMapping(IO &IO, ELFYAML::StackSizesSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Entries", Section.Entries); @@ -1403,6 +1410,11 @@ Section.reset(new ELFYAML::CallGraphProfileSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_LLVM_BB_ADDR_MAP: + if (!IO.outputting()) + Section.reset(new ELFYAML::BBAddrMapSection()); + sectionMapping(IO, *cast(Section.get())); + break; default: if (!IO.outputting()) { StringRef Name; @@ -1483,6 +1495,12 @@ return ""; } + if (const auto *BBAM = dyn_cast(C.get())) { + if ((BBAM->Content || BBAM->Size) && BBAM->Entries) + return "\"Entries\" cannot be used with \"Content\" or \"Size\""; + return ""; + } + return ""; } @@ -1518,6 +1536,21 @@ IO.mapRequired("Size", E.Size); } +void MappingTraits::mapping( + IO &IO, ELFYAML::BBAddrMapEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapOptional("Address", E.Address, Hex64(0)); + IO.mapOptional("BBEntries", E.BBEntries); +} + +void MappingTraits::mapping( + IO &IO, ELFYAML::BBAddrMapEntry::BBEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapRequired("AddressOffset", E.AddressOffset); + IO.mapRequired("Size", E.Size); + IO.mapRequired("Metadata", E.Metadata); +} + void MappingTraits::mapping(IO &IO, ELFYAML::GnuHashHeader &E) { assert(IO.getContext() && "The IO context is not initialized"); diff --git a/llvm/test/tools/obj2yaml/ELF/bb-addr-map.yaml b/llvm/test/tools/obj2yaml/ELF/bb-addr-map.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/ELF/bb-addr-map.yaml @@ -0,0 +1,157 @@ +## Check how obj2yaml produces YAML .llvm_bb_addr_map descriptions. + +## Check that obj2yaml uses the "Entries" tag to describe a .llvm_bb_addr_map section. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=VALID + +# VALID: --- !ELF +# VALID-NEXT: FileHeader: +# VALID-NEXT: Class: ELFCLASS64 +# VALID-NEXT: Data: ELFDATA2LSB +# VALID-NEXT: Type: ET_EXEC +# VALID-NEXT: Sections: +# VALID-NEXT: - Name: .llvm_bb_addr_map +# VALID-NEXT: Type: SHT_LLVM_BB_ADDR_MAP +# VALID-NEXT: Entries: +## Verify that the 'Address' field is omitted when it's zero. +# VALID-NOT: Address +# VALID-NEXT: BBEntries: +# VALID-NEXT: - AddressOffset: 0x00000001 +# VALID-NEXT: Size: 0x00000002 +# VALID-NEXT: Metadata: 0x00000003 +# VALID-NEXT: - AddressOffset: 0x00000004 +# VALID-NEXT: Size: 0x00000005 +# VALID-NEXT: Metadata: 0x00000006 +# VALID-NEXT: - AddressOffset: 0x00000007 +# VALID-NEXT: Size: 0x00000008 +# VALID-NEXT: Metadata: 0x00000009 +# VALID-NEXT: - Address: 0x0000000000000020 +# VALID-NEXT: BBEntries: +# VALID-NEXT: - AddressOffset: 0x0000000A +# VALID-NEXT: Size: 0x0000000B +# VALID-NEXT: Metadata: 0x0000000C + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - Address: 0x0000000000000000 + BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + - AddressOffset: 0x00000004 + Size: 0x00000005 + Metadata: 0x00000006 + - AddressOffset: 0x00000007 + Size: 0x00000008 + Metadata: 0x00000009 + - Address: 0x0000000000000020 + BBEntries: + - AddressOffset: 0x0000000A + Size: 0x0000000B + Metadata: 0x0000000C + +## Check that obj2yaml uses the "Content" tag to describe a .llvm_bb_addr_map section +## when it can't extract the entries. For instance, when truncated data is given as +## 'Content'. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=INVALID + +# INVALID: --- !ELF +# INVALID-NEXT: FileHeader: +# INVALID-NEXT: Class: ELFCLASS64 +# INVALID-NEXT: Data: ELFDATA2LSB +# INVALID-NEXT: Type: ET_EXEC +# INVALID-NEXT: Sections: +# INVALID-NEXT: - Name: .llvm_bb_addr_map +# INVALID-NEXT: Type: SHT_LLVM_BB_ADDR_MAP +# INVALID-NEXT: Content: '10000000000000' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Content: '10000000000000' + +## Check obj2yaml can dump empty .llvm_bb_addr_map sections. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: obj2yaml %t3 | FileCheck %s --check-prefix=EMPTY + +# EMPTY: --- !ELF +# EMPTY-NEXT: FileHeader: +# EMPTY-NEXT: Class: ELFCLASS64 +# EMPTY-NEXT: Data: ELFDATA2LSB +# EMPTY-NEXT: Type: ET_EXEC +# EMPTY-NEXT: Sections: +# EMPTY-NEXT: - Name: .llvm_bb_addr_map +# EMPTY-NEXT: Type: SHT_LLVM_BB_ADDR_MAP +# EMPTY-NEXT: Content: '' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Content: "" + +## Check obj2yaml can dump multiple .llvm_bb_addr_map sections. + +# RUN: yaml2obj --docnum=4 %s -o %t4 +# RUN: obj2yaml %t4 | FileCheck %s --check-prefix=MULTI + +# MULTI: --- !ELF +# MULTI-NEXT: FileHeader: +# MULTI-NEXT: Class: ELFCLASS64 +# MULTI-NEXT: Data: ELFDATA2LSB +# MULTI-NEXT: Type: ET_EXEC +# MULTI-NEXT: Sections: +# MULTI-NEXT: - Name: .llvm_bb_addr_map +# MULTI-NEXT: Type: SHT_LLVM_BB_ADDR_MAP +# MULTI-NEXT: Entries: +## Check that Address is not emitted when it's zero. +# MULTI-NOT: Address +# MULTI-NEXT: - BBEntries: +# MULTI-NEXT: - AddressOffset: 0x00000001 +# MULTI-NEXT: Size: 0x00000002 +# MULTI-NEXT: Metadata: 0x00000003 +# MULTI-NEXT: - Name: '.llvm_bb_addr_map (1)' +# MULTI-NEXT: Type: SHT_LLVM_BB_ADDR_MAP +# MULTI-NEXT: Entries: +# MULTI-NEXT: - Address: 0x0000000000000020 +# MULTI-NEXT: BBEntries: [] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Entries: +## Check that obj2yaml does not emit the Address field when it's zero. + - Address: 0x0000000000000000 + BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + - Name: '.llvm_bb_addr_map (1)' + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - Address: 0x0000000000000020 diff --git a/llvm/test/tools/yaml2obj/ELF/bb-addr-map.yaml b/llvm/test/tools/yaml2obj/ELF/bb-addr-map.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/bb-addr-map.yaml @@ -0,0 +1,139 @@ +## Check how yaml2obj produces .llvm_bb_addr_map sections. + +## Test the following cases" +## 1) We can produce a .llvm_bb_addr_map section from a description with section +## content. +## 2) We can produce an empty .llvm_bb_addr_map section from a description +## swith empty section content. +## 3) We can produce a zero .llvm_bb_addr_map section of a specific size when +## we specify the size only. +## 4) We can produce a .llvm_bb_addr_map section from a description with +## Entries. +## 5) When specifying the description with Entries, the 'Address' field will be +## zero when omitted. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s + +## Case 1: Specify content. +# CHECK: Section { +# CHECK: Index: 1 +# CHECK-NEXT: Name: .llvm_bb_addr_map (1) +# CHECK-NEXT: Type: SHT_LLVM_BB_ADDR_MAP (0x6FFF4C08) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Offset: 0x40 +# CHECK-NEXT: Size: 12 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 01010203 +# CHECK-NEXT: ) +# CHECK-NEXT: } + +## Case 2: Empty. +# CHECK: Name: .llvm_bb_addr_map (1) +# CHECK: Size: +# CHECK-SAME: 0 + +## Case 3: Specify Size only. +# CHECK: Name: .llvm_bb_addr_map (1) +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 +# CHECK-NEXT: ) + +# Case 4: Specify Entries. +# CHECK: Name: .llvm_bb_addr_map (1) +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 20000000 00000000 01010203 +# CHECK-NEXT: ) + +# Case 5: Specify Entries and omit the Address field. +# CHECK: Name: .llvm_bb_addr_map (1) +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 00000000 00000000 01010203 +# CHECK-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + +## Specify Content. + - Name: '.llvm_bb_addr_map (1)' + Type: SHT_LLVM_BB_ADDR_MAP + Content: "000000000000000001010203" + +## Empty. + - Name: '.llvm_bb_addr_map (2)' + Type: SHT_LLVM_BB_ADDR_MAP + +## Specify Size. + - Name: '.llvm_bb_addr_map (3)' + Type: SHT_LLVM_BB_ADDR_MAP + Size: 8 + +## Specify Entries. + - Name: '.llvm_bb_addr_map (4)' + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - Address: 0x0000000000000020 + BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + +## Check we can omit the "Address" tag. In this case the address will be zero. + - Name: '.llvm_bb_addr_map (5)' + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + + +## Check we can't use both "Content" and "Entries" tags at the same time. +# RUN: not yaml2obj --docnum=2 %s 2>&1 | FileCheck %s --check-prefix=ENTRIES-AND-CONTENT +# ENTRIES-AND-CONTENT: error: "Entries" cannot be used with "Content" or "Size" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: +## Specify Content and Entries + - Name: '.llvm_bb_addr_map (1)' + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + Content: "000000000000000001010203" + +## Check we can't use both "Size" and "Entries" tags at the same time. +# RUN: not yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=ENTRIES-AND-SIZE +# ENTRIES-AND-SIZE: error: "Entries" cannot be used with "Content" or "Size" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: +## Specify Content and Size + - Name: '.llvm_bb_addr_map (1)' + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - BBEntries: + - AddressOffset: 0x00000001 + Size: 0x00000002 + Metadata: 0x00000003 + Size: 12 diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -100,6 +100,8 @@ Expected dumpMipsABIFlags(const Elf_Shdr *Shdr); Expected dumpStackSizesSection(const Elf_Shdr *Shdr); + Expected + dumpBBAddrMapSection(const Elf_Shdr *Shdr); Expected dumpPlaceholderSection(const Elf_Shdr *Shdr); @@ -506,6 +508,9 @@ case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: return [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_STRTAB: case ELF::SHT_SYMTAB: case ELF::SHT_DYNSYM: @@ -763,6 +768,47 @@ return S.release(); } +template +Expected +ELFDumper::dumpBBAddrMapSection(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; + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector Entries; + DataExtractor::Cursor Cur(0); + while (Cur && Cur.tell() < Content.size()) { + uint64_t Address = Data.getAddress(Cur); + uint32_t NumBlocks = Data.getULEB128(Cur); + std::vector BBEntries; + // Read the specified number of BB entries, or until decoding fails. + for (uint32_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) { + uint32_t Offset = Data.getULEB128(Cur); + uint32_t Size = Data.getULEB128(Cur); + uint32_t Metadata = Data.getULEB128(Cur); + BBEntries.push_back({Offset, Size, Metadata}); + } + Entries.push_back({Address, BBEntries}); + } + + if (Content.empty() || !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) {