YAML support allows us to better test the feature in the subsequent patches. The implementation is quite similar to the .stack_sizes section.
Reviewed By: jhenderson, grimar
Differential Revision: https://reviews.llvm.org/D88717
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<std::vector<BBEntry>> BBEntries;
+};
+
struct StackSizeEntry {
llvm::yaml::Hex64 Address;
llvm::yaml::Hex64 Size;
Fill,
LinkerOptions,
DependentLibraries,
- CallGraphProfile
+ CallGraphProfile,
+ BBAddrMap
};
ChunkKind Kind;
static bool classof(const Chunk *S) { return S->Kind == ChunkKind::Fill; }
};
+struct BBAddrMapSection : Section {
+ Optional<std::vector<BBAddrMapEntry>> Entries;
+
+ BBAddrMapSection() : Section(ChunkKind::BBAddrMap) {}
+
+ static bool classof(const Chunk *S) {
+ return S->Kind == ChunkKind::BBAddrMap;
+ }
+};
+
struct StackSizesSection : Section {
Optional<std::vector<StackSizeEntry>> Entries;
} // 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)
static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel);
};
+template <> struct MappingTraits<ELFYAML::BBAddrMapEntry> {
+ static void mapping(IO &IO, ELFYAML::BBAddrMapEntry &Rel);
+};
+
+template <> struct MappingTraits<ELFYAML::BBAddrMapEntry::BBEntry> {
+ static void mapping(IO &IO, ELFYAML::BBAddrMapEntry::BBEntry &Rel);
+};
+
template <> struct MappingTraits<ELFYAML::GnuHashHeader> {
static void mapping(IO &IO, ELFYAML::GnuHashHeader &Rel);
};
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);
void writeSectionContent(Elf_Shdr &SHeader,
writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::CallGraphProfileSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA);
+ } else if (auto S = dyn_cast<ELFYAML::BBAddrMapSection>(Sec)) {
+ writeSectionContent(SHeader, *S, CBA);
} else {
llvm_unreachable("Unknown section type");
}
template <class ELFT>
void ELFState<ELFT>::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<uintX_t>(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 <class ELFT>
+void ELFState<ELFT>::writeSectionContent(
Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section,
ContiguousBlobAccumulator &CBA) {
if (!Section.Options)
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);
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);
Section.reset(new ELFYAML::CallGraphProfileSection());
sectionMapping(IO, *cast<ELFYAML::CallGraphProfileSection>(Section.get()));
break;
+ case ELF::SHT_LLVM_BB_ADDR_MAP:
+ if (!IO.outputting())
+ Section.reset(new ELFYAML::BBAddrMapSection());
+ sectionMapping(IO, *cast<ELFYAML::BBAddrMapSection>(Section.get()));
+ break;
default:
if (!IO.outputting()) {
StringRef Name;
return "";
}
+ if (const auto *BBAM = dyn_cast<ELFYAML::BBAddrMapSection>(C.get())) {
+ if ((BBAM->Content || BBAM->Size) && BBAM->Entries)
+ return "\"Entries\" cannot be used with \"Content\" or \"Size\"";
+ return "";
+ }
+
return "";
}
IO.mapRequired("Size", E.Size);
}
+void MappingTraits<ELFYAML::BBAddrMapEntry>::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<ELFYAML::BBAddrMapEntry::BBEntry>::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<ELFYAML::GnuHashHeader>::mapping(IO &IO,
ELFYAML::GnuHashHeader &E) {
assert(IO.getContext() && "The IO context is not initialized");
--- /dev/null
+## Check how obj2yaml produces YAML .llvm_bb_addr_map descriptions.
+
+## Check that obj2yaml uses the "Entries" tag to describe an .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:
+## The 'Address' field is omitted when it's zero.
+# 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 an .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-NOT: 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:
+## The 'Address' field is omitted when it's zero.
+# 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
--- /dev/null
+## Check how yaml2obj produces .llvm_bb_addr_map sections.
+
+# 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: Address:
+# CHECK-SAME: {{^ 0x0$}}
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 00000000 00000000 01010203
+# CHECK-NEXT: )
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+Sections:
+
+## Test the following cases:
+
+## 1) We can produce an .llvm_bb_addr_map section from a description with section
+## content.
+## Specify Content.
+ - Name: '.llvm_bb_addr_map (1)'
+ Type: SHT_LLVM_BB_ADDR_MAP
+ Content: "000000000000000001010203"
+
+## 2) We can produce an empty .llvm_bb_addr_map section from a description
+## with empty section content.
+ - Name: '.llvm_bb_addr_map (2)'
+ Type: SHT_LLVM_BB_ADDR_MAP
+
+## 3) We can produce a zero .llvm_bb_addr_map section of a specific size when
+## we specify the size only.
+ - Name: '.llvm_bb_addr_map (3)'
+ Type: SHT_LLVM_BB_ADDR_MAP
+ Size: 8
+
+## 4) We can produce an .llvm_bb_addr_map section from a description with
+## Entries.
+ - Name: '.llvm_bb_addr_map (4)'
+ Type: SHT_LLVM_BB_ADDR_MAP
+ Entries:
+ - Address: 0x0000000000000020
+ BBEntries:
+ - AddressOffset: 0x00000001
+ Size: 0x00000002
+ Metadata: 0x00000003
+
+## 5) When specifying the description with Entries, the 'Address' field will be
+## zero when omitted.
+ - 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 Entries at the same time as either Content or Size.
+# RUN: not yaml2obj --docnum=2 -DCONTENT="00" %s 2>&1 | FileCheck %s --check-prefix=INVALID
+# RUN: not yaml2obj --docnum=2 -DSIZE="0" %s 2>&1 | FileCheck %s --check-prefix=INVALID
+
+# INVALID: 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'
+ Type: SHT_LLVM_BB_ADDR_MAP
+ Entries: []
+ Content: [[CONTENT=<none>]]
+ Size: [[SIZE=<none>]]
Expected<ELFYAML::MipsABIFlags *> dumpMipsABIFlags(const Elf_Shdr *Shdr);
Expected<ELFYAML::StackSizesSection *>
dumpStackSizesSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::BBAddrMapSection *>
+ dumpBBAddrMapSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::RawContentSection *>
dumpPlaceholderSection(const Elf_Shdr *Shdr);
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:
}
template <class ELFT>
+Expected<ELFYAML::BBAddrMapSection *>
+ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::BBAddrMapSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.empty())
+ return S.release();
+
+ DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4);
+
+ std::vector<ELFYAML::BBAddrMapEntry> 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<ELFYAML::BBAddrMapEntry::BBEntry> 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 (!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 <class ELFT>
Expected<ELFYAML::AddrsigSection *>
ELFDumper<ELFT>::dumpAddrsigSection(const Elf_Shdr *Shdr) {
auto S = std::make_unique<ELFYAML::AddrsigSection>();