diff --git a/llvm/lib/Object/BuildID.cpp b/llvm/lib/Object/BuildID.cpp index 89d6bc3ab550d..d1ee597a11327 100644 --- a/llvm/lib/Object/BuildID.cpp +++ b/llvm/lib/Object/BuildID.cpp @@ -24,6 +24,24 @@ using namespace llvm::object; namespace { template BuildIDRef getBuildID(const ELFFile &Obj) { + auto findBuildID = [&Obj](const auto &ShdrOrPhdr, + uint64_t Alignment) -> std::optional { + Error Err = Error::success(); + for (auto N : Obj.notes(ShdrOrPhdr, Err)) + if (N.getType() == ELF::NT_GNU_BUILD_ID && + N.getName() == ELF::ELF_NOTE_GNU) + return N.getDesc(Alignment); + consumeError(std::move(Err)); + return std::nullopt; + }; + + auto Sections = cantFail(Obj.sections()); + for (const auto &S : Sections) { + if (S.sh_type != ELF::SHT_NOTE) + continue; + if (std::optional ShdrRes = findBuildID(S, S.sh_addralign)) + return ShdrRes.value(); + } auto PhdrsOrErr = Obj.program_headers(); if (!PhdrsOrErr) { consumeError(PhdrsOrErr.takeError()); @@ -32,12 +50,8 @@ template BuildIDRef getBuildID(const ELFFile &Obj) { for (const auto &P : *PhdrsOrErr) { if (P.p_type != ELF::PT_NOTE) continue; - Error Err = Error::success(); - for (auto N : Obj.notes(P, Err)) - if (N.getType() == ELF::NT_GNU_BUILD_ID && - N.getName() == ELF::ELF_NOTE_GNU) - return N.getDesc(P.p_align); - consumeError(std::move(Err)); + if (std::optional PhdrRes = findBuildID(P, P.p_align)) + return PhdrRes.value(); } return {}; } diff --git a/llvm/test/DebugInfo/symbolize-build-id.test b/llvm/test/DebugInfo/symbolize-build-id.test index d63f43ff859e6..2620718293320 100644 --- a/llvm/test/DebugInfo/symbolize-build-id.test +++ b/llvm/test/DebugInfo/symbolize-build-id.test @@ -21,6 +21,7 @@ Sections: Type: SHT_NOTE Flags: [ SHF_ALLOC ] Content: 040000000800000003000000474e5500abb50d82b6bdc861 + AddressAlign: 4 ProgramHeaders: - Type: PT_NOTE Flags: [ PF_R ] diff --git a/llvm/unittests/Object/BuildIDTest.cpp b/llvm/unittests/Object/BuildIDTest.cpp new file mode 100644 index 0000000000000..04ca6364fbc4d --- /dev/null +++ b/llvm/unittests/Object/BuildIDTest.cpp @@ -0,0 +1,120 @@ +//===- BuildIDTest.cpp - Tests for getBuildID ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/BuildID.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; + +template +static Expected> toBinary(SmallVectorImpl &Storage, + StringRef Yaml) { + raw_svector_ostream OS(Storage); + yaml::Input YIn(Yaml); + if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {})) + return createStringError(std::errc::invalid_argument, + "unable to convert YAML"); + return ELFObjectFile::create(MemoryBufferRef(OS.str(), "dummyELF")); +} + +static StringRef getInvalidNoteELF(bool WithShdr) { + static std::string WithSection(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_NOTE + FileSize: 0x1a + FirstSec: .note.gnu.build-id + LastSec: .note.gnu.build-id +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + AddressAlign: 0x04 + Notes: + - Name: "GNU" + Desc: "abb50d82b6bdc861" + Type: 3 +)"); + static std::string WithoutSection(WithSection + R"( + - Type: SectionHeaderTable + NoHeaders: true +)"); + if (WithShdr) + return WithSection; + return WithoutSection; +} + +// The BuildID can be looked up from a section header, if there is no program +// header. +TEST(BuildIDTest, InvalidPhdrFileSizeWithShdrs) { + SmallString<0> Storage; + Expected> ElfOrErr = + toBinary(Storage, getInvalidNoteELF(true)); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + BuildIDRef BuildID = getBuildID(&ElfOrErr.get()); + EXPECT_EQ( + StringRef(reinterpret_cast(BuildID.data()), BuildID.size()), + "\xAB\xB5\x0D\x82\xB6\xBD\xC8\x61"); +} + +// The code handles a malformed program header that points at data outside the +// file. +TEST(BuildIDTest, InvalidPhdrFileSizeNoShdrs) { + SmallString<0> Storage; + Expected> ElfOrErr = + toBinary(Storage, getInvalidNoteELF(false)); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + BuildIDRef BuildID = getBuildID(&ElfOrErr.get()); + EXPECT_EQ( + StringRef(reinterpret_cast(BuildID.data()), BuildID.size()), + ""); +} + +// The code handles a malformed section header that points at data outside the +// file. +TEST(BuildIDTest, InvalidSectionHeader) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_NOTE + FirstSec: .note.gnu.build-id + LastSec: .note.gnu.build-id +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + AddressAlign: 0x04 + ShOffset: 0x1a1 + Notes: + - Name: "GNU" + Desc: "abb50d82b6bdc861" + Type: 3 +)"); + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + BuildIDRef BuildID = getBuildID(&ElfOrErr.get()); + EXPECT_EQ( + StringRef(reinterpret_cast(BuildID.data()), BuildID.size()), + "\xAB\xB5\x0D\x82\xB6\xBD\xC8\x61"); +} diff --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt index 1343352d1dc69..cd70a7b18b5f4 100644 --- a/llvm/unittests/Object/CMakeLists.txt +++ b/llvm/unittests/Object/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ObjectTests ArchiveTest.cpp + BuildIDTest.cpp COFFObjectFileTest.cpp DXContainerTest.cpp ELFObjectFileTest.cpp