1//===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===//
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// A utility for creating / splitting / inspecting universal binaries.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/BinaryFormat/MachO.h"
15#include "llvm/IR/LLVMContext.h"
16#include "llvm/IR/Module.h"
17#include "llvm/Object/Archive.h"
18#include "llvm/Object/Binary.h"
19#include "llvm/Object/IRObjectFile.h"
20#include "llvm/Object/MachO.h"
21#include "llvm/Object/MachOUniversal.h"
22#include "llvm/Object/MachOUniversalWriter.h"
23#include "llvm/Object/ObjectFile.h"
24#include "llvm/Option/Arg.h"
25#include "llvm/Option/ArgList.h"
26#include "llvm/Support/CommandLine.h"
27#include "llvm/Support/Error.h"
28#include "llvm/Support/FileOutputBuffer.h"
29#include "llvm/Support/LLVMDriver.h"
30#include "llvm/Support/TargetSelect.h"
31#include "llvm/Support/WithColor.h"
32#include "llvm/TargetParser/Triple.h"
33#include "llvm/TextAPI/Architecture.h"
34#include <optional>
35
36using namespace llvm;
37using namespace llvm::object;
38
39static const StringRef ToolName = "llvm-lipo";
40
41[[noreturn]] static void reportError(Twine Message) {
42 WithColor::error(OS&: errs(), Prefix: ToolName) << Message << "\n";
43 errs().flush();
44 exit(EXIT_FAILURE);
45}
46
47[[noreturn]] static void reportError(Error E) {
48 assert(E);
49 std::string Buf;
50 raw_string_ostream OS(Buf);
51 logAllUnhandledErrors(E: std::move(E), OS);
52 OS.flush();
53 reportError(Message: Buf);
54}
55
56[[noreturn]] static void reportError(StringRef File, Error E) {
57 assert(E);
58 std::string Buf;
59 raw_string_ostream OS(Buf);
60 logAllUnhandledErrors(E: std::move(E), OS);
61 OS.flush();
62 WithColor::error(OS&: errs(), Prefix: ToolName) << "'" << File << "': " << Buf;
63 exit(EXIT_FAILURE);
64}
65
66namespace {
67enum LipoID {
68 LIPO_INVALID = 0, // This is not an option ID.
69#define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(LIPO_, __VA_ARGS__),
70#include "LipoOpts.inc"
71#undef OPTION
72};
73
74namespace lipo {
75#define OPTTABLE_STR_TABLE_CODE
76#include "LipoOpts.inc"
77#undef OPTTABLE_STR_TABLE_CODE
78
79#define OPTTABLE_PREFIXES_TABLE_CODE
80#include "LipoOpts.inc"
81#undef OPTTABLE_PREFIXES_TABLE_CODE
82
83using namespace llvm::opt;
84static constexpr opt::OptTable::Info LipoInfoTable[] = {
85#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(LIPO_, __VA_ARGS__),
86#include "LipoOpts.inc"
87#undef OPTION
88};
89} // namespace lipo
90
91class LipoOptTable : public opt::GenericOptTable {
92public:
93 LipoOptTable()
94 : opt::GenericOptTable(lipo::OptionStrTable, lipo::OptionPrefixesTable,
95 lipo::LipoInfoTable) {}
96};
97
98enum class LipoAction {
99 PrintArchs,
100 PrintInfo,
101 VerifyArch,
102 ThinArch,
103 ExtractArch,
104 CreateUniversal,
105 ReplaceArch,
106};
107
108struct InputFile {
109 std::optional<StringRef> ArchType;
110 StringRef FileName;
111};
112
113struct Config {
114 SmallVector<InputFile, 1> InputFiles;
115 SmallVector<std::string, 1> VerifyArchList;
116 SmallVector<InputFile, 1> ReplacementFiles;
117 StringMap<const uint32_t> SegmentAlignments;
118 std::string ArchType;
119 std::string OutputFile;
120 LipoAction ActionToPerform;
121 bool UseFat64;
122};
123
124static Slice createSliceFromArchive(LLVMContext &LLVMCtx, const Archive &A) {
125 Expected<Slice> ArchiveOrSlice = Slice::create(A, LLVMCtx: &LLVMCtx);
126 if (!ArchiveOrSlice)
127 reportError(File: A.getFileName(), E: ArchiveOrSlice.takeError());
128 return *ArchiveOrSlice;
129}
130
131static Slice createSliceFromIR(const IRObjectFile &IRO, unsigned Align) {
132 Expected<Slice> IROrErr = Slice::create(IRO, Align);
133 if (!IROrErr)
134 reportError(File: IRO.getFileName(), E: IROrErr.takeError());
135 return *IROrErr;
136}
137
138} // end namespace
139
140static void validateArchitectureName(StringRef ArchitectureName) {
141 if (!MachOObjectFile::isValidArch(ArchFlag: ArchitectureName)) {
142 std::string Buf;
143 raw_string_ostream OS(Buf);
144 OS << "Invalid architecture: " << ArchitectureName
145 << "\nValid architecture names are:";
146 for (auto arch : MachOObjectFile::getValidArchs())
147 OS << " " << arch;
148 reportError(Message: Buf);
149 }
150}
151
152static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
153 Config C;
154 LipoOptTable T;
155 unsigned MissingArgumentIndex, MissingArgumentCount;
156 opt::InputArgList InputArgs =
157 T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MissingArgumentIndex, MissingArgCount&: MissingArgumentCount);
158
159 if (MissingArgumentCount)
160 reportError(Message: "missing argument to " +
161 StringRef(InputArgs.getArgString(Index: MissingArgumentIndex)) +
162 " option");
163
164 if (InputArgs.size() == 0) {
165 // printHelp does not accept Twine.
166 T.printHelp(OS&: errs(), Usage: "llvm-lipo input[s] option[s]", Title: "llvm-lipo");
167 exit(EXIT_FAILURE);
168 }
169
170 if (InputArgs.hasArg(Ids: LIPO_help)) {
171 // printHelp does not accept Twine.
172 T.printHelp(OS&: outs(), Usage: "llvm-lipo input[s] option[s]", Title: "llvm-lipo");
173 exit(EXIT_SUCCESS);
174 }
175
176 if (InputArgs.hasArg(Ids: LIPO_version)) {
177 outs() << ToolName + "\n";
178 cl::PrintVersionMessage();
179 exit(EXIT_SUCCESS);
180 }
181
182 for (auto *Arg : InputArgs.filtered(Ids: LIPO_UNKNOWN))
183 reportError(Message: "unknown argument '" + Arg->getAsString(Args: InputArgs) + "'");
184
185 for (auto *Arg : InputArgs.filtered(Ids: LIPO_INPUT))
186 C.InputFiles.push_back(Elt: {.ArchType: std::nullopt, .FileName: Arg->getValue()});
187 for (auto *Arg : InputArgs.filtered(Ids: LIPO_arch)) {
188 validateArchitectureName(ArchitectureName: Arg->getValue(N: 0));
189 assert(Arg->getValue(1) && "file_name is missing");
190 C.InputFiles.push_back(Elt: {.ArchType: StringRef(Arg->getValue(N: 0)), .FileName: Arg->getValue(N: 1)});
191 }
192
193 if (C.InputFiles.empty())
194 reportError(Message: "at least one input file should be specified");
195
196 if (InputArgs.hasArg(Ids: LIPO_output))
197 C.OutputFile = std::string(InputArgs.getLastArgValue(Id: LIPO_output));
198
199 for (auto *Segalign : InputArgs.filtered(Ids: LIPO_segalign)) {
200 if (!Segalign->getValue(N: 1))
201 reportError(Message: "segalign is missing an argument: expects -segalign "
202 "arch_type alignment_value");
203
204 validateArchitectureName(ArchitectureName: Segalign->getValue(N: 0));
205
206 uint32_t AlignmentValue;
207 if (!to_integer<uint32_t>(S: Segalign->getValue(N: 1), Num&: AlignmentValue, Base: 16))
208 reportError(Message: "argument to -segalign <arch_type> " +
209 Twine(Segalign->getValue(N: 1)) +
210 " (hex) is not a proper hexadecimal number");
211 if (!isPowerOf2_32(Value: AlignmentValue))
212 reportError(Message: "argument to -segalign <arch_type> " +
213 Twine(Segalign->getValue(N: 1)) +
214 " (hex) must be a non-zero power of two");
215 if (Log2_32(Value: AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)
216 reportError(
217 Message: "argument to -segalign <arch_type> " + Twine(Segalign->getValue(N: 1)) +
218 " (hex) must be less than or equal to the maximum section align 2^" +
219 Twine(MachOUniversalBinary::MaxSectionAlignment));
220 auto Entry = C.SegmentAlignments.try_emplace(Key: Segalign->getValue(N: 0),
221 Args: Log2_32(Value: AlignmentValue));
222 if (!Entry.second)
223 reportError(Message: "-segalign " + Twine(Segalign->getValue(N: 0)) +
224 " <alignment_value> specified multiple times: " +
225 Twine(1 << Entry.first->second) + ", " +
226 Twine(AlignmentValue));
227 }
228
229 C.UseFat64 = InputArgs.hasArg(Ids: LIPO_fat64);
230
231 SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(Ids: LIPO_action_group));
232 if (ActionArgs.empty())
233 reportError(Message: "at least one action should be specified");
234 // errors if multiple actions specified other than replace
235 // multiple replace flags may be specified, as long as they are not mixed with
236 // other action flags
237 auto ReplacementArgsRange = InputArgs.filtered(Ids: LIPO_replace);
238 if (ActionArgs.size() > 1 &&
239 ActionArgs.size() !=
240 static_cast<size_t>(std::distance(first: ReplacementArgsRange.begin(),
241 last: ReplacementArgsRange.end()))) {
242 std::string Buf;
243 raw_string_ostream OS(Buf);
244 OS << "only one of the following actions can be specified:";
245 for (auto *Arg : ActionArgs)
246 OS << " " << Arg->getSpelling();
247 reportError(Message: Buf);
248 }
249
250 switch (ActionArgs[0]->getOption().getID()) {
251 case LIPO_verify_arch:
252 llvm::append_range(C&: C.VerifyArchList,
253 R: InputArgs.getAllArgValues(Id: LIPO_verify_arch));
254 if (C.VerifyArchList.empty())
255 reportError(
256 Message: "verify_arch requires at least one architecture to be specified");
257 if (C.InputFiles.size() > 1)
258 reportError(Message: "verify_arch expects a single input file");
259 C.ActionToPerform = LipoAction::VerifyArch;
260 return C;
261
262 case LIPO_archs:
263 if (C.InputFiles.size() > 1)
264 reportError(Message: "archs expects a single input file");
265 C.ActionToPerform = LipoAction::PrintArchs;
266 return C;
267
268 case LIPO_info:
269 C.ActionToPerform = LipoAction::PrintInfo;
270 return C;
271
272 case LIPO_thin:
273 if (C.InputFiles.size() > 1)
274 reportError(Message: "thin expects a single input file");
275 if (C.OutputFile.empty())
276 reportError(Message: "thin expects a single output file");
277 C.ArchType = ActionArgs[0]->getValue();
278 validateArchitectureName(ArchitectureName: C.ArchType);
279 C.ActionToPerform = LipoAction::ThinArch;
280 return C;
281
282 case LIPO_extract:
283 if (C.InputFiles.size() > 1)
284 reportError(Message: "extract expects a single input file");
285 if (C.OutputFile.empty())
286 reportError(Message: "extract expects a single output file");
287 C.ArchType = ActionArgs[0]->getValue();
288 validateArchitectureName(ArchitectureName: C.ArchType);
289 C.ActionToPerform = LipoAction::ExtractArch;
290 return C;
291
292 case LIPO_create:
293 if (C.OutputFile.empty())
294 reportError(Message: "create expects a single output file to be specified");
295 C.ActionToPerform = LipoAction::CreateUniversal;
296 return C;
297
298 case LIPO_replace:
299 for (auto *Action : ActionArgs) {
300 assert(Action->getValue(1) && "file_name is missing");
301 validateArchitectureName(ArchitectureName: Action->getValue(N: 0));
302 C.ReplacementFiles.push_back(
303 Elt: {.ArchType: StringRef(Action->getValue(N: 0)), .FileName: Action->getValue(N: 1)});
304 }
305
306 if (C.OutputFile.empty())
307 reportError(Message: "replace expects a single output file to be specified");
308 if (C.InputFiles.size() > 1)
309 reportError(Message: "replace expects a single input file");
310 C.ActionToPerform = LipoAction::ReplaceArch;
311 return C;
312
313 default:
314 reportError(Message: "llvm-lipo action unspecified");
315 }
316}
317
318static SmallVector<OwningBinary<Binary>, 1>
319readInputBinaries(LLVMContext &LLVMCtx, ArrayRef<InputFile> InputFiles) {
320 SmallVector<OwningBinary<Binary>, 1> InputBinaries;
321 for (const InputFile &IF : InputFiles) {
322 Expected<OwningBinary<Binary>> BinaryOrErr =
323 createBinary(Path: IF.FileName, Context: &LLVMCtx);
324 if (!BinaryOrErr)
325 reportError(File: IF.FileName, E: BinaryOrErr.takeError());
326 const Binary *B = BinaryOrErr->getBinary();
327 if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() &&
328 !B->isIR())
329 reportError(Message: "File " + IF.FileName + " has unsupported binary format");
330 if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) {
331 const auto S = B->isMachO() ? Slice(*cast<MachOObjectFile>(Val: B))
332 : B->isArchive()
333 ? createSliceFromArchive(LLVMCtx, A: *cast<Archive>(Val: B))
334 : createSliceFromIR(IRO: *cast<IRObjectFile>(Val: B), Align: 0);
335 const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
336 Arch: MachO::getArchitectureFromName(
337 Name: Triple(*IF.ArchType).getArchName()))
338 .first;
339 // For compatibility with cctools' lipo the comparison is relaxed just to
340 // checking cputypes.
341 if (S.getCPUType() != SpecifiedCPUType)
342 reportError(Message: "specified architecture: " + *IF.ArchType +
343 " for file: " + B->getFileName() +
344 " does not match the file's architecture (" +
345 S.getArchString() + ")");
346 }
347 InputBinaries.push_back(Elt: std::move(*BinaryOrErr));
348 }
349 return InputBinaries;
350}
351
352[[noreturn]] static void
353verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
354 ArrayRef<std::string> VerifyArchList) {
355 assert(!VerifyArchList.empty() &&
356 "The list of architectures should be non-empty");
357 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
358
359 for (StringRef Arch : VerifyArchList)
360 validateArchitectureName(ArchitectureName: Arch);
361
362 if (auto UO =
363 dyn_cast<MachOUniversalBinary>(Val: InputBinaries.front().getBinary())) {
364 for (StringRef Arch : VerifyArchList) {
365 Expected<MachOUniversalBinary::ObjectForArch> Obj =
366 UO->getObjectForArch(ArchName: Arch);
367 if (!Obj)
368 exit(EXIT_FAILURE);
369 }
370 } else if (auto O =
371 dyn_cast<MachOObjectFile>(Val: InputBinaries.front().getBinary())) {
372 const Triple::ArchType ObjectArch = O->getArch();
373 for (StringRef Arch : VerifyArchList)
374 if (ObjectArch != Triple(Arch).getArch())
375 exit(EXIT_FAILURE);
376 } else {
377 llvm_unreachable("Unexpected binary format");
378 }
379 exit(EXIT_SUCCESS);
380}
381
382static void printBinaryArchs(LLVMContext &LLVMCtx, const Binary *Binary,
383 raw_ostream &OS) {
384 // Prints trailing space for compatibility with cctools lipo.
385 if (auto UO = dyn_cast<MachOUniversalBinary>(Val: Binary)) {
386 for (const auto &O : UO->objects()) {
387 // Order here is important, because both MachOObjectFile and
388 // IRObjectFile can be created with a binary that has embedded bitcode.
389 Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
390 O.getAsObjectFile();
391 if (MachOObjOrError) {
392 OS << Slice(*(MachOObjOrError->get())).getArchString() << " ";
393 continue;
394 }
395 Expected<std::unique_ptr<IRObjectFile>> IROrError =
396 O.getAsIRObject(Ctx&: LLVMCtx);
397 if (IROrError) {
398 consumeError(Err: MachOObjOrError.takeError());
399 Expected<Slice> SliceOrErr = Slice::create(IRO: **IROrError, Align: O.getAlign());
400 if (!SliceOrErr) {
401 reportError(File: Binary->getFileName(), E: SliceOrErr.takeError());
402 continue;
403 }
404 OS << SliceOrErr.get().getArchString() << " ";
405 continue;
406 }
407 Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
408 if (ArchiveOrError) {
409 consumeError(Err: MachOObjOrError.takeError());
410 consumeError(Err: IROrError.takeError());
411 OS << createSliceFromArchive(LLVMCtx, A: **ArchiveOrError).getArchString()
412 << " ";
413 continue;
414 }
415 consumeError(Err: ArchiveOrError.takeError());
416 reportError(File: Binary->getFileName(), E: MachOObjOrError.takeError());
417 reportError(File: Binary->getFileName(), E: IROrError.takeError());
418 }
419 OS << "\n";
420 return;
421 }
422
423 if (const auto *MachO = dyn_cast<MachOObjectFile>(Val: Binary)) {
424 OS << Slice(*MachO).getArchString() << " \n";
425 return;
426 }
427
428 // This should be always the case, as this is tested in readInputBinaries
429 const auto *IR = cast<IRObjectFile>(Val: Binary);
430 Expected<Slice> SliceOrErr = createSliceFromIR(IRO: *IR, Align: 0);
431 if (!SliceOrErr)
432 reportError(File: IR->getFileName(), E: SliceOrErr.takeError());
433
434 OS << SliceOrErr->getArchString() << " \n";
435}
436
437[[noreturn]] static void
438printArchs(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {
439 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
440 printBinaryArchs(LLVMCtx, Binary: InputBinaries.front().getBinary(), OS&: outs());
441 exit(EXIT_SUCCESS);
442}
443
444[[noreturn]] static void
445printInfo(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {
446 // Group universal and thin files together for compatibility with cctools lipo
447 for (auto &IB : InputBinaries) {
448 const Binary *Binary = IB.getBinary();
449 if (Binary->isMachOUniversalBinary()) {
450 outs() << "Architectures in the fat file: " << Binary->getFileName()
451 << " are: ";
452 printBinaryArchs(LLVMCtx, Binary, OS&: outs());
453 }
454 }
455 for (auto &IB : InputBinaries) {
456 const Binary *Binary = IB.getBinary();
457 if (!Binary->isMachOUniversalBinary()) {
458 assert(Binary->isMachO() && "expected MachO binary");
459 outs() << "Non-fat file: " << Binary->getFileName()
460 << " is architecture: ";
461 printBinaryArchs(LLVMCtx, Binary, OS&: outs());
462 }
463 }
464 exit(EXIT_SUCCESS);
465}
466
467[[noreturn]] static void thinSlice(LLVMContext &LLVMCtx,
468 ArrayRef<OwningBinary<Binary>> InputBinaries,
469 StringRef ArchType,
470 StringRef OutputFileName) {
471 assert(!ArchType.empty() && "The architecture type should be non-empty");
472 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
473 assert(!OutputFileName.empty() && "Thin expects a single output file");
474
475 if (InputBinaries.front().getBinary()->isMachO()) {
476 reportError(Message: "input file " +
477 InputBinaries.front().getBinary()->getFileName() +
478 " must be a fat file when the -thin option is specified");
479 exit(EXIT_FAILURE);
480 }
481
482 auto *UO = cast<MachOUniversalBinary>(Val: InputBinaries.front().getBinary());
483 Expected<std::unique_ptr<MachOObjectFile>> Obj =
484 UO->getMachOObjectForArch(ArchName: ArchType);
485 Expected<std::unique_ptr<IRObjectFile>> IRObj =
486 UO->getIRObjectForArch(ArchName: ArchType, Ctx&: LLVMCtx);
487 Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchName: ArchType);
488 if (!Obj && !IRObj && !Ar)
489 reportError(Message: "fat input file " + UO->getFileName() +
490 " does not contain the specified architecture " + ArchType +
491 " to thin it to");
492 Binary *B;
493 // Order here is important, because both Obj and IRObj will be valid with a
494 // binary that has embedded bitcode.
495 if (Obj)
496 B = Obj->get();
497 else if (IRObj)
498 B = IRObj->get();
499 else
500 B = Ar->get();
501
502 Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
503 FileOutputBuffer::create(FilePath: OutputFileName,
504 Size: B->getMemoryBufferRef().getBufferSize(),
505 Flags: sys::fs::can_execute(Path: UO->getFileName())
506 ? FileOutputBuffer::F_executable
507 : 0);
508 if (!OutFileOrError)
509 reportError(File: OutputFileName, E: OutFileOrError.takeError());
510 std::copy(first: B->getMemoryBufferRef().getBufferStart(),
511 last: B->getMemoryBufferRef().getBufferEnd(),
512 result: OutFileOrError.get()->getBufferStart());
513 if (Error E = OutFileOrError.get()->commit())
514 reportError(File: OutputFileName, E: std::move(E));
515 exit(EXIT_SUCCESS);
516}
517
518static void checkArchDuplicates(ArrayRef<Slice> Slices) {
519 DenseMap<uint64_t, const Binary *> CPUIds;
520 for (const auto &S : Slices) {
521 auto Entry = CPUIds.try_emplace(Key: S.getCPUID(), Args: S.getBinary());
522 if (!Entry.second)
523 reportError(Message: Entry.first->second->getFileName() + " and " +
524 S.getBinary()->getFileName() +
525 " have the same architecture " + S.getArchString() +
526 " and therefore cannot be in the same universal binary");
527 }
528}
529
530template <typename Range>
531static void updateAlignments(Range &Slices,
532 const StringMap<const uint32_t> &Alignments) {
533 for (auto &Slice : Slices) {
534 auto Alignment = Alignments.find(Slice.getArchString());
535 if (Alignment != Alignments.end())
536 Slice.setP2Alignment(Alignment->second);
537 }
538}
539
540static void checkUnusedAlignments(ArrayRef<Slice> Slices,
541 const StringMap<const uint32_t> &Alignments) {
542 auto HasArch = [&](StringRef Arch) {
543 return llvm::any_of(Range&: Slices,
544 P: [Arch](Slice S) { return S.getArchString() == Arch; });
545 };
546 for (StringRef Arch : Alignments.keys())
547 if (!HasArch(Arch))
548 reportError(Message: "-segalign " + Arch +
549 " <value> specified but resulting fat file does not contain "
550 "that architecture ");
551}
552
553// Updates vector ExtractedObjects with the MachOObjectFiles extracted from
554// Universal Binary files to transfer ownership.
555static SmallVector<Slice, 2>
556buildSlices(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
557 const StringMap<const uint32_t> &Alignments,
558 SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {
559 SmallVector<Slice, 2> Slices;
560 for (auto &IB : InputBinaries) {
561 const Binary *InputBinary = IB.getBinary();
562 if (auto UO = dyn_cast<MachOUniversalBinary>(Val: InputBinary)) {
563 for (const auto &O : UO->objects()) {
564 // Order here is important, because both MachOObjectFile and
565 // IRObjectFile can be created with a binary that has embedded bitcode.
566 Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
567 O.getAsObjectFile();
568 if (BinaryOrError) {
569 Slices.emplace_back(Args&: *(BinaryOrError.get()), Args: O.getAlign());
570 ExtractedObjects.push_back(Elt: std::move(BinaryOrError.get()));
571 continue;
572 }
573 Expected<std::unique_ptr<IRObjectFile>> IROrError =
574 O.getAsIRObject(Ctx&: LLVMCtx);
575 if (IROrError) {
576 consumeError(Err: BinaryOrError.takeError());
577 Slice S = createSliceFromIR(IRO: **IROrError, Align: O.getAlign());
578 ExtractedObjects.emplace_back(Args: std::move(IROrError.get()));
579 Slices.emplace_back(Args: std::move(S));
580 continue;
581 }
582 reportError(File: InputBinary->getFileName(), E: BinaryOrError.takeError());
583 }
584 } else if (const auto *O = dyn_cast<MachOObjectFile>(Val: InputBinary)) {
585 Slices.emplace_back(Args: *O);
586 } else if (const auto *A = dyn_cast<Archive>(Val: InputBinary)) {
587 Slices.push_back(Elt: createSliceFromArchive(LLVMCtx, A: *A));
588 } else if (const auto *IRO = dyn_cast<IRObjectFile>(Val: InputBinary)) {
589 // Original Apple's lipo set the alignment to 0
590 Expected<Slice> SliceOrErr = Slice::create(IRO: *IRO, Align: 0);
591 if (!SliceOrErr) {
592 reportError(File: InputBinary->getFileName(), E: SliceOrErr.takeError());
593 continue;
594 }
595 Slices.emplace_back(Args: std::move(SliceOrErr.get()));
596 } else {
597 llvm_unreachable("Unexpected binary format");
598 }
599 }
600 updateAlignments(Slices, Alignments);
601 return Slices;
602}
603
604[[noreturn]] static void
605createUniversalBinary(LLVMContext &LLVMCtx,
606 ArrayRef<OwningBinary<Binary>> InputBinaries,
607 const StringMap<const uint32_t> &Alignments,
608 StringRef OutputFileName, FatHeaderType HeaderType) {
609 assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
610 assert(!OutputFileName.empty() && "Create expects a single output file");
611
612 SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;
613 SmallVector<Slice, 1> Slices =
614 buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
615 checkArchDuplicates(Slices);
616 checkUnusedAlignments(Slices, Alignments);
617
618 llvm::stable_sort(Range&: Slices);
619 if (Error E = writeUniversalBinary(Slices, OutputFileName, FatHeader: HeaderType))
620 reportError(E: std::move(E));
621
622 exit(EXIT_SUCCESS);
623}
624
625[[noreturn]] static void
626extractSlice(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
627 const StringMap<const uint32_t> &Alignments, StringRef ArchType,
628 StringRef OutputFileName) {
629 assert(!ArchType.empty() &&
630 "The architecture type should be non-empty");
631 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
632 assert(!OutputFileName.empty() && "Thin expects a single output file");
633
634 if (InputBinaries.front().getBinary()->isMachO()) {
635 reportError(Message: "input file " +
636 InputBinaries.front().getBinary()->getFileName() +
637 " must be a fat file when the -extract option is specified");
638 }
639
640 SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
641 SmallVector<Slice, 2> Slices =
642 buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
643 erase_if(C&: Slices, P: [ArchType](const Slice &S) {
644 return ArchType != S.getArchString();
645 });
646
647 if (Slices.empty())
648 reportError(
649 Message: "fat input file " + InputBinaries.front().getBinary()->getFileName() +
650 " does not contain the specified architecture " + ArchType);
651
652 llvm::stable_sort(Range&: Slices);
653 if (Error E = writeUniversalBinary(Slices, OutputFileName))
654 reportError(E: std::move(E));
655 exit(EXIT_SUCCESS);
656}
657
658static StringMap<Slice>
659buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
660 const StringMap<const uint32_t> &Alignments) {
661 StringMap<Slice> Slices;
662 // populates StringMap of slices to replace with; error checks for mismatched
663 // replace flag args, fat files, and duplicate arch_types
664 for (const auto &OB : ReplacementBinaries) {
665 const Binary *ReplacementBinary = OB.getBinary();
666 auto O = dyn_cast<MachOObjectFile>(Val: ReplacementBinary);
667 if (!O)
668 reportError(Message: "replacement file: " + ReplacementBinary->getFileName() +
669 " is a fat file (must be a thin file)");
670 Slice S(*O);
671 auto Entry = Slices.try_emplace(Key: S.getArchString(), Args&: S);
672 if (!Entry.second)
673 reportError(Message: "-replace " + S.getArchString() +
674 " <file_name> specified multiple times: " +
675 Entry.first->second.getBinary()->getFileName() + ", " +
676 O->getFileName());
677 }
678 auto SlicesMapRange = map_range(
679 C&: Slices, F: [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
680 updateAlignments(Slices&: SlicesMapRange, Alignments);
681 return Slices;
682}
683
684[[noreturn]] static void
685replaceSlices(LLVMContext &LLVMCtx,
686 ArrayRef<OwningBinary<Binary>> InputBinaries,
687 const StringMap<const uint32_t> &Alignments,
688 StringRef OutputFileName, ArrayRef<InputFile> ReplacementFiles) {
689 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
690 assert(!OutputFileName.empty() && "Replace expects a single output file");
691
692 if (InputBinaries.front().getBinary()->isMachO())
693 reportError(Message: "input file " +
694 InputBinaries.front().getBinary()->getFileName() +
695 " must be a fat file when the -replace option is specified");
696
697 SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =
698 readInputBinaries(LLVMCtx, InputFiles: ReplacementFiles);
699
700 StringMap<Slice> ReplacementSlices =
701 buildReplacementSlices(ReplacementBinaries, Alignments);
702 SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
703 SmallVector<Slice, 2> Slices =
704 buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
705
706 for (auto &Slice : Slices) {
707 auto It = ReplacementSlices.find(Key: Slice.getArchString());
708 if (It != ReplacementSlices.end()) {
709 Slice = It->second;
710 ReplacementSlices.erase(I: It); // only keep remaining replacing arch_types
711 }
712 }
713
714 if (!ReplacementSlices.empty())
715 reportError(Message: "-replace " + ReplacementSlices.begin()->first() +
716 " <file_name> specified but fat file: " +
717 InputBinaries.front().getBinary()->getFileName() +
718 " does not contain that architecture");
719
720 checkUnusedAlignments(Slices, Alignments);
721
722 llvm::stable_sort(Range&: Slices);
723 if (Error E = writeUniversalBinary(Slices, OutputFileName))
724 reportError(E: std::move(E));
725 exit(EXIT_SUCCESS);
726}
727
728int llvm_lipo_main(int argc, char **argv, const llvm::ToolContext &) {
729 llvm::InitializeAllTargetInfos();
730 llvm::InitializeAllTargetMCs();
731 llvm::InitializeAllAsmParsers();
732
733 Config C = parseLipoOptions(ArgsArr: ArrayRef(argv + 1, argc - 1));
734 LLVMContext LLVMCtx;
735 SmallVector<OwningBinary<Binary>, 1> InputBinaries =
736 readInputBinaries(LLVMCtx, InputFiles: C.InputFiles);
737
738 switch (C.ActionToPerform) {
739 case LipoAction::VerifyArch:
740 verifyArch(InputBinaries, VerifyArchList: C.VerifyArchList);
741 break;
742 case LipoAction::PrintArchs:
743 printArchs(LLVMCtx, InputBinaries);
744 break;
745 case LipoAction::PrintInfo:
746 printInfo(LLVMCtx, InputBinaries);
747 break;
748 case LipoAction::ThinArch:
749 thinSlice(LLVMCtx, InputBinaries, ArchType: C.ArchType, OutputFileName: C.OutputFile);
750 break;
751 case LipoAction::ExtractArch:
752 extractSlice(LLVMCtx, InputBinaries, Alignments: C.SegmentAlignments, ArchType: C.ArchType,
753 OutputFileName: C.OutputFile);
754 break;
755 case LipoAction::CreateUniversal:
756 createUniversalBinary(
757 LLVMCtx, InputBinaries, Alignments: C.SegmentAlignments, OutputFileName: C.OutputFile,
758 HeaderType: C.UseFat64 ? FatHeaderType::Fat64Header : FatHeaderType::FatHeader);
759 break;
760 case LipoAction::ReplaceArch:
761 replaceSlices(LLVMCtx, InputBinaries, Alignments: C.SegmentAlignments, OutputFileName: C.OutputFile,
762 ReplacementFiles: C.ReplacementFiles);
763 break;
764 }
765 return EXIT_SUCCESS;
766}
767