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/ADT/Triple.h"
15 #include "llvm/Object/Binary.h"
16 #include "llvm/Object/MachO.h"
17 #include "llvm/Object/MachOUniversal.h"
18 #include "llvm/Object/ObjectFile.h"
19 #include "llvm/Option/Arg.h"
20 #include "llvm/Option/ArgList.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/FileOutputBuffer.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/WithColor.h"
26 #include "llvm/TextAPI/MachO/Architecture.h"
27
28 using namespace llvm;
29 using namespace llvm::object;
30
31 static const StringRef ToolName = "llvm-lipo";
32
reportError(Twine Message)33 LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) {
34 WithColor::error(errs(), ToolName) << Message << "\n";
35 errs().flush();
36 exit(EXIT_FAILURE);
37 }
38
reportError(StringRef File,Error E)39 LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) {
40 assert(E);
41 std::string Buf;
42 raw_string_ostream OS(Buf);
43 logAllUnhandledErrors(std::move(E), OS);
44 OS.flush();
45 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
46 exit(EXIT_FAILURE);
47 }
48
49 namespace {
50 enum LipoID {
51 LIPO_INVALID = 0, // This is not an option ID.
52 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
53 HELPTEXT, METAVAR, VALUES) \
54 LIPO_##ID,
55 #include "LipoOpts.inc"
56 #undef OPTION
57 };
58
59 // LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr.
60 const char *const *LIPO_nullptr = nullptr;
61 #define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
62 #include "LipoOpts.inc"
63 #undef PREFIX
64
65 static const opt::OptTable::Info LipoInfoTable[] = {
66 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
67 HELPTEXT, METAVAR, VALUES) \
68 {LIPO_##PREFIX, NAME, HELPTEXT, \
69 METAVAR, LIPO_##ID, opt::Option::KIND##Class, \
70 PARAM, FLAGS, LIPO_##GROUP, \
71 LIPO_##ALIAS, ALIASARGS, VALUES},
72 #include "LipoOpts.inc"
73 #undef OPTION
74 };
75
76 class LipoOptTable : public opt::OptTable {
77 public:
LipoOptTable()78 LipoOptTable() : OptTable(LipoInfoTable) {}
79 };
80
81 enum class LipoAction {
82 PrintArchs,
83 PrintInfo,
84 VerifyArch,
85 ThinArch,
86 ExtractArch,
87 CreateUniversal,
88 ReplaceArch,
89 };
90
91 struct InputFile {
92 Optional<StringRef> ArchType;
93 StringRef FileName;
94 };
95
96 struct Config {
97 SmallVector<InputFile, 1> InputFiles;
98 SmallVector<std::string, 1> VerifyArchList;
99 SmallVector<InputFile, 1> ReplacementFiles;
100 StringMap<const uint32_t> SegmentAlignments;
101 std::string ArchType;
102 std::string OutputFile;
103 LipoAction ActionToPerform;
104 };
105
106 // For compatibility with cctools lipo, a file's alignment is calculated as the
107 // minimum aligment of all segments. For object files, the file's alignment is
108 // the maximum alignment of its sections.
calculateFileAlignment(const MachOObjectFile & O)109 static uint32_t calculateFileAlignment(const MachOObjectFile &O) {
110 uint32_t P2CurrentAlignment;
111 uint32_t P2MinAlignment = MachOUniversalBinary::MaxSectionAlignment;
112 const bool Is64Bit = O.is64Bit();
113
114 for (const auto &LC : O.load_commands()) {
115 if (LC.C.cmd != (Is64Bit ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT))
116 continue;
117 if (O.getHeader().filetype == MachO::MH_OBJECT) {
118 unsigned NumberOfSections =
119 (Is64Bit ? O.getSegment64LoadCommand(LC).nsects
120 : O.getSegmentLoadCommand(LC).nsects);
121 P2CurrentAlignment = NumberOfSections ? 2 : P2MinAlignment;
122 for (unsigned SI = 0; SI < NumberOfSections; ++SI) {
123 P2CurrentAlignment = std::max(P2CurrentAlignment,
124 (Is64Bit ? O.getSection64(LC, SI).align
125 : O.getSection(LC, SI).align));
126 }
127 } else {
128 P2CurrentAlignment =
129 countTrailingZeros(Is64Bit ? O.getSegment64LoadCommand(LC).vmaddr
130 : O.getSegmentLoadCommand(LC).vmaddr);
131 }
132 P2MinAlignment = std::min(P2MinAlignment, P2CurrentAlignment);
133 }
134 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment
135 return std::max(
136 static_cast<uint32_t>(2),
137 std::min(P2MinAlignment, static_cast<uint32_t>(
138 MachOUniversalBinary::MaxSectionAlignment)));
139 }
140
calculateAlignment(const MachOObjectFile * ObjectFile)141 static uint32_t calculateAlignment(const MachOObjectFile *ObjectFile) {
142 switch (ObjectFile->getHeader().cputype) {
143 case MachO::CPU_TYPE_I386:
144 case MachO::CPU_TYPE_X86_64:
145 case MachO::CPU_TYPE_POWERPC:
146 case MachO::CPU_TYPE_POWERPC64:
147 return 12; // log2 value of page size(4k) for x86 and PPC
148 case MachO::CPU_TYPE_ARM:
149 case MachO::CPU_TYPE_ARM64:
150 case MachO::CPU_TYPE_ARM64_32:
151 return 14; // log2 value of page size(16k) for Darwin ARM
152 default:
153 return calculateFileAlignment(*ObjectFile);
154 }
155 }
156
157 class Slice {
158 const Binary *B;
159 uint32_t CPUType;
160 uint32_t CPUSubType;
161 std::string ArchName;
162
163 // P2Alignment field stores slice alignment values from universal
164 // binaries. This is also needed to order the slices so the total
165 // file size can be calculated before creating the output buffer.
166 uint32_t P2Alignment;
167
168 public:
Slice(const MachOObjectFile * O,uint32_t Align)169 Slice(const MachOObjectFile *O, uint32_t Align)
170 : B(O), CPUType(O->getHeader().cputype),
171 CPUSubType(O->getHeader().cpusubtype),
172 ArchName(std::string(O->getArchTriple().getArchName())),
173 P2Alignment(Align) {}
174
Slice(const MachOObjectFile * O)175 explicit Slice(const MachOObjectFile *O) : Slice(O, calculateAlignment(O)){};
176
Slice(const Archive * A)177 explicit Slice(const Archive *A) : B(A) {
178 Error Err = Error::success();
179 std::unique_ptr<MachOObjectFile> FO = nullptr;
180 for (const Archive::Child &Child : A->children(Err)) {
181 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
182 if (!ChildOrErr)
183 reportError(A->getFileName(), ChildOrErr.takeError());
184 Binary *Bin = ChildOrErr.get().get();
185 if (Bin->isMachOUniversalBinary())
186 reportError(("archive member " + Bin->getFileName() +
187 " is a fat file (not allowed in an archive)")
188 .str());
189 if (!Bin->isMachO())
190 reportError(("archive member " + Bin->getFileName() +
191 " is not a MachO file (not allowed in an archive)"));
192 MachOObjectFile *O = cast<MachOObjectFile>(Bin);
193 if (FO &&
194 std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) !=
195 std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
196 reportError(("archive member " + O->getFileName() + " cputype (" +
197 Twine(O->getHeader().cputype) + ") and cpusubtype(" +
198 Twine(O->getHeader().cpusubtype) +
199 ") does not match previous archive members cputype (" +
200 Twine(FO->getHeader().cputype) + ") and cpusubtype(" +
201 Twine(FO->getHeader().cpusubtype) +
202 ") (all members must match) " + FO->getFileName())
203 .str());
204 }
205 if (!FO) {
206 ChildOrErr.get().release();
207 FO.reset(O);
208 }
209 }
210 if (Err)
211 reportError(A->getFileName(), std::move(Err));
212 if (!FO)
213 reportError(("empty archive with no architecture specification: " +
214 A->getFileName() + " (can't determine architecture for it)")
215 .str());
216 CPUType = FO->getHeader().cputype;
217 CPUSubType = FO->getHeader().cpusubtype;
218 ArchName = std::string(FO->getArchTriple().getArchName());
219 // Replicate the behavior of cctools lipo.
220 P2Alignment = FO->is64Bit() ? 3 : 2;
221 }
222
setP2Alignment(uint32_t Align)223 void setP2Alignment(uint32_t Align) { P2Alignment = Align; }
224
getBinary() const225 const Binary *getBinary() const { return B; }
226
getCPUType() const227 uint32_t getCPUType() const { return CPUType; }
228
getCPUSubType() const229 uint32_t getCPUSubType() const { return CPUSubType; }
230
getP2Alignment() const231 uint32_t getP2Alignment() const { return P2Alignment; }
232
getCPUID() const233 uint64_t getCPUID() const {
234 return static_cast<uint64_t>(CPUType) << 32 | CPUSubType;
235 }
236
getArchString() const237 std::string getArchString() const {
238 if (!ArchName.empty())
239 return ArchName;
240 return ("unknown(" + Twine(CPUType) + "," +
241 Twine(CPUSubType & ~MachO::CPU_SUBTYPE_MASK) + ")")
242 .str();
243 }
244
operator <(const Slice & Lhs,const Slice & Rhs)245 friend bool operator<(const Slice &Lhs, const Slice &Rhs) {
246 if (Lhs.CPUType == Rhs.CPUType)
247 return Lhs.CPUSubType < Rhs.CPUSubType;
248 // force arm64-family to follow after all other slices for
249 // compatibility with cctools lipo
250 if (Lhs.CPUType == MachO::CPU_TYPE_ARM64)
251 return false;
252 if (Rhs.CPUType == MachO::CPU_TYPE_ARM64)
253 return true;
254 // Sort by alignment to minimize file size
255 return Lhs.P2Alignment < Rhs.P2Alignment;
256 }
257 };
258
259 } // end namespace
260
validateArchitectureName(StringRef ArchitectureName)261 static void validateArchitectureName(StringRef ArchitectureName) {
262 if (!MachOObjectFile::isValidArch(ArchitectureName)) {
263 std::string Buf;
264 raw_string_ostream OS(Buf);
265 OS << "Invalid architecture: " << ArchitectureName
266 << "\nValid architecture names are:";
267 for (auto arch : MachOObjectFile::getValidArchs())
268 OS << " " << arch;
269 reportError(OS.str());
270 }
271 }
272
parseLipoOptions(ArrayRef<const char * > ArgsArr)273 static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
274 Config C;
275 LipoOptTable T;
276 unsigned MissingArgumentIndex, MissingArgumentCount;
277 opt::InputArgList InputArgs =
278 T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
279
280 if (MissingArgumentCount)
281 reportError("missing argument to " +
282 StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
283 " option");
284
285 if (InputArgs.size() == 0) {
286 // PrintHelp does not accept Twine.
287 T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
288 exit(EXIT_FAILURE);
289 }
290
291 if (InputArgs.hasArg(LIPO_help)) {
292 // PrintHelp does not accept Twine.
293 T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
294 exit(EXIT_SUCCESS);
295 }
296
297 if (InputArgs.hasArg(LIPO_version)) {
298 outs() << ToolName + "\n";
299 cl::PrintVersionMessage();
300 exit(EXIT_SUCCESS);
301 }
302
303 for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN))
304 reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");
305
306 for (auto Arg : InputArgs.filtered(LIPO_INPUT))
307 C.InputFiles.push_back({None, Arg->getValue()});
308 for (auto Arg : InputArgs.filtered(LIPO_arch)) {
309 validateArchitectureName(Arg->getValue(0));
310 if (!Arg->getValue(1))
311 reportError(
312 "arch is missing an argument: expects -arch arch_type file_name");
313 C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)});
314 }
315
316 if (C.InputFiles.empty())
317 reportError("at least one input file should be specified");
318
319 if (InputArgs.hasArg(LIPO_output))
320 C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output));
321
322 for (auto Segalign : InputArgs.filtered(LIPO_segalign)) {
323 if (!Segalign->getValue(1))
324 reportError("segalign is missing an argument: expects -segalign "
325 "arch_type alignment_value");
326
327 validateArchitectureName(Segalign->getValue(0));
328
329 uint32_t AlignmentValue;
330 if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16))
331 reportError("argument to -segalign <arch_type> " +
332 Twine(Segalign->getValue(1)) +
333 " (hex) is not a proper hexadecimal number");
334 if (!isPowerOf2_32(AlignmentValue))
335 reportError("argument to -segalign <arch_type> " +
336 Twine(Segalign->getValue(1)) +
337 " (hex) must be a non-zero power of two");
338 if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)
339 reportError(
340 "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) +
341 " (hex) must be less than or equal to the maximum section align 2^" +
342 Twine(MachOUniversalBinary::MaxSectionAlignment));
343 auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0),
344 Log2_32(AlignmentValue));
345 if (!Entry.second)
346 reportError("-segalign " + Twine(Segalign->getValue(0)) +
347 " <alignment_value> specified multiple times: " +
348 Twine(1 << Entry.first->second) + ", " +
349 Twine(AlignmentValue));
350 }
351
352 SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
353 if (ActionArgs.empty())
354 reportError("at least one action should be specified");
355 // errors if multiple actions specified other than replace
356 // multiple replace flags may be specified, as long as they are not mixed with
357 // other action flags
358 auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace);
359 if (ActionArgs.size() > 1 &&
360 ActionArgs.size() !=
361 static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
362 ReplacementArgsRange.end()))) {
363 std::string Buf;
364 raw_string_ostream OS(Buf);
365 OS << "only one of the following actions can be specified:";
366 for (auto Arg : ActionArgs)
367 OS << " " << Arg->getSpelling();
368 reportError(OS.str());
369 }
370
371 switch (ActionArgs[0]->getOption().getID()) {
372 case LIPO_verify_arch:
373 for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch))
374 C.VerifyArchList.push_back(A);
375 if (C.VerifyArchList.empty())
376 reportError(
377 "verify_arch requires at least one architecture to be specified");
378 if (C.InputFiles.size() > 1)
379 reportError("verify_arch expects a single input file");
380 C.ActionToPerform = LipoAction::VerifyArch;
381 return C;
382
383 case LIPO_archs:
384 if (C.InputFiles.size() > 1)
385 reportError("archs expects a single input file");
386 C.ActionToPerform = LipoAction::PrintArchs;
387 return C;
388
389 case LIPO_info:
390 C.ActionToPerform = LipoAction::PrintInfo;
391 return C;
392
393 case LIPO_thin:
394 if (C.InputFiles.size() > 1)
395 reportError("thin expects a single input file");
396 if (C.OutputFile.empty())
397 reportError("thin expects a single output file");
398 C.ArchType = ActionArgs[0]->getValue();
399 validateArchitectureName(C.ArchType);
400 C.ActionToPerform = LipoAction::ThinArch;
401 return C;
402
403 case LIPO_extract:
404 if (C.InputFiles.size() > 1)
405 reportError("extract expects a single input file");
406 if (C.OutputFile.empty())
407 reportError("extract expects a single output file");
408 C.ArchType = ActionArgs[0]->getValue();
409 validateArchitectureName(C.ArchType);
410 C.ActionToPerform = LipoAction::ExtractArch;
411 return C;
412
413 case LIPO_create:
414 if (C.OutputFile.empty())
415 reportError("create expects a single output file to be specified");
416 C.ActionToPerform = LipoAction::CreateUniversal;
417 return C;
418
419 case LIPO_replace:
420 for (auto Action : ActionArgs) {
421 if (!Action->getValue(1))
422 reportError(
423 "replace is missing an argument: expects -replace arch_type "
424 "file_name");
425 validateArchitectureName(Action->getValue(0));
426 C.ReplacementFiles.push_back(
427 {StringRef(Action->getValue(0)), Action->getValue(1)});
428 }
429
430 if (C.OutputFile.empty())
431 reportError("replace expects a single output file to be specified");
432 if (C.InputFiles.size() > 1)
433 reportError("replace expects a single input file");
434 C.ActionToPerform = LipoAction::ReplaceArch;
435 return C;
436
437 default:
438 reportError("llvm-lipo action unspecified");
439 }
440 }
441
442 static SmallVector<OwningBinary<Binary>, 1>
readInputBinaries(ArrayRef<InputFile> InputFiles)443 readInputBinaries(ArrayRef<InputFile> InputFiles) {
444 SmallVector<OwningBinary<Binary>, 1> InputBinaries;
445 for (const InputFile &IF : InputFiles) {
446 Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(IF.FileName);
447 if (!BinaryOrErr)
448 reportError(IF.FileName, BinaryOrErr.takeError());
449 const Binary *B = BinaryOrErr->getBinary();
450 if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary())
451 reportError("File " + IF.FileName + " has unsupported binary format");
452 if (IF.ArchType && (B->isMachO() || B->isArchive())) {
453 const auto S = B->isMachO() ? Slice(cast<MachOObjectFile>(B))
454 : Slice(cast<Archive>(B));
455 const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
456 MachO::getArchitectureFromName(
457 Triple(*IF.ArchType).getArchName()))
458 .first;
459 // For compatibility with cctools' lipo the comparison is relaxed just to
460 // checking cputypes.
461 if (S.getCPUType() != SpecifiedCPUType)
462 reportError("specified architecture: " + *IF.ArchType +
463 " for file: " + B->getFileName() +
464 " does not match the file's architecture (" +
465 S.getArchString() + ")");
466 }
467 InputBinaries.push_back(std::move(*BinaryOrErr));
468 }
469 return InputBinaries;
470 }
471
472 LLVM_ATTRIBUTE_NORETURN
verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,ArrayRef<std::string> VerifyArchList)473 static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
474 ArrayRef<std::string> VerifyArchList) {
475 assert(!VerifyArchList.empty() &&
476 "The list of architectures should be non-empty");
477 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
478
479 for (StringRef Arch : VerifyArchList)
480 validateArchitectureName(Arch);
481
482 if (auto UO =
483 dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
484 for (StringRef Arch : VerifyArchList) {
485 Expected<MachOUniversalBinary::ObjectForArch> Obj =
486 UO->getObjectForArch(Arch);
487 if (!Obj)
488 exit(EXIT_FAILURE);
489 }
490 } else if (auto O =
491 dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) {
492 const Triple::ArchType ObjectArch = O->getArch();
493 for (StringRef Arch : VerifyArchList)
494 if (ObjectArch != Triple(Arch).getArch())
495 exit(EXIT_FAILURE);
496 } else {
497 llvm_unreachable("Unexpected binary format");
498 }
499 exit(EXIT_SUCCESS);
500 }
501
printBinaryArchs(const Binary * Binary,raw_ostream & OS)502 static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) {
503 // Prints trailing space for compatibility with cctools lipo.
504 if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) {
505 for (const auto &O : UO->objects()) {
506 Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
507 O.getAsObjectFile();
508 if (MachOObjOrError) {
509 OS << Slice(MachOObjOrError->get()).getArchString() << " ";
510 continue;
511 }
512 Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
513 if (ArchiveOrError) {
514 consumeError(MachOObjOrError.takeError());
515 OS << Slice(ArchiveOrError->get()).getArchString() << " ";
516 continue;
517 }
518 consumeError(ArchiveOrError.takeError());
519 reportError(Binary->getFileName(), MachOObjOrError.takeError());
520 }
521 OS << "\n";
522 return;
523 }
524 OS << Slice(cast<MachOObjectFile>(Binary)).getArchString() << " \n";
525 }
526
527 LLVM_ATTRIBUTE_NORETURN
printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries)528 static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) {
529 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
530 printBinaryArchs(InputBinaries.front().getBinary(), outs());
531 exit(EXIT_SUCCESS);
532 }
533
534 LLVM_ATTRIBUTE_NORETURN
printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries)535 static void printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries) {
536 // Group universal and thin files together for compatibility with cctools lipo
537 for (auto &IB : InputBinaries) {
538 const Binary *Binary = IB.getBinary();
539 if (Binary->isMachOUniversalBinary()) {
540 outs() << "Architectures in the fat file: " << Binary->getFileName()
541 << " are: ";
542 printBinaryArchs(Binary, outs());
543 }
544 }
545 for (auto &IB : InputBinaries) {
546 const Binary *Binary = IB.getBinary();
547 if (!Binary->isMachOUniversalBinary()) {
548 assert(Binary->isMachO() && "expected MachO binary");
549 outs() << "Non-fat file: " << Binary->getFileName()
550 << " is architecture: ";
551 printBinaryArchs(Binary, outs());
552 }
553 }
554 exit(EXIT_SUCCESS);
555 }
556
557 LLVM_ATTRIBUTE_NORETURN
thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,StringRef ArchType,StringRef OutputFileName)558 static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
559 StringRef ArchType, StringRef OutputFileName) {
560 assert(!ArchType.empty() && "The architecture type should be non-empty");
561 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
562 assert(!OutputFileName.empty() && "Thin expects a single output file");
563
564 if (InputBinaries.front().getBinary()->isMachO()) {
565 reportError("input file " +
566 InputBinaries.front().getBinary()->getFileName() +
567 " must be a fat file when the -thin option is specified");
568 exit(EXIT_FAILURE);
569 }
570
571 auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
572 Expected<std::unique_ptr<MachOObjectFile>> Obj =
573 UO->getMachOObjectForArch(ArchType);
574 Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);
575 if (!Obj && !Ar)
576 reportError("fat input file " + UO->getFileName() +
577 " does not contain the specified architecture " + ArchType +
578 " to thin it to");
579 Binary *B = Obj ? static_cast<Binary *>(Obj->get())
580 : static_cast<Binary *>(Ar->get());
581 Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
582 FileOutputBuffer::create(OutputFileName,
583 B->getMemoryBufferRef().getBufferSize(),
584 sys::fs::can_execute(UO->getFileName())
585 ? FileOutputBuffer::F_executable
586 : 0);
587 if (!OutFileOrError)
588 reportError(OutputFileName, OutFileOrError.takeError());
589 std::copy(B->getMemoryBufferRef().getBufferStart(),
590 B->getMemoryBufferRef().getBufferEnd(),
591 OutFileOrError.get()->getBufferStart());
592 if (Error E = OutFileOrError.get()->commit())
593 reportError(OutputFileName, std::move(E));
594 exit(EXIT_SUCCESS);
595 }
596
checkArchDuplicates(ArrayRef<Slice> Slices)597 static void checkArchDuplicates(ArrayRef<Slice> Slices) {
598 DenseMap<uint64_t, const Binary *> CPUIds;
599 for (const auto &S : Slices) {
600 auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary());
601 if (!Entry.second)
602 reportError(Entry.first->second->getFileName() + " and " +
603 S.getBinary()->getFileName() +
604 " have the same architecture " + S.getArchString() +
605 " and therefore cannot be in the same universal binary");
606 }
607 }
608
609 template <typename Range>
updateAlignments(Range & Slices,const StringMap<const uint32_t> & Alignments)610 static void updateAlignments(Range &Slices,
611 const StringMap<const uint32_t> &Alignments) {
612 for (auto &Slice : Slices) {
613 auto Alignment = Alignments.find(Slice.getArchString());
614 if (Alignment != Alignments.end())
615 Slice.setP2Alignment(Alignment->second);
616 }
617 }
618
checkUnusedAlignments(ArrayRef<Slice> Slices,const StringMap<const uint32_t> & Alignments)619 static void checkUnusedAlignments(ArrayRef<Slice> Slices,
620 const StringMap<const uint32_t> &Alignments) {
621 auto HasArch = [&](StringRef Arch) {
622 return llvm::find_if(Slices, [Arch](Slice S) {
623 return S.getArchString() == Arch;
624 }) != Slices.end();
625 };
626 for (StringRef Arch : Alignments.keys())
627 if (!HasArch(Arch))
628 reportError("-segalign " + Arch +
629 " <value> specified but resulting fat file does not contain "
630 "that architecture ");
631 }
632
633 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
634 // Universal Binary files to transfer ownership.
buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,SmallVectorImpl<std::unique_ptr<MachOObjectFile>> & ExtractedObjects)635 static SmallVector<Slice, 2> buildSlices(
636 ArrayRef<OwningBinary<Binary>> InputBinaries,
637 const StringMap<const uint32_t> &Alignments,
638 SmallVectorImpl<std::unique_ptr<MachOObjectFile>> &ExtractedObjects) {
639 SmallVector<Slice, 2> Slices;
640 for (auto &IB : InputBinaries) {
641 const Binary *InputBinary = IB.getBinary();
642 if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
643 for (const auto &O : UO->objects()) {
644 Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
645 O.getAsObjectFile();
646 if (!BinaryOrError)
647 reportError(InputBinary->getFileName(), BinaryOrError.takeError());
648 ExtractedObjects.push_back(std::move(BinaryOrError.get()));
649 Slices.emplace_back(ExtractedObjects.back().get(), O.getAlign());
650 }
651 } else if (auto O = dyn_cast<MachOObjectFile>(InputBinary)) {
652 Slices.emplace_back(O);
653 } else if (auto A = dyn_cast<Archive>(InputBinary)) {
654 Slices.emplace_back(A);
655 } else {
656 llvm_unreachable("Unexpected binary format");
657 }
658 }
659 updateAlignments(Slices, Alignments);
660 return Slices;
661 }
662
663 static SmallVector<MachO::fat_arch, 2>
buildFatArchList(ArrayRef<Slice> Slices)664 buildFatArchList(ArrayRef<Slice> Slices) {
665 SmallVector<MachO::fat_arch, 2> FatArchList;
666 uint64_t Offset =
667 sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch);
668
669 for (const auto &S : Slices) {
670 Offset = alignTo(Offset, 1ull << S.getP2Alignment());
671 if (Offset > UINT32_MAX)
672 reportError("fat file too large to be created because the offset "
673 "field in struct fat_arch is only 32-bits and the offset " +
674 Twine(Offset) + " for " + S.getBinary()->getFileName() +
675 " for architecture " + S.getArchString() + "exceeds that.");
676
677 MachO::fat_arch FatArch;
678 FatArch.cputype = S.getCPUType();
679 FatArch.cpusubtype = S.getCPUSubType();
680 FatArch.offset = Offset;
681 FatArch.size = S.getBinary()->getMemoryBufferRef().getBufferSize();
682 FatArch.align = S.getP2Alignment();
683 Offset += FatArch.size;
684 FatArchList.push_back(FatArch);
685 }
686 return FatArchList;
687 }
688
createUniversalBinary(SmallVectorImpl<Slice> & Slices,StringRef OutputFileName)689 static void createUniversalBinary(SmallVectorImpl<Slice> &Slices,
690 StringRef OutputFileName) {
691 MachO::fat_header FatHeader;
692 FatHeader.magic = MachO::FAT_MAGIC;
693 FatHeader.nfat_arch = Slices.size();
694
695 stable_sort(Slices);
696 SmallVector<MachO::fat_arch, 2> FatArchList = buildFatArchList(Slices);
697
698 const bool IsExecutable = any_of(Slices, [](Slice S) {
699 return sys::fs::can_execute(S.getBinary()->getFileName());
700 });
701 const uint64_t OutputFileSize =
702 static_cast<uint64_t>(FatArchList.back().offset) +
703 FatArchList.back().size;
704 Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
705 FileOutputBuffer::create(OutputFileName, OutputFileSize,
706 IsExecutable ? FileOutputBuffer::F_executable
707 : 0);
708 if (!OutFileOrError)
709 reportError(OutputFileName, OutFileOrError.takeError());
710 std::unique_ptr<FileOutputBuffer> OutFile = std::move(OutFileOrError.get());
711 std::memset(OutFile->getBufferStart(), 0, OutputFileSize);
712
713 if (sys::IsLittleEndianHost)
714 MachO::swapStruct(FatHeader);
715 std::memcpy(OutFile->getBufferStart(), &FatHeader, sizeof(MachO::fat_header));
716
717 for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) {
718 MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef();
719 std::copy(BufferRef.getBufferStart(), BufferRef.getBufferEnd(),
720 OutFile->getBufferStart() + FatArchList[Index].offset);
721 }
722
723 // FatArchs written after Slices in order to reduce the number of swaps for
724 // the LittleEndian case
725 if (sys::IsLittleEndianHost)
726 for (MachO::fat_arch &FA : FatArchList)
727 MachO::swapStruct(FA);
728 std::memcpy(OutFile->getBufferStart() + sizeof(MachO::fat_header),
729 FatArchList.begin(),
730 sizeof(MachO::fat_arch) * FatArchList.size());
731
732 if (Error E = OutFile->commit())
733 reportError(OutputFileName, std::move(E));
734 }
735
736 LLVM_ATTRIBUTE_NORETURN
createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName)737 static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
738 const StringMap<const uint32_t> &Alignments,
739 StringRef OutputFileName) {
740 assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
741 assert(!OutputFileName.empty() && "Create expects a single output file");
742
743 SmallVector<std::unique_ptr<MachOObjectFile>, 1> ExtractedObjects;
744 SmallVector<Slice, 1> Slices =
745 buildSlices(InputBinaries, Alignments, ExtractedObjects);
746 checkArchDuplicates(Slices);
747 checkUnusedAlignments(Slices, Alignments);
748 createUniversalBinary(Slices, OutputFileName);
749
750 exit(EXIT_SUCCESS);
751 }
752
753 LLVM_ATTRIBUTE_NORETURN
extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef ArchType,StringRef OutputFileName)754 static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
755 const StringMap<const uint32_t> &Alignments,
756 StringRef ArchType, StringRef OutputFileName) {
757 assert(!ArchType.empty() &&
758 "The architecture type should be non-empty");
759 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
760 assert(!OutputFileName.empty() && "Thin expects a single output file");
761
762 if (InputBinaries.front().getBinary()->isMachO()) {
763 reportError("input file " +
764 InputBinaries.front().getBinary()->getFileName() +
765 " must be a fat file when the -extract option is specified");
766 }
767
768 SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
769 SmallVector<Slice, 2> Slices =
770 buildSlices(InputBinaries, Alignments, ExtractedObjects);
771 erase_if(Slices, [ArchType](const Slice &S) {
772 return ArchType != S.getArchString();
773 });
774
775 if (Slices.empty())
776 reportError(
777 "fat input file " + InputBinaries.front().getBinary()->getFileName() +
778 " does not contain the specified architecture " + ArchType);
779 createUniversalBinary(Slices, OutputFileName);
780 exit(EXIT_SUCCESS);
781 }
782
783 static StringMap<Slice>
buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,const StringMap<const uint32_t> & Alignments)784 buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
785 const StringMap<const uint32_t> &Alignments) {
786 StringMap<Slice> Slices;
787 // populates StringMap of slices to replace with; error checks for mismatched
788 // replace flag args, fat files, and duplicate arch_types
789 for (const auto &OB : ReplacementBinaries) {
790 const Binary *ReplacementBinary = OB.getBinary();
791 auto O = dyn_cast<MachOObjectFile>(ReplacementBinary);
792 if (!O)
793 reportError("replacement file: " + ReplacementBinary->getFileName() +
794 " is a fat file (must be a thin file)");
795 Slice S(O);
796 auto Entry = Slices.try_emplace(S.getArchString(), S);
797 if (!Entry.second)
798 reportError("-replace " + S.getArchString() +
799 " <file_name> specified multiple times: " +
800 Entry.first->second.getBinary()->getFileName() + ", " +
801 O->getFileName());
802 }
803 auto SlicesMapRange = map_range(
804 Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
805 updateAlignments(SlicesMapRange, Alignments);
806 return Slices;
807 }
808
809 LLVM_ATTRIBUTE_NORETURN
replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName,ArrayRef<InputFile> ReplacementFiles)810 static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
811 const StringMap<const uint32_t> &Alignments,
812 StringRef OutputFileName,
813 ArrayRef<InputFile> ReplacementFiles) {
814 assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
815 assert(!OutputFileName.empty() && "Replace expects a single output file");
816
817 if (InputBinaries.front().getBinary()->isMachO())
818 reportError("input file " +
819 InputBinaries.front().getBinary()->getFileName() +
820 " must be a fat file when the -replace option is specified");
821
822 SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =
823 readInputBinaries(ReplacementFiles);
824
825 StringMap<Slice> ReplacementSlices =
826 buildReplacementSlices(ReplacementBinaries, Alignments);
827 SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
828 SmallVector<Slice, 2> Slices =
829 buildSlices(InputBinaries, Alignments, ExtractedObjects);
830
831 for (auto &Slice : Slices) {
832 auto It = ReplacementSlices.find(Slice.getArchString());
833 if (It != ReplacementSlices.end()) {
834 Slice = It->second;
835 ReplacementSlices.erase(It); // only keep remaining replacing arch_types
836 }
837 }
838
839 if (!ReplacementSlices.empty())
840 reportError("-replace " + ReplacementSlices.begin()->first() +
841 " <file_name> specified but fat file: " +
842 InputBinaries.front().getBinary()->getFileName() +
843 " does not contain that architecture");
844
845 checkUnusedAlignments(Slices, Alignments);
846 createUniversalBinary(Slices, OutputFileName);
847 exit(EXIT_SUCCESS);
848 }
849
main(int argc,char ** argv)850 int main(int argc, char **argv) {
851 InitLLVM X(argc, argv);
852 Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
853 SmallVector<OwningBinary<Binary>, 1> InputBinaries =
854 readInputBinaries(C.InputFiles);
855
856 switch (C.ActionToPerform) {
857 case LipoAction::VerifyArch:
858 verifyArch(InputBinaries, C.VerifyArchList);
859 break;
860 case LipoAction::PrintArchs:
861 printArchs(InputBinaries);
862 break;
863 case LipoAction::PrintInfo:
864 printInfo(InputBinaries);
865 break;
866 case LipoAction::ThinArch:
867 thinSlice(InputBinaries, C.ArchType, C.OutputFile);
868 break;
869 case LipoAction::ExtractArch:
870 extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile);
871 break;
872 case LipoAction::CreateUniversal:
873 createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);
874 break;
875 case LipoAction::ReplaceArch:
876 replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile,
877 C.ReplacementFiles);
878 break;
879 }
880 return EXIT_SUCCESS;
881 }
882