brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.1 KiB · 1244bfb Raw
634 lines · cpp
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-exception6//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 <string>38#include <vector>39 40using namespace llvm;41using namespace llvm::yaml;42using namespace llvm::MachO;43using namespace llvm::ifs;44 45#define DEBUG_TYPE "llvm-ifs"46 47namespace {48const VersionTuple IfsVersionCurrent(3, 0);49 50enum class FileFormat { IFS, ELF, TBD };51} // end anonymous namespace52 53using namespace llvm::opt;54enum ID {55  OPT_INVALID = 0, // This is not an option ID.56#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),57#include "Opts.inc"58#undef OPTION59};60 61#define OPTTABLE_STR_TABLE_CODE62#include "Opts.inc"63#undef OPTTABLE_STR_TABLE_CODE64 65#define OPTTABLE_PREFIXES_TABLE_CODE66#include "Opts.inc"67#undef OPTTABLE_PREFIXES_TABLE_CODE68 69static constexpr opt::OptTable::Info InfoTable[] = {70#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),71#include "Opts.inc"72#undef OPTION73};74 75class IFSOptTable : public opt::GenericOptTable {76public:77  IFSOptTable()78      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {79    setGroupedShortOptions(true);80  }81};82 83struct DriverConfig {84  std::vector<std::string> InputFilePaths;85 86  std::optional<FileFormat> InputFormat;87  std::optional<FileFormat> OutputFormat;88 89  std::optional<std::string> HintIfsTarget;90  std::optional<std::string> OptTargetTriple;91  std::optional<IFSArch> OverrideArch;92  std::optional<IFSBitWidthType> OverrideBitWidth;93  std::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  std::optional<std::string> SoName;106 107  std::optional<std::string> Output;108  std::optional<std::string> OutputElf;109  std::optional<std::string> OutputIfs;110  std::optional<std::string> OutputTbd;111 112  bool WriteIfChanged = false;113};114 115static 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 131static Expected<std::unique_ptr<IFSStub>>132readInputFile(std::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      else169        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 185static 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 according197    // 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 = EncodeKind::GlobalSymbol;217    switch (Symbol.Type) {218    default:219    case IFSSymbolType::NoType:220      Kind = EncodeKind::GlobalSymbol;221      break;222    case IFSSymbolType::Object:223      Kind = EncodeKind::GlobalSymbol;224      break;225    case IFSSymbolType::Func:226      Kind = EncodeKind::GlobalSymbol;227      break;228    }229    if (Symbol.Weak)230      File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined);231    else232      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 243static void fatalError(Error Err) {244  WithColor::defaultErrorHandler(std::move(Err));245  exit(1);246}247 248static 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 version254/// of the YAML parser.255static 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, avoid268      // 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 283static 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<std::optional<FileFormat>>(A->getValue())309                             .Case("IFS", FileFormat::IFS)310                             .Case("ELF", FileFormat::ELF)311                             .Default(std::nullopt);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<std::optional<FileFormat>>(A->getValue())322                              .Case("IFS", FileFormat::IFS)323                              .Case("ELF", FileFormat::ELF)324                              .Case("TBD", FileFormat::TBD)325                              .Default(std::nullopt);326    if (!Config.OutputFormat)327      OptionNotFound("--output-format", A->getValue());328  }329  if (const opt::Arg *A = Args.getLastArg(OPT_arch_EQ)) {330    uint16_t eMachine = ELF::convertArchNameToEMachine(A->getValue());331    if (eMachine == ELF::EM_NONE) {332      fatalError(Twine("unknown arch '") + A->getValue() + "'");333    }334    Config.OverrideArch = eMachine;335  }336  if (const opt::Arg *A = Args.getLastArg(OPT_bitwidth_EQ)) {337    size_t Width;338    llvm::StringRef S(A->getValue());339    if (!S.getAsInteger<size_t>(10, Width) || Width == 64 || Width == 32)340      Config.OverrideBitWidth =341          Width == 64 ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;342    else343      OptionNotFound("--bitwidth", A->getValue());344  }345  if (const opt::Arg *A = Args.getLastArg(OPT_endianness_EQ)) {346    Config.OverrideEndianness =347        StringSwitch<std::optional<IFSEndiannessType>>(A->getValue())348            .Case("little", IFSEndiannessType::Little)349            .Case("big", IFSEndiannessType::Big)350            .Default(std::nullopt);351    if (!Config.OverrideEndianness)352      OptionNotFound("--endianness", A->getValue());353  }354  if (const opt::Arg *A = Args.getLastArg(OPT_target_EQ))355    Config.OptTargetTriple = A->getValue();356  if (const opt::Arg *A = Args.getLastArg(OPT_hint_ifs_target_EQ))357    Config.HintIfsTarget = A->getValue();358 359  Config.StripIfsArch = Args.hasArg(OPT_strip_ifs_arch);360  Config.StripIfsBitwidth = Args.hasArg(OPT_strip_ifs_bitwidth);361  Config.StripIfsEndianness = Args.hasArg(OPT_strip_ifs_endianness);362  Config.StripIfsTarget = Args.hasArg(OPT_strip_ifs_target);363  Config.StripUndefined = Args.hasArg(OPT_strip_undefined);364  Config.StripNeeded = Args.hasArg(OPT_strip_needed);365  Config.StripSize = Args.hasArg(OPT_strip_size);366 367  for (const opt::Arg *A : Args.filtered(OPT_exclude_EQ))368    Config.Exclude.push_back(A->getValue());369  if (const opt::Arg *A = Args.getLastArg(OPT_soname_EQ))370    Config.SoName = A->getValue();371  if (const opt::Arg *A = Args.getLastArg(OPT_output_EQ))372    Config.Output = A->getValue();373  if (const opt::Arg *A = Args.getLastArg(OPT_output_elf_EQ))374    Config.OutputElf = A->getValue();375  if (const opt::Arg *A = Args.getLastArg(OPT_output_ifs_EQ))376    Config.OutputIfs = A->getValue();377  if (const opt::Arg *A = Args.getLastArg(OPT_output_tbd_EQ))378    Config.OutputTbd = A->getValue();379  Config.WriteIfChanged = Args.hasArg(OPT_write_if_changed);380  return Config;381}382 383int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {384  DriverConfig Config = parseArgs(argc, argv);385 386  if (Config.InputFilePaths.empty())387    Config.InputFilePaths.push_back("-");388 389  // If input files are more than one, they can only be IFS files.390  if (Config.InputFilePaths.size() > 1)391    Config.InputFormat = FileFormat::IFS;392 393  // Attempt to merge input.394  IFSStub Stub;395  std::map<std::string, IFSSymbol> SymbolMap;396  std::string PreviousInputFilePath;397  for (const std::string &InputFilePath : Config.InputFilePaths) {398    Expected<std::unique_ptr<IFSStub>> StubOrErr =399        readInputFile(Config.InputFormat, InputFilePath);400    if (!StubOrErr)401      fatalError(StubOrErr.takeError());402 403    std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());404    if (PreviousInputFilePath.empty()) {405      Stub.IfsVersion = TargetStub->IfsVersion;406      Stub.Target = TargetStub->Target;407      Stub.SoName = TargetStub->SoName;408      Stub.NeededLibs = TargetStub->NeededLibs;409    } else {410      if (Stub.IfsVersion != TargetStub->IfsVersion) {411        if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {412          WithColor::error()413              << "Interface Stub: IfsVersion Mismatch."414              << "\nFilenames: " << PreviousInputFilePath << " "415              << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion416              << " " << TargetStub->IfsVersion << "\n";417          return -1;418        }419        if (TargetStub->IfsVersion > Stub.IfsVersion)420          Stub.IfsVersion = TargetStub->IfsVersion;421      }422      if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {423        WithColor::error() << "Interface Stub: Target Mismatch."424                           << "\nFilenames: " << PreviousInputFilePath << " "425                           << InputFilePath;426        return -1;427      }428      if (Stub.SoName != TargetStub->SoName) {429        WithColor::error() << "Interface Stub: SoName Mismatch."430                           << "\nFilenames: " << PreviousInputFilePath << " "431                           << InputFilePath432                           << "\nSoName Values: " << Stub.SoName << " "433                           << TargetStub->SoName << "\n";434        return -1;435      }436      if (Stub.NeededLibs != TargetStub->NeededLibs) {437        WithColor::error() << "Interface Stub: NeededLibs Mismatch."438                           << "\nFilenames: " << PreviousInputFilePath << " "439                           << InputFilePath << "\n";440        return -1;441      }442    }443 444    for (auto Symbol : TargetStub->Symbols) {445      auto [SI, Inserted] = SymbolMap.try_emplace(Symbol.Name, Symbol);446      if (Inserted)447        continue;448 449      assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");450 451      // Check conflicts:452      if (Symbol.Type != SI->second.Type) {453        WithColor::error() << "Interface Stub: Type Mismatch for "454                           << Symbol.Name << ".\nFilename: " << InputFilePath455                           << "\nType Values: " << getTypeName(SI->second.Type)456                           << " " << getTypeName(Symbol.Type) << "\n";457 458        return -1;459      }460      if (Symbol.Size != SI->second.Size) {461        WithColor::error() << "Interface Stub: Size Mismatch for "462                           << Symbol.Name << ".\nFilename: " << InputFilePath463                           << "\nSize Values: " << SI->second.Size << " "464                           << Symbol.Size << "\n";465 466        return -1;467      }468      if (Symbol.Weak != SI->second.Weak) {469        Symbol.Weak = false;470        continue;471      }472      // TODO: Not checking Warning. Will be dropped.473    }474 475    PreviousInputFilePath = InputFilePath;476  }477 478  if (Stub.IfsVersion != IfsVersionCurrent)479    if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {480      WithColor::error() << "Interface Stub: Bad IfsVersion: "481                         << Stub.IfsVersion << ", llvm-ifs supported version: "482                         << IfsVersionCurrent << ".\n";483      return -1;484    }485 486  for (auto &Entry : SymbolMap)487    Stub.Symbols.push_back(Entry.second);488 489  // Change SoName before emitting stubs.490  if (Config.SoName)491    Stub.SoName = *Config.SoName;492 493  Error OverrideError =494      overrideIFSTarget(Stub, Config.OverrideArch, Config.OverrideEndianness,495                        Config.OverrideBitWidth, Config.OptTargetTriple);496  if (OverrideError)497    fatalError(std::move(OverrideError));498 499  if (Config.StripNeeded)500    Stub.NeededLibs.clear();501 502  if (Error E = filterIFSSyms(Stub, Config.StripUndefined, Config.Exclude))503    fatalError(std::move(E));504 505  if (Config.StripSize)506    for (IFSSymbol &Sym : Stub.Symbols)507      Sym.Size.reset();508 509  if (!Config.OutputElf && !Config.OutputIfs && !Config.OutputTbd) {510    if (!Config.OutputFormat) {511      WithColor::error() << "at least one output should be specified.";512      return -1;513    }514  } else if (Config.OutputFormat) {515    WithColor::error() << "'--output-format' cannot be used with "516                          "'--output-{FILE_FORMAT}' options at the same time";517    return -1;518  }519  if (Config.OutputFormat) {520    // TODO: Remove OutputFormat flag in the next revision.521    WithColor::warning() << "--output-format option is deprecated, please use "522                            "--output-{FILE_FORMAT} options instead\n";523    switch (*Config.OutputFormat) {524    case FileFormat::TBD: {525      std::error_code SysErr;526      raw_fd_ostream Out(*Config.Output, SysErr);527      if (SysErr) {528        WithColor::error() << "Couldn't open " << *Config.Output529                           << " for writing.\n";530        return -1;531      }532      if (!Stub.Target.Triple) {533        WithColor::error()534            << "Triple should be defined when output format is TBD";535        return -1;536      }537      return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,538                          "TBD", Out);539    }540    case FileFormat::IFS: {541      Stub.IfsVersion = IfsVersionCurrent;542      if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {543        std::error_code HintEC(1, std::generic_category());544        IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);545        if (*Stub.Target.Arch != *HintTarget.Arch)546          fatalError(make_error<StringError>(547              "Triple hint does not match the actual architecture", HintEC));548        if (*Stub.Target.Endianness != *HintTarget.Endianness)549          fatalError(make_error<StringError>(550              "Triple hint does not match the actual endianness", HintEC));551        if (*Stub.Target.BitWidth != *HintTarget.BitWidth)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;557      } else {558        stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,559                       Config.StripIfsEndianness, Config.StripIfsBitwidth);560      }561      Error IFSWriteError =562          writeIFS(*Config.Output, 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 == FileFormat::ELF && Config.HintIfsTarget) {592        std::error_code HintEC(1, std::generic_category());593        IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);594        if (*Stub.Target.Arch != *HintTarget.Arch)595          fatalError(make_error<StringError>(596              "Triple hint does not match the actual architecture", HintEC));597        if (*Stub.Target.Endianness != *HintTarget.Endianness)598          fatalError(make_error<StringError>(599              "Triple hint does not match the actual endianness", HintEC));600        if (*Stub.Target.BitWidth != *HintTarget.BitWidth)601          fatalError(make_error<StringError>(602              "Triple hint does not match the actual bit width", HintEC));603 604        stripIFSTarget(Stub, true, false, false, false);605        Stub.Target.Triple = *Config.HintIfsTarget;606      } else {607        stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,608                       Config.StripIfsEndianness, Config.StripIfsBitwidth);609      }610      Error IFSWriteError =611          writeIFS(*Config.OutputIfs, Stub, Config.WriteIfChanged);612      if (IFSWriteError)613        fatalError(std::move(IFSWriteError));614    }615    if (Config.OutputTbd) {616      std::error_code SysErr;617      raw_fd_ostream Out(*Config.OutputTbd, SysErr);618      if (SysErr) {619        WithColor::error() << "Couldn't open " << *Config.OutputTbd620                           << " for writing.\n";621        return -1;622      }623      if (!Stub.Target.Triple) {624        WithColor::error()625            << "Triple should be defined when output format is TBD";626        return -1;627      }628      return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,629                          "TBD", Out);630    }631  }632  return 0;633}634