Skip to content

Commit 67cee1d

Browse files
committedJul 15, 2019
[llvm-lipo] Implement -create (with hardcoded alignments)
Creates universal binary output file from input files. Currently uses hard coded value for alignment. Want to get the create functionality approved before implementing the alignment function. Patch by Anusha Basana <anusha.basana@gmail.com> Differential Revision: https://reviews.llvm.org/D64102 llvm-svn: 366142
1 parent 8538132 commit 67cee1d

9 files changed

+527
-19
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
--- !mach-o
2+
FileHeader:
3+
magic: 0xFEEDFACF
4+
cputype: 0x0100000C
5+
cpusubtype: 0x00000000
6+
filetype: 0x00000001
7+
ncmds: 4
8+
sizeofcmds: 352
9+
flags: 0x00002000
10+
reserved: 0x00000000
11+
LoadCommands:
12+
- cmd: LC_SEGMENT_64
13+
cmdsize: 232
14+
segname: ''
15+
vmaddr: 0
16+
vmsize: 56
17+
fileoff: 384
18+
filesize: 56
19+
maxprot: 7
20+
initprot: 7
21+
nsects: 2
22+
flags: 0
23+
Sections:
24+
- sectname: __text
25+
segname: __TEXT
26+
addr: 0x0000000000000000
27+
size: 20
28+
offset: 0x00000180
29+
align: 2
30+
reloff: 0x00000000
31+
nreloc: 0
32+
flags: 0x80000400
33+
reserved1: 0x00000000
34+
reserved2: 0x00000000
35+
reserved3: 0x00000000
36+
- sectname: __compact_unwind
37+
segname: __LD
38+
addr: 0x0000000000000018
39+
size: 32
40+
offset: 0x00000198
41+
align: 3
42+
reloff: 0x000001B8
43+
nreloc: 1
44+
flags: 0x02000000
45+
reserved1: 0x00000000
46+
reserved2: 0x00000000
47+
reserved3: 0x00000000
48+
- cmd: LC_VERSION_MIN_IPHONEOS
49+
cmdsize: 16
50+
version: 327680
51+
sdk: 0
52+
- cmd: LC_SYMTAB
53+
cmdsize: 24
54+
symoff: 448
55+
nsyms: 3
56+
stroff: 496
57+
strsize: 20
58+
- cmd: LC_DYSYMTAB
59+
cmdsize: 80
60+
ilocalsym: 0
61+
nlocalsym: 2
62+
iextdefsym: 2
63+
nextdefsym: 1
64+
iundefsym: 3
65+
nundefsym: 0
66+
tocoff: 0
67+
ntoc: 0
68+
modtaboff: 0
69+
nmodtab: 0
70+
extrefsymoff: 0
71+
nextrefsyms: 0
72+
indirectsymoff: 0
73+
nindirectsyms: 0
74+
extreloff: 0
75+
nextrel: 0
76+
locreloff: 0
77+
nlocrel: 0
78+
LinkEditData:
79+
NameList:
80+
- n_strx: 13
81+
n_type: 0x0E
82+
n_sect: 1
83+
n_desc: 0
84+
n_value: 0
85+
- n_strx: 7
86+
n_type: 0x0E
87+
n_sect: 2
88+
n_desc: 0
89+
n_value: 24
90+
- n_strx: 1
91+
n_type: 0x0F
92+
n_sect: 1
93+
n_desc: 0
94+
n_value: 0
95+
StringTable:
96+
- ''
97+
- _main
98+
- ltmp1
99+
- ltmp0
100+
- ''
101+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--- !mach-o
2+
FileHeader:
3+
magic: 0xFEEDFACE
4+
cputype: 0x0000000C
5+
cpusubtype: 0x00000009
6+
filetype: 0x00000001
7+
ncmds: 4
8+
sizeofcmds: 244
9+
flags: 0x00002000
10+
LoadCommands:
11+
- cmd: LC_SEGMENT
12+
cmdsize: 124
13+
segname: ''
14+
vmaddr: 0
15+
vmsize: 10
16+
fileoff: 272
17+
filesize: 10
18+
maxprot: 7
19+
initprot: 7
20+
nsects: 1
21+
flags: 0
22+
Sections:
23+
- sectname: __text
24+
segname: __TEXT
25+
addr: 0x0000000000000000
26+
size: 10
27+
offset: 0x00000110
28+
align: 1
29+
reloff: 0x00000000
30+
nreloc: 0
31+
flags: 0x80000400
32+
reserved1: 0x00000000
33+
reserved2: 0x00000000
34+
reserved3: 0x00000000
35+
- cmd: LC_VERSION_MIN_IPHONEOS
36+
cmdsize: 16
37+
version: 327680
38+
sdk: 0
39+
- cmd: LC_SYMTAB
40+
cmdsize: 24
41+
symoff: 284
42+
nsyms: 1
43+
stroff: 296
44+
strsize: 8
45+
- cmd: LC_DYSYMTAB
46+
cmdsize: 80
47+
ilocalsym: 0
48+
nlocalsym: 0
49+
iextdefsym: 0
50+
nextdefsym: 1
51+
iundefsym: 1
52+
nundefsym: 0
53+
tocoff: 0
54+
ntoc: 0
55+
modtaboff: 0
56+
nmodtab: 0
57+
extrefsymoff: 0
58+
nextrefsyms: 0
59+
indirectsymoff: 0
60+
nindirectsyms: 0
61+
extreloff: 0
62+
nextrel: 0
63+
locreloff: 0
64+
nlocrel: 0
65+
LinkEditData:
66+
NameList:
67+
- n_strx: 1
68+
n_type: 0x0F
69+
n_sect: 1
70+
n_desc: 8
71+
n_value: 0
72+
StringTable:
73+
- ''
74+
- _main
75+
- ''
76+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--- !mach-o
2+
FileHeader:
3+
magic: 0xFEEDFACF
4+
cputype: 0x01000007
5+
cpusubtype: 0x00000003
6+
filetype: 0x00000001
7+
ncmds: 4
8+
sizeofcmds: 352
9+
flags: 0x00002000
10+
reserved: 0x00000000
11+
LoadCommands:
12+
- cmd: LC_SEGMENT_64
13+
cmdsize: 232
14+
segname: ''
15+
vmaddr: 0
16+
vmsize: 80
17+
fileoff: 384
18+
filesize: 80
19+
maxprot: 7
20+
initprot: 7
21+
nsects: 2
22+
flags: 0
23+
Sections:
24+
- sectname: __text
25+
segname: __TEXT
26+
addr: 0x0000000000000000
27+
size: 15
28+
offset: 0x00000180
29+
align: 4
30+
reloff: 0x00000000
31+
nreloc: 0
32+
flags: 0x80000400
33+
reserved1: 0x00000000
34+
reserved2: 0x00000000
35+
reserved3: 0x00000000
36+
- sectname: __eh_frame
37+
segname: __TEXT
38+
addr: 0x0000000000000010
39+
size: 64
40+
offset: 0x00000190
41+
align: 3
42+
reloff: 0x00000000
43+
nreloc: 0
44+
flags: 0x6800000B
45+
reserved1: 0x00000000
46+
reserved2: 0x00000000
47+
reserved3: 0x00000000
48+
- cmd: LC_VERSION_MIN_MACOSX
49+
cmdsize: 16
50+
version: 656384
51+
sdk: 0
52+
- cmd: LC_SYMTAB
53+
cmdsize: 24
54+
symoff: 464
55+
nsyms: 1
56+
stroff: 480
57+
strsize: 8
58+
- cmd: LC_DYSYMTAB
59+
cmdsize: 80
60+
ilocalsym: 0
61+
nlocalsym: 0
62+
iextdefsym: 0
63+
nextdefsym: 1
64+
iundefsym: 1
65+
nundefsym: 0
66+
tocoff: 0
67+
ntoc: 0
68+
modtaboff: 0
69+
nmodtab: 0
70+
extrefsymoff: 0
71+
nextrefsyms: 0
72+
indirectsymoff: 0
73+
nindirectsyms: 0
74+
extreloff: 0
75+
nextrel: 0
76+
locreloff: 0
77+
nlocrel: 0
78+
LinkEditData:
79+
NameList:
80+
- n_strx: 1
81+
n_type: 0x0F
82+
n_sect: 1
83+
n_desc: 0
84+
n_value: 0
85+
StringTable:
86+
- ''
87+
- _main
88+
- ''
89+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-i386.o
2+
# RUN: yaml2obj %p/Inputs/x86_64-slice.yaml > %t-x86_64.o
3+
4+
# RUN: chmod -x %t-i386.o
5+
# RUN: chmod -x %t-x86_64.o
6+
# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -output %t-universal.o
7+
# RUN: ! test -x %t-universal.o
8+
9+
# RUN: chmod +x %t-i386.o
10+
# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -output %t-universal.o
11+
# RUN: test -x %t-universal.o
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-32.o
2+
# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o
3+
4+
# RUN: not llvm-lipo %t-32.o -create 2>&1 | FileCheck --check-prefix=NO_OUTPUT %s
5+
# NO_OUTPUT: error: create expects a single output file to be specified
6+
7+
# RUN: not llvm-lipo %t-universal.o %t-32.o -create -output %t.o 2>&1 | FileCheck --check-prefix=DUPLICATE_ARCHS %s
8+
# DUPLICATE_ARCHS: have the same architecture i386 and therefore cannot be in the same universal binary
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-i386.o
2+
# RUN: yaml2obj %p/Inputs/x86_64-slice.yaml > %t-x86_64.o
3+
4+
# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -output %t-universal-llvm.o
5+
6+
# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o
7+
# RUN: cmp %t-universal-llvm.o %t-universal.o
8+
9+
# RUN: yaml2obj %p/Inputs/armv7-slice.yaml > %t-armv7.o
10+
# RUN: yaml2obj %p/Inputs/arm64-slice.yaml > %t-arm64.o
11+
12+
# RUN: llvm-lipo %t-arm64.o %t-armv7.o %t-universal.o -create -output %t-universal-2.o
13+
# RUN: llvm-lipo %t-universal-2.o -thin x86_64 -output %t-x86_64_extracted.o
14+
# RUN: cmp %t-x86_64_extracted.o %t-x86_64.o
15+
# RUN: llvm-lipo %t-universal-2.o -thin armv7 -output %t-armv7-extracted.o
16+
# RUN: cmp %t-armv7-extracted.o %t-armv7.o
17+
18+
# RUN: llvm-objdump %t-universal-2.o -m --universal-headers | FileCheck %s
19+
# CHECK: fat_magic FAT_MAGIC
20+
# CHECK: nfat_arch 4
21+
# CHECK: architecture i386
22+
# CHECK: offset 4096
23+
# CHECK: align 2^12 (4096)
24+
# CHECK: architecture x86_64
25+
# CHECK: offset 8192
26+
# CHECK: align 2^12 (4096)
27+
# CHECK: architecture armv7
28+
# CHECK: offset 16384
29+
# CHECK: align 2^14 (16384)
30+
# CHECK: architecture arm64
31+
# CHECK: offset 32768
32+
# CHECK: align 2^14 (16384)

