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