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