[obj2yaml] [yaml2obj] Add yaml support for SHT_LLVM_BB_ADDR_MAP section.

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
This commit is contained in:
Rahman Lavaee 2020-11-06 12:44:24 -08:00
parent 062b5c598f
commit 82e7c4ce45
6 changed files with 412 additions and 1 deletions

View File

@ -126,6 +126,16 @@ struct DynamicEntry {
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;
@ -159,7 +169,8 @@ struct Chunk {
Fill,
LinkerOptions,
DependentLibraries,
CallGraphProfile
CallGraphProfile,
BBAddrMap
};
ChunkKind Kind;
@ -240,6 +251,16 @@ struct Fill : Chunk {
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;
@ -640,6 +661,8 @@ struct Object {
} // 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)
@ -801,6 +824,14 @@ template <> struct MappingTraits<ELFYAML::StackSizeEntry> {
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);
};

View File

@ -272,6 +272,9 @@ template <class ELFT> class ELFState {
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);
@ -756,6 +759,8 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
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");
}
@ -1316,6 +1321,29 @@ void ELFState<ELFT>::writeSectionContent(
}
}
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,

View File

@ -492,6 +492,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
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);
@ -1146,6 +1147,12 @@ static void sectionMapping(IO &IO, ELFYAML::RawContentSection &Section) {
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);
@ -1405,6 +1412,11 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
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;
@ -1485,6 +1497,12 @@ std::string MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::validate(
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 "";
}
@ -1520,6 +1538,21 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
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");

View File

@ -0,0 +1,155 @@
## 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

View File

@ -0,0 +1,116 @@
## 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>]]

View File

@ -100,6 +100,8 @@ class ELFDumper {
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);
@ -505,6 +507,8 @@ ELFDumper<ELFT>::dumpSections() {
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:
@ -762,6 +766,50 @@ ELFDumper<ELFT>::dumpStackSizesSection(const Elf_Shdr *Shdr) {
return S.release();
}
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) {