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