Skip to content

Commit 315970d

Browse files
author
Alexander Shaposhnikov
committed
[llvm-objcopy][MachO] Add support for universal binaries
This diff adds support for universal binaries to llvm-objcopy. This is a recommit of 32c8435 with the asan issue fixed. Test plan: make check-all Differential revision: https://reviews.llvm.org/D88400
1 parent c08d48f commit 315970d

File tree

8 files changed

+190
-3
lines changed

8 files changed

+190
-3
lines changed

llvm/include/llvm/Object/MachOUniversalWriter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ class Slice {
4343

4444
Slice(const MachOObjectFile &O, uint32_t Align);
4545

46+
/// This constructor takes prespecified \param CPUType, \param CPUSubType,
47+
/// \param ArchName, \param Align instead of inferring them from the archive
48+
/// memebers.
49+
Slice(const Archive &A, uint32_t CPUType, uint32_t CPUSubType,
50+
std::string ArchName, uint32_t Align);
51+
4652
static Expected<Slice> create(const Archive &A,
4753
LLVMContext *LLVMCtx = nullptr);
4854

llvm/lib/Object/MachOUniversalWriter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ static uint32_t calculateAlignment(const MachOObjectFile &ObjectFile) {
7575
}
7676
}
7777

78+
Slice::Slice(const Archive &A, uint32_t CPUType, uint32_t CPUSubType,
79+
std::string ArchName, uint32_t Align)
80+
: B(&A), CPUType(CPUType), CPUSubType(CPUSubType),
81+
ArchName(std::move(ArchName)), P2Alignment(Align) {}
82+
7883
Slice::Slice(const MachOObjectFile &O, uint32_t Align)
7984
: B(&O), CPUType(O.getHeader().cputype),
8085
CPUSubType(O.getHeader().cpusubtype),

llvm/test/tools/llvm-objcopy/MachO/strip-all.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
# cmp %t4 %t.dwarf.stripped
2828
# cmp %t5 %t.dwarf.stripped
2929

30+
# RUN: llvm-lipo %t.dwarf -create -output %t.dwarf.universal
31+
# RUN: llvm-strip %t.dwarf.universal -o %t.dwarf.universal.stripped
32+
# RUN: llvm-lipo %t.dwarf.universal.stripped -thin x86_64 -output %t6
33+
# RUN: cmp %t6 %t.dwarf.stripped
34+
3035
## Make sure that debug sections are removed.
3136
# DWARF: Sections [
3237
# DWARF-NOT: Name: __debug_str
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# This test verifies that llvm-objcopy copies a univeral Mach-O object file properly.
2+
3+
# RUN: yaml2obj %p/Inputs/i386.yaml -o %t.i386
4+
# RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t.x86_64
5+
6+
## Case 1: copy a universal object containing regular Mach-O objects.
7+
# RUN: llvm-lipo %t.i386 %t.x86_64 -create -output %t.universal
8+
# RUN: llvm-objcopy %t.universal %t.universal.copy
9+
# RUN: llvm-lipo %t.universal.copy -archs | FileCheck --check-prefix=VERIFY_ARCHS %s
10+
# RUN: llvm-lipo %t.universal.copy -thin i386 -output %t.i386.copy
11+
# RUN: llvm-lipo %t.universal.copy -thin x86_64 -output %t.x86_64.copy
12+
# RUN: cmp %t.i386 %t.i386.copy
13+
# RUN: cmp %t.x86_64 %t.x86_64.copy
14+
15+
## Case 2: copy a universal object file containing an archive.
16+
# RUN: rm -f %t.archive.i386
17+
# RUN: llvm-ar cr %t.archive.i386 %t.i386
18+
# RUN: llvm-lipo %t.archive.i386 %t.x86_64 -create -output %t.universal.containing.archive
19+
# RUN: llvm-objcopy %t.universal.containing.archive %t.universal.containing.archive.copy
20+
# RUN: llvm-lipo %t.universal.containing.archive.copy -archs | FileCheck --check-prefix=VERIFY_ARCHS %s
21+
# RUN: llvm-lipo %t.universal.containing.archive.copy -thin i386 -output %t.archive.i386.copy
22+
# RUN: llvm-lipo %t.universal.containing.archive.copy -thin x86_64 -output %t.archive.x86_64.copy
23+
# RUN: cmp %t.archive.i386 %t.archive.i386.copy
24+
# RUN: cmp %t.x86_64 %t.archive.x86_64.copy
25+
26+
## Case 3: copy an archive containing a universal object.
27+
# RUN: llvm-ar cr %t.archive.containing.universal %t.universal
28+
# RUN: llvm-objcopy %t.archive.containing.universal %t.archive.containing.universal.copy
29+
30+
## Case 4: try to copy a universal object file contaning a bitcode slice.
31+
# RUN: echo 'target triple = "arm64-apple-ios8.0.0"' | llvm-as -o %t.bitcode
32+
# RUN: llvm-lipo %t.bitcode %t.x86_64 -create -output %t.universal.containing.bitcode
33+
# RUN: not llvm-objcopy %t.universal.containing.bitcode %t.universal.containing.bitcode.copy 2>&1 \
34+
# RUN: | FileCheck --check-prefix=UNSUPPORTED_UNIVERSAL_OBJECT %s
35+
36+
## Case 5: try to copy an archive containing an unsupported universal object.
37+
# RUN: llvm-ar cr %t.archive.universal.bitcode %t.universal.containing.bitcode
38+
# RUN: not llvm-objcopy %t.archive.universal.bitcode %t.archive.universal.bitcode.copy 2>&1 \
39+
# RUN: | FileCheck --check-prefix=UNSUPPORTED_UNIVERSAL_OBJECT %s
40+
41+
# VERIFY_ARCHS: i386 x86_64
42+
# UNSUPPORTED_UNIVERSAL_OBJECT: slice for 'arm64' of the universal Mach-O binary {{.*}} is not a Mach-O object or an archive

llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88

99
#include "MachOObjcopy.h"
1010
#include "../CopyConfig.h"
11+
#include "../llvm-objcopy.h"
1112
#include "MachOReader.h"
1213
#include "MachOWriter.h"
1314
#include "llvm/ADT/DenseSet.h"
15+
#include "llvm/Object/ArchiveWriter.h"
16+
#include "llvm/Object/MachOUniversal.h"
17+
#include "llvm/Object/MachOUniversalWriter.h"
1418
#include "llvm/Support/Errc.h"
1519
#include "llvm/Support/Error.h"
1620

@@ -386,6 +390,75 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
386390
return Writer.write();
387391
}
388392

393+
Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config,
394+
const MachOUniversalBinary &In,
395+
Buffer &Out) {
396+
SmallVector<OwningBinary<Binary>, 2> Binaries;
397+
SmallVector<Slice, 2> Slices;
398+
for (const auto &O : In.objects()) {
399+
Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive();
400+
if (ArOrErr) {
401+
Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
402+
createNewArchiveMembers(Config, **ArOrErr);
403+
if (!NewArchiveMembersOrErr)
404+
return NewArchiveMembersOrErr.takeError();
405+
Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
406+
writeArchiveToBuffer(*NewArchiveMembersOrErr,
407+
(*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(),
408+
Config.DeterministicArchives,
409+
(*ArOrErr)->isThin());
410+
if (!OutputBufferOrErr)
411+
return OutputBufferOrErr.takeError();
412+
Expected<std::unique_ptr<Binary>> BinaryOrErr =
413+
object::createBinary(**OutputBufferOrErr);
414+
if (!BinaryOrErr)
415+
return BinaryOrErr.takeError();
416+
Binaries.emplace_back(std::move(*BinaryOrErr),
417+
std::move(*OutputBufferOrErr));
418+
Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),
419+
O.getCPUType(), O.getCPUSubType(),
420+
O.getArchFlagName(), O.getAlign());
421+
continue;
422+
}
423+
// The methods getAsArchive, getAsObjectFile, getAsIRObject of the class
424+
// ObjectForArch return an Error in case of the type mismatch. We need to
425+
// check each in turn to see what kind of slice this is, so ignore errors
426+
// produced along the way.
427+
consumeError(ArOrErr.takeError());
428+
429+
Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();
430+
if (!ObjOrErr) {
431+
consumeError(ObjOrErr.takeError());
432+
return createStringError(std::errc::invalid_argument,
433+
"slice for '%s' of the universal Mach-O binary "
434+
"'%s' is not a Mach-O object or an archive",
435+
O.getArchFlagName().c_str(),
436+
Config.InputFilename.str().c_str());
437+
}
438+
std::string ArchFlagName = O.getArchFlagName();
439+
MemBuffer MB(ArchFlagName);
440+
if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB))
441+
return E;
442+
std::unique_ptr<WritableMemoryBuffer> OutputBuffer =
443+
MB.releaseMemoryBuffer();
444+
Expected<std::unique_ptr<Binary>> BinaryOrErr =
445+
object::createBinary(*OutputBuffer);
446+
if (!BinaryOrErr)
447+
return BinaryOrErr.takeError();
448+
Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer));
449+
Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),
450+
O.getAlign());
451+
}
452+
Expected<std::unique_ptr<MemoryBuffer>> B =
453+
writeUniversalBinaryToBuffer(Slices);
454+
if (!B)
455+
return B.takeError();
456+
if (Error E = Out.allocate((*B)->getBufferSize()))
457+
return E;
458+
memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize());
459+
return Out.commit();
460+
}
461+
389462
} // end namespace macho
390463
} // end namespace objcopy
391464
} // end namespace llvm

llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class Buffer;
2424
namespace macho {
2525
Error executeObjcopyOnBinary(const CopyConfig &Config,
2626
object::MachOObjectFile &In, Buffer &Out);
27+
28+
Error executeObjcopyOnMachOUniversalBinary(
29+
CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out);
30+
2731
} // end namespace macho
2832
} // end namespace objcopy
2933
} // end namespace llvm

llvm/tools/llvm-objcopy/llvm-objcopy.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/Object/ELFTypes.h"
2626
#include "llvm/Object/Error.h"
2727
#include "llvm/Object/MachO.h"
28+
#include "llvm/Object/MachOUniversal.h"
2829
#include "llvm/Object/Wasm.h"
2930
#include "llvm/Option/Arg.h"
3031
#include "llvm/Option/ArgList.h"
@@ -144,14 +145,22 @@ static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In,
144145
return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
145146
else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In))
146147
return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out);
148+
else if (auto *MachOUniversalBinary =
149+
dyn_cast<object::MachOUniversalBinary>(&In))
150+
return macho::executeObjcopyOnMachOUniversalBinary(
151+
Config, *MachOUniversalBinary, Out);
147152
else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In))
148153
return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out);
149154
else
150155
return createStringError(object_error::invalid_file_type,
151156
"unsupported object file format");
152157
}
153158

154-
static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
159+
namespace llvm {
160+
namespace objcopy {
161+
162+
Expected<std::vector<NewArchiveMember>>
163+
createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) {
155164
std::vector<NewArchiveMember> NewArchiveMembers;
156165
Error Err = Error::success();
157166
for (const Archive::Child &Child : Ar.children(Err)) {
@@ -166,7 +175,7 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
166175

167176
MemBuffer MB(ChildNameOrErr.get());
168177
if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB))
169-
return E;
178+
return std::move(E);
170179

171180
Expected<NewArchiveMember> Member =
172181
NewArchiveMember::getOldMember(Child, Config.DeterministicArchives);
@@ -178,8 +187,19 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
178187
}
179188
if (Err)
180189
return createFileError(Config.InputFilename, std::move(Err));
190+
return std::move(NewArchiveMembers);
191+
}
192+
193+
} // end namespace objcopy
194+
} // end namespace llvm
181195

182-
return deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
196+
static Error executeObjcopyOnArchive(CopyConfig &Config,
197+
const object::Archive &Ar) {
198+
Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
199+
createNewArchiveMembers(Config, Ar);
200+
if (!NewArchiveMembersOrErr)
201+
return NewArchiveMembersOrErr.takeError();
202+
return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr,
183203
Ar.hasSymbolTable(), Ar.kind(),
184204
Config.DeterministicArchives, Ar.isThin());
185205
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- llvm-objcopy.h -------------------------------------------*- C++ -*-===//
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+
#ifndef LLVM_TOOLS_OBJCOPY_OBJCOPY_H
10+
#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H
11+
12+
#include "llvm/Support/Error.h"
13+
14+
namespace llvm {
15+
16+
struct NewArchiveMember;
17+
18+
namespace object {
19+
20+
class Archive;
21+
22+
} // end namespace object
23+
24+
namespace objcopy {
25+
struct CopyConfig;
26+
Expected<std::vector<NewArchiveMember>>
27+
createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar);
28+
29+
} // end namespace objcopy
30+
} // end namespace llvm
31+
32+
#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H

0 commit comments

Comments
 (0)