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