[yaml2obj][XCOFF] customize the string table

Summary: The patch adds support for yaml2obj customizing the string table.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D107421
This commit is contained in:
Esme-Yi 2021-09-13 09:24:38 +00:00
parent bbada9ff45
commit 909f3d7380
5 changed files with 436 additions and 18 deletions

View File

@ -59,10 +59,19 @@ struct Symbol {
uint8_t NumberOfAuxEntries;
};
struct StringTable {
Optional<uint32_t> ContentSize; // The total size of the string table.
Optional<uint32_t> Length; // The value of the length field for the first
// 4 bytes of the table.
Optional<std::vector<StringRef>> Strings;
Optional<yaml::BinaryRef> RawContent;
};
struct Object {
FileHeader Header;
std::vector<Section> Sections;
std::vector<Symbol> Symbols;
StringTable StrTbl;
Object();
};
} // namespace XCOFFYAML
@ -100,6 +109,10 @@ template <> struct MappingTraits<XCOFFYAML::Section> {
static void mapping(IO &IO, XCOFFYAML::Section &Sec);
};
template <> struct MappingTraits<XCOFFYAML::StringTable> {
static void mapping(IO &IO, XCOFFYAML::StringTable &Str);
};
template <> struct MappingTraits<XCOFFYAML::Object> {
static void mapping(IO &IO, XCOFFYAML::Object &Obj);
};

View File

