[llvm] 32c8435 - [llvm-objcopy][MachO] Add support for universal binaries
Alexander Shaposhnikov via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 5 10:44:50 PDT 2020
Author: Alexander Shaposhnikov
Date: 2020-10-05T10:44:28-07:00
New Revision: 32c8435ef70031d7bd3dce48e41bdce65747e123
URL: https://github.com/llvm/llvm-project/commit/32c8435ef70031d7bd3dce48e41bdce65747e123
DIFF: https://github.com/llvm/llvm-project/commit/32c8435ef70031d7bd3dce48e41bdce65747e123.diff
LOG: [llvm-objcopy][MachO] Add support for universal binaries
This diff adds support for universal binaries to llvm-objcopy.
Test plan: make check-all
Differential revision: https://reviews.llvm.org/D88400
Added:
llvm/test/tools/llvm-objcopy/MachO/universal-object.test
llvm/tools/llvm-objcopy/llvm-objcopy.h
Modified:
llvm/include/llvm/Object/MachOUniversalWriter.h
llvm/lib/Object/MachOUniversalWriter.cpp
llvm/test/tools/llvm-objcopy/MachO/strip-all.test
llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
llvm/tools/llvm-objcopy/llvm-objcopy.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Object/MachOUniversalWriter.h b/llvm/include/llvm/Object/MachOUniversalWriter.h
index 49352440dca1..606db94c9f20 100644
--- a/llvm/include/llvm/Object/MachOUniversalWriter.h
+++ b/llvm/include/llvm/Object/MachOUniversalWriter.h
@@ -43,6 +43,12 @@ class Slice {
Slice(const MachOObjectFile &O, uint32_t Align);
+ /// This constructor takes prespecified \param CPUType, \param CPUSubType,
+ /// \param ArchName, \param Align instead of inferring them from the archive
+ /// memebers.
+ Slice(const Archive &A, uint32_t CPUType, uint32_t CPUSubType,
+ std::string ArchName, uint32_t Align);
+
static Expected<Slice> create(const Archive &A,
LLVMContext *LLVMCtx = nullptr);
diff --git a/llvm/lib/Object/MachOUniversalWriter.cpp b/llvm/lib/Object/MachOUniversalWriter.cpp
index 165964e077ce..4bb467e56a6f 100644
--- a/llvm/lib/Object/MachOUniversalWriter.cpp
+++ b/llvm/lib/Object/MachOUniversalWriter.cpp
@@ -75,6 +75,11 @@ static uint32_t calculateAlignment(const MachOObjectFile &ObjectFile) {
}
}
+Slice::Slice(const Archive &A, uint32_t CPUType, uint32_t CPUSubType,
+ std::string ArchName, uint32_t Align)
+ : B(&A), CPUType(CPUType), CPUSubType(CPUSubType),
+ ArchName(std::move(ArchName)), P2Alignment(Align) {}
+
Slice::Slice(const MachOObjectFile &O, uint32_t Align)
: B(&O), CPUType(O.getHeader().cputype),
CPUSubType(O.getHeader().cpusubtype),
diff --git a/llvm/test/tools/llvm-objcopy/MachO/strip-all.test b/llvm/test/tools/llvm-objcopy/MachO/strip-all.test
index 4ff31f5c1e42..cb41b353ec53 100644
--- a/llvm/test/tools/llvm-objcopy/MachO/strip-all.test
+++ b/llvm/test/tools/llvm-objcopy/MachO/strip-all.test
@@ -27,6 +27,11 @@
# cmp %t4 %t.dwarf.stripped
# cmp %t5 %t.dwarf.stripped
+# RUN: llvm-lipo %t.dwarf -create -output %t.dwarf.universal
+# RUN: llvm-strip %t.dwarf.universal -o %t.dwarf.universal.stripped
+# RUN: llvm-lipo %t.dwarf.universal.stripped -thin x86_64 -output %t6
+# RUN: cmp %t6 %t.dwarf.stripped
+
## Make sure that debug sections are removed.
# DWARF: Sections [
# DWARF-NOT: Name: __debug_str
diff --git a/llvm/test/tools/llvm-objcopy/MachO/universal-object.test b/llvm/test/tools/llvm-objcopy/MachO/universal-object.test
new file mode 100644
index 000000000000..a6146fd56483
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/MachO/universal-object.test
@@ -0,0 +1,42 @@
+# This test verifies that llvm-objcopy copies a univeral Mach-O object file properly.
+
+# RUN: yaml2obj %p/Inputs/i386.yaml -o %t.i386
+# RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t.x86_64
+
+## Case 1: copy a universal object containing regular Mach-O objects.
+# RUN: llvm-lipo %t.i386 %t.x86_64 -create -output %t.universal
+# RUN: llvm-objcopy %t.universal %t.universal.copy
+# RUN: llvm-lipo %t.universal.copy -archs | FileCheck --check-prefix=VERIFY_ARCHS %s
+# RUN: llvm-lipo %t.universal.copy -thin i386 -output %t.i386.copy
+# RUN: llvm-lipo %t.universal.copy -thin x86_64 -output %t.x86_64.copy
+# RUN: cmp %t.i386 %t.i386.copy
+# RUN: cmp %t.x86_64 %t.x86_64.copy
+
+## Case 2: copy a universal object file containing an archive.
+# RUN: rm -f %t.archive.i386
+# RUN: llvm-ar cr %t.archive.i386 %t.i386
+# RUN: llvm-lipo %t.archive.i386 %t.x86_64 -create -output %t.universal.containing.archive
+# RUN: llvm-objcopy %t.universal.containing.archive %t.universal.containing.archive.copy
+# RUN: llvm-lipo %t.universal.containing.archive.copy -archs | FileCheck --check-prefix=VERIFY_ARCHS %s
+# RUN: llvm-lipo %t.universal.containing.archive.copy -thin i386 -output %t.archive.i386.copy
+# RUN: llvm-lipo %t.universal.containing.archive.copy -thin x86_64 -output %t.archive.x86_64.copy
+# RUN: cmp %t.archive.i386 %t.archive.i386.copy
+# RUN: cmp %t.x86_64 %t.archive.x86_64.copy
+
+## Case 3: copy an archive containing a universal object.
+# RUN: llvm-ar cr %t.archive.containing.universal %t.universal
+# RUN: llvm-objcopy %t.archive.containing.universal %t.archive.containing.universal.copy
+
+## Case 4: try to copy a universal object file contaning a bitcode slice.
+# RUN: echo 'target triple = "arm64-apple-ios8.0.0"' | llvm-as -o %t.bitcode
+# RUN: llvm-lipo %t.bitcode %t.x86_64 -create -output %t.universal.containing.bitcode
+# RUN: not llvm-objcopy %t.universal.containing.bitcode %t.universal.containing.bitcode.copy 2>&1 \
+# RUN: | FileCheck --check-prefix=UNSUPPORTED_UNIVERSAL_OBJECT %s
+
+## Case 5: try to copy an archive containing an unsupported universal object.
+# RUN: llvm-ar cr %t.archive.universal.bitcode %t.universal.containing.bitcode
+# RUN: not llvm-objcopy %t.archive.universal.bitcode %t.archive.universal.bitcode.copy 2>&1 \
+# RUN: | FileCheck --check-prefix=UNSUPPORTED_UNIVERSAL_OBJECT %s
+
+# VERIFY_ARCHS: i386 x86_64
+# UNSUPPORTED_UNIVERSAL_OBJECT: slice for 'arm64' of the universal Mach-O binary {{.*}} is not a Mach-O object or an archive
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
index 47a08d33002a..28b4ec655a2e 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -8,9 +8,13 @@
#include "MachOObjcopy.h"
#include "../CopyConfig.h"
+#include "../llvm-objcopy.h"
#include "MachOReader.h"
#include "MachOWriter.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/MachOUniversalWriter.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
@@ -386,6 +390,74 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
return Writer.write();
}
+Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config,
+ const MachOUniversalBinary &In,
+ Buffer &Out) {
+ SmallVector<OwningBinary<Binary>, 2> Binaries;
+ SmallVector<Slice, 2> Slices;
+ for (const auto &O : In.objects()) {
+ Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive();
+ if (ArOrErr) {
+ Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
+ createNewArchiveMembers(Config, **ArOrErr);
+ if (!NewArchiveMembersOrErr)
+ return NewArchiveMembersOrErr.takeError();
+ Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
+ writeArchiveToBuffer(*NewArchiveMembersOrErr,
+ (*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(),
+ Config.DeterministicArchives,
+ (*ArOrErr)->isThin());
+ if (!OutputBufferOrErr)
+ return OutputBufferOrErr.takeError();
+ Expected<std::unique_ptr<Binary>> BinaryOrErr =
+ object::createBinary(**OutputBufferOrErr);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+ Binaries.emplace_back(std::move(*BinaryOrErr),
+ std::move(*OutputBufferOrErr));
+ Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),
+ O.getCPUType(), O.getCPUSubType(),
+ O.getArchFlagName(), O.getAlign());
+ continue;
+ }
+ // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class
+ // ObjectForArch return an Error in case of the type mismatch. We need to
+ // check each in turn to see what kind of slice this is, so ignore errors
+ // produced along the way.
+ consumeError(ArOrErr.takeError());
+
+ Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();
+ if (!ObjOrErr) {
+ consumeError(ObjOrErr.takeError());
+ return createStringError(std::errc::invalid_argument,
+ "slice for '%s' of the universal Mach-O binary "
+ "'%s' is not a Mach-O object or an archive",
+ O.getArchFlagName().c_str(),
+ Config.InputFilename.str().c_str());
+ }
+ MemBuffer MB(O.getArchFlagName());
+ if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB))
+ return E;
+ std::unique_ptr<WritableMemoryBuffer> OutputBuffer =
+ MB.releaseMemoryBuffer();
+ Expected<std::unique_ptr<Binary>> BinaryOrErr =
+ object::createBinary(*OutputBuffer);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+ Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer));
+ Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),
+ O.getAlign());
+ }
+ Expected<std::unique_ptr<MemoryBuffer>> B =
+ writeUniversalBinaryToBuffer(Slices);
+ if (!B)
+ return B.takeError();
+ if (Error E = Out.allocate((*B)->getBufferSize()))
+ return E;
+ memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize());
+ return Out.commit();
+}
+
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
index f34e361db7ea..c3f5391f79b6 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
@@ -24,6 +24,10 @@ class Buffer;
namespace macho {
Error executeObjcopyOnBinary(const CopyConfig &Config,
object::MachOObjectFile &In, Buffer &Out);
+
+Error executeObjcopyOnMachOUniversalBinary(
+ CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out);
+
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 175f2929eb23..8cd58572f5a1 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -25,6 +25,7 @@
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
@@ -144,6 +145,10 @@ static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In,
return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In))
return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out);
+ else if (auto *MachOUniversalBinary =
+ dyn_cast<object::MachOUniversalBinary>(&In))
+ return macho::executeObjcopyOnMachOUniversalBinary(
+ Config, *MachOUniversalBinary, Out);
else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In))
return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out);
else
@@ -151,7 +156,11 @@ static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In,
"unsupported object file format");
}
-static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
+namespace llvm {
+namespace objcopy {
+
+Expected<std::vector<NewArchiveMember>>
+createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) {
std::vector<NewArchiveMember> NewArchiveMembers;
Error Err = Error::success();
for (const Archive::Child &Child : Ar.children(Err)) {
@@ -166,7 +175,7 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
MemBuffer MB(ChildNameOrErr.get());
if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB))
- return E;
+ return std::move(E);
Expected<NewArchiveMember> Member =
NewArchiveMember::getOldMember(Child, Config.DeterministicArchives);
@@ -178,8 +187,19 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
}
if (Err)
return createFileError(Config.InputFilename, std::move(Err));
+ return NewArchiveMembers;
+}
+
+} // end namespace objcopy
+} // end namespace llvm
- return deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
+static Error executeObjcopyOnArchive(CopyConfig &Config,
+ const object::Archive &Ar) {
+ Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
+ createNewArchiveMembers(Config, Ar);
+ if (!NewArchiveMembersOrErr)
+ return NewArchiveMembersOrErr.takeError();
+ return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr,
Ar.hasSymbolTable(), Ar.kind(),
Config.DeterministicArchives, Ar.isThin());
}
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.h b/llvm/tools/llvm-objcopy/llvm-objcopy.h
new file mode 100644
index 000000000000..97a166769f95
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.h
@@ -0,0 +1,32 @@
+//===- llvm-objcopy.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_OBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+
+struct NewArchiveMember;
+
+namespace object {
+
+class Archive;
+
+} // end namespace object
+
+namespace objcopy {
+struct CopyConfig;
+Expected<std::vector<NewArchiveMember>>
+createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar);
+
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H
More information about the llvm-commits
mailing list