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