1//===- llvm-ifs.cpp -------------------------------------------------------===//
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#include "ErrorCollector.h"
10#include "llvm/ADT/StringRef.h"
11#include "llvm/ADT/StringSwitch.h"
12#include "llvm/BinaryFormat/ELF.h"
13#include "llvm/InterfaceStub/ELFObjHandler.h"
14#include "llvm/InterfaceStub/IFSHandler.h"
15#include "llvm/InterfaceStub/IFSStub.h"
16#include "llvm/ObjectYAML/yaml2obj.h"
17#include "llvm/Option/Arg.h"
18#include "llvm/Option/ArgList.h"
19#include "llvm/Option/Option.h"
20#include "llvm/Support/CommandLine.h"
21#include "llvm/Support/Debug.h"
22#include "llvm/Support/Errc.h"
23#include "llvm/Support/Error.h"
24#include "llvm/Support/FileOutputBuffer.h"
25#include "llvm/Support/LLVMDriver.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/VersionTuple.h"
29#include "llvm/Support/WithColor.h"
30#include "llvm/Support/YAMLTraits.h"
31#include "llvm/Support/raw_ostream.h"
32#include "llvm/TargetParser/Triple.h"
33#include "llvm/TextAPI/InterfaceFile.h"
34#include "llvm/TextAPI/TextAPIReader.h"
35#include "llvm/TextAPI/TextAPIWriter.h"
36#include <optional>
37#include <set>
38#include <string>
39#include <vector>
40
41using namespace llvm;
42using namespace llvm::yaml;
43using namespace llvm::MachO;
44using namespace llvm::ifs;
45
46#define DEBUG_TYPE "llvm-ifs"
47
48namespace {
49const VersionTuple IfsVersionCurrent(3, 0);
50
51enum class FileFormat { IFS, ELF, TBD };
52} // end anonymous namespace
53
54using namespace llvm::opt;
55enum ID {
56 OPT_INVALID = 0, // This is not an option ID.
57#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
58#include "Opts.inc"
59#undef OPTION
60};
61
62#define OPTTABLE_STR_TABLE_CODE
63#include "Opts.inc"
64#undef OPTTABLE_STR_TABLE_CODE
65
66#define OPTTABLE_PREFIXES_TABLE_CODE
67#include "Opts.inc"
68#undef OPTTABLE_PREFIXES_TABLE_CODE
69
70static constexpr opt::OptTable::Info InfoTable[] = {
71#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
72#include "Opts.inc"
73#undef OPTION
74};
75
76class IFSOptTable : public opt::GenericOptTable {
77public:
78 IFSOptTable()
79 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
80 setGroupedShortOptions(true);
81 }
82};
83
84struct DriverConfig {
85 std::vector<std::string> InputFilePaths;
86
87 std::optional<FileFormat> InputFormat;
88 std::optional<FileFormat> OutputFormat;
89
90 std::optional<std::string> HintIfsTarget;
91 std::optional<std::string> OptTargetTriple;
92 std::optional<IFSArch> OverrideArch;
93 std::optional<IFSBitWidthType> OverrideBitWidth;
94 std::optional<IFSEndiannessType> OverrideEndianness;
95
96 bool StripIfsArch = false;
97 bool StripIfsBitwidth = false;
98 bool StripIfsEndianness = false;
99 bool StripIfsTarget = false;
100 bool StripNeeded = false;
101 bool StripSize = false;
102 bool StripUndefined = false;
103
104 std::vector<std::string> Exclude;
105
106 std::optional<std::string> SoName;
107
108 std::optional<std::string> Output;
109 std::optional<std::string> OutputElf;
110 std::optional<std::string> OutputIfs;
111 std::optional<std::string> OutputTbd;
112
113 bool WriteIfChanged = false;
114};
115
116static std::string getTypeName(IFSSymbolType Type) {
117 switch (Type) {
118 case IFSSymbolType::NoType:
119 return "NoType";
120 case IFSSymbolType::Func:
121 return "Func";
122 case IFSSymbolType::Object:
123 return "Object";
124 case IFSSymbolType::TLS:
125 return "TLS";
126 case IFSSymbolType::Unknown:
127 return "Unknown";
128 }
129 llvm_unreachable("Unexpected ifs symbol type.");
130}
131
132static Expected<std::unique_ptr<IFSStub>>
133readInputFile(std::optional<FileFormat> &InputFormat, StringRef FilePath) {
134 // Read in file.
135 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
136 MemoryBuffer::getFileOrSTDIN(Filename: FilePath, /*IsText=*/true);
137 if (!BufOrError)
138 return createStringError(EC: BufOrError.getError(), Fmt: "Could not open `%s`",
139 Vals: FilePath.data());
140
141 std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
142 ErrorCollector EC(/*UseFatalErrors=*/false);
143
144 // First try to read as a binary (fails fast if not binary).
145 if (!InputFormat || *InputFormat == FileFormat::ELF) {
146 Expected<std::unique_ptr<IFSStub>> StubFromELF =
147 readELFFile(Buf: FileReadBuffer->getMemBufferRef());
148 if (StubFromELF) {
149 InputFormat = FileFormat::ELF;
150 (*StubFromELF)->IfsVersion = IfsVersionCurrent;
151 return std::move(*StubFromELF);
152 }
153 EC.addError(E: StubFromELF.takeError(), Tag: "BinaryRead");
154 }
155
156 // Fall back to reading as a ifs.
157 if (!InputFormat || *InputFormat == FileFormat::IFS) {
158 Expected<std::unique_ptr<IFSStub>> StubFromIFS =
159 readIFSFromBuffer(Buf: FileReadBuffer->getBuffer());
160 if (StubFromIFS) {
161 InputFormat = FileFormat::IFS;
162 if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)
163 EC.addError(
164 E: createStringError(EC: errc::not_supported,
165 S: "IFS version " +
166 (*StubFromIFS)->IfsVersion.getAsString() +
167 " is unsupported."),
168 Tag: "ReadInputFile");
169 else
170 return std::move(*StubFromIFS);
171 } else {
172 EC.addError(E: StubFromIFS.takeError(), Tag: "YamlParse");
173 }
174 }
175
176 // If both readers fail, build a new error that includes all information.
177 EC.addError(E: createStringError(EC: errc::not_supported,
178 Fmt: "No file readers succeeded reading `%s` "
179 "(unsupported/malformed file?)",
180 Vals: FilePath.data()),
181 Tag: "ReadInputFile");
182 EC.escalateToFatal();
183 return EC.makeError();
184}
185
186static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols,
187 const StringRef Format, raw_ostream &Out) {
188
189 auto PlatformTypeOrError =
190 [](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformType> {
191 if (T.isMacOSX())
192 return llvm::MachO::PLATFORM_MACOS;
193 if (T.isTvOS())
194 return llvm::MachO::PLATFORM_TVOS;
195 if (T.isWatchOS())
196 return llvm::MachO::PLATFORM_WATCHOS;
197 // Note: put isiOS last because tvOS and watchOS are also iOS according
198 // to the Triple.
199 if (T.isiOS())
200 return llvm::MachO::PLATFORM_IOS;
201
202 return createStringError(EC: errc::not_supported, S: "Invalid Platform.\n");
203 }(T);
204
205 if (!PlatformTypeOrError)
206 return -1;
207
208 PlatformType Plat = PlatformTypeOrError.get();
209 TargetList Targets({Target(llvm::MachO::mapToArchitecture(Target: T), Plat)});
210
211 InterfaceFile File;
212 File.setFileType(FileType::TBD_V3); // Only supporting v3 for now.
213 File.addTargets(Targets);
214
215 for (const auto &Symbol : Symbols) {
216 auto Name = Symbol.Name;
217 auto Kind = EncodeKind::GlobalSymbol;
218 switch (Symbol.Type) {
219 default:
220 case IFSSymbolType::NoType:
221 Kind = EncodeKind::GlobalSymbol;
222 break;
223 case IFSSymbolType::Object:
224 Kind = EncodeKind::GlobalSymbol;
225 break;
226 case IFSSymbolType::Func:
227 Kind = EncodeKind::GlobalSymbol;
228 break;
229 }
230 if (Symbol.Weak)
231 File.addSymbol(Kind, Name, Targets, Flags: SymbolFlags::WeakDefined);
232 else
233 File.addSymbol(Kind, Name, Targets);
234 }
235
236 SmallString<4096> Buffer;
237 raw_svector_ostream OS(Buffer);
238 if (Error Result = TextAPIWriter::writeToStream(OS, File))
239 return -1;
240 Out << OS.str();
241 return 0;
242}
243
244static void fatalError(Error Err) {
245 WithColor::defaultErrorHandler(Err: std::move(Err));
246 exit(status: 1);
247}
248
249static void fatalError(Twine T) {
250 WithColor::error() << T.str() << '\n';
251 exit(status: 1);
252}
253
254/// writeIFS() writes a Text-Based ELF stub to a file using the latest version
255/// of the YAML parser.
256static Error writeIFS(StringRef FilePath, IFSStub &Stub, bool WriteIfChanged) {
257 // Write IFS to memory first.
258 std::string IFSStr;
259 raw_string_ostream OutStr(IFSStr);
260 Error YAMLErr = writeIFSToOutputStream(OS&: OutStr, Stub);
261 if (YAMLErr)
262 return YAMLErr;
263 OutStr.flush();
264
265 if (WriteIfChanged) {
266 if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
267 MemoryBuffer::getFile(Filename: FilePath)) {
268 // Compare IFS output with the existing IFS file. If unchanged, avoid
269 // changing the file.
270 if ((*BufOrError)->getBuffer() == IFSStr)
271 return Error::success();
272 }
273 }
274 // Open IFS file for writing.
275 std::error_code SysErr;
276 raw_fd_ostream Out(FilePath, SysErr);
277 if (SysErr)
278 return createStringError(EC: SysErr, Fmt: "Couldn't open `%s` for writing",
279 Vals: FilePath.data());
280 Out << IFSStr;
281 return Error::success();
282}
283
284static DriverConfig parseArgs(int argc, char *const *argv) {
285 BumpPtrAllocator A;
286 StringSaver Saver(A);
287 IFSOptTable Tbl;
288 StringRef ToolName = argv[0];
289 llvm::opt::InputArgList Args = Tbl.parseArgs(
290 Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) { fatalError(T: Msg); });
291 if (Args.hasArg(Ids: OPT_help)) {
292 Tbl.printHelp(OS&: llvm::outs(),
293 Usage: (Twine(ToolName) + " <input_file> <output_file> [options]")
294 .str()
295 .c_str(),
296 Title: "shared object stubbing tool");
297 std::exit(status: 0);
298 }
299 if (Args.hasArg(Ids: OPT_version)) {
300 llvm::outs() << ToolName << '\n';
301 cl::PrintVersionMessage();
302 std::exit(status: 0);
303 }
304
305 DriverConfig Config;
306 for (const opt::Arg *A : Args.filtered(Ids: OPT_INPUT))
307 Config.InputFilePaths.push_back(x: A->getValue());
308 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_input_format_EQ)) {
309 Config.InputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
310 .Case(S: "IFS", Value: FileFormat::IFS)
311 .Case(S: "ELF", Value: FileFormat::ELF)
312 .Default(Value: std::nullopt);
313 if (!Config.InputFormat)
314 fatalError(T: Twine("invalid argument '") + A->getValue());
315 }
316
317 auto OptionNotFound = [ToolName](StringRef FlagName, StringRef OptionName) {
318 fatalError(T: Twine(ToolName) + ": for the " + FlagName +
319 " option: Cannot find option named '" + OptionName + "'!");
320 };
321 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_output_format_EQ)) {
322 Config.OutputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
323 .Case(S: "IFS", Value: FileFormat::IFS)
324 .Case(S: "ELF", Value: FileFormat::ELF)
325 .Case(S: "TBD", Value: FileFormat::TBD)
326 .Default(Value: std::nullopt);
327 if (!Config.OutputFormat)
328 OptionNotFound("--output-format", A->getValue());
329 }
330 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_arch_EQ)) {
331 uint16_t eMachine = ELF::convertArchNameToEMachine(Arch: A->getValue());
332 if (eMachine == ELF::EM_NONE) {
333 fatalError(T: Twine("unknown arch '") + A->getValue() + "'");
334 }
335 Config.OverrideArch = eMachine;
336 }
337 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_bitwidth_EQ)) {
338 size_t Width;
339 llvm::StringRef S(A->getValue());
340 if (!S.getAsInteger<size_t>(Radix: 10, Result&: Width) || Width == 64 || Width == 32)
341 Config.OverrideBitWidth =
342 Width == 64 ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;
343 else
344 OptionNotFound("--bitwidth", A->getValue());
345 }
346 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_endianness_EQ)) {
347 Config.OverrideEndianness =
348 StringSwitch<std::optional<IFSEndiannessType>>(A->getValue())
349 .Case(S: "little", Value: IFSEndiannessType::Little)
350 .Case(S: "big", Value: IFSEndiannessType::Big)
351 .Default(Value: std::nullopt);
352 if (!Config.OverrideEndianness)
353 OptionNotFound("--endianness", A->getValue());
354 }
355 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_target_EQ))
356 Config.OptTargetTriple = A->getValue();
357 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_hint_ifs_target_EQ))
358 Config.HintIfsTarget = A->getValue();
359
360 Config.StripIfsArch = Args.hasArg(Ids: OPT_strip_ifs_arch);
361 Config.StripIfsBitwidth = Args.hasArg(Ids: OPT_strip_ifs_bitwidth);
362 Config.StripIfsEndianness = Args.hasArg(Ids: OPT_strip_ifs_endianness);
363 Config.StripIfsTarget = Args.hasArg(Ids: OPT_strip_ifs_target);
364 Config.StripUndefined = Args.hasArg(Ids: OPT_strip_undefined);
365 Config.StripNeeded = Args.hasArg(Ids: OPT_strip_needed);
366 Config.StripSize = Args.hasArg(Ids: OPT_strip_size);
367
368 for (const opt::Arg *A : Args.filtered(Ids: OPT_exclude_EQ))
369 Config.Exclude.push_back(x: A->getValue());
370 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_soname_EQ))
371 Config.SoName = A->getValue();
372 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_output_EQ))
373 Config.Output = A->getValue();
374 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_output_elf_EQ))
375 Config.OutputElf = A->getValue();
376 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_output_ifs_EQ))
377 Config.OutputIfs = A->getValue();
378 if (const opt::Arg *A = Args.getLastArg(Ids: OPT_output_tbd_EQ))
379 Config.OutputTbd = A->getValue();
380 Config.WriteIfChanged = Args.hasArg(Ids: OPT_write_if_changed);
381 return Config;
382}
383
384int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
385 DriverConfig Config = parseArgs(argc, argv);
386
387 if (Config.InputFilePaths.empty())
388 Config.InputFilePaths.push_back(x: "-");
389
390 // If input files are more than one, they can only be IFS files.
391 if (Config.InputFilePaths.size() > 1)
392 Config.InputFormat = FileFormat::IFS;
393
394 // Attempt to merge input.
395 IFSStub Stub;
396 std::map<std::string, IFSSymbol> SymbolMap;
397 std::string PreviousInputFilePath;
398 for (const std::string &InputFilePath : Config.InputFilePaths) {
399 Expected<std::unique_ptr<IFSStub>> StubOrErr =
400 readInputFile(InputFormat&: Config.InputFormat, FilePath: InputFilePath);
401 if (!StubOrErr)
402 fatalError(Err: StubOrErr.takeError());
403
404 std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());
405 if (PreviousInputFilePath.empty()) {
406 Stub.IfsVersion = TargetStub->IfsVersion;
407 Stub.Target = TargetStub->Target;
408 Stub.SoName = TargetStub->SoName;
409 Stub.NeededLibs = TargetStub->NeededLibs;
410 } else {
411 if (Stub.IfsVersion != TargetStub->IfsVersion) {
412 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
413 WithColor::error()
414 << "Interface Stub: IfsVersion Mismatch."
415 << "\nFilenames: " << PreviousInputFilePath << " "
416 << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion
417 << " " << TargetStub->IfsVersion << "\n";
418 return -1;
419 }
420 if (TargetStub->IfsVersion > Stub.IfsVersion)
421 Stub.IfsVersion = TargetStub->IfsVersion;
422 }
423 if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {
424 WithColor::error() << "Interface Stub: Target Mismatch."
425 << "\nFilenames: " << PreviousInputFilePath << " "
426 << InputFilePath;
427 return -1;
428 }
429 if (Stub.SoName != TargetStub->SoName) {
430 WithColor::error() << "Interface Stub: SoName Mismatch."
431 << "\nFilenames: " << PreviousInputFilePath << " "
432 << InputFilePath
433 << "\nSoName Values: " << Stub.SoName << " "
434 << TargetStub->SoName << "\n";
435 return -1;
436 }
437 if (Stub.NeededLibs != TargetStub->NeededLibs) {
438 WithColor::error() << "Interface Stub: NeededLibs Mismatch."
439 << "\nFilenames: " << PreviousInputFilePath << " "
440 << InputFilePath << "\n";
441 return -1;
442 }
443 }
444
445 for (auto Symbol : TargetStub->Symbols) {
446 auto [SI, Inserted] = SymbolMap.try_emplace(k: Symbol.Name, args&: Symbol);
447 if (Inserted)
448 continue;
449
450 assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");
451
452 // Check conflicts:
453 if (Symbol.Type != SI->second.Type) {
454 WithColor::error() << "Interface Stub: Type Mismatch for "
455 << Symbol.Name << ".\nFilename: " << InputFilePath
456 << "\nType Values: " << getTypeName(Type: SI->second.Type)
457 << " " << getTypeName(Type: Symbol.Type) << "\n";
458
459 return -1;
460 }
461 if (Symbol.Size != SI->second.Size) {
462 WithColor::error() << "Interface Stub: Size Mismatch for "
463 << Symbol.Name << ".\nFilename: " << InputFilePath
464 << "\nSize Values: " << SI->second.Size << " "
465 << Symbol.Size << "\n";
466
467 return -1;
468 }
469 if (Symbol.Weak != SI->second.Weak) {
470 Symbol.Weak = false;
471 continue;
472 }
473 // TODO: Not checking Warning. Will be dropped.
474 }
475
476 PreviousInputFilePath = InputFilePath;
477 }
478
479 if (Stub.IfsVersion != IfsVersionCurrent)
480 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
481 WithColor::error() << "Interface Stub: Bad IfsVersion: "
482 << Stub.IfsVersion << ", llvm-ifs supported version: "
483 << IfsVersionCurrent << ".\n";
484 return -1;
485 }
486
487 for (auto &Entry : SymbolMap)
488 Stub.Symbols.push_back(x: Entry.second);
489
490 // Change SoName before emitting stubs.
491 if (Config.SoName)
492 Stub.SoName = *Config.SoName;
493
494 Error OverrideError =
495 overrideIFSTarget(Stub, OverrideArch: Config.OverrideArch, OverrideEndianness: Config.OverrideEndianness,
496 OverrideBitWidth: Config.OverrideBitWidth, OverrideTriple: Config.OptTargetTriple);
497 if (OverrideError)
498 fatalError(Err: std::move(OverrideError));
499
500 if (Config.StripNeeded)
501 Stub.NeededLibs.clear();
502
503 if (Error E = filterIFSSyms(Stub, StripUndefined: Config.StripUndefined, Exclude: Config.Exclude))
504 fatalError(Err: std::move(E));
505
506 if (Config.StripSize)
507 for (IFSSymbol &Sym : Stub.Symbols)
508 Sym.Size.reset();
509
510 if (!Config.OutputElf && !Config.OutputIfs && !Config.OutputTbd) {
511 if (!Config.OutputFormat) {
512 WithColor::error() << "at least one output should be specified.";
513 return -1;
514 }
515 } else if (Config.OutputFormat) {
516 WithColor::error() << "'--output-format' cannot be used with "
517 "'--output-{FILE_FORMAT}' options at the same time";
518 return -1;
519 }
520 if (Config.OutputFormat) {
521 // TODO: Remove OutputFormat flag in the next revision.
522 WithColor::warning() << "--output-format option is deprecated, please use "
523 "--output-{FILE_FORMAT} options instead\n";
524 switch (*Config.OutputFormat) {
525 case FileFormat::TBD: {
526 std::error_code SysErr;
527 raw_fd_ostream Out(*Config.Output, SysErr);
528 if (SysErr) {
529 WithColor::error() << "Couldn't open " << *Config.Output
530 << " for writing.\n";
531 return -1;
532 }
533 if (!Stub.Target.Triple) {
534 WithColor::error()
535 << "Triple should be defined when output format is TBD";
536 return -1;
537 }
538 return writeTbdStub(T: llvm::Triple(*Stub.Target.Triple), Symbols: Stub.Symbols,
539 Format: "TBD", Out);
540 }
541 case FileFormat::IFS: {
542 Stub.IfsVersion = IfsVersionCurrent;
543 if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {
544 std::error_code HintEC(1, std::generic_category());
545 IFSTarget HintTarget = parseTriple(TripleStr: *Config.HintIfsTarget);
546 if (*Stub.Target.Arch != *HintTarget.Arch)
547 fatalError(Err: make_error<StringError>(
548 Args: "Triple hint does not match the actual architecture", Args&: HintEC));
549 if (*Stub.Target.Endianness != *HintTarget.Endianness)
550 fatalError(Err: make_error<StringError>(
551 Args: "Triple hint does not match the actual endianness", Args&: HintEC));
552 if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
553 fatalError(Err: make_error<StringError>(
554 Args: "Triple hint does not match the actual bit width", Args&: HintEC));
555
556 stripIFSTarget(Stub, StripTriple: true, StripArch: false, StripEndianness: false, StripBitWidth: false);
557 Stub.Target.Triple = *Config.HintIfsTarget;
558 } else {
559 stripIFSTarget(Stub, StripTriple: Config.StripIfsTarget, StripArch: Config.StripIfsArch,
560 StripEndianness: Config.StripIfsEndianness, StripBitWidth: Config.StripIfsBitwidth);
561 }
562 Error IFSWriteError =
563 writeIFS(FilePath: *Config.Output, Stub, WriteIfChanged: Config.WriteIfChanged);
564 if (IFSWriteError)
565 fatalError(Err: std::move(IFSWriteError));
566 break;
567 }
568 case FileFormat::ELF: {
569 Error TargetError = validateIFSTarget(Stub, ParseTriple: true);
570 if (TargetError)
571 fatalError(Err: std::move(TargetError));
572 Error BinaryWriteError =
573 writeBinaryStub(FilePath: *Config.Output, Stub, WriteIfChanged: Config.WriteIfChanged);
574 if (BinaryWriteError)
575 fatalError(Err: std::move(BinaryWriteError));
576 break;
577 }
578 }
579 } else {
580 // Check if output path for individual format.
581 if (Config.OutputElf) {
582 Error TargetError = validateIFSTarget(Stub, ParseTriple: true);
583 if (TargetError)
584 fatalError(Err: std::move(TargetError));
585 Error BinaryWriteError =
586 writeBinaryStub(FilePath: *Config.OutputElf, Stub, WriteIfChanged: Config.WriteIfChanged);
587 if (BinaryWriteError)
588 fatalError(Err: std::move(BinaryWriteError));
589 }
590 if (Config.OutputIfs) {
591 Stub.IfsVersion = IfsVersionCurrent;
592 if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {
593 std::error_code HintEC(1, std::generic_category());
594 IFSTarget HintTarget = parseTriple(TripleStr: *Config.HintIfsTarget);
595 if (*Stub.Target.Arch != *HintTarget.Arch)
596 fatalError(Err: make_error<StringError>(
597 Args: "Triple hint does not match the actual architecture", Args&: HintEC));
598 if (*Stub.Target.Endianness != *HintTarget.Endianness)
599 fatalError(Err: make_error<StringError>(
600 Args: "Triple hint does not match the actual endianness", Args&: HintEC));
601 if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
602 fatalError(Err: make_error<StringError>(
603 Args: "Triple hint does not match the actual bit width", Args&: HintEC));
604
605 stripIFSTarget(Stub, StripTriple: true, StripArch: false, StripEndianness: false, StripBitWidth: false);
606 Stub.Target.Triple = *Config.HintIfsTarget;
607 } else {
608 stripIFSTarget(Stub, StripTriple: Config.StripIfsTarget, StripArch: Config.StripIfsArch,
609 StripEndianness: Config.StripIfsEndianness, StripBitWidth: Config.StripIfsBitwidth);
610 }
611 Error IFSWriteError =
612 writeIFS(FilePath: *Config.OutputIfs, Stub, WriteIfChanged: Config.WriteIfChanged);
613 if (IFSWriteError)
614 fatalError(Err: std::move(IFSWriteError));
615 }
616 if (Config.OutputTbd) {
617 std::error_code SysErr;
618 raw_fd_ostream Out(*Config.OutputTbd, SysErr);
619 if (SysErr) {
620 WithColor::error() << "Couldn't open " << *Config.OutputTbd
621 << " for writing.\n";
622 return -1;
623 }
624 if (!Stub.Target.Triple) {
625 WithColor::error()
626 << "Triple should be defined when output format is TBD";
627 return -1;
628 }
629 return writeTbdStub(T: llvm::Triple(*Stub.Target.Triple), Symbols: Stub.Symbols,
630 Format: "TBD", Out);
631 }
632 }
633 return 0;
634}
635