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