|  | //===- yaml2obj - Convert YAML to a binary object file --------------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This program takes a YAML description of an object file and outputs the | 
|  | // binary equivalent. | 
|  | // | 
|  | // This is used for writing tests that require binary files. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/ObjectYAML/yaml2obj.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "llvm/ObjectYAML/ObjectYAML.h" | 
|  | #include "llvm/Support/CommandLine.h" | 
|  | #include "llvm/Support/FileSystem.h" | 
|  | #include "llvm/Support/InitLLVM.h" | 
|  | #include "llvm/Support/MemoryBuffer.h" | 
|  | #include "llvm/Support/ToolOutputFile.h" | 
|  | #include "llvm/Support/WithColor.h" | 
|  | #include "llvm/Support/YAMLTraits.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include <optional> | 
|  | #include <system_error> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  | cl::OptionCategory Cat("yaml2obj Options"); | 
|  |  | 
|  | cl::opt<std::string> Input(cl::Positional, cl::desc("<input file>"), | 
|  | cl::init("-"), cl::cat(Cat)); | 
|  |  | 
|  | static cl::list<std::string> | 
|  | D("D", cl::Prefix, | 
|  | cl::desc("Defined the specified macros to their specified " | 
|  | "definition. The syntax is <macro>=<definition>"), | 
|  | cl::cat(Cat)); | 
|  |  | 
|  | cl::opt<bool> PreprocessOnly("E", cl::desc("Just print the preprocessed file"), | 
|  | cl::cat(Cat)); | 
|  |  | 
|  | cl::opt<unsigned> | 
|  | DocNum("docnum", cl::init(1), | 
|  | cl::desc("Read specified document from input (default = 1)"), | 
|  | cl::cat(Cat)); | 
|  |  | 
|  | static cl::opt<uint64_t> MaxSize( | 
|  | "max-size", cl::init(10 * 1024 * 1024), | 
|  | cl::desc( | 
|  | "Sets the maximum allowed output size (0 means no limit) [ELF only]"), | 
|  | cl::cat(Cat)); | 
|  |  | 
|  | cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), | 
|  | cl::value_desc("filename"), cl::init("-"), | 
|  | cl::Prefix, cl::cat(Cat)); | 
|  | } // namespace | 
|  |  | 
|  | static std::optional<std::string> preprocess(StringRef Buf, | 
|  | yaml::ErrorHandler ErrHandler) { | 
|  | DenseMap<StringRef, StringRef> Defines; | 
|  | for (StringRef Define : D) { | 
|  | StringRef Macro, Definition; | 
|  | std::tie(Macro, Definition) = Define.split('='); | 
|  | if (!Define.count('=') || Macro.empty()) { | 
|  | ErrHandler("invalid syntax for -D: " + Define); | 
|  | return {}; | 
|  | } | 
|  | if (!Defines.try_emplace(Macro, Definition).second) { | 
|  | ErrHandler("'" + Macro + "'" + " redefined"); | 
|  | return {}; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string Preprocessed; | 
|  | while (!Buf.empty()) { | 
|  | if (Buf.starts_with("[[")) { | 
|  | size_t I = Buf.find_first_of("[]", 2); | 
|  | if (Buf.substr(I).starts_with("]]")) { | 
|  | StringRef MacroExpr = Buf.substr(2, I - 2); | 
|  | StringRef Macro; | 
|  | StringRef Default; | 
|  | std::tie(Macro, Default) = MacroExpr.split('='); | 
|  |  | 
|  | // When the -D option is requested, we use the provided value. | 
|  | // Otherwise we use a default macro value if present. | 
|  | auto It = Defines.find(Macro); | 
|  | std::optional<StringRef> Value; | 
|  | if (It != Defines.end()) | 
|  | Value = It->second; | 
|  | else if (!Default.empty() || MacroExpr.ends_with("=")) | 
|  | Value = Default; | 
|  |  | 
|  | if (Value) { | 
|  | Preprocessed += *Value; | 
|  | Buf = Buf.substr(I + 2); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Preprocessed += Buf[0]; | 
|  | Buf = Buf.substr(1); | 
|  | } | 
|  |  | 
|  | return Preprocessed; | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) { | 
|  | InitLLVM X(argc, argv); | 
|  | cl::HideUnrelatedOptions(Cat); | 
|  | cl::ParseCommandLineOptions( | 
|  | argc, argv, "Create an object file from a YAML description", nullptr, | 
|  | nullptr, /*LongOptionsUseDoubleDash=*/true); | 
|  |  | 
|  | auto ErrHandler = [](const Twine &Msg) { | 
|  | WithColor::error(errs(), "yaml2obj") << Msg << "\n"; | 
|  | }; | 
|  |  | 
|  | std::error_code EC; | 
|  | std::unique_ptr<ToolOutputFile> Out( | 
|  | new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); | 
|  | if (EC) { | 
|  | ErrHandler("failed to open '" + OutputFilename + "': " + EC.message()); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = | 
|  | MemoryBuffer::getFileOrSTDIN(Input, /*IsText=*/true); | 
|  | if (!Buf) | 
|  | return 1; | 
|  |  | 
|  | std::optional<std::string> Buffer = | 
|  | preprocess(Buf.get()->getBuffer(), ErrHandler); | 
|  | if (!Buffer) | 
|  | return 1; | 
|  |  | 
|  | if (PreprocessOnly) { | 
|  | Out->os() << Buffer; | 
|  | } else { | 
|  | yaml::Input YIn(*Buffer); | 
|  |  | 
|  | if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum, | 
|  | MaxSize == 0 ? UINT64_MAX : MaxSize)) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Out->keep(); | 
|  | Out->os().flush(); | 
|  | return 0; | 
|  | } |