‎llvm/test/tools/llvm-lipo/thin-executable-universal-binary.test

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# RUN: chmod -x %t-universal.o
66
# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32.o
7-
# RUN: test ! -x %t32.o
7+
# RUN: ! test -x %t32.o
88

99
# RUN: chmod +x %t-universal.o
1010
# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32-ex.o

‎llvm/tools/llvm-lipo/LipoOpts.td

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ def thin : Option<["-", "--"], "thin", KIND_SEPARATE>,
2323
HelpText<"Create a thin output file of specified arch_type from the "
2424
"fat input file. Requires -output option">;
2525

26+
def create : Option<["-", "--"], "create", KIND_FLAG>,
27+
Group<action_group>,
28+
HelpText<"Create a universal binary output file from the input "
29+
"files. Requires -output option">;
30+
2631
def output : Option<["-", "--"], "output", KIND_SEPARATE>,
2732
HelpText<"Create output file with specified name">;
2833
def o : JoinedOrSeparate<["-"], "o">, Alias<output>;

‎llvm/tools/llvm-lipo/llvm-lipo.cpp

+204-18
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ enum class LipoAction {
8080
PrintArchs,
8181
VerifyArch,
8282
ThinArch,
83+
CreateUniversal,
8384
};
8485

8586
struct Config {
@@ -90,6 +91,14 @@ struct Config {
9091
LipoAction ActionToPerform;
9192
};
9293

94+
struct Slice {
95+
const MachOObjectFile *ObjectFile;
96+
// Requires Alignment field to store slice alignment values from universal
97+
// binaries. Also needed to order the slices using compareSlices, so the total
98+
// file size can be calculated before creating the output buffer.
99+
uint32_t Alignment;
100+
};
101+
93102
} // end namespace
94103

