159 lines · cpp
1//===- yaml2obj - Convert YAML to a binary object file --------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This program takes a YAML description of an object file and outputs the10// binary equivalent.11//12// This is used for writing tests that require binary files.13//14//===----------------------------------------------------------------------===//15 16#include "llvm/ObjectYAML/yaml2obj.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/ObjectYAML/ObjectYAML.h"19#include "llvm/Support/CommandLine.h"20#include "llvm/Support/FileSystem.h"21#include "llvm/Support/InitLLVM.h"22#include "llvm/Support/MemoryBuffer.h"23#include "llvm/Support/ToolOutputFile.h"24#include "llvm/Support/WithColor.h"25#include "llvm/Support/YAMLTraits.h"26#include "llvm/Support/raw_ostream.h"27#include <optional>28#include <system_error>29 30using namespace llvm;31 32namespace {33cl::OptionCategory Cat("yaml2obj Options");34 35cl::opt<std::string> Input(cl::Positional, cl::desc("<input file>"),36 cl::init("-"), cl::cat(Cat));37 38static cl::list<std::string>39 D("D", cl::Prefix,40 cl::desc("Defined the specified macros to their specified "41 "definition. The syntax is <macro>=<definition>"),42 cl::cat(Cat));43 44cl::opt<bool> PreprocessOnly("E", cl::desc("Just print the preprocessed file"),45 cl::cat(Cat));46 47cl::opt<unsigned>48 DocNum("docnum", cl::init(1),49 cl::desc("Read specified document from input (default = 1)"),50 cl::cat(Cat));51 52static cl::opt<uint64_t> MaxSize(53 "max-size", cl::init(10 * 1024 * 1024),54 cl::desc(55 "Sets the maximum allowed output size (0 means no limit) [ELF only]"),56 cl::cat(Cat));57 58cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),59 cl::value_desc("filename"), cl::init("-"),60 cl::Prefix, cl::cat(Cat));61} // namespace62 63static std::optional<std::string> preprocess(StringRef Buf,64 yaml::ErrorHandler ErrHandler) {65 DenseMap<StringRef, StringRef> Defines;66 for (StringRef Define : D) {67 StringRef Macro, Definition;68 std::tie(Macro, Definition) = Define.split('=');69 if (!Define.count('=') || Macro.empty()) {70 ErrHandler("invalid syntax for -D: " + Define);71 return {};72 }73 if (!Defines.try_emplace(Macro, Definition).second) {74 ErrHandler("'" + Macro + "'" + " redefined");75 return {};76 }77 }78 79 std::string Preprocessed;80 while (!Buf.empty()) {81 if (Buf.starts_with("[[")) {82 size_t I = Buf.find_first_of("[]", 2);83 if (Buf.substr(I).starts_with("]]")) {84 StringRef MacroExpr = Buf.substr(2, I - 2);85 StringRef Macro;86 StringRef Default;87 std::tie(Macro, Default) = MacroExpr.split('=');88 89 // When the -D option is requested, we use the provided value.90 // Otherwise we use a default macro value if present.91 auto It = Defines.find(Macro);92 std::optional<StringRef> Value;93 if (It != Defines.end())94 Value = It->second;95 else if (!Default.empty() || MacroExpr.ends_with("="))96 Value = Default;97 98 if (Value) {99 Preprocessed += *Value;100 Buf = Buf.substr(I + 2);101 continue;102 }103 }104 }105 106 Preprocessed += Buf[0];107 Buf = Buf.substr(1);108 }109 110 return Preprocessed;111}112 113int main(int argc, char **argv) {114 InitLLVM X(argc, argv);115 cl::HideUnrelatedOptions(Cat);116 cl::ParseCommandLineOptions(117 argc, argv, "Create an object file from a YAML description", nullptr,118 nullptr, nullptr, /*LongOptionsUseDoubleDash=*/true);119 120 constexpr StringRef ProgName = "yaml2obj";121 auto ErrHandler = [&](const Twine &Msg) {122 WithColor::error(errs(), ProgName) << Msg << "\n";123 };124 125 std::error_code EC;126 std::unique_ptr<ToolOutputFile> Out(127 new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None));128 if (EC) {129 ErrHandler("failed to open '" + OutputFilename + "': " + EC.message());130 return 1;131 }132 133 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =134 MemoryBuffer::getFileOrSTDIN(Input, /*IsText=*/true);135 if (std::error_code EC = Buf.getError()) {136 WithColor::error(errs(), ProgName) << Input << ": " << EC.message() << '\n';137 return 1;138 }139 140 std::optional<std::string> Buffer =141 preprocess(Buf.get()->getBuffer(), ErrHandler);142 if (!Buffer)143 return 1;144 145 if (PreprocessOnly) {146 Out->os() << Buffer;147 } else {148 yaml::Input YIn(*Buffer);149 150 if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum,151 MaxSize == 0 ? UINT64_MAX : MaxSize))152 return 1;153 }154 155 Out->keep();156 Out->os().flush();157 return 0;158}159