@ -18,8 +18,9 @@
#include "llvm/ObjectYAML/ObjectYAML.h"
#include "llvm/ObjectYAML/yaml2obj.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@ -33,7 +34,7 @@ class XCOFFWriter {
public:
XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH)
: Obj(Obj), W(OS, support::big), ErrHandler(EH),
Strings(StringTableBuilder::XCOFF) {
StrTblBuilder(StringTableBuilder::XCOFF) {
Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
}
bool writeXCOFF();
@ -43,18 +44,20 @@ private:
bool initFileHeader(uint64_t CurrentOffset);
bool initSectionHeader(uint64_t &CurrentOffset);
bool initRelocations(uint64_t &CurrentOffset);
bool initStringTable();
bool assignAddressesAndIndices();
void writeFileHeader();
void writeSectionHeader();
bool writeSectionData();
bool writeRelocations();
bool writeSymbols();
void writeStringTable();
XCOFFYAML::Object &Obj;
bool Is64Bit = false;
support::endian::Writer W;
yaml::ErrorHandler ErrHandler;
StringTableBuilder Strings;
StringTableBuilder StrTblBuilder;
uint64_t StartOffset;
// Map the section name to its corrresponding section index.
DenseMap<StringRef, int16_t> SectionIndexMap = {
@ -140,20 +143,79 @@ bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
return initRelocations(CurrentOffset);
}
bool XCOFFWriter::initStringTable() {
if (Obj.StrTbl.RawContent) {
size_t RawSize = Obj.StrTbl.RawContent->binary_size();
if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
ErrHandler(
"can't specify Strings or Length when RawContent is specified");
return false;
}
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
") is less than the RawContent data size (" + Twine(RawSize) +
")");
return false;
}
return true;
}
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
return false;
}
// Build the string table.
StrTblBuilder.clear();
if (Obj.StrTbl.Strings) {
// All specified strings should be added to the string table.
for (StringRef StringEnt : *Obj.StrTbl.Strings)
StrTblBuilder.add(StringEnt);
size_t StrTblIdx = 0;
size_t NumOfStrings = Obj.StrTbl.Strings->size();
for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
if (StrTblIdx < NumOfStrings) {
// Overwrite the symbol name with the specified string.
YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
++StrTblIdx;
} else
// Names that are not overwritten are still stored in the string
// table.
StrTblBuilder.add(YamlSym.SymbolName);
}
}
} else {
for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (nameShouldBeInStringTable(YamlSym.SymbolName))
StrTblBuilder.add(YamlSym.SymbolName);
}
}
StrTblBuilder.finalize();
size_t StrTblSize = StrTblBuilder.getSize();
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
") is less than the size of the data that would otherwise be "
"written (" +
Twine(StrTblSize) + ")");
return false;
}
return true;
}
bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
// The default format of the object file is XCOFF32.
InitFileHdr.Magic = XCOFF::XCOFF32;
InitFileHdr.NumberOfSections = Obj.Sections.size();
InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols)
// Add the number of auxiliary symbols to the total number.
InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
if (nameShouldBeInStringTable(YamlSym.SymbolName))
Strings.add(YamlSym.SymbolName);
}
// Finalize the string table.
Strings.finalize();
// Calculate SymbolTableOffset for the file header.
if (InitFileHdr.NumberOfSymTableEntries) {
@ -171,7 +233,6 @@ bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
}
bool XCOFFWriter::assignAddressesAndIndices() {
Strings.clear();
uint64_t FileHdrSize =
Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
uint64_t SecHdrSize =
@ -182,8 +243,13 @@ bool XCOFFWriter::assignAddressesAndIndices() {
// Calculate section header info.
if (!initSectionHeader(CurrentOffset))
return false;
// Calculate file header info.
return initFileHeader(CurrentOffset);
if (!initFileHeader(CurrentOffset))
return false;
// Initialize the string table.
return initStringTable();
}
void XCOFFWriter::writeFileHeader() {
@ -309,13 +375,13 @@ bool XCOFFWriter::writeSymbols() {
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (Is64Bit) {
W.write<uint64_t>(YamlSym.Value);
W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName));
W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
} else {
if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
// For XCOFF32: A value of 0 indicates that the symbol name is in the
// string table.
W.write<int32_t>(0);
W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName));
W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
} else {
writeName(YamlSym.SymbolName, W);
}
@ -340,6 +406,48 @@ bool XCOFFWriter::writeSymbols() {
return true;
}
void XCOFFWriter::writeStringTable() {
if (Obj.StrTbl.RawContent) {
Obj.StrTbl.RawContent->writeAsBinary(W.OS);
if (Obj.StrTbl.ContentSize) {
assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
"Specified ContentSize is less than the RawContent size.");
W.OS.write_zeros(*Obj.StrTbl.ContentSize -
Obj.StrTbl.RawContent->binary_size());
}
return;
}
size_t StrTblBuilderSize = StrTblBuilder.getSize();
// If neither Length nor ContentSize is specified, write the StrTblBuilder
// directly, which contains the auto-generated Length value.
if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
if (StrTblBuilderSize <= 4)
return;
StrTblBuilder.write(W.OS);
return;
}
// Serialize the string table's content to a temporary buffer.
std::unique_ptr<WritableMemoryBuffer> Buf =
WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize);
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
StrTblBuilder.write(Ptr);
// Replace the first 4 bytes, which contain the auto-generated Length value,
// with the specified value.
memset(Ptr, 0, 4);
support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length
: *Obj.StrTbl.ContentSize);
// Copy the buffer content to the actual output stream.
W.OS.write(Buf->getBufferStart(), Buf->getBufferSize());
// Add zeros as padding after strings.
if (Obj.StrTbl.ContentSize) {
assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
"Specified ContentSize is less than the StringTableBuilder size.");
W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize);
}
}
bool XCOFFWriter::writeXCOFF() {
if (!assignAddressesAndIndices())
return false;
@ -354,9 +462,7 @@ bool XCOFFWriter::writeXCOFF() {
}
if (!Obj.Symbols.empty() && !writeSymbols())
return false;
// Write the string table.
if (Strings.getSize() > 4)
Strings.write(W.OS);
writeStringTable();
return true;
}

View File

