1 //===- llvm-cvtres.cpp - Serialize .res files into .obj ---------*- C++ -*-===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Serialize .res files into .obj files. This is intended to be a 10 // platform-independent port of Microsoft's cvtres.exe. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/BinaryFormat/Magic.h" 15 #include "llvm/Object/Binary.h" 16 #include "llvm/Object/WindowsMachineFlag.h" 17 #include "llvm/Object/WindowsResource.h" 18 #include "llvm/Option/Arg.h" 19 #include "llvm/Option/ArgList.h" 20 #include "llvm/Option/Option.h" 21 #include "llvm/Support/BinaryStreamError.h" 22 #include "llvm/Support/Error.h" 23 #include "llvm/Support/InitLLVM.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/PrettyStackTrace.h" 26 #include "llvm/Support/Process.h" 27 #include "llvm/Support/ScopedPrinter.h" 28 #include "llvm/Support/Signals.h" 29 #include "llvm/Support/raw_ostream.h" 30 31 #include <system_error> 32 33 using namespace llvm; 34 using namespace object; 35 36 namespace { 37 38 enum ID { 39 OPT_INVALID = 0, // This is not an option ID. 40 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 41 #include "Opts.inc" 42 #undef OPTION 43 }; 44 45 #define OPTTABLE_STR_TABLE_CODE 46 #include "Opts.inc" 47 #undef OPTTABLE_STR_TABLE_CODE 48 49 #define OPTTABLE_PREFIXES_TABLE_CODE 50 #include "Opts.inc" 51 #undef OPTTABLE_PREFIXES_TABLE_CODE 52 53 using namespace llvm::opt; 54 static constexpr opt::OptTable::Info InfoTable[] = { 55 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 56 #include "Opts.inc" 57 #undef OPTION 58 }; 59 60 class CvtResOptTable : public opt::GenericOptTable { 61 public: 62 CvtResOptTable() 63 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, 64 true) {} 65 }; 66 } 67 68 [[noreturn]] static void reportError(Twine Msg) { 69 errs() << Msg; 70 exit(1); 71 } 72 73 static void reportError(StringRef Input, std::error_code EC) { 74 reportError(Twine(Input) + ": " + EC.message() + ".\n"); 75 } 76 77 static void error(StringRef Input, Error EC) { 78 if (!EC) 79 return; 80 handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) { 81 reportError(Twine(Input) + ": " + EI.message() + ".\n"); 82 }); 83 } 84 85 static void error(Error EC) { 86 if (!EC) 87 return; 88 handleAllErrors(std::move(EC), 89 [&](const ErrorInfoBase &EI) { reportError(EI.message()); }); 90 } 91 92 static uint32_t getTime() { 93 std::time_t Now = time(nullptr); 94 if (Now < 0 || !isUInt<32>(Now)) 95 return UINT32_MAX; 96 return static_cast<uint32_t>(Now); 97 } 98 99 template <typename T> T error(Expected<T> EC) { 100 if (!EC) 101 error(EC.takeError()); 102 return std::move(EC.get()); 103 } 104 105 template <typename T> T error(StringRef Input, Expected<T> EC) { 106 if (!EC) 107 error(Input, EC.takeError()); 108 return std::move(EC.get()); 109 } 110 111 template <typename T> T error(StringRef Input, ErrorOr<T> &&EC) { 112 return error(Input, errorOrToExpected(std::move(EC))); 113 } 114 115 int main(int Argc, const char **Argv) { 116 InitLLVM X(Argc, Argv); 117 118 CvtResOptTable T; 119 unsigned MAI, MAC; 120 ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1); 121 opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); 122 123 if (InputArgs.hasArg(OPT_HELP)) { 124 T.printHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); 125 return 0; 126 } 127 128 bool Verbose = InputArgs.hasArg(OPT_VERBOSE); 129 130 COFF::MachineTypes MachineType; 131 132 if (opt::Arg *Arg = InputArgs.getLastArg(OPT_MACHINE)) { 133 MachineType = getMachineType(Arg->getValue()); 134 if (MachineType == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { 135 reportError(Twine("Unsupported machine architecture ") + Arg->getValue() + 136 "\n"); 137 } 138 } else { 139 if (Verbose) 140 outs() << "Machine architecture not specified; assumed X64.\n"; 141 MachineType = COFF::IMAGE_FILE_MACHINE_AMD64; 142 } 143 144 std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT); 145 146 if (InputFiles.size() == 0) { 147 reportError("No input file specified.\n"); 148 } 149 150 SmallString<128> OutputFile; 151 152 if (opt::Arg *Arg = InputArgs.getLastArg(OPT_OUT)) { 153 OutputFile = Arg->getValue(); 154 } else { 155 OutputFile = sys::path::filename(StringRef(InputFiles[0])); 156 sys::path::replace_extension(OutputFile, ".obj"); 157 } 158 159 uint32_t DateTimeStamp; 160 if (llvm::opt::Arg *Arg = InputArgs.getLastArg(OPT_TIMESTAMP)) { 161 StringRef Value(Arg->getValue()); 162 if (Value.getAsInteger(0, DateTimeStamp)) 163 reportError(Twine("invalid timestamp: ") + Value + 164 ". Expected 32-bit integer\n"); 165 } else { 166 DateTimeStamp = getTime(); 167 } 168 169 if (Verbose) 170 outs() << "Machine: " << machineToStr(MachineType) << '\n'; 171 172 WindowsResourceParser Parser; 173 174 for (const auto &File : InputFiles) { 175 std::unique_ptr<MemoryBuffer> Buffer = error( 176 File, MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, 177 /*RequiresNullTerminator=*/false)); 178 file_magic Type = identify_magic(Buffer->getMemBufferRef().getBuffer()); 179 if (Type != file_magic::windows_resource) 180 reportError(File + ": unrecognized file format.\n"); 181 std::unique_ptr<WindowsResource> Binary = error( 182 File, 183 WindowsResource::createWindowsResource(Buffer->getMemBufferRef())); 184 185 WindowsResource *RF = Binary.get(); 186 187 if (Verbose) { 188 int EntryNumber = 0; 189 ResourceEntryRef Entry = error(RF->getHeadEntry()); 190 bool End = false; 191 while (!End) { 192 error(Entry.moveNext(End)); 193 EntryNumber++; 194 } 195 outs() << "Number of resources: " << EntryNumber << "\n"; 196 } 197 198 std::vector<std::string> Duplicates; 199 error(Parser.parse(RF, Duplicates)); 200 for (const auto& DupeDiag : Duplicates) 201 reportError(DupeDiag); 202 } 203 204 if (Verbose) { 205 Parser.printTree(outs()); 206 } 207 208 std::unique_ptr<MemoryBuffer> OutputBuffer = 209 error(llvm::object::writeWindowsResourceCOFF(MachineType, Parser, 210 DateTimeStamp)); 211 auto FileOrErr = 212 FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); 213 if (!FileOrErr) 214 reportError(OutputFile, errorToErrorCode(FileOrErr.takeError())); 215 std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); 216 std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), 217 FileBuffer->getBufferStart()); 218 error(FileBuffer->commit()); 219 220 if (Verbose) { 221 std::unique_ptr<MemoryBuffer> Buffer = 222 error(OutputFile, 223 MemoryBuffer::getFileOrSTDIN(OutputFile, /*IsText=*/false, 224 /*RequiresNullTerminator=*/false)); 225 226 ScopedPrinter W(errs()); 227 W.printBinaryBlock("Output File Raw Data", 228 Buffer->getMemBufferRef().getBuffer()); 229 } 230 231 return 0; 232 } 233