95104
static void validateArchitectureName(StringRef ArchitectureName) {
@@ -108,7 +117,7 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
108117
Config C;
109118
LipoOptTable T;
110119
unsigned MissingArgumentIndex, MissingArgumentCount;
111-
llvm::opt::InputArgList InputArgs =
120+
opt::InputArgList InputArgs =
112121
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
113122

114123
if (MissingArgumentCount)
@@ -186,6 +195,12 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
186195
C.ActionToPerform = LipoAction::ThinArch;
187196
return C;
188197

198+
case LIPO_create:
199+
if (C.OutputFile.empty())
200+
reportError("create expects a single output file to be specified");
201+
C.ActionToPerform = LipoAction::CreateUniversal;
202+
return C;
203+
189204
default:
190205
reportError("llvm-lipo action unspecified");
191206
}
@@ -195,8 +210,7 @@ static SmallVector<OwningBinary<Binary>, 1>
195210
readInputBinaries(ArrayRef<std::string> InputFiles) {
196211
SmallVector<OwningBinary<Binary>, 1> InputBinaries;
197212
for (StringRef InputFile : InputFiles) {
198-
Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
199-
createBinary(InputFile);
213+
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFile);
200214
if (!BinaryOrErr)
201215
reportError(InputFile, BinaryOrErr.takeError());
202216
// TODO: Add compatibility for archive files
@@ -241,33 +255,35 @@ static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
241255
exit(EXIT_SUCCESS);
242256
}
243257

