Skip to content

Commit

Permalink
[llvm-objcopy][MachO] Add support for universal binaries
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Alexander Shaposhnikov committed Oct 6, 2020
1 parent c08d48f commit 315970d
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 3 deletions.
6 changes: 6 additions & 0 deletions llvm/include/llvm/Object/MachOUniversalWriter.h
Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Object/MachOUniversalWriter.cpp
Expand Up @@ -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),
Expand Down
5 changes: 5 additions & 0 deletions llvm/test/tools/llvm-objcopy/MachO/strip-all.test
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions 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
73 changes: 73 additions & 0 deletions llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
Expand Up @@ -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"

Expand Down Expand Up @@ -386,6 +390,75 @@ 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());
}
std::string ArchFlagName = O.getArchFlagName();
MemBuffer MB(ArchFlagName);
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
4 changes: 4 additions & 0 deletions llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
Expand Up @@ -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
Expand Down
26 changes: 23 additions & 3 deletions llvm/tools/llvm-objcopy/llvm-objcopy.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -144,14 +145,22 @@ 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
return createStringError(object_error::invalid_file_type,
"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)) {
Expand All @@ -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);
Expand All @@ -178,8 +187,19 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
}
if (Err)
return createFileError(Config.InputFilename, std::move(Err));
return std::move(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());
}
Expand Down
32 changes: 32 additions & 0 deletions 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

0 comments on commit 315970d

Please sign in to comment.