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