244-
static void printArchOrUnknown(const MachOObjectFile *ObjectFile) {
245-
// Prints trailing space and unknown in this format for compatibility with
246-
// cctools lipo.
247-
const std::string ObjectArch = ObjectFile->getArchTriple().getArchName();
248-
if (ObjectArch.empty())
249-
outs() << "unknown(" << ObjectFile->getHeader().cputype << ","
250-
<< ObjectFile->getHeader().cpusubtype << ") ";
251-
else
252-
outs() << ObjectArch + " ";
258+
// Returns a string of the given Object file's architecture type
259+
// Unknown architectures formatted unknown(CPUType,CPUSubType) for compatibility
260+
// with cctools lipo
261+
static std::string getArchString(const MachOObjectFile &ObjectFile) {
262+
const Triple T = ObjectFile.getArchTriple();
263+
const StringRef ObjectArch = T.getArchName();
264+
if (!ObjectArch.empty())
265+
return ObjectArch;
266+
return ("unknown(" + Twine(ObjectFile.getHeader().cputype) + "," +
267+
Twine(ObjectFile.getHeader().cpusubtype & ~MachO::CPU_SUBTYPE_MASK) +
268+
")")
269+
.str();
253270
}
254271

255272
LLVM_ATTRIBUTE_NORETURN
256273
static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) {
274+
// Prints trailing space for compatibility with cctools lipo.
257275
assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
258276
const Binary *InputBinary = InputBinaries.front().getBinary();
259277
if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
260-
for (MachOUniversalBinary::object_iterator I = UO->begin_objects(),
261-
E = UO->end_objects();
262-
I != E; ++I) {
278+
for (const auto &O : UO->objects()) {
263279
Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
264-
I->getAsObjectFile();
280+
O.getAsObjectFile();
265281
if (!BinaryOrError)
266282
reportError(InputBinary->getFileName(), BinaryOrError.takeError());
267-
printArchOrUnknown(BinaryOrError.get().get());
283+
outs() << getArchString(*BinaryOrError.get().get()) << " ";
268284
}
269285
} else if (auto O = dyn_cast<MachOObjectFile>(InputBinary)) {
270-
printArchOrUnknown(O);
286+
outs() << getArchString(*O) << " ";
271287
} else {
272288
llvm_unreachable("Unexpected binary format");
273289
}
@@ -314,6 +330,173 @@ static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
314330
exit(EXIT_SUCCESS);
315331
}
316332