@ -143,7 +143,7 @@ void MappingTraits<XCOFFYAML::Section>::mapping(IO &IO,
}
void MappingTraits<XCOFFYAML::Symbol>::mapping(IO &IO, XCOFFYAML::Symbol &S) {
IO.mapRequired("Name", S.SymbolName);
IO.mapOptional("Name", S.SymbolName);
IO.mapOptional("Value", S.Value);
IO.mapOptional("Section", S.SectionName);
IO.mapOptional("Type", S.Type);
@ -151,11 +151,19 @@ void MappingTraits<XCOFFYAML::Symbol>::mapping(IO &IO, XCOFFYAML::Symbol &S) {
IO.mapOptional("NumberOfAuxEntries", S.NumberOfAuxEntries);
}
void MappingTraits<XCOFFYAML::StringTable>::mapping(IO &IO, XCOFFYAML::StringTable &Str) {
IO.mapOptional("ContentSize", Str.ContentSize);
IO.mapOptional("Length", Str.Length);
IO.mapOptional("Strings", Str.Strings);
IO.mapOptional("RawContent", Str.RawContent);
}
void MappingTraits<XCOFFYAML::Object>::mapping(IO &IO, XCOFFYAML::Object &Obj) {
IO.mapTag("!XCOFF", true);
IO.mapRequired("FileHeader", Obj.Header);
IO.mapOptional("Sections", Obj.Sections);
IO.mapOptional("Symbols", Obj.Symbols);
IO.mapOptional("StringTable", Obj.StrTbl);
}
} // namespace yaml

View File

@ -77,4 +77,4 @@
# CHECK-NEXT: Type: 0x0
# CHECK-NEXT: StorageClass: C_HIDEXT
# CHECK-NEXT: NumberOfAuxEntries: 1
# CHECK-NEXT: ...
## TODO: Dump the string table.

View File

