[llvm] [yaml2obj] Allow the creation of note segments without sections (PR #123704)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 20 23:38:30 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-objectyaml

Author: Igor Kudrin (igorkudrin)

<details>
<summary>Changes</summary>

This adds a new section type, `NoteChunk`, which, provides its content to a segment, but does not create an ELF section. This makes it possible to generate ELF core files, where note segments do not have associated sections.

---
Full diff: https://github.com/llvm/llvm-project/pull/123704.diff


4 Files Affected:

- (modified) llvm/include/llvm/ObjectYAML/ELFYAML.h (+15) 
- (modified) llvm/lib/ObjectYAML/ELFEmitter.cpp (+55-20) 
- (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+18) 
- (added) llvm/test/tools/yaml2obj/ELF/note-segment.yaml (+127) 


``````````diff
diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h
index dfdfa055d65fa6..104c17bf34b9e4 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -234,6 +234,7 @@ struct Chunk {
     SpecialChunksStart,
     Fill = SpecialChunksStart,
     SectionHeaderTable,
+    NoteChunk,
   };
 
   ChunkKind Kind;
@@ -340,6 +341,20 @@ struct SectionHeaderTable : Chunk {
   static constexpr StringRef TypeStr = "SectionHeaderTable";
 };
 
+struct NoteChunk : Chunk {
+  llvm::yaml::Hex64 NoteAlign;
+  std::vector<ELFYAML::NoteEntry> Notes;
+  uint64_t Size = 0;
+
+  NoteChunk() : Chunk(ChunkKind::NoteChunk, /*Implicit=*/false) {}
+
+  static bool classof(const Chunk *S) {
+    return S->Kind == ChunkKind::NoteChunk;
+  }
+
+  static constexpr StringRef TypeStr = "NoteChunk";
+};
+
 struct BBAddrMapSection : Section {
   std::optional<std::vector<BBAddrMapEntry>> Entries;
   std::optional<std::vector<PGOAnalysisMapEntry>> PGOAnalyses;
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index cc41bbe6bbde24..44830571a76e5c 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -310,6 +310,11 @@ template <class ELFT> class ELFState {
                            ContiguousBlobAccumulator &CBA);
 
   void writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA);
+  void writeNoteChunk(ELFYAML::NoteChunk &NoteChunk,
+                      ContiguousBlobAccumulator &CBA);
+
+  uint64_t writeNotes(StringRef SecName, ArrayRef<ELFYAML::NoteEntry> Notes,
+                      uint64_t Align, ContiguousBlobAccumulator &CBA);
 
   ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH);
 
@@ -792,6 +797,13 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
       continue;
     }
 
+    if (ELFYAML::NoteChunk *S = dyn_cast<ELFYAML::NoteChunk>(D.get())) {
+      S->Offset = alignToOffset(CBA, S->NoteAlign, S->Offset);
+      writeNoteChunk(*S, CBA);
+      LocationCounter += S->Size;
+      continue;
+    }
+
     ELFYAML::Section *Sec = cast<ELFYAML::Section>(D.get());
     bool IsFirstUndefSection = Sec == Doc.getSections().front();
     if (IsFirstUndefSection && Sec->IsImplicit)
@@ -1157,6 +1169,11 @@ ELFState<ELFT>::getPhdrFragments(const ELFYAML::ProgramHeader &Phdr,
                      /*ShAddrAlign=*/1});
       continue;
     }
+    if (const auto *F = dyn_cast<ELFYAML::NoteChunk>(C)) {
+      Ret.push_back(
+          {*F->Offset, F->Size, llvm::ELF::SHT_PROGBITS, F->NoteAlign});
+      continue;
+    }
 
     const ELFYAML::Section *S = cast<ELFYAML::Section>(C);
     const Elf_Shdr &H = SHeaders[SN2I.get(S->Name)];
