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
33using namespace llvm;
34using namespace object;
35
36namespace {
37
38enum 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
53using namespace llvm::opt;
54static constexpr opt::OptTable::Info InfoTable[] = {
55#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
56#include "Opts.inc"
57#undef OPTION
58};
59
60class CvtResOptTable : public opt::GenericOptTable {
61public:
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(status: 1);
71}
72
73static void reportError(StringRef Input, std::error_code EC) {
74 reportError(Msg: Twine(Input) + ": " + EC.message() + ".\n");
75}
76
77static void error(StringRef Input, Error EC) {
78 if (!EC)
79 return;
80 handleAllErrors(E: std::move(EC), Handlers: [&](const ErrorInfoBase &EI) {
81 reportError(Msg: Twine(Input) + ": " + EI.message() + ".\n");
82 });
83}
84
85static void error(Error EC) {
86 if (!EC)
87 return;
88 handleAllErrors(E: std::move(EC),
89 Handlers: [&](const ErrorInfoBase &EI) { reportError(Msg: EI.message()); });
90}
91
92static uint32_t getTime() {
93 std::time_t Now = time(timer: nullptr);
94 if (Now < 0 || !isUInt<32>(x: Now))
95 return UINT32_MAX;
96 return static_cast<uint32_t>(Now);
97}
98
99template <typename T> T error(Expected<T> EC) {
100 if (!EC)
101 error(EC.takeError());
102 return std::move(EC.get());
103}
104
105template <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
111template <typename T> T error(StringRef Input, ErrorOr<T> &&EC) {
112 return error(Input, errorOrToExpected(std::move(EC)));
113}
114
115int 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(Args: ArgsArr, MissingArgIndex&: MAI, MissingArgCount&: MAC);
122
123 if (InputArgs.hasArg(Ids: OPT_HELP)) {
124 T.printHelp(OS&: outs(), Usage: "llvm-cvtres [options] file...", Title: "Resource Converter");
125 return 0;
126 }
127
128 bool Verbose = InputArgs.hasArg(Ids: OPT_VERBOSE);
129
130 COFF::MachineTypes MachineType;
131
132 if (opt::Arg *Arg = InputArgs.getLastArg(Ids: OPT_MACHINE)) {
133 MachineType = getMachineType(S: Arg->getValue());
134 if (MachineType == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
135 reportError(Msg: 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(Id: OPT_INPUT);
145
146 if (InputFiles.size() == 0) {
147 reportError(Msg: "No input file specified.\n");
148 }
149
150 SmallString<128> OutputFile;
151
152 if (opt::Arg *Arg = InputArgs.getLastArg(Ids: OPT_OUT)) {
153 OutputFile = Arg->getValue();
154 } else {
155 OutputFile = sys::path::filename(path: StringRef(InputFiles[0]));
156 sys::path::replace_extension(path&: OutputFile, extension: ".obj");
157 }
158
159 uint32_t DateTimeStamp;
160 if (llvm::opt::Arg *Arg = InputArgs.getLastArg(Ids: OPT_TIMESTAMP)) {
161 StringRef Value(Arg->getValue());
162 if (Value.getAsInteger(Radix: 0, Result&: DateTimeStamp))
163 reportError(Msg: Twine("invalid timestamp: ") + Value +
164 ". Expected 32-bit integer\n");
165 } else {
166 DateTimeStamp = getTime();
167 }
168
169 if (Verbose)
170 outs() << "Machine: " << machineToStr(MT: MachineType) << '\n';
171
172 WindowsResourceParser Parser;
173
174 for (const auto &File : InputFiles) {
175 std::unique_ptr<MemoryBuffer> Buffer = error(
176 Input: File, EC: MemoryBuffer::getFileOrSTDIN(Filename: File, /*IsText=*/false,
177 /*RequiresNullTerminator=*/false));
178 file_magic Type = identify_magic(magic: Buffer->getMemBufferRef().getBuffer());
179 if (Type != file_magic::windows_resource)
180 reportError(Msg: File + ": unrecognized file format.\n");
181 std::unique_ptr<WindowsResource> Binary = error(
182 Input: File,
183 EC: WindowsResource::createWindowsResource(Source: Buffer->getMemBufferRef()));
184
185 WindowsResource *RF = Binary.get();
186
187 if (Verbose) {
188 int EntryNumber = 0;
189 ResourceEntryRef Entry = error(EC: RF->getHeadEntry());
190 bool End = false;
191 while (!End) {
192 error(EC: Entry.moveNext(End));
193 EntryNumber++;
194 }
195 outs() << "Number of resources: " << EntryNumber << "\n";
196 }
197
198 std::vector<std::string> Duplicates;
199 error(EC: Parser.parse(WR: RF, Duplicates));
200 for (const auto& DupeDiag : Duplicates)
201 reportError(Msg: DupeDiag);
202 }
203
204 if (Verbose) {
205 Parser.printTree(OS&: outs());
206 }
207
208 std::unique_ptr<MemoryBuffer> OutputBuffer =
209 error(EC: llvm::object::writeWindowsResourceCOFF(MachineType, Parser,
210 TimeDateStamp: DateTimeStamp));
211 auto FileOrErr =
212 FileOutputBuffer::create(FilePath: OutputFile, Size: OutputBuffer->getBufferSize());
213 if (!FileOrErr)
214 reportError(Input: OutputFile, EC: errorToErrorCode(Err: FileOrErr.takeError()));
215 std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
216 std::copy(first: OutputBuffer->getBufferStart(), last: OutputBuffer->getBufferEnd(),
217 result: FileBuffer->getBufferStart());
218 error(EC: FileBuffer->commit());
219
220 if (Verbose) {
221 std::unique_ptr<MemoryBuffer> Buffer =
222 error(Input: OutputFile,
223 EC: MemoryBuffer::getFileOrSTDIN(Filename: OutputFile, /*IsText=*/false,
224 /*RequiresNullTerminator=*/false));
225
226 ScopedPrinter W(errs());
227 W.printBinaryBlock(Label: "Output File Raw Data",
228 Value: Buffer->getMemBufferRef().getBuffer());
229 }
230
231 return 0;
232}
233