diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -389,6 +389,8 @@ Expected> getSectionContentsAsArray(const Elf_Shdr &Sec) const; Expected> getSectionContents(const Elf_Shdr &Sec) const; Expected> getSegmentContents(const Elf_Phdr &Phdr) const; + Expected> + decodeBBAddrMap(const Elf_Shdr &Sec) const; }; using ELF32LEFile = ELFFile; diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -43,6 +43,7 @@ template class Elf_Note_Impl; template class Elf_Note_Iterator_Impl; template struct Elf_CGProfile_Impl; +template struct Elf_BBAddrMap_Impl; template struct ELFType { private: @@ -74,6 +75,7 @@ using Note = Elf_Note_Impl>; using NoteIterator = Elf_Note_Iterator_Impl>; using CGProfile = Elf_CGProfile_Impl>; + using BBAddrMap = Elf_BBAddrMap_Impl>; using DynRange = ArrayRef; using ShdrRange = ArrayRef; using SymRange = ArrayRef; @@ -128,13 +130,14 @@ using Elf_Note = typename ELFT::Note; \ using Elf_Note_Iterator = typename ELFT::NoteIterator; \ using Elf_CGProfile = typename ELFT::CGProfile; \ + using Elf_BBAddrMap = typename ELFT::BBAddrMap; \ using Elf_Dyn_Range = typename ELFT::DynRange; \ using Elf_Shdr_Range = typename ELFT::ShdrRange; \ using Elf_Sym_Range = typename ELFT::SymRange; \ using Elf_Rel_Range = typename ELFT::RelRange; \ using Elf_Rela_Range = typename ELFT::RelaRange; \ using Elf_Relr_Range = typename ELFT::RelrRange; \ - using Elf_Phdr_Range = typename ELFT::PhdrRange; \ + using Elf_Phdr_Range = typename ELFT::PhdrRange; #define LLVM_ELF_COMMA , #define LLVM_ELF_IMPORT_TYPES(E, W) \ @@ -788,6 +791,19 @@ Elf_Word flags2; // General flags }; +// Struct representing the BBAddrMap for one function. +template struct Elf_BBAddrMap_Impl { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + uintX_t Addr; // Function address + // Struct representing the BBAddrMap information for one basic block. + struct BBEntry { + uint32_t Offset; // Offset of basic block relative to function begin. + uint32_t Size; // Size of the basic block. + uint32_t Metadata; // Metadata for this basic block. + }; + std::vector BBEntries; // Basic block entries for this function. +}; + } // end namespace object. } // end namespace llvm. diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -605,6 +605,48 @@ return base() + Offset; } +template +Expected> +ELFFile::decodeBBAddrMap(const Elf_Shdr &Sec) const { + Expected> ContentsOrErr = getSectionContents(Sec); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + ArrayRef Content = *ContentsOrErr; + DataExtractor Data(Content, isLE(), ELFT::Is64Bits ? 8 : 4); + std::vector FunctionEntries; + + DataExtractor::Cursor Cur(0); + Error ULEBSizeErr = Error::success(); + auto readULEB128AsUInt32 = [&Data, &Cur, &ULEBSizeErr]() -> uint32_t { + if (ULEBSizeErr) + return 0; + uint64_t Value = Data.getULEB128(Cur); + if (Value > UINT32_MAX) + ULEBSizeErr = createError( + "ULEB128 value at offset 0x" + Twine::utohexstr(Cur.tell()) + + " exceeds UINT32_MAX (0x" + Twine::utohexstr(Value) + ")"); + return static_cast(Value); + }; + + while (!ULEBSizeErr && Cur && Cur.tell() < Content.size()) { + uintX_t Address = static_cast(Data.getAddress(Cur)); + uint32_t NumBlocks = readULEB128AsUInt32(); + std::vector BBEntries; + for (uint32_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) { + uint32_t Offset = readULEB128AsUInt32(); + uint32_t Size = readULEB128AsUInt32(); + uint32_t Metadata = readULEB128AsUInt32(); + BBEntries.push_back({Offset, Size, Metadata}); + } + FunctionEntries.push_back({Address, BBEntries}); + } + if (ULEBSizeErr) + return std::move(ULEBSizeErr); + if (!Cur) + return std::move(Cur.takeError()); + return FunctionEntries; +} + template class llvm::object::ELFFile; template class llvm::object::ELFFile; template class llvm::object::ELFFile; diff --git a/llvm/test/tools/llvm-readobj/ELF/bb-addr-map.test b/llvm/test/tools/llvm-readobj/ELF/bb-addr-map.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/bb-addr-map.test @@ -0,0 +1,77 @@ +## This test checks how we handle the --bb-addr-map option. + +## Check x64 +# RUN: yaml2obj %s -DBITS=64 -DADDR=0xFFFFFFFF1 -o %t1.x64.o +# RUN: llvm-readobj %t1.x64.o --bb-addr-map | FileCheck %s -DADDR=0xFFFFFFFF1 --check-prefix=LLVM +# RUN: llvm-readelf %t1.x64.o --bb-addr-map | FileCheck %s --check-prefix=GNU + +## Check x32 +# RUN: yaml2obj %s -DBITS=32 -o %t1.x32.o +# RUN: llvm-readobj %t1.x32.o --bb-addr-map | FileCheck -DADDR=0x11111 %s --check-prefix=LLVM +# RUN: llvm-readelf %t1.x32.o --bb-addr-map | FileCheck %s --check-prefix=GNU + +## Check that we can detect the malformed encoding when the section is truncated to include one function address only (4 bytes). +# RUN: yaml2obj %s -DBITS=32 -DSIZE=4 -o %t2.o +# RUN: llvm-readobj %t2.o --bb-addr-map 2>&1 | FileCheck %s -DOFFSET=0x00000004 -DFILE=%t2.o --check-prefix=TRUNCATED + +## Check that we can detect when enoded BB entry fields exceed UINT32 limits. +# RUN: yaml2obj %s -DBITS=64 -DBBADDRESSOFFSET=0xFFFFFFFF4 -o %t3.o +# RUN: llvm-readobj %t3.o --bb-addr-map 2>&1 | FileCheck %s -DOFFSET=0x16 -DVALUE=0xffffffff4 -DFILE=%t3.o --check-prefix=UINT32_LIMIT + +# LLVM: BBAddrMap [ +# LLVM-NEXT: Function { +# LLVM-NEXT: At: [[ADDR]] +# LLVM-NEXT: BB entries [ +# LLVM-NEXT: { +# LLVM-NEXT: Offset: 0x0 +# LLVM-NEXT: Size: 0x1 +# LLVM-NEXT: Metadata: 0xF0000002 +# LLVM-NEXT: } +# LLVM-NEXT: { +# LLVM-NEXT: Offset: 0x3 +# LLVM-NEXT: Size: 0x4 +# LLVM-NEXT: Metadata: 0x5 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: At: 0x22222 +# LLVM-NEXT: BB entries [ +# LLVM-NEXT: { +# LLVM-NEXT: Offset: 0x6 +# LLVM-NEXT: Size: 0x7 +# LLVM-NEXT: Metadata: 0x8 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: } +# LLVM-NEXT: ] + +# GNU: GNUStyle::printBBAddrMap not implemented + +# TRUNCATED: warning: '[[FILE]]': unable to dump SHT_LLVM_BB_ADDR_MAP section with index 1: unable to decode LEB128 at offset [[OFFSET]]: malformed uleb128, extends past end + +# UINT32_LIMIT: warning: '[[FILE]]': unable to dump SHT_LLVM_BB_ADDR_MAP section with index 1: ULEB128 value at offset [[OFFSET]] exceeds UINT32_MAX ([[VALUE]]) + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + ShSize: [[SIZE=]] + Entries: + - Address: [[ADDR=0x11111]] + BBEntries: + - AddressOffset: 0x0 + Size: 0x1 + Metadata: 0xF0000002 + - AddressOffset: [[BBADDRESSOFFSET=0x3]] + Size: 0x4 + Metadata: 0x5 + - Address: 0x22222 + BBEntries: + - AddressOffset: 0x6 + Size: 0x7 + Metadata: 0x8 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -350,6 +350,7 @@ const Elf_Shdr *DotSymtabSec = nullptr; const Elf_Shdr *DotDynsymSec = nullptr; const Elf_Shdr *DotCGProfileSec = nullptr; + const Elf_Shdr *DotBBAddrMapSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr; DenseMap> ShndxTables; Optional SONameOffset; @@ -550,6 +551,7 @@ void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMap() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -660,6 +662,7 @@ void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMap() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -1747,6 +1750,10 @@ if (!DotCGProfileSec) DotCGProfileSec = &Sec; break; + case ELF::SHT_LLVM_BB_ADDR_MAP: + if (!DotBBAddrMapSec) + DotBBAddrMapSec = &Sec; + break; case ELF::SHT_LLVM_ADDRSIG: if (!DotAddrsigSec) DotAddrsigSec = &Sec; @@ -4623,6 +4630,10 @@ OS << "GNUStyle::printCGProfile not implemented\n"; } +template void GNUELFDumper::printBBAddrMap() { + OS << "GNUStyle::printBBAddrMap not implemented\n"; +} + static Expected> toULEB128Array(ArrayRef Data) { std::vector Ret; const uint8_t *Cur = Data.begin(); @@ -6431,6 +6442,33 @@ } } +template void LLVMELFDumper::printBBAddrMap() { + ListScope L(W, "BBAddrMap"); + if (!this->DotBBAddrMapSec) + return; + + Expected> BBAddrMapOrErr = + this->Obj.decodeBBAddrMap(*this->DotBBAddrMapSec); + if (!BBAddrMapOrErr) { + this->reportUniqueWarning("unable to dump " + + this->describe(*this->DotBBAddrMapSec) + ": " + + toString(BBAddrMapOrErr.takeError())); + return; + } + + for (const Elf_BBAddrMap &AM : *BBAddrMapOrErr) { + DictScope D(W, "Function"); + W.printHex("At", AM.Addr); + ListScope L(W, "BB entries"); + for (const typename Elf_BBAddrMap::BBEntry &BBE : AM.BBEntries) { + DictScope L(W); + W.printHex("Offset", BBE.Offset); + W.printHex("Size", BBE.Size); + W.printHex("Metadata", BBE.Metadata); + } + } +} + template void LLVMELFDumper::printAddrsig() { ListScope L(W, "Addrsig"); if (!this->DotAddrsigSec) diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -72,6 +72,7 @@ virtual void printGroupSections() {} virtual void printHashHistograms() {} virtual void printCGProfile() {} + virtual void printBBAddrMap() {} virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -367,6 +367,10 @@ cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"), cl::aliasopt(CGProfile)); + // --bb-addr-map + cl::opt BBAddrMap("bb-addr-map", + cl::desc("Display the BB address map section")); + // -addrsig cl::opt Addrsig("addrsig", cl::desc("Display address-significance table")); @@ -542,6 +546,8 @@ Dumper->printHashHistograms(); if (opts::CGProfile) Dumper->printCGProfile(); + if (opts::BBAddrMap) + Dumper->printBBAddrMap(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::Notes) diff --git a/llvm/unittests/Object/ELFObjectFileTest.cpp b/llvm/unittests/Object/ELFObjectFileTest.cpp --- a/llvm/unittests/Object/ELFObjectFileTest.cpp +++ b/llvm/unittests/Object/ELFObjectFileTest.cpp @@ -482,3 +482,76 @@ DoCheck(0xFFFFFFFF, "can't read an entry at 0x17ffffffe8: it goes past the " "end of the section (0x18)"); } + +// Tests for error paths of the ELFFile::decodeBBAddrMap API. +TEST(ELFObjectFileTest, InvalidBBAddrMap) { + StringRef CommonYamlString(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm_bb_addr_map + Type: SHT_LLVM_BB_ADDR_MAP + Entries: + - Address: 0x11111 + BBEntries: + - AddressOffset: 0x0 + Size: 0x1 + Metadata: 0x2 +)"); + + auto DoCheck = [&](StringRef YamlString, const char *ErrMsg) { + SmallString<0> Storage; + Expected> ElfOrErr = + toBinary(Storage, YamlString); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + Expected BBAddrMapSecOrErr = + Elf.getSection(1); + ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); + ASSERT_EQ((*BBAddrMapSecOrErr)->sh_type, ELF::SHT_LLVM_BB_ADDR_MAP); + EXPECT_THAT_ERROR(Elf.decodeBBAddrMap(**BBAddrMapSecOrErr).takeError(), + FailedWithMessage(ErrMsg)); + }; + + // Check that we can detect the malformed encoding when the section is + // truncated. + SmallString<128> TruncatedYamlString(CommonYamlString); + TruncatedYamlString += R"( + ShSize: 0x8 +)"; + DoCheck(TruncatedYamlString, "unable to decode LEB128 at offset 0x00000008: " + "malformed uleb128, extends past end"); + + // Check that we can detect when the enoded BB entry fields exceed the UINT32 + // limit. + SmallVector, 3> OverInt32LimitYamlStrings(3, + CommonYamlString); + OverInt32LimitYamlStrings[0] += R"( + - AddressOffset: 0xFFFFFFFF3 + Size: 0x4 + Metadata: 0x5 +)"; + + OverInt32LimitYamlStrings[1] += R"( + - AddressOffset: 0x3 + Size: 0xFFFFFFFF4 + Metadata: 0x5 +)"; + + OverInt32LimitYamlStrings[2] += R"( + - AddressOffset: 0x3 + Size: 0x4 + Metadata: 0xFFFFFFFF5 +)"; + + DoCheck(OverInt32LimitYamlStrings[0], + "ULEB128 value at offset 0x12 exceeds UINT32_MAX (0xffffffff3)"); + DoCheck(OverInt32LimitYamlStrings[1], + "ULEB128 value at offset 0x13 exceeds UINT32_MAX (0xffffffff4)"); + DoCheck(OverInt32LimitYamlStrings[2], + "ULEB128 value at offset 0x14 exceeds UINT32_MAX (0xffffffff5)"); +}