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