xref: /llvm-project/llvm/tools/llvm-ifs/llvm-ifs.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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 
41 using namespace llvm;
42 using namespace llvm::yaml;
43 using namespace llvm::MachO;
44 using namespace llvm::ifs;
45 
46 #define DEBUG_TYPE "llvm-ifs"
47 
48 namespace {
49 const VersionTuple IfsVersionCurrent(3, 0);
50 
51 enum class FileFormat { IFS, ELF, TBD };
52 } // end anonymous namespace
53 
54 using namespace llvm::opt;
55 enum 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 
70 static constexpr opt::OptTable::Info InfoTable[] = {
71 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
72 #include "Opts.inc"
73 #undef OPTION
74 };
75 
76 class IFSOptTable : public opt::GenericOptTable {
77 public:
78   IFSOptTable()
79       : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
80     setGroupedShortOptions(true);
81   }
82 };
83 
84 struct 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 
116 static 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 
132 static Expected<std::unique_ptr<IFSStub>>
133 readInputFile(std::optional<FileFormat> &InputFormat, StringRef FilePath) {
134   // Read in file.
135   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
136       MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true);
137   if (!BufOrError)
138     return createStringError(BufOrError.getError(), "Could not open `%s`",
139                              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(FileReadBuffer->getMemBufferRef());
148     if (StubFromELF) {
149       InputFormat = FileFormat::ELF;
150       (*StubFromELF)->IfsVersion = IfsVersionCurrent;
151       return std::move(*StubFromELF);
152     }
153     EC.addError(StubFromELF.takeError(), "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(FileReadBuffer->getBuffer());
160     if (StubFromIFS) {
161       InputFormat = FileFormat::IFS;
162       if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)
163         EC.addError(
164             createStringError(errc::not_supported,
165                               "IFS version " +
166                                   (*StubFromIFS)->IfsVersion.getAsString() +
167                                   " is unsupported."),
168             "ReadInputFile");
169       else
170         return std::move(*StubFromIFS);
171     } else {
172       EC.addError(StubFromIFS.takeError(), "YamlParse");
173     }
174   }
175 
176   // If both readers fail, build a new error that includes all information.
177   EC.addError(createStringError(errc::not_supported,
178                                 "No file readers succeeded reading `%s` "
179                                 "(unsupported/malformed file?)",
180                                 FilePath.data()),
181               "ReadInputFile");
182   EC.escalateToFatal();
183   return EC.makeError();
184 }
185 
186 static 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(errc::not_supported, "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(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, 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 
244 static void fatalError(Error Err) {
245   WithColor::defaultErrorHandler(std::move(Err));
246   exit(1);
247 }
248 
249 static void fatalError(Twine T) {
250   WithColor::error() << T.str() << '\n';
251   exit(1);
252 }
253 
254 /// writeIFS() writes a Text-Based ELF stub to a file using the latest version
255 /// of the YAML parser.
256 static 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(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(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(SysErr, "Couldn't open `%s` for writing",
279                              FilePath.data());
280   Out << IFSStr;
281   return Error::success();
282 }
283 
284 static 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, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { fatalError(Msg); });
291   if (Args.hasArg(OPT_help)) {
292     Tbl.printHelp(llvm::outs(),
293                   (Twine(ToolName) + " <input_file> <output_file> [options]")
294                       .str()
295                       .c_str(),
296                   "shared object stubbing tool");
297     std::exit(0);
298   }
299   if (Args.hasArg(OPT_version)) {
300     llvm::outs() << ToolName << '\n';
301     cl::PrintVersionMessage();
302     std::exit(0);
303   }
304 
305   DriverConfig Config;
306   for (const opt::Arg *A : Args.filtered(OPT_INPUT))
307     Config.InputFilePaths.push_back(A->getValue());
308   if (const opt::Arg *A = Args.getLastArg(OPT_input_format_EQ)) {
309     Config.InputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
310                              .Case("IFS", FileFormat::IFS)
311                              .Case("ELF", FileFormat::ELF)
312                              .Default(std::nullopt);
313     if (!Config.InputFormat)
314       fatalError(Twine("invalid argument '") + A->getValue());
315   }
316 
317   auto OptionNotFound = [ToolName](StringRef FlagName, StringRef OptionName) {
318     fatalError(Twine(ToolName) + ": for the " + FlagName +
319                " option: Cannot find option named '" + OptionName + "'!");
320   };
321   if (const opt::Arg *A = Args.getLastArg(OPT_output_format_EQ)) {
322     Config.OutputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
323                               .Case("IFS", FileFormat::IFS)
324                               .Case("ELF", FileFormat::ELF)
325                               .Case("TBD", FileFormat::TBD)
326                               .Default(std::nullopt);
327     if (!Config.OutputFormat)
328       OptionNotFound("--output-format", A->getValue());
329   }
330   if (const opt::Arg *A = Args.getLastArg(OPT_arch_EQ)) {
331     uint16_t eMachine = ELF::convertArchNameToEMachine(A->getValue());
332     if (eMachine == ELF::EM_NONE) {
333       fatalError(Twine("unknown arch '") + A->getValue() + "'");
334     }
335     Config.OverrideArch = eMachine;
336   }
337   if (const opt::Arg *A = Args.getLastArg(OPT_bitwidth_EQ)) {
338     size_t Width;
339     llvm::StringRef S(A->getValue());
340     if (!S.getAsInteger<size_t>(10, 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(OPT_endianness_EQ)) {
347     Config.OverrideEndianness =
348         StringSwitch<std::optional<IFSEndiannessType>>(A->getValue())
349             .Case("little", IFSEndiannessType::Little)
350             .Case("big", IFSEndiannessType::Big)
351             .Default(std::nullopt);
352     if (!Config.OverrideEndianness)
353       OptionNotFound("--endianness", A->getValue());
354   }
355   if (const opt::Arg *A = Args.getLastArg(OPT_target_EQ))
356     Config.OptTargetTriple = A->getValue();
357   if (const opt::Arg *A = Args.getLastArg(OPT_hint_ifs_target_EQ))
358     Config.HintIfsTarget = A->getValue();
359 
360   Config.StripIfsArch = Args.hasArg(OPT_strip_ifs_arch);
361   Config.StripIfsBitwidth = Args.hasArg(OPT_strip_ifs_bitwidth);
362   Config.StripIfsEndianness = Args.hasArg(OPT_strip_ifs_endianness);
363   Config.StripIfsTarget = Args.hasArg(OPT_strip_ifs_target);
364   Config.StripUndefined = Args.hasArg(OPT_strip_undefined);
365   Config.StripNeeded = Args.hasArg(OPT_strip_needed);
366   Config.StripSize = Args.hasArg(OPT_strip_size);
367 
368   for (const opt::Arg *A : Args.filtered(OPT_exclude_EQ))
369     Config.Exclude.push_back(A->getValue());
370   if (const opt::Arg *A = Args.getLastArg(OPT_soname_EQ))
371     Config.SoName = A->getValue();
372   if (const opt::Arg *A = Args.getLastArg(OPT_output_EQ))
373     Config.Output = A->getValue();
374   if (const opt::Arg *A = Args.getLastArg(OPT_output_elf_EQ))
375     Config.OutputElf = A->getValue();
376   if (const opt::Arg *A = Args.getLastArg(OPT_output_ifs_EQ))
377     Config.OutputIfs = A->getValue();
378   if (const opt::Arg *A = Args.getLastArg(OPT_output_tbd_EQ))
379     Config.OutputTbd = A->getValue();
380   Config.WriteIfChanged = Args.hasArg(OPT_write_if_changed);
381   return Config;
382 }
383 
384 int 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("-");
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(Config.InputFormat, InputFilePath);
401     if (!StubOrErr)
402       fatalError(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(Symbol.Name, 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(SI->second.Type)
457                            << " " << getTypeName(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(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, Config.OverrideArch, Config.OverrideEndianness,
496                         Config.OverrideBitWidth, Config.OptTargetTriple);
497   if (OverrideError)
498     fatalError(std::move(OverrideError));
499 
500   if (Config.StripNeeded)
501     Stub.NeededLibs.clear();
502 
503   if (Error E = filterIFSSyms(Stub, Config.StripUndefined, Config.Exclude))
504     fatalError(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(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,
539                           "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(*Config.HintIfsTarget);
546         if (*Stub.Target.Arch != *HintTarget.Arch)
547           fatalError(make_error<StringError>(
548               "Triple hint does not match the actual architecture", HintEC));
549         if (*Stub.Target.Endianness != *HintTarget.Endianness)
550           fatalError(make_error<StringError>(
551               "Triple hint does not match the actual endianness", HintEC));
552         if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
553           fatalError(make_error<StringError>(
554               "Triple hint does not match the actual bit width", HintEC));
555 
556         stripIFSTarget(Stub, true, false, false, false);
557         Stub.Target.Triple = *Config.HintIfsTarget;
558       } else {
559         stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,
560                        Config.StripIfsEndianness, Config.StripIfsBitwidth);
561       }
562       Error IFSWriteError =
563           writeIFS(*Config.Output, Stub, Config.WriteIfChanged);
564       if (IFSWriteError)
565         fatalError(std::move(IFSWriteError));
566       break;
567     }
568     case FileFormat::ELF: {
569       Error TargetError = validateIFSTarget(Stub, true);
570       if (TargetError)
571         fatalError(std::move(TargetError));
572       Error BinaryWriteError =
573           writeBinaryStub(*Config.Output, Stub, Config.WriteIfChanged);
574       if (BinaryWriteError)
575         fatalError(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, true);
583       if (TargetError)
584         fatalError(std::move(TargetError));
585       Error BinaryWriteError =
586           writeBinaryStub(*Config.OutputElf, Stub, Config.WriteIfChanged);
587       if (BinaryWriteError)
588         fatalError(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(*Config.HintIfsTarget);
595         if (*Stub.Target.Arch != *HintTarget.Arch)
596           fatalError(make_error<StringError>(
597               "Triple hint does not match the actual architecture", HintEC));
598         if (*Stub.Target.Endianness != *HintTarget.Endianness)
599           fatalError(make_error<StringError>(
600               "Triple hint does not match the actual endianness", HintEC));
601         if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
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;
607       } else {
608         stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,
609                        Config.StripIfsEndianness, Config.StripIfsBitwidth);
610       }
611       Error IFSWriteError =
612           writeIFS(*Config.OutputIfs, 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), Stub.Symbols,
630                           "TBD", Out);
631     }
632   }
633   return 0;
634 }
635