[yaml2obj/obj2yaml] - Make RawContentSection::Content and RawContentSection::Size optional

This is a follow-up for D62809.

Content and Size fields should be optional as was discussed in comments
of the D62809's thread. With that, we can describe a specific string table and
symbol table sections in a more correct way and also show appropriate errors.

The patch adds lots of test cases where the behavior is described in details.

Differential revision: https://reviews.llvm.org/D62957

llvm-svn: 362931
diff --git a/llvm/tools/yaml2obj/yaml2elf.cpp b/llvm/tools/yaml2obj/yaml2elf.cpp
index 8a53a1b..a02ac8d 100644
--- a/llvm/tools/yaml2obj/yaml2elf.cpp
+++ b/llvm/tools/yaml2obj/yaml2elf.cpp
@@ -358,13 +358,49 @@
   return Symbols.size();
 }
 
+static uint64_t writeRawSectionData(raw_ostream &OS,
+                                    const ELFYAML::RawContentSection &RawSec) {
+  size_t ContentSize = 0;
+  if (RawSec.Content) {
+    RawSec.Content->writeAsBinary(OS);
+    ContentSize = RawSec.Content->binary_size();
+  }
+
+  if (!RawSec.Size)
+    return ContentSize;
+
+  OS.write_zeros(*RawSec.Size - ContentSize);
+  return *RawSec.Size;
+}
+
 template <class ELFT>
 void ELFState<ELFT>::initSymtabSectionHeader(Elf_Shdr &SHeader,
                                              SymtabType STType,
                                              ContiguousBlobAccumulator &CBA,
                                              ELFYAML::Section *YAMLSec) {
-  zero(SHeader);
+
   bool IsStatic = STType == SymtabType::Static;
+  const auto &Symbols = IsStatic ? Doc.Symbols : Doc.DynamicSymbols;
+
+  ELFYAML::RawContentSection *RawSec =
+      dyn_cast_or_null<ELFYAML::RawContentSection>(YAMLSec);
+  if (RawSec && !Symbols.empty() && (RawSec->Content || RawSec->Size)) {
+    if (RawSec->Content)
+      WithColor::error() << "Cannot specify both `Content` and " +
+                                (IsStatic ? Twine("`Symbols`")
+                                          : Twine("`DynamicSymbols`")) +
+                                " for symbol table section '"
+                         << RawSec->Name << "'.\n";
+    if (RawSec->Size)
+      WithColor::error() << "Cannot specify both `Size` and " +
+                                (IsStatic ? Twine("`Symbols`")
+                                          : Twine("`DynamicSymbols`")) +
+                                " for symbol table section '"
+                         << RawSec->Name << "'.\n";
+    exit(1);
+  }
+
+  zero(SHeader);
   SHeader.sh_name = DotShStrtab.getOffset(IsStatic ? ".symtab" : ".dynsym");
   SHeader.sh_type = IsStatic ? ELF::SHT_SYMTAB : ELF::SHT_DYNSYM;
 
@@ -382,13 +418,8 @@
   if (!IsStatic)
     SHeader.sh_flags |= ELF::SHF_ALLOC;
 
-  // One greater than symbol table index of the last local symbol.
-  const auto &Symbols = IsStatic ? Doc.Symbols : Doc.DynamicSymbols;
-
   // If the symbol table section is explicitly described in the YAML
   // then we should set the fields requested.
-  ELFYAML::RawContentSection *RawSec =
-      dyn_cast_or_null<ELFYAML::RawContentSection>(YAMLSec);
   SHeader.sh_info =
       RawSec ? (unsigned)RawSec->Info : findFirstNonGlobal(Symbols) + 1;
   SHeader.sh_entsize = (YAMLSec && YAMLSec->EntSize)
@@ -397,26 +428,24 @@
   SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 8;
   SHeader.sh_addr = YAMLSec ? (uint64_t)YAMLSec->Address : 0;
 
-  if (RawSec && RawSec->Content.binary_size()) {
-    RawSec->Content.writeAsBinary(
-        CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign));
-    SHeader.sh_size = RawSec->Size;
-  } else {
-    std::vector<Elf_Sym> Syms;
-    {
-      // Ensure STN_UNDEF is present
-      Elf_Sym Sym;
-      zero(Sym);
-      Syms.push_back(Sym);
-    }
-
-    addSymbols(Symbols, Syms, IsStatic ? DotStrtab : DotDynstr);
-
-    writeArrayData(
-        CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign),
-        makeArrayRef(Syms));
-    SHeader.sh_size = arrayDataSize(makeArrayRef(Syms));
+  auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
+  if (RawSec && (RawSec->Content || RawSec->Size)) {
+    assert(Symbols.empty());
+    SHeader.sh_size = writeRawSectionData(OS, *RawSec);
+    return;
   }
+
+  std::vector<Elf_Sym> Syms;
+  {
+    // Ensure STN_UNDEF is present
+    Elf_Sym Sym;
+    zero(Sym);
+    Syms.push_back(Sym);
+  }
+
+  addSymbols(Symbols, Syms, IsStatic ? DotStrtab : DotDynstr);
+  writeArrayData(OS, makeArrayRef(Syms));
+  SHeader.sh_size = arrayDataSize(makeArrayRef(Syms));
 }
 
 template <class ELFT>
@@ -431,13 +460,12 @@
 
   ELFYAML::RawContentSection *RawSec =
       dyn_cast_or_null<ELFYAML::RawContentSection>(YAMLSec);
-  if (RawSec && RawSec->Content.binary_size()) {
-    RawSec->Content.writeAsBinary(
-        CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign));
-    SHeader.sh_size = RawSec->Size;
+
+  auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
+  if (RawSec && (RawSec->Content || RawSec->Size)) {
+    SHeader.sh_size = writeRawSectionData(OS, *RawSec);
   } else {
-    STB.write(
-        CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign));
+    STB.write(OS);
     SHeader.sh_size = STB.getSize();
   }
 
@@ -569,12 +597,9 @@
 bool ELFState<ELFT>::writeSectionContent(
     Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section,
     ContiguousBlobAccumulator &CBA) {
-  assert(Section.Size >= Section.Content.binary_size() &&
-         "Section size and section content are inconsistent");
   raw_ostream &OS =
       CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
-  Section.Content.writeAsBinary(OS);
-  OS.write_zeros(Section.Size - Section.Content.binary_size());
+  SHeader.sh_size = writeRawSectionData(OS, Section);
 
   if (Section.EntSize)
     SHeader.sh_entsize = *Section.EntSize;
@@ -582,7 +607,6 @@
     SHeader.sh_entsize = sizeof(Elf_Relr);
   else
     SHeader.sh_entsize = 0;
-  SHeader.sh_size = Section.Size;
   SHeader.sh_info = Section.Info;
   return true;
 }