@ -0,0 +1,291 @@
## Check that yaml2obj is able to customize the string table.
## `ContentSize`, `Length`, `Strings` and/or `RawContent` can be specified in
## YAML. Here we test the behaviour in various cases.
## Case 1: yaml2obj writes the default content (i.e. long symbol names in
## XCOFF32 or any symbol names in XCOFF64) when no StringTable field
## is specified.
# RUN: yaml2obj --docnum=1 %s -DSYMNAME='nameInStrTbl' -o %t1
# RUN: llvm-readobj %t1 --string-table | FileCheck %s --check-prefix=CASE1
# CASE1: StringTable {
# CASE1-NEXT: Length: 17
# CASE1-NEXT: [ 4] nameInStrTbl
# CASE1-NEXT: }
--- !XCOFF
FileHeader:
MagicNumber: 0x1DF
Symbols:
- Name: [[SYMNAME=<none>]]
- Name: [[SYMNAME2=<none>]]
StringTable:
ContentSize: [[CONTENTSIZE=<none>]]
Length: [[LENGTHVALUE=<none>]]
RawContent: [[RAWCONTENT=<none>]]
## We can specify `ContentSize` only when the value is equal to or greater
## than the content size. For greater cases, zeros are added as padding.
## Cases 2-6 are trying to check this.
## Case 2: produce a string table with a specified `ContentSize`. In this case,
## there is no default content and the content is filled with zeroes.
# RUN: yaml2obj --docnum=1 %s -DCONTENTSIZE=20 -o %t2
# RUN: llvm-readobj %t2 -s --string-table | FileCheck %s --check-prefix=CASE2
# CASE2: StringTable {
# CASE2-NEXT: Length: 20
# CASE2-NEXT: }
## Case 3: if the value of `ContentSize` is greater than the content size,
## yaml2obj adds zeros as padding after the default content.
# RUN: yaml2obj --docnum=1 %s -DSYMNAME='nameInStrTbl' -DCONTENTSIZE=20 -o %t3
# RUN: llvm-readobj %t3 --string-table | FileCheck %s --check-prefix=CASE3
# CASE3: StringTable {
# CASE3-NEXT: Length: 20
# CASE3-NEXT: [ 4] nameInStrTbl
# CASE3-NEXT: }
## Case 4: the value of `ContentSize` matches the actual content size.
# RUN: yaml2obj --docnum=1 %s -DSYMNAME='nameInStrTbl' -DCONTENTSIZE=17 -o %t4
# RUN: llvm-readobj %t4 --string-table | FileCheck %s --check-prefix=CASE4
# CASE4: StringTable {
# CASE4-NEXT: Length: 17
# CASE4-NEXT: [ 4] nameInStrTbl
# CASE4-NEXT: }
## Case 5: an error is reported when the value of "ContentSize" is less than
## the content size.
# RUN: not yaml2obj --docnum=1 %s -DSYMNAME='nameInStrTbl' -DCONTENTSIZE=16 \
# RUN: -o %t5 2>&1 | FileCheck %s --check-prefix=CASE5
# CASE5: error: specified ContentSize (16) is less than the size of the data that would otherwise be written (17)
## Case 6: an error is reported when `ContentSize` is less than 4 without
## `RawContent`.
# RUN: not yaml2obj --docnum=1 %s -DCONTENTSIZE=3 -o %t6 2>&1 \
# RUN: | FileCheck %s --check-prefix=CASE6
# CASE6: error: ContentSize shouldn't be less than 4 without RawContent
## We can specify `Strings` for a string table. Default contents (ie. symbol
## names in string table) will be overwritten by specified values. Cases 7-9
## are trying to check this function.
## Case 7: produce a string table with specified `Strings` directly. In this
## case, there is no default content.
# RUN: yaml2obj --docnum=2 %s -o %t7
# RUN: llvm-readobj %t7 --string-table | FileCheck %s --check-prefix=CASE7
# CASE7: StringTable {
# CASE7-NEXT: Length: 8
# CASE7-NEXT: [ 4] b
# CASE7-NEXT: [ 6] a
# CASE7-NEXT: }
--- !XCOFF
FileHeader:
MagicNumber: 0x1DF
Symbols:
- Name: [[SYMNAME=<none>]]
- Name: [[SYMNAME2=<none>]]
- Name: [[SYMNAME3=<none>]]
StringTable:
ContentSize: [[CONTENTSIZE=<none>]]
Length: [[LENGTHVALUE=<none>]]
RawContent: [[RAWCONTENT=<none>]]
Strings:
- a
- b
## Case 8: if the number of `Strings` is greater than or equal to the number
## of default strings, all default strings will be overwritten by
## specified ones.
# RUN: yaml2obj --docnum=2 %s -DSYMNAME='nameInStrTbl' -o %t8
# RUN: llvm-readobj %t8 -s --string-table | FileCheck %s --check-prefix=CASE8
# CASE8: Symbols [
# CASE8-NEXT: Symbol {
# CASE8-NEXT: Index: 0
# CASE8-NEXT: Name: a
# CASE8-NEXT: Value: 0x0
# CASE8-NEXT: Section: N_UNDEF
# CASE8-NEXT: Type: 0x0
# CASE8-NEXT: StorageClass: C_NULL (0x0)
# CASE8-NEXT: NumberOfAuxEntries: 0
# CASE8-NEXT: }
# CASE8-NEXT: Symbol {
# CASE8-NEXT: Index: 1
# CASE8-NEXT: Name: <none>
# CASE8-NEXT: Value: 0x0
# CASE8-NEXT: Section: N_UNDEF
# CASE8-NEXT: Type: 0x0
# CASE8-NEXT: StorageClass: C_NULL (0x0)
# CASE8-NEXT: NumberOfAuxEntries: 0
# CASE8-NEXT: }
# CASE8-NEXT: Symbol {
# CASE8-NEXT: Index: 2
# CASE8-NEXT: Name: <none>
# CASE8-NEXT: Value: 0x0
# CASE8-NEXT: Section: N_UNDEF
# CASE8-NEXT: Type: 0x0
# CASE8-NEXT: StorageClass: C_NULL (0x0)
# CASE8-NEXT: NumberOfAuxEntries: 0
# CASE8-NEXT: }
# CASE8-NEXT: ]
# CASE8-NEXT: StringTable {
# CASE8-NEXT: Length: 8
# CASE8-NEXT: [ 4] b
# CASE8-NEXT: [ 6] a
# CASE8-NEXT: }
## Case 9: if the number of `Strings` is less than the number of default
## strings, default strings will be partially overwritten. The
## remaining strings will still be stored after the specified strings
## in the string table.
# RUN: yaml2obj --docnum=2 %s -DSYMNAME='nameInStrTbl' \
# RUN: -DSYMNAME2='name2InStrTbl' -DSYMNAME3='name3InStrTbl' -o %t9
# RUN: llvm-readobj %t9 -s --string-table | FileCheck %s --check-prefix=CASE9
# CASE9: Symbols [
# CASE9-NEXT: Symbol {
# CASE9-NEXT: Index: 0
# CASE9-NEXT: Name: a
# CASE9-NEXT: Value: 0x0
# CASE9-NEXT: Section: N_UNDEF
# CASE9-NEXT: Type: 0x0
# CASE9-NEXT: StorageClass: C_NULL (0x0)
# CASE9-NEXT: NumberOfAuxEntries: 0
# CASE9-NEXT: }
# CASE9-NEXT: Symbol {
# CASE9-NEXT: Index: 1
# CASE9-NEXT: Name: b
# CASE9-NEXT: Value: 0x0
# CASE9-NEXT: Section: N_UNDEF
# CASE9-NEXT: Type: 0x0
# CASE9-NEXT: StorageClass: C_NULL (0x0)
# CASE9-NEXT: NumberOfAuxEntries: 0
# CASE9-NEXT: }
# CASE9-NEXT: Symbol {
# CASE9-NEXT: Index: 2
# CASE9-NEXT: Name: name3InStrTbl
# CASE9-NEXT: Value: 0x0
# CASE9-NEXT: Section: N_UNDEF
# CASE9-NEXT: Type: 0x0
# CASE9-NEXT: StorageClass: C_NULL (0x0)
# CASE9-NEXT: NumberOfAuxEntries: 0
# CASE9-NEXT: }
# CASE9-NEXT: ]
# CASE9-NEXT: StringTable {
# CASE9-NEXT: Length: 22
# CASE9-NEXT: [ 4] name3InStrTbl
# CASE9-NEXT: [ 12] b
# CASE9-NEXT: [ 14] a
# CASE9-NEXT: }
## We can specify both `ContentSize` and `Strings` when `ContentSize` is equal
## to or greater than the content size. Cases 10-12 are trying to check this.
## Case 10: produce a string table with specified `ContentSize` and `Strings`
## when the value is greater than the size of specified strings.
## In this case, there is no default content.
# RUN: yaml2obj --docnum=2 %s -DCONTENTSIZE=20 -o %t10
# RUN: llvm-readobj %t10 --string-table | FileCheck %s --check-prefix=CASE10
# CASE10: StringTable {
# CASE10-NEXT: Length: 20
# CASE10-NEXT: [ 4] b
# CASE10-NEXT: [ 6] a
# CASE10-NEXT: }
## Case 11: for a string table with default contents, we can specify
## `ContentSize` and `Strings` when the `ContentSize` is greater
## than the data that would otherwise be written.
# RUN: yaml2obj --docnum=2 %s -DCONTENTSIZE=30 -DSYMNAME='nameInStrTbl' \
# RUN: -DSYMNAME2='name2InStrTbl' -DSYMNAME3='name3InStrTbl' -o %t11
# RUN: llvm-readobj %t11 --string-table | FileCheck %s --check-prefix=CASE11
# CASE11: StringTable {
# CASE11-NEXT: Length: 30
# CASE11-NEXT: [ 4] name3InStrTbl
# CASE11-NEXT: [ 12] b
# CASE11-NEXT: [ 14] a
# CASE11-NEXT: }
## Case 12: an error is reported when the value of `ContentSize` is less
## than the final content size. None of `ContentSize`, `Strings` or
## default contents is empty in this case.
# RUN: not yaml2obj --docnum=2 %s -DCONTENTSIZE=10 -DSYMNAME='nameInStrTbl' \
# RUN: -DSYMNAME2='name2InStrTbl' -DSYMNAME3='name3InStrTbl' -o %t12 2>&1 \
# RUN: | FileCheck %s --check-prefix=CASE12
# CASE12: error: specified ContentSize (10) is less than the size of the data that would otherwise be written (22)
## We can use `RawContent` to generate a string table. Cases 13-16 are trying to
## check the `RawContent`.
## Case 13: if `RawContent` is specified and no `ContentSize` is specified.
## Write the `RawContent` data.
# RUN: yaml2obj --docnum=1 %s -DRAWCONTENT="000000090062006300" -o %t13
# RUN: llvm-readobj %t13 --string-table | FileCheck %s --check-prefix=CASE13
# CASE13: StringTable {
# CASE13-NEXT: Length: 9
# CASE13-NEXT: [ 5] b
# CASE13-NEXT: [ 7] c
# CASE13-NEXT: }
## Case 14: if `RawContent` is specified and `ContentSize` matches the size
## of the `RawContent` data. Write the `RawContent` data.
# RUN: yaml2obj --docnum=1 %s -DRAWCONTENT="000000090062006300" -DCONTENTSIZE=9 -o %t14
# RUN: llvm-readobj %t14 --string-table | FileCheck %s --check-prefix=CASE14
# CASE14: StringTable {
# CASE14-NEXT: Length: 9
# CASE14-NEXT: [ 5] b
# CASE14-NEXT: [ 7] c
# CASE14-NEXT: }
## Case 15: an error is reported when `ContentSize` is less than the `RawContent`
## data size.
# RUN: not yaml2obj --docnum=1 %s -DRAWCONTENT="000000090062006300" -DCONTENTSIZE=6 \
# RUN: -o %t15 2>&1 | FileCheck %s --check-prefix=CASE15
# CASE15: error: specified ContentSize (6) is less than the RawContent data size (9)
## Case 16: if `RawContent` is specified and `ContentSize` is greater than the
## `RawContent` data size, pad the RawContent with trailing zeroes.
# RUN: yaml2obj --docnum=1 %s -DRAWCONTENT="000000090062006300" -DCONTENTSIZE=11 -o %t16
# RUN: llvm-readobj %t16 --string-table | FileCheck %s --check-prefix=CASE16
# CASE16: StringTable {
# CASE16-NEXT: Length: 9
# CASE16-NEXT: [ 5] b
# CASE16-NEXT: [ 7] c
# CASE16-NEXT: }
## We can specify `Length`. Use the value of the `Length` field for the first
## 4 bytes of the table. The value may not make sense for the data that is
## being written. Cases 17-20 are trying to check this.
## Case 17: report an error if the `Length` is specified as well as `RawContent`.
# RUN: not yaml2obj --docnum=1 %s -DRAWCONTENT="0062006300" -DLENGTHVALUE=9 \
# RUN: -o %t17 2>&1 | FileCheck %s --check-prefix=CASE17
# CASE17: error: can't specify Strings or Length when RawContent is specified
## Case 18: report an error if both `RawContent` and `Strings` are specified.
# RUN: not yaml2obj --docnum=2 %s -DRAWCONTENT="0062006300" -o %t18 2>&1 \
# RUN: | FileCheck %s --check-prefix=CASE18
# CASE18: error: can't specify Strings or Length when RawContent is specified
## Case 19: use the value of the `Length` field for the first 4 bytes of the
## table. We dump the string table from the offset of 0x38.
# RUN: yaml2obj --docnum=1 %s -DSYMNAME='nameInStrTbl' -DLENGTHVALUE=20 -o %t19
# RUN: od -A n -t x1 -v -j 0x38 %t19 | FileCheck %s --check-prefix=CASE19
# CASE19: 00 00 00 14 6e 61 6d 65 49 6e 53 74 72 54 62 6c
# CASE19-NEXT: 00