ORC supports loading relocatable object files into a JIT'd process. The raw "add object file" API (ObjectLayer::add) accepts plain relocatable object files as llvm::MemoryBuffers only and does not check that the object file's format or architecture are compatible with the process that it will be linked in to. This API is flexible, but places the burden of error checking and universal binary support on clients. This commit introduces a new utility, loadRelocatableObject, that takes a path to load and a target triple and then: 1. If the path does not exist, returns a FileError containing the invalid path. 2. If the path points to a MachO universal binary, identifies and returns MemoryBuffer covering the slice that matches the given triple (checking that the slice really does contains a valid MachO relocatable object with a compatible arch). 3. If the path points to a regular relocatable object file, verifies that the format and architecture are compatible with the triple. Clients can use loadRelocatableObject in the common case of loading object files from disk to simplify their code. Note: Error checking for ELF and COFF is left as a FIXME. rdar://133653290
165 lines
5.5 KiB
C++
165 lines
5.5 KiB
C++
//===----------------- MachO.cpp - MachO format utilities -----------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/Orc/MachO.h"
|
|
|
|
#include "llvm/BinaryFormat/MachO.h"
|
|
#include "llvm/Object/MachOUniversal.h"
|
|
|
|
#define DEBUG_TYPE "orc"
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
static std::string objDesc(MemoryBuffer &Obj, const Triple &TT,
|
|
bool ObjIsSlice) {
|
|
std::string Desc;
|
|
if (ObjIsSlice)
|
|
Desc += (TT.getArchName() + " slice of universal binary").str();
|
|
Desc += Obj.getBufferIdentifier();
|
|
return Desc;
|
|
}
|
|
|
|
template <typename HeaderType>
|
|
static Expected<std::unique_ptr<MemoryBuffer>>
|
|
checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj,
|
|
bool SwapEndianness, const Triple &TT,
|
|
bool ObjIsSlice) {
|
|
StringRef Data = Obj->getBuffer();
|
|
|
|
HeaderType Hdr;
|
|
memcpy(&Hdr, Data.data(), sizeof(HeaderType));
|
|
|
|
if (SwapEndianness)
|
|
swapStruct(Hdr);
|
|
|
|
if (Hdr.filetype != MachO::MH_OBJECT)
|
|
return make_error<StringError>(objDesc(*Obj, TT, ObjIsSlice) +
|
|
" is not a MachO relocatable object",
|
|
inconvertibleErrorCode());
|
|
|
|
auto ObjArch = object::MachOObjectFile::getArch(Hdr.cputype, Hdr.cpusubtype);
|
|
if (ObjArch != TT.getArch())
|
|
return make_error<StringError>(
|
|
objDesc(*Obj, TT, ObjIsSlice) + Triple::getArchTypeName(ObjArch) +
|
|
", cannot be loaded into " + TT.str() + " process",
|
|
inconvertibleErrorCode());
|
|
|
|
return std::move(Obj);
|
|
}
|
|
|
|
Expected<std::unique_ptr<MemoryBuffer>>
|
|
checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT,
|
|
bool ObjIsSlice) {
|
|
StringRef Data = Obj->getBuffer();
|
|
|
|
if (Data.size() < 4)
|
|
return make_error<StringError>(
|
|
objDesc(*Obj, TT, ObjIsSlice) +
|
|
" is not a valid MachO relocatable object file (truncated header)",
|
|
inconvertibleErrorCode());
|
|
|
|
uint32_t Magic;
|
|
memcpy(&Magic, Data.data(), sizeof(uint32_t));
|
|
|
|
switch (Magic) {
|
|
case MachO::MH_MAGIC:
|
|
case MachO::MH_CIGAM:
|
|
return checkMachORelocatableObject<MachO::mach_header>(
|
|
std::move(Obj), Magic == MachO::MH_CIGAM, TT, ObjIsSlice);
|
|
case MachO::MH_MAGIC_64:
|
|
case MachO::MH_CIGAM_64:
|
|
return checkMachORelocatableObject<MachO::mach_header_64>(
|
|
std::move(Obj), Magic == MachO::MH_CIGAM_64, TT, ObjIsSlice);
|
|
default:
|
|
return make_error<StringError>(
|
|
objDesc(*Obj, TT, ObjIsSlice) +
|
|
" is not a valid MachO relocatable object (bad magic value)",
|
|
inconvertibleErrorCode());
|
|
}
|
|
}
|
|
|
|
Expected<std::unique_ptr<MemoryBuffer>>
|
|
loadMachORelocatableObject(StringRef Path, const Triple &TT) {
|
|
assert((TT.getObjectFormat() == Triple::UnknownObjectFormat ||
|
|
TT.getObjectFormat() == Triple::MachO) &&
|
|
"TT must specify MachO or Unknown object format");
|
|
|
|
auto Buf = MemoryBuffer::getFile(Path);
|
|
if (!Buf)
|
|
return createFileError(Path, Buf.getError());
|
|
|
|
switch (identify_magic((*Buf)->getBuffer())) {
|
|
case file_magic::macho_object:
|
|
return checkMachORelocatableObject(std::move(*Buf), TT, false);
|
|
case file_magic::macho_universal_binary:
|
|
return loadMachORelocatableObjectFromUniversalBinary(Path, std::move(*Buf),
|
|
TT);
|
|
default:
|
|
return make_error<StringError>(
|
|
Path + " does not contain a relocatable object file compatible with " +
|
|
TT.str(),
|
|
inconvertibleErrorCode());
|
|
}
|
|
}
|
|
|
|
Expected<std::unique_ptr<MemoryBuffer>>
|
|
loadMachORelocatableObjectFromUniversalBinary(
|
|
StringRef UBPath, std::unique_ptr<MemoryBuffer> UBBuf, const Triple &TT) {
|
|
|
|
auto UniversalBin =
|
|
object::MachOUniversalBinary::create(UBBuf->getMemBufferRef());
|
|
if (!UniversalBin)
|
|
return UniversalBin.takeError();
|
|
|
|
auto SliceRange = getMachOSliceRangeForTriple(**UniversalBin, TT);
|
|
if (!SliceRange)
|
|
return SliceRange.takeError();
|
|
|
|
auto ObjBuf = errorOrToExpected(MemoryBuffer::getFileSlice(
|
|
UBPath, SliceRange->second, SliceRange->first, false));
|
|
if (!ObjBuf)
|
|
return createFileError(UBPath, ObjBuf.takeError());
|
|
|
|
return checkMachORelocatableObject(std::move(*ObjBuf), TT, true);
|
|
}
|
|
|
|
Expected<std::pair<size_t, size_t>>
|
|
getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB,
|
|
const Triple &TT) {
|
|
|
|
for (const auto &Obj : UB.objects()) {
|
|
auto ObjTT = Obj.getTriple();
|
|
if (ObjTT.getArch() == TT.getArch() &&
|
|
ObjTT.getSubArch() == TT.getSubArch() &&
|
|
(TT.getVendor() == Triple::UnknownVendor ||
|
|
ObjTT.getVendor() == TT.getVendor())) {
|
|
// We found a match. Return the range for the slice.
|
|
return std::make_pair(Obj.getOffset(), Obj.getSize());
|
|
}
|
|
}
|
|
|
|
return make_error<StringError>(Twine("Universal binary ") + UB.getFileName() +
|
|
" does not contain a slice for " +
|
|
TT.str(),
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
Expected<std::pair<size_t, size_t>>
|
|
getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) {
|
|
|
|
auto UB = object::MachOUniversalBinary::create(UBBuf);
|
|
if (!UB)
|
|
return UB.takeError();
|
|
|
|
return getMachOSliceRangeForTriple(**UB, TT);
|
|
}
|
|
|
|
} // End namespace orc.
|
|
} // End namespace llvm.
|