@@ -1229,7 +1246,7 @@ bool llvm::ELFYAML::shouldAllocateFileSpace(
     auto It = llvm::find_if(
         PH.Chunks, [&](ELFYAML::Chunk *C) { return C->Name == S.Name; });
     if (std::any_of(It, PH.Chunks.end(), [](ELFYAML::Chunk *C) {
-          return (isa<ELFYAML::Fill>(C) ||
+          return (!isa<ELFYAML::Section>(C) ||
                   cast<ELFYAML::Section>(C)->Type != ELF::SHT_NOBITS);
         }))
       return true;
@@ -1793,36 +1810,35 @@ void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
 }
 
 template <class ELFT>
-void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
-                                         const ELFYAML::NoteSection &Section,
-                                         ContiguousBlobAccumulator &CBA) {
-  if (!Section.Notes || Section.Notes->empty())
-    return;
+uint64_t ELFState<ELFT>::writeNotes(StringRef SecName,
+                                    ArrayRef<ELFYAML::NoteEntry> Notes,
+                                    uint64_t Align,
+                                    ContiguousBlobAccumulator &CBA) {
+  if (Notes.empty())
+    return 0;
 
-  unsigned Align;
-  switch (Section.AddressAlign) {
+  switch (Align) {
   case 0:
-  case 4:
     Align = 4;
-    break;
+    [[fallthrough]];
+  case 4:
   case 8:
-    Align = 8;
     break;
   default:
-    reportError(Section.Name + ": invalid alignment for a note section: 0x" +
-                Twine::utohexstr(Section.AddressAlign));
-    return;
+    reportError(SecName + ": invalid alignment for a note section: 0x" +
+                Twine::utohexstr(Align));
+    return 0;
   }
 
   if (CBA.getOffset() != alignTo(CBA.getOffset(), Align)) {
-    reportError(Section.Name + ": invalid offset of a note section: 0x" +
+    reportError(SecName + ": invalid offset of a note section: 0x" +
                 Twine::utohexstr(CBA.getOffset()) + ", should be aligned to " +
                 Twine(Align));
-    return;
+    return 0;
   }
 
   uint64_t Offset = CBA.tell();
-  for (const ELFYAML::NoteEntry &NE : *Section.Notes) {
+  for (const ELFYAML::NoteEntry &NE : Notes) {
     // Write name size.
     if (NE.Name.empty())
       CBA.write<uint32_t>(0, ELFT::Endianness);
@@ -1838,22 +1854,41 @@ void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
     // Write type.
     CBA.write<uint32_t>(NE.Type, ELFT::Endianness);
 
-    // Write name, null terminator and padding.
+    // Write name and the null terminator.
     if (!NE.Name.empty()) {
       CBA.write(NE.Name.data(), NE.Name.size());
       CBA.write('\0');
     }
 
-    // Write description and padding.
+    // Write description, padding to the alignment.
     if (NE.Desc.binary_size() != 0) {
       CBA.padToAlignment(Align);
       CBA.writeAsBinary(NE.Desc);
     }
 
+    // Write the padding for the next entry.
     CBA.padToAlignment(Align);
   }
 
-  SHeader.sh_size = CBA.tell() - Offset;
+  return CBA.tell() - Offset;
+}
+
+template <class ELFT>
+void ELFState<ELFT>::writeNoteChunk(ELFYAML::NoteChunk &NoteChunk,
+                                    ContiguousBlobAccumulator &CBA) {
+  NoteChunk.Size =
+      writeNotes(NoteChunk.Name, NoteChunk.Notes, NoteChunk.NoteAlign, CBA);
+}
+
+template <class ELFT>
+void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
+                                         const ELFYAML::NoteSection &Section,
+                                         ContiguousBlobAccumulator &CBA) {
+  if (!Section.Notes)
+    return;
+
+  SHeader.sh_size =
+      writeNotes(Section.Name, *Section.Notes, Section.AddressAlign, CBA);
 }
 
 template <class ELFT>
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 7e94d01a971534..550aa3fed6f043 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -1525,6 +1525,13 @@ static void sectionHeaderTableMapping(IO &IO,
   IO.mapOptional("NoHeaders", SHT.NoHeaders);
 }
 
+static void noteChunkMapping(IO &IO, ELFYAML::NoteChunk &Chunk) {
+  IO.mapOptional("Name", Chunk.Name, StringRef());
+  IO.mapOptional("Offset", Chunk.Offset);
+  IO.mapOptional("NoteAlign", Chunk.NoteAlign, Hex64(4));
+  IO.mapRequired("Notes", Chunk.Notes);
+}
+
 static void sectionMapping(IO &IO, ELFYAML::LinkerOptionsSection &Section) {
   commonSectionMapping(IO, Section);
   IO.mapOptional("Options", Section.Options);
@@ -1621,6 +1628,14 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
     return;
   }
 
+  if (TypeStr == ELFYAML::NoteChunk::TypeStr) {
+    assert(!IO.outputting()); // We don't dump note chunks currently.
+    Section.reset(new ELFYAML::NoteChunk());
+    noteChunkMapping(IO, *cast<ELFYAML::NoteChunk>(Section.get()));
+    return;
+  }
+
+
   const auto &Obj = *static_cast<ELFYAML::Object *>(IO.getContext());
   if (Obj.getMachine() == ELF::EM_MIPS && Type == ELF::SHT_MIPS_ABIFLAGS) {
     if (!IO.outputting())
@@ -1758,6 +1773,9 @@ std::string MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::validate(
     return "";
   }
 
+  if (isa<ELFYAML::NoteChunk>(C.get()))
+    return "";
+
   const ELFYAML::Section &Sec = *cast<ELFYAML::Section>(C.get());
   if (Sec.Size && Sec.Content &&
       (uint64_t)(*Sec.Size) < Sec.Content->binary_size())
diff --git a/llvm/test/tools/yaml2obj/ELF/note-segment.yaml b/llvm/test/tools/yaml2obj/ELF/note-segment.yaml
new file mode 100644
index 00000000000000..777c50468fb4f1
--- /dev/null
+++ b/llvm/test/tools/yaml2obj/ELF/note-segment.yaml
@@ -0,0 +1,127 @@
+## Check that NoteChunks can generate note content for a segment without
+## creating note sections.
+
+# RUN: yaml2obj --docnum=1 -D ENDIANNESS=LSB -D ALIGN=4 %s -o %t1.lsb4
+# RUN: llvm-readelf --segments --sections --notes %t1.lsb4 | \
+# RUN:   FileCheck %s --check-prefix=TEST1 -D#SIZE0=24 -D#SIZE1=20 -D#SIZE2=20 -D#ALIGN=4
+
+# RUN: yaml2obj --docnum=1 -D ENDIANNESS=MSB -D ALIGN=8 %s -o %t1.msb8
+# RUN: llvm-readelf --segments --sections --notes %t1.msb8 | \
+# RUN:   FileCheck %s --check-prefix=TEST1 -D#SIZE0=32 -D#SIZE1=24 -D#SIZE2=24 -D#ALIGN=8
+
+# TEST1:      Section Headers:
+# TEST1-NEXT:  [Nr] Name      Type Address  Off              Size
+# TEST1-NEXT:  [ 0]           NULL
+# TEST1-NEXT:  [ 1] .note1    NOTE [[#%x,]] [[#%x,OFFSET1:]] [[#%.6x,SIZE1]]
+# TEST1-NEXT:  [ 2] .strtab
+# TEST1-NEXT:  [ 3] .shstrtab
+#
+# TEST1:      Program Headers:
+# TEST1-NEXT:  Type Offset                   VirtAddr  PhysAddr  FileSiz                      MemSiz                       Flg Align
+# TEST1-NEXT:  NOTE [[#%#.6x,OFFSET1-SIZE0]] [[#%#x,]] [[#%#x,]] [[#%#.6x,SIZE0+SIZE1+SIZE2]] [[#%#.6x,SIZE0+SIZE1+SIZE2]]     [[#%#x,ALIGN]]
+#
+# TEST1:      Section to Segment mapping:
+# TEST1-NEXT:  Segment Sections...
+# TEST1-NEXT:   00     .note1{{[ ]*$}}
+#
+# TEST1:      Displaying notes found at file offset [[#%#.8x,== OFFSET1-SIZE0]] with length [[#%#.8x,SIZE0+SIZE1+SIZE2]]:
+# TEST1-NEXT:   Owner                Data size 	Description
+# TEST1-NEXT:   ABCD                 0x00000002	NT_PRSTATUS
+# TEST1-NEXT:    description data: 01 02
+# TEST1-NEXT:   EFG                  0x00000002	NT_FPREGSET
+# TEST1-NEXT:    description data: 03 04
+# TEST1-NEXT:   XYZ                  0x00000002	NT_PRPSINFO
+# TEST1-NEXT:    description data: 05 06
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2[[ENDIANNESS]]
+  Type:  ET_CORE
+ProgramHeaders:
+  - Type:      PT_NOTE
+    FirstSec:  .note0
+    LastSec:   .note2
+Sections:
+  - Name:      .note0
+    Type:      NoteChunk
+    NoteAlign: [[ALIGN]]
+    Notes:
+      - Name: ABCD
+        Type: 0x1
+        Desc: 0102
+  - Name:         .note1
+    Type:         SHT_NOTE
+    AddressAlign: [[ALIGN]]
+    Notes:
+      - Name: EFG
+        Type: 0x2
+        Desc: 0304
+  - Name:      .note2
+    Type:      NoteChunk
+    NoteAlign: [[ALIGN]]
+    Notes:
+      - Name: XYZ
+        Type: 0x3
+        Desc: 0506
+
+## Check the default value for 'NoteAlign'
+
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: llvm-readelf --segments %t2 | FileCheck %s --check-prefix=TEST2
+
+# TEST2:      Program Headers:
+# TEST2-NEXT:  Type {{.*}} Align{{$}}
+# TEST2-NEXT:  NOTE {{.*}} 0x4{{$}}
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_CORE
+ProgramHeaders:
+  - Type:      PT_NOTE
+    FirstSec:  .note0
+    LastSec:   .note0
+Sections:
+  - Name:      .note0
+    Type:      NoteChunk
+    Notes:
+      - Name: ABCD
+        Type: 0x1
+        Desc: 0102
+
+## Check that an incorrect alignment is reported.
+
+# RUN: not yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=TEST3
+# TEST3: error: .note0: invalid alignment for a note section: 0x1
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name:      .note0
+    Type:      NoteChunk
+    NoteAlign: 1
+    Notes:
+      - Type: 0x1
+
+## Check that an incorrect offset for generating notes is reported.
+
+# RUN: not yaml2obj --docnum=4 %s 2>&1 | FileCheck %s --check-prefix=TEST4
+# TEST4: error: .note: invalid offset of a note section: 0x504, should be aligned to 8
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS32
+  Data:  ELFDATA2LSB
+  Type:  ET_EXEC
+Sections:
+  - Name:      .note
+    Type:      NoteChunk
+    Offset:    0x504
+    NoteAlign: 8
+    Notes:
+      - Type: 0x1

``````````

</details>


https://github.com/llvm/llvm-project/pull/123704


More information about the llvm-commits mailing list