From 32c8435ef70031d7bd3dce48e41bdce65747e123 Mon Sep 17 00:00:00 2001 From: Alexander Shaposhnikov Date: Mon, 28 Sep 2020 03:37:21 -0700 Subject: [PATCH] [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 --- llvm/include/llvm/Object/MachOUniversalWriter.h | 6 ++ llvm/lib/Object/MachOUniversalWriter.cpp | 5 ++ llvm/test/tools/llvm-objcopy/MachO/strip-all.test | 5 ++ .../tools/llvm-objcopy/MachO/universal-object.test | 42 +++++++++++++ llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp | 72 ++++++++++++++++++++++ llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h | 4 ++ llvm/tools/llvm-objcopy/llvm-objcopy.cpp | 26 +++++++- llvm/tools/llvm-objcopy/llvm-objcopy.h | 32 ++++++++++ 8 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 llvm/test/tools/llvm-objcopy/MachO/universal-object.test create mode 100644 llvm/tools/llvm-objcopy/llvm-objcopy.h diff --git a/llvm/include/llvm/Object/MachOUniversalWriter.h b/llvm/include/llvm/Object/MachOUniversalWriter.h index 4935244..606db94 100644 --- a/llvm/include/llvm/Object/MachOUniversalWriter.h +++ b/llvm/include/llvm/Object/MachOUniversalWriter.h @@ -43,6 +43,12 @@ public: 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 create(const Archive &A, LLVMContext *LLVMCtx = nullptr); diff --git a/llvm/lib/Object/MachOUniversalWriter.cpp b/llvm/lib/Object/MachOUniversalWriter.cpp index 165964e..4bb467e 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 4ff31f5..cb41b35 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 0000000..a6146fd --- /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 47a08d3..28b4ec6 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, 2> Binaries; + SmallVector Slices; + for (const auto &O : In.objects()) { + Expected> ArOrErr = O.getAsArchive(); + if (ArOrErr) { + Expected> NewArchiveMembersOrErr = + createNewArchiveMembers(Config, **ArOrErr); + if (!NewArchiveMembersOrErr) + return NewArchiveMembersOrErr.takeError(); + Expected> OutputBufferOrErr = + writeArchiveToBuffer(*NewArchiveMembersOrErr, + (*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(), + Config.DeterministicArchives, + (*ArOrErr)->isThin()); + if (!OutputBufferOrErr) + return OutputBufferOrErr.takeError(); + Expected> BinaryOrErr = + object::createBinary(**OutputBufferOrErr); + if (!BinaryOrErr) + return BinaryOrErr.takeError(); + Binaries.emplace_back(std::move(*BinaryOrErr), + std::move(*OutputBufferOrErr)); + Slices.emplace_back(*cast(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> 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 OutputBuffer = + MB.releaseMemoryBuffer(); + Expected> BinaryOrErr = + object::createBinary(*OutputBuffer); + if (!BinaryOrErr) + return BinaryOrErr.takeError(); + Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer)); + Slices.emplace_back(*cast(Binaries.back().getBinary()), + O.getAlign()); + } + Expected> 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 f34e361..c3f5391 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 175f292..8cd5857 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(&In)) return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); + else if (auto *MachOUniversalBinary = + dyn_cast(&In)) + return macho::executeObjcopyOnMachOUniversalBinary( + Config, *MachOUniversalBinary, Out); else if (auto *WasmBinary = dyn_cast(&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> +createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { std::vector 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 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> 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 0000000..97a1667 --- /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> +createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar); + +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H -- 2.7.4