Index: include/llvm/BinaryFormat/ELF.h =================================================================== --- include/llvm/BinaryFormat/ELF.h +++ include/llvm/BinaryFormat/ELF.h @@ -1483,6 +1483,20 @@ Elf64_Xword ch_addralign; }; +// Node header for ELF32. +struct Elf32_Nhdr { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +}; + +// Node header for ELF64. +struct Elf64_Nhdr { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +}; + // Legal values for ch_type field of compressed section header. enum { ELFCOMPRESS_ZLIB = 1, // ZLIB/DEFLATE algorithm. Index: include/llvm/Object/ELF.h =================================================================== --- include/llvm/Object/ELF.h +++ include/llvm/Object/ELF.h @@ -67,6 +67,9 @@ using Elf_Versym = typename ELFT::Versym; using Elf_Hash = typename ELFT::Hash; using Elf_GnuHash = typename ELFT::GnuHash; + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + using Elf_Note_Iterator = typename ELFT::NoteIterator; using Elf_Dyn_Range = typename ELFT::DynRange; using Elf_Shdr_Range = typename ELFT::ShdrRange; using Elf_Sym_Range = typename ELFT::SymRange; @@ -155,6 +158,73 @@ return makeArrayRef(Begin, Begin + getHeader()->e_phnum); } + /// Get an iterator over notes in a program header. + /// + /// The program header must be of type \c PT_NOTE. + /// + /// \param Phdr the program header to iterate over. + /// \param Err [out] an error to support fallible iteration, which should + /// be checked after iteration ends. + Elf_Note_Iterator notes_begin(const Elf_Phdr &Phdr, Error &Err) const { + if (Phdr.p_type != ELF::PT_NOTE) { + Err = createError("attempt to iterate notes of non-note program header"); + return Elf_Note_Iterator(&Err); + } + if (Phdr.p_offset + Phdr.p_filesz > getBufSize()) { + Err = createError("invalid program header offset/size"); + return Elf_Note_Iterator(&Err); + } + return Elf_Note_Iterator(base() + Phdr.p_offset, Phdr.p_filesz, &Err); + } + + /// Get an iterator over notes in a section. + /// + /// The section must be of type \c SHT_NOTE. + /// + /// \param Shdr the section to iterate over. + /// \param Err [out] an error to support fallible iteration, which should + /// be checked after iteration ends. + Elf_Note_Iterator notes_begin(const Elf_Shdr &Shdr, Error &Err) const { + if (Shdr.sh_type != ELF::SHT_NOTE) { + Err = createError("attempt to iterate notes of non-note section"); + return Elf_Note_Iterator(&Err); + } + if (Shdr.sh_offset + Shdr.sh_size > getBufSize()) { + Err = createError("invalid section offset/size"); + return Elf_Note_Iterator(&Err); + } + return Elf_Note_Iterator(base() + Shdr.sh_offset, Shdr.sh_size, &Err); + } + + /// Get the end iterator for notes. + Elf_Note_Iterator notes_end() const { + return Elf_Note_Iterator(); + } + + /// Get an iterator range over notes of a program header. + /// + /// The program header must be of type \c PT_NOTE. + /// + /// \param Phdr the program header to iterate over. + /// \param Err [out] an error to support fallible iteration, which should + /// be checked after iteration ends. + iterator_range notes(const Elf_Phdr &Phdr, + Error &Err) const { + return make_range(notes_begin(Phdr, Err), notes_end()); + } + + /// Get an iterator range over notes of a section. + /// + /// The section must be of type \c SHT_NOTE. + /// + /// \param Shdr the section to iterate over. + /// \param Err [out] an error to support fallible iteration, which should + /// be checked after iteration ends. + iterator_range notes(const Elf_Shdr &Shdr, + Error &Err) const { + return make_range(notes_begin(Shdr, Err), notes_end()); + } + Expected getSectionStringTable(Elf_Shdr_Range Sections) const; Expected getSectionIndex(const Elf_Sym *Sym, Elf_Sym_Range Syms, ArrayRef ShndxTable) const; Index: include/llvm/Object/ELFTypes.h =================================================================== --- include/llvm/Object/ELFTypes.h +++ include/llvm/Object/ELFTypes.h @@ -40,6 +40,9 @@ template struct Elf_Hash_Impl; template struct Elf_GnuHash_Impl; template struct Elf_Chdr_Impl; +template struct Elf_Nhdr_Impl; +template class Elf_Note_Impl; +template class Elf_Note_Iterator_Impl; template struct ELFType { private: @@ -66,6 +69,9 @@ using Hash = Elf_Hash_Impl>; using GnuHash = Elf_GnuHash_Impl>; using Chdr = Elf_Chdr_Impl>; + using Nhdr = Elf_Nhdr_Impl>; + using Note = Elf_Note_Impl>; + using NoteIterator = Elf_Note_Iterator_Impl>; using DynRange = ArrayRef; using ShdrRange = ArrayRef; using SymRange = ArrayRef; @@ -551,6 +557,136 @@ Elf_Xword ch_addralign; }; +/// Note header +template +struct Elf_Nhdr_Impl { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + Elf_Word n_namesz; + Elf_Word n_descsz; + Elf_Word n_type; + + /// The alignment of the name and descriptor. + /// + /// Implementations differ from the specification here: in practice all + /// variants align both the name and descriptor to 4-bytes. + static const unsigned int Align = 4; + + /// Get the size of the note, including name, descriptor, and padding. + size_t getSize() const { + return sizeof(*this) + alignTo(n_namesz) + alignTo(n_descsz); + } +}; + +/// An ELF note. +/// +/// Wraps a note header, providing methods for accessing the name and +/// descriptor safely. +template +class Elf_Note_Impl { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + + const Elf_Nhdr_Impl &Nhdr; + size_t MaxSize; + + template friend class Elf_Note_Iterator_Impl; + + Elf_Note_Impl(const Elf_Nhdr_Impl &Nhdr, size_t MaxSize) + : Nhdr(Nhdr), MaxSize(MaxSize) {} + +public: + /// Get the note's name, excluding the terminating null byte. + Expected getName() const { + if (!Nhdr.n_namesz) + return StringRef(); + if (sizeof(Nhdr) + alignTo::Align>(Nhdr.n_namesz) > + MaxSize) + return make_error("ELF note name overflows container", + object_error::parse_failed); + return StringRef(reinterpret_cast(&Nhdr) + sizeof(Nhdr), + Nhdr.n_namesz - 1); + } + + /// Get the note's descriptor. + Expected> getDesc() const { + if (!Nhdr.n_descsz) + return ArrayRef(); + if (Nhdr.getSize() > MaxSize) + return make_error("ELF note descriptor overflows container", + object_error::parse_failed); + return ArrayRef( + reinterpret_cast( + reinterpret_cast(&Nhdr) + sizeof(Nhdr) + + alignTo::Align>(Nhdr.n_namesz)), + Nhdr.n_descsz); + } + + /// Get the note's type. + Elf_Word getType() const { return Nhdr.n_type; } +}; + +template +class Elf_Note_Iterator_Impl + : std::iterator> { + // Nhdr being a nullptr marks the end of iteration. + const Elf_Nhdr_Impl *Nhdr = nullptr; + size_t RemainingSize = 0u; + Error *Err = nullptr; + + template friend class ELFFile; + + Error makeOverflowError() { + return make_error("ELF note overflows container", + object_error::parse_failed); + } + + Elf_Note_Iterator_Impl() {} + explicit Elf_Note_Iterator_Impl(Error *Err) : Err(Err) {} + Elf_Note_Iterator_Impl(const uint8_t *Start, size_t Size, Error *Err) + : RemainingSize(Size), Err(Err) { + assert(Start && "ELF note iterator starting at NULL"); + assert(!Err && "ELF note iterator starting with an Error"); + if (RemainingSize == 0) + return; // Support iterating an empty container. + + if (sizeof(*Nhdr) > RemainingSize) + *Err = makeOverflowError(); + else + Nhdr = reinterpret_cast *>(Start); + } + +public: + Elf_Note_Iterator_Impl &operator++() { + assert(Nhdr && "incremented ELF note end iterator"); + size_t NhdrSize = Nhdr->getSize(); + if (NhdrSize >= RemainingSize) { + if (NhdrSize > RemainingSize) + *Err = makeOverflowError(); + Nhdr = nullptr; + return *this; + } + RemainingSize -= NhdrSize; + if (sizeof(*Nhdr) > RemainingSize) { + *Err = makeOverflowError(); + Nhdr = nullptr; + } else { + const uint8_t *NhdrPos = reinterpret_cast(Nhdr); + NhdrPos += NhdrSize; + Nhdr = reinterpret_cast *>(NhdrPos); + } + return *this; + } + bool operator==(Elf_Note_Iterator_Impl Other) const { + return Nhdr == Other.Nhdr; + } + bool operator!=(Elf_Note_Iterator_Impl Other) const { + return !(*this == Other); + } + Elf_Note_Impl operator*() const { + assert(Nhdr && "dereferenced ELF note end iterator"); + return Elf_Note_Impl(*Nhdr, RemainingSize); + } +}; + // MIPS .reginfo section template struct Elf_Mips_RegInfo; Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -92,6 +92,7 @@ using Elf_Word = typename ELFT::Word; \ using Elf_Hash = typename ELFT::Hash; \ using Elf_GnuHash = typename ELFT::GnuHash; \ + using Elf_Note = typename ELFT::Note; \ using Elf_Sym_Range = typename ELFT::SymRange; \ using Elf_Versym = typename ELFT::Versym; \ using Elf_Verneed = typename ELFT::Verneed; \ @@ -3534,62 +3535,57 @@ const Elf_Ehdr *e = Obj->getHeader(); bool IsCore = e->e_type == ELF::ET_CORE; - auto process = [&](const typename ELFT::Off Offset, - const typename ELFT::Addr Size) { - if (Size <= 0) - return; - - const auto *P = static_cast(Obj->base() + Offset); - const auto *E = P + Size; - + auto PrintHeader = [&](const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { OS << "Displaying notes found at file offset " << format_hex(Offset, 10) << " with length " << format_hex(Size, 10) << ":\n" << " Owner Data size\tDescription\n"; + }; - while (P < E) { - const Elf_Word *Words = reinterpret_cast(&P[0]); - - uint32_t NameSize = Words[0]; - uint32_t DescriptorSize = Words[1]; - uint32_t Type = Words[2]; - - ArrayRef Descriptor(&Words[3 + (alignTo<4>(NameSize) / 4)], - alignTo<4>(DescriptorSize) / 4); - - StringRef Name; - if (NameSize) - Name = - StringRef(reinterpret_cast(&Words[3]), NameSize - 1); - - OS << " " << Name << std::string(22 - NameSize, ' ') - << format_hex(DescriptorSize, 10) << '\t'; - - if (Name == "GNU") { - OS << getGNUNoteTypeName(Type) << '\n'; - printGNUNote(OS, Type, Descriptor, DescriptorSize); - } else if (Name == "FreeBSD") { - OS << getFreeBSDNoteTypeName(Type) << '\n'; - } else if (Name == "AMD") { - OS << getAMDGPUNoteTypeName(Type) << '\n'; - printAMDGPUNote(OS, Type, Descriptor, DescriptorSize); - } else { - OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; - } - OS << '\n'; - - P = P + 3 * sizeof(Elf_Word) + alignTo<4>(NameSize) + - alignTo<4>(DescriptorSize); + auto ProcessNote = [&](const Elf_Note Note) { + StringRef Name = unwrapOrError(Note.getName()); + ArrayRef Descriptor = unwrapOrError(Note.getDesc()); + Elf_Word Type = Note.getType(); + + OS << " " << Name << std::string(22 - Name.size(), ' ') + << format_hex(Descriptor.size(), 10) << '\t'; + + if (Name == "GNU") { + OS << getGNUNoteTypeName(Type) << '\n'; + printGNUNote(OS, Type, Descriptor, Descriptor.size()); + } else if (Name == "FreeBSD") { + OS << getFreeBSDNoteTypeName(Type) << '\n'; + } else if (Name == "AMD") { + OS << getAMDGPUNoteTypeName(Type) << '\n'; + printAMDGPUNote(OS, Type, Descriptor, Descriptor.size()); + } else { + OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; } + OS << '\n'; }; if (IsCore) { - for (const auto &P : unwrapOrError(Obj->program_headers())) - if (P.p_type == PT_NOTE) - process(P.p_offset, P.p_filesz); + for (const auto &P : unwrapOrError(Obj->program_headers())) { + if (P.p_type != PT_NOTE) + continue; + PrintHeader(P.p_offset, P.p_filesz); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(P, Err)) + ProcessNote(Note); + if (Err) + error(std::move(Err)); + } } else { - for (const auto &S : unwrapOrError(Obj->sections())) - if (S.sh_type == SHT_NOTE) - process(S.sh_offset, S.sh_size); + for (const auto &S : unwrapOrError(Obj->sections())) { + if (S.sh_type != SHT_NOTE) + continue; + PrintHeader(S.sh_offset, S.sh_size); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(S, Err)) + ProcessNote(Note); + if (Err) + error(std::move(Err)); + } } }