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