333+
static void checkArchDuplicates(const ArrayRef<Slice> &Slices) {
334+
DenseMap<uint64_t, const MachOObjectFile *> CPUIds;
335+
auto CPUIDForSlice = [](const Slice &S) {
336+
return static_cast<uint64_t>(S.ObjectFile->getHeader().cputype) << 32 |
337+
S.ObjectFile->getHeader().cpusubtype;
338+
};
339+
for (const auto &S : Slices) {
340+
auto Entry = CPUIds.try_emplace(CPUIDForSlice(S), S.ObjectFile);
341+
if (!Entry.second)
342+
reportError(Entry.first->second->getFileName() + " and " +
343+
S.ObjectFile->getFileName() + " have the same architecture " +
344+
getArchString(*S.ObjectFile) +
345+
" and therefore cannot be in the same universal binary");
346+
}
347+
}
348+
349+
static uint32_t calculateAlignment(const MachOObjectFile *ObjectFile) {
350+
// TODO: Implement getAlign() and remove hard coding
351+
// Will be implemented in a follow-up.
352+
353+
switch (ObjectFile->getHeader().cputype) {
354+
case MachO::CPU_TYPE_I386:
355+
case MachO::CPU_TYPE_X86_64:
356+
case MachO::CPU_TYPE_POWERPC:
357+
case MachO::CPU_TYPE_POWERPC64:
358+
return 12; // log2 value of page size(4k) for x86 and PPC
359+
case MachO::CPU_TYPE_ARM:
360+
case MachO::CPU_TYPE_ARM64:
361+
case MachO::CPU_TYPE_ARM64_32:
362+
return 14; // log2 value of page size(16k) for Darwin ARM
363+
default:
364+
return 12;
365+
}
366+
}
367+
368+
// This function replicates ordering from cctools lipo for consistency
369+
static bool compareSlices(const Slice &Lhs, const Slice &Rhs) {
370+
if (Lhs.ObjectFile->getHeader().cputype ==
371+
Rhs.ObjectFile->getHeader().cputype)
372+
return Lhs.ObjectFile->getHeader().cpusubtype <
373+
Rhs.ObjectFile->getHeader().cpusubtype;
374+
375+
// force arm64-family to follow after all other slices for compatibility
376+
// with cctools lipo
377+
if (Lhs.ObjectFile->getHeader().cputype == MachO::CPU_TYPE_ARM64)
378+
return false;
379+
if (Rhs.ObjectFile->getHeader().cputype == MachO::CPU_TYPE_ARM64)
380+
return true;
381+
382+
// Sort by alignment to minimize file size
383+
return Lhs.Alignment < Rhs.Alignment;
384+
}
385+
386+
// Updates vector ExtractedObjects with the MachOObjectFiles extracted from
387+
// Universal Binary files to transfer ownership.
388+
static SmallVector<Slice, 2> buildSlices(
389+
ArrayRef<OwningBinary<Binary>> InputBinaries,
390+
SmallVectorImpl<std::unique_ptr<MachOObjectFile>> &ExtractedObjects) {
391+
SmallVector<Slice, 2> Slices;
392+
for (auto &IB : InputBinaries) {
393+
const Binary *InputBinary = IB.getBinary();
394+
if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
395+
for (const auto &O : UO->objects()) {
396+
Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
397+
O.getAsObjectFile();
398+
if (!BinaryOrError)
399+
reportError(InputBinary->getFileName(), BinaryOrError.takeError());
400+
ExtractedObjects.push_back(std::move(BinaryOrError.get()));
401+
Slices.push_back(Slice{ExtractedObjects.back().get(), O.getAlign()});
402+
}
403+
} else if (auto O = dyn_cast<MachOObjectFile>(InputBinary)) {
404+
Slices.push_back(Slice{O, calculateAlignment(O)});
405+
} else {
406+
llvm_unreachable("Unexpected binary format");
407+
}
408+
}
409+
return Slices;
410+
}
411+
412+
static SmallVector<MachO::fat_arch, 2>
413+
buildFatArchList(ArrayRef<Slice> Slices) {
414+
SmallVector<MachO::fat_arch, 2> FatArchList;
415+
uint64_t Offset =
416+
sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch);
417+
418+
for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) {
419+
Offset = alignTo(Offset, 1 << Slices[Index].Alignment);
420+
const MachOObjectFile *ObjectFile = Slices[Index].ObjectFile;
421+
if (Offset > UINT32_MAX)
422+
reportError("fat file too large to be created because the offset "
423+
"field in struct fat_arch is only 32-bits and the offset " +
424+
Twine(Offset) + " for " + ObjectFile->getFileName() +
425+
" for architecture " + getArchString(*ObjectFile) +
426+
"exceeds that.");
427+
428+
MachO::fat_arch FatArch;
429+
FatArch.cputype = ObjectFile->getHeader().cputype;
430+
FatArch.cpusubtype = ObjectFile->getHeader().cpusubtype;
431+
FatArch.offset = Offset;
432+
FatArch.size = ObjectFile->getMemoryBufferRef().getBufferSize();
433+
FatArch.align = Slices[Index].Alignment;
434+
Offset += FatArch.size;
435+
FatArchList.push_back(FatArch);
436+
}
437+
return FatArchList;
438+
}
439+
440+
static void createUniversalBinary(SmallVectorImpl<Slice> &Slices,
441+
StringRef OutputFileName) {
442+
MachO::fat_header FatHeader;
443+
FatHeader.magic = MachO::FAT_MAGIC;
444+
FatHeader.nfat_arch = Slices.size();
445+
446+
stable_sort(Slices, compareSlices);
447+
SmallVector<MachO::fat_arch, 2> FatArchList = buildFatArchList(Slices);
448+
449+
const bool IsExecutable = any_of(Slices, [](Slice S) {
450+
return sys::fs::can_execute(S.ObjectFile->getFileName());
451+
});
452+
const uint64_t OutputFileSize =
453+
FatArchList.back().offset + FatArchList.back().size;
454+
Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
455+
FileOutputBuffer::create(OutputFileName, OutputFileSize,
456+
IsExecutable ? FileOutputBuffer::F_executable
457+
: 0);
458+
if (!OutFileOrError)
459+
reportError(OutputFileName, OutFileOrError.takeError());
460+
std::unique_ptr<FileOutputBuffer> OutFile = std::move(OutFileOrError.get());
461+
std::memset(OutFile->getBufferStart(), 0, OutputFileSize);
462+
463+
if (sys::IsLittleEndianHost)
464+
MachO::swapStruct(FatHeader);
465+
std::memcpy(OutFile->getBufferStart(), &FatHeader, sizeof(MachO::fat_header));
466+
467+
for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) {
468+
MemoryBufferRef BufferRef = Slices[Index].ObjectFile->getMemoryBufferRef();
469+
std::copy(BufferRef.getBufferStart(), BufferRef.getBufferEnd(),
470+
OutFile->getBufferStart() + FatArchList[Index].offset);
471+
}
472+
473+
// FatArchs written after Slices in order reduce the number of swaps for the
474+
// LittleEndian case
475+
if (sys::IsLittleEndianHost)
476+
for (MachO::fat_arch &FA : FatArchList)
477+
MachO::swapStruct(FA);
478+
std::memcpy(OutFile->getBufferStart() + sizeof(MachO::fat_header),
479+
FatArchList.begin(),
480+
sizeof(MachO::fat_arch) * FatArchList.size());
481+
482+
if (Error E = OutFile->commit())
483+
reportError(OutputFileName, std::move(E));
484+
}
485+
486+
LLVM_ATTRIBUTE_NORETURN
487+
static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
488+
StringRef OutputFileName) {
489+
assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
490+
assert(!OutputFileName.empty() && "Create expects a single output file");
491+
492+
SmallVector<std::unique_ptr<MachOObjectFile>, 1> ExtractedObjects;
493+
SmallVector<Slice, 1> Slices = buildSlices(InputBinaries, ExtractedObjects);
494+
checkArchDuplicates(Slices);
495+
createUniversalBinary(Slices, OutputFileName);
496+
497+
exit(EXIT_SUCCESS);
498+
}
499+
317500
int main(int argc, char **argv) {
318501
InitLLVM X(argc, argv);
319502
Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
@@ -330,6 +513,9 @@ int main(int argc, char **argv) {
330513
case LipoAction::ThinArch:
331514
extractSlice(InputBinaries, C.ThinArchType, C.OutputFile);
332515
break;
516+
case LipoAction::CreateUniversal:
517+
createUniversalBinary(InputBinaries, C.OutputFile);
518+
break;
333519
}
334520
return EXIT_SUCCESS;
335521
}

0 commit comments

Comments
 (0)
Please sign in to comment.