[obj2yaml] [yaml2obj] Add yaml support for SHT_LLVM_BB_ADDR_MAP section.
authorRahman Lavaee <rahmanl@google.com>
Fri, 6 Nov 2020 20:44:24 +0000 (12:44 -0800)
committerRahman Lavaee <rahmanl@google.com>
Fri, 6 Nov 2020 20:44:42 +0000 (12:44 -0800)
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/include/llvm/ObjectYAML/ELFYAML.h
llvm/lib/ObjectYAML/ELFEmitter.cpp
llvm/lib/ObjectYAML/ELFYAML.cpp
llvm/test/tools/obj2yaml/ELF/bb-addr-map.yaml [new file with mode: 0644]
llvm/test/tools/yaml2obj/ELF/bb-addr-map.yaml [new file with mode: 0644]
llvm/tools/obj2yaml/elf2yaml.cpp

index f7b02f5..16d8cf6 100644 (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);
 };
index 6b4343e..fbe31a3 100644 (file)
@@ -273,6 +273,9 @@ template <class ELFT> class ELFState {
                            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,
@@ -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");
     }
@@ -1318,6 +1323,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,
     ContiguousBlobAccumulator &CBA) {
   if (!Section.Options)
index 211a6ab..0df8482 100644 (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");
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 (file)
index 0000000..30e2a4f
--- /dev/null
@@ -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
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 (file)
index 0000000..bc958a3
--- /dev/null
@@ -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>]]
index edb2aa3..ba1bacd 100644 (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:
@@ -763,6 +767,50 @@ ELFDumper<ELFT>::dumpStackSizesSection(const Elf_Shdr *Shdr) {
 }
 
 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>();