llvm-project/llvm/lib/ObjCopy/MachO/MachOWriter.cpp
Alexey Lapshin 25d7b4fb44 [objcopy][NFC] Rename files to avoid clashing of archive members.
libtool uses file names to name members of an static library.
Files, located in different directories and having matching name,
would have the same name inside an archive. This is not a problem
for ld, but may be a problem for ar. This patch renames files
from ObjCopy library to avoid names clashing.

See https://reviews.llvm.org/D88827#3335814

Differential Revision: https://reviews.llvm.org/D120345
2022-02-23 13:05:39 +03:00

749 lines
29 KiB
C++

//===- MachOWriter.cpp ------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#include "MachOWriter.h"
#include "MachOLayoutBuilder.h"
#include "MachOObject.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SHA256.h"
#include <memory>
#if defined(__APPLE__)
#include <sys/mman.h>
#endif
using namespace llvm;
using namespace llvm::objcopy::macho;
using namespace llvm::support::endian;
size_t MachOWriter::headerSize() const {
return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
}
size_t MachOWriter::loadCommandsSize() const { return O.Header.SizeOfCmds; }
size_t MachOWriter::symTableSize() const {
return O.SymTable.Symbols.size() *
(Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist));
}
size_t MachOWriter::totalSize() const {
// Going from tail to head and looking for an appropriate "anchor" to
// calculate the total size assuming that all the offsets are either valid
// ("true") or 0 (0 indicates that the corresponding part is missing).
SmallVector<size_t, 7> Ends;
if (O.SymTabCommandIndex) {
const MachO::symtab_command &SymTabCommand =
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
if (SymTabCommand.symoff)
Ends.push_back(SymTabCommand.symoff + symTableSize());
if (SymTabCommand.stroff)
Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize);
}
if (O.DyLdInfoCommandIndex) {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
if (DyLdInfoCommand.rebase_off) {
assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) &&
"Incorrect rebase opcodes size");
Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size);
}
if (DyLdInfoCommand.bind_off) {
assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) &&
"Incorrect bind opcodes size");
Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size);
}
if (DyLdInfoCommand.weak_bind_off) {
assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) &&
"Incorrect weak bind opcodes size");
Ends.push_back(DyLdInfoCommand.weak_bind_off +
DyLdInfoCommand.weak_bind_size);
}
if (DyLdInfoCommand.lazy_bind_off) {
assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) &&
"Incorrect lazy bind opcodes size");
Ends.push_back(DyLdInfoCommand.lazy_bind_off +
DyLdInfoCommand.lazy_bind_size);
}
if (DyLdInfoCommand.export_off) {
assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) &&
"Incorrect trie size");
Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size);
}
}
if (O.DySymTabCommandIndex) {
const MachO::dysymtab_command &DySymTabCommand =
O.LoadCommands[*O.DySymTabCommandIndex]
.MachOLoadCommand.dysymtab_command_data;
if (DySymTabCommand.indirectsymoff)
Ends.push_back(DySymTabCommand.indirectsymoff +
sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
}
if (O.CodeSignatureCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.CodeSignatureCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
if (O.DataInCodeCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.DataInCodeCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
if (O.LinkerOptimizationHintCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
if (O.FunctionStartsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.FunctionStartsCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
if (O.ChainedFixupsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.ChainedFixupsCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
if (O.ExportsTrieCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.ExportsTrieCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Ends.push_back(LinkEditDataCommand.dataoff +
LinkEditDataCommand.datasize);
}
// Otherwise, use the last section / reloction.
for (const LoadCommand &LC : O.LoadCommands)
for (const std::unique_ptr<Section> &S : LC.Sections) {
if (!S->hasValidOffset()) {
assert((S->Offset == 0) && "Skipped section's offset must be zero");
assert((S->isVirtualSection() || S->Size == 0) &&
"Non-zero-fill sections with zero offset must have zero size");
continue;
}
assert((S->Offset != 0) &&
"Non-zero-fill section's offset cannot be zero");
Ends.push_back(S->Offset + S->Size);
if (S->RelOff)
Ends.push_back(S->RelOff +
S->NReloc * sizeof(MachO::any_relocation_info));
}
if (!Ends.empty())
return *std::max_element(Ends.begin(), Ends.end());
// Otherwise, we have only Mach header and load commands.
return headerSize() + loadCommandsSize();
}
void MachOWriter::writeHeader() {
MachO::mach_header_64 Header;
Header.magic = O.Header.Magic;
Header.cputype = O.Header.CPUType;
Header.cpusubtype = O.Header.CPUSubType;
Header.filetype = O.Header.FileType;
Header.ncmds = O.Header.NCmds;
Header.sizeofcmds = O.Header.SizeOfCmds;
Header.flags = O.Header.Flags;
Header.reserved = O.Header.Reserved;
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(Header);
auto HeaderSize =
Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
memcpy(Buf->getBufferStart(), &Header, HeaderSize);
}
void MachOWriter::writeLoadCommands() {
uint8_t *Begin =
reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + headerSize();
for (const LoadCommand &LC : O.LoadCommands) {
// Construct a load command.
MachO::macho_load_command MLC = LC.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(MLC.segment_command_data);
memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command));
Begin += sizeof(MachO::segment_command);
for (const std::unique_ptr<Section> &Sec : LC.Sections)
writeSectionInLoadCommand<MachO::section>(*Sec, Begin);
continue;
case MachO::LC_SEGMENT_64:
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(MLC.segment_command_64_data);
memcpy(Begin, &MLC.segment_command_64_data,
sizeof(MachO::segment_command_64));
Begin += sizeof(MachO::segment_command_64);
for (const std::unique_ptr<Section> &Sec : LC.Sections)
writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin);
continue;
}
#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
case MachO::LCName: \
assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \
MLC.load_command_data.cmdsize); \
if (IsLittleEndian != sys::IsLittleEndianHost) \
MachO::swapStruct(MLC.LCStruct##_data); \
memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \
Begin += sizeof(MachO::LCStruct); \
if (!LC.Payload.empty()) \
memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \
Begin += LC.Payload.size(); \
break;
// Copy the load command as it is.
switch (MLC.load_command_data.cmd) {
default:
assert(sizeof(MachO::load_command) + LC.Payload.size() ==
MLC.load_command_data.cmdsize);
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(MLC.load_command_data);
memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command));
Begin += sizeof(MachO::load_command);
if (!LC.Payload.empty())
memcpy(Begin, LC.Payload.data(), LC.Payload.size());
Begin += LC.Payload.size();
break;
#include "llvm/BinaryFormat/MachO.def"
}
}
}
template <typename StructType>
void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) {
StructType Temp;
assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name");
assert(Sec.Sectname.size() <= sizeof(Temp.sectname) &&
"too long section name");
memset(&Temp, 0, sizeof(StructType));
memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size());
memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size());
Temp.addr = Sec.Addr;
Temp.size = Sec.Size;
Temp.offset = Sec.Offset;
Temp.align = Sec.Align;
Temp.reloff = Sec.RelOff;
Temp.nreloc = Sec.NReloc;
Temp.flags = Sec.Flags;
Temp.reserved1 = Sec.Reserved1;
Temp.reserved2 = Sec.Reserved2;
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(Temp);
memcpy(Out, &Temp, sizeof(StructType));
Out += sizeof(StructType);
}
void MachOWriter::writeSections() {
for (const LoadCommand &LC : O.LoadCommands)
for (const std::unique_ptr<Section> &Sec : LC.Sections) {
if (!Sec->hasValidOffset()) {
assert((Sec->Offset == 0) && "Skipped section's offset must be zero");
assert((Sec->isVirtualSection() || Sec->Size == 0) &&
"Non-zero-fill sections with zero offset must have zero size");
continue;
}
assert(Sec->Offset && "Section offset can not be zero");
assert((Sec->Size == Sec->Content.size()) && "Incorrect section size");
memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(),
Sec->Content.size());
for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) {
RelocationInfo RelocInfo = Sec->Relocations[Index];
if (!RelocInfo.Scattered && !RelocInfo.IsAddend) {
const uint32_t SymbolNum = RelocInfo.Extern
? (*RelocInfo.Symbol)->Index
: (*RelocInfo.Sec)->Index;
RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian);
}
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(
reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info));
memcpy(Buf->getBufferStart() + Sec->RelOff +
Index * sizeof(MachO::any_relocation_info),
&RelocInfo.Info, sizeof(RelocInfo.Info));
}
}
}
template <typename NListType>
void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out,
uint32_t Nstrx) {
NListType ListEntry;
ListEntry.n_strx = Nstrx;
ListEntry.n_type = SE.n_type;
ListEntry.n_sect = SE.n_sect;
ListEntry.n_desc = SE.n_desc;
ListEntry.n_value = SE.n_value;
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(ListEntry);
memcpy(Out, reinterpret_cast<const char *>(&ListEntry), sizeof(NListType));
Out += sizeof(NListType);
}
void MachOWriter::writeStringTable() {
if (!O.SymTabCommandIndex)
return;
const MachO::symtab_command &SymTabCommand =
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff;
LayoutBuilder.getStringTableBuilder().write(StrTable);
}
void MachOWriter::writeSymbolTable() {
if (!O.SymTabCommandIndex)
return;
const MachO::symtab_command &SymTabCommand =
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff;
for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end();
Iter != End; Iter++) {
SymbolEntry *Sym = Iter->get();
uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name);
if (Is64Bit)
writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx);
else
writeNListEntry<MachO::nlist>(*Sym, IsLittleEndian, SymTable, Nstrx);
}
}
void MachOWriter::writeRebaseInfo() {
if (!O.DyLdInfoCommandIndex)
return;
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off;
assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) &&
"Incorrect rebase opcodes size");
memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size());
}
void MachOWriter::writeBindInfo() {
if (!O.DyLdInfoCommandIndex)
return;
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off;
assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) &&
"Incorrect bind opcodes size");
memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size());
}
void MachOWriter::writeWeakBindInfo() {
if (!O.DyLdInfoCommandIndex)
return;
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off;
assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) &&
"Incorrect weak bind opcodes size");
memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size());
}
void MachOWriter::writeLazyBindInfo() {
if (!O.DyLdInfoCommandIndex)
return;
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off;
assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) &&
"Incorrect lazy bind opcodes size");
memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size());
}
void MachOWriter::writeExportInfo() {
if (!O.DyLdInfoCommandIndex)
return;
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off;
assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) &&
"Incorrect export trie size");
memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size());
}
void MachOWriter::writeIndirectSymbolTable() {
if (!O.DySymTabCommandIndex)
return;
const MachO::dysymtab_command &DySymTabCommand =
O.LoadCommands[*O.DySymTabCommandIndex]
.MachOLoadCommand.dysymtab_command_data;
uint32_t *Out =
(uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff);
for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) {
uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex;
if (IsLittleEndian != sys::IsLittleEndianHost)
sys::swapByteOrder(Entry);
*Out++ = Entry;
}
}
void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) {
if (!LCIndex)
return;
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff;
assert((LinkEditDataCommand.datasize == LD.Data.size()) &&
"Incorrect data size");
memcpy(Out, LD.Data.data(), LD.Data.size());
}
static uint64_t
getSegmentFileOffset(const LoadCommand &TextSegmentLoadCommand) {
const MachO::macho_load_command &MLC =
TextSegmentLoadCommand.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return MLC.segment_command_data.fileoff;
case MachO::LC_SEGMENT_64:
return MLC.segment_command_64_data.fileoff;
default:
return 0;
}
}
static uint64_t getSegmentFileSize(const LoadCommand &TextSegmentLoadCommand) {
const MachO::macho_load_command &MLC =
TextSegmentLoadCommand.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return MLC.segment_command_data.filesize;
case MachO::LC_SEGMENT_64:
return MLC.segment_command_64_data.filesize;
default:
return 0;
}
}
void MachOWriter::writeCodeSignatureData() {
// NOTE: This CodeSignature section behaviour must be kept in sync with that
// performed in LLD's CodeSignatureSection::write /
// CodeSignatureSection::writeHashes. Furthermore, this call must occur only
// after the rest of the binary has already been written to the buffer. This
// is because the buffer is read from to perform the necessary hashing.
// The CodeSignature section is the last section in the MachO binary and
// contains a hash of all content in the binary before it. Since llvm-objcopy
// has likely modified the target binary, the hash must be regenerated
// entirely. To generate this hash, we must read from the start of the binary
// (HashReadStart) to just before the start of the CodeSignature section
// (HashReadEnd).
const CodeSignatureInfo &CodeSignature = LayoutBuilder.getCodeSignature();
uint8_t *BufferStart = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
uint8_t *HashReadStart = BufferStart;
uint8_t *HashReadEnd = BufferStart + CodeSignature.StartOffset;
// The CodeSignature section begins with a header, after which the hashes
// of each page of the binary are written.
uint8_t *HashWriteStart = HashReadEnd + CodeSignature.AllHeadersSize;
uint32_t TextSegmentFileOff = 0;
uint32_t TextSegmentFileSize = 0;
if (O.TextSegmentCommandIndex) {
const LoadCommand &TextSegmentLoadCommand =
O.LoadCommands[*O.TextSegmentCommandIndex];
assert(TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
MachO::LC_SEGMENT ||
TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
MachO::LC_SEGMENT_64);
assert(StringRef(TextSegmentLoadCommand.MachOLoadCommand
.segment_command_data.segname) == "__TEXT");
TextSegmentFileOff = getSegmentFileOffset(TextSegmentLoadCommand);
TextSegmentFileSize = getSegmentFileSize(TextSegmentLoadCommand);
}
const uint32_t FileNamePad = CodeSignature.AllHeadersSize -
CodeSignature.FixedHeadersSize -
CodeSignature.OutputFileName.size();
// Write code section header.
auto *SuperBlob = reinterpret_cast<MachO::CS_SuperBlob *>(HashReadEnd);
write32be(&SuperBlob->magic, MachO::CSMAGIC_EMBEDDED_SIGNATURE);
write32be(&SuperBlob->length, CodeSignature.Size);
write32be(&SuperBlob->count, 1);
auto *BlobIndex = reinterpret_cast<MachO::CS_BlobIndex *>(&SuperBlob[1]);
write32be(&BlobIndex->type, MachO::CSSLOT_CODEDIRECTORY);
write32be(&BlobIndex->offset, CodeSignature.BlobHeadersSize);
auto *CodeDirectory = reinterpret_cast<MachO::CS_CodeDirectory *>(
HashReadEnd + CodeSignature.BlobHeadersSize);
write32be(&CodeDirectory->magic, MachO::CSMAGIC_CODEDIRECTORY);
write32be(&CodeDirectory->length,
CodeSignature.Size - CodeSignature.BlobHeadersSize);
write32be(&CodeDirectory->version, MachO::CS_SUPPORTSEXECSEG);
write32be(&CodeDirectory->flags, MachO::CS_ADHOC | MachO::CS_LINKER_SIGNED);
write32be(&CodeDirectory->hashOffset,
sizeof(MachO::CS_CodeDirectory) +
CodeSignature.OutputFileName.size() + FileNamePad);
write32be(&CodeDirectory->identOffset, sizeof(MachO::CS_CodeDirectory));
CodeDirectory->nSpecialSlots = 0;
write32be(&CodeDirectory->nCodeSlots, CodeSignature.BlockCount);
write32be(&CodeDirectory->codeLimit, CodeSignature.StartOffset);
CodeDirectory->hashSize = static_cast<uint8_t>(CodeSignature.HashSize);
CodeDirectory->hashType = MachO::kSecCodeSignatureHashSHA256;
CodeDirectory->platform = 0;
CodeDirectory->pageSize = CodeSignature.BlockSizeShift;
CodeDirectory->spare2 = 0;
CodeDirectory->scatterOffset = 0;
CodeDirectory->teamOffset = 0;
CodeDirectory->spare3 = 0;
CodeDirectory->codeLimit64 = 0;
write64be(&CodeDirectory->execSegBase, TextSegmentFileOff);
write64be(&CodeDirectory->execSegLimit, TextSegmentFileSize);
write64be(&CodeDirectory->execSegFlags, O.Header.FileType == MachO::MH_EXECUTE
? MachO::CS_EXECSEG_MAIN_BINARY
: 0);
auto *Id = reinterpret_cast<char *>(&CodeDirectory[1]);
memcpy(Id, CodeSignature.OutputFileName.begin(),
CodeSignature.OutputFileName.size());
memset(Id + CodeSignature.OutputFileName.size(), 0, FileNamePad);
// Write the hashes.
uint8_t *CurrHashReadPosition = HashReadStart;
uint8_t *CurrHashWritePosition = HashWriteStart;
while (CurrHashReadPosition < HashReadEnd) {
StringRef Block(reinterpret_cast<char *>(CurrHashReadPosition),
std::min(HashReadEnd - CurrHashReadPosition,
static_cast<ssize_t>(CodeSignature.BlockSize)));
SHA256 Hasher;
Hasher.update(Block);
StringRef Hash = Hasher.final();
assert(Hash.size() == CodeSignature.HashSize);
memcpy(CurrHashWritePosition, Hash.data(), CodeSignature.HashSize);
CurrHashReadPosition += CodeSignature.BlockSize;
CurrHashWritePosition += CodeSignature.HashSize;
}
#if defined(__APPLE__)
// This is macOS-specific work-around and makes no sense for any
// other host OS. See https://openradar.appspot.com/FB8914231
//
// The macOS kernel maintains a signature-verification cache to
// quickly validate applications at time of execve(2). The trouble
// is that for the kernel creates the cache entry at the time of the
// mmap(2) call, before we have a chance to write either the code to
// sign or the signature header+hashes. The fix is to invalidate
// all cached data associated with the output file, thus discarding
// the bogus prematurely-cached signature.
msync(BufferStart, CodeSignature.StartOffset + CodeSignature.Size,
MS_INVALIDATE);
#endif
}
void MachOWriter::writeDataInCodeData() {
return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode);
}
void MachOWriter::writeLinkerOptimizationHint() {
return writeLinkData(O.LinkerOptimizationHintCommandIndex,
O.LinkerOptimizationHint);
}
void MachOWriter::writeFunctionStartsData() {
return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts);
}
void MachOWriter::writeChainedFixupsData() {
return writeLinkData(O.ChainedFixupsCommandIndex, O.ChainedFixups);
}
void MachOWriter::writeExportsTrieData() {
return writeLinkData(O.ExportsTrieCommandIndex, O.ExportsTrie);
}
void MachOWriter::writeTail() {
typedef void (MachOWriter::*WriteHandlerType)();
typedef std::pair<uint64_t, WriteHandlerType> WriteOperation;
SmallVector<WriteOperation, 7> Queue;
if (O.SymTabCommandIndex) {
const MachO::symtab_command &SymTabCommand =
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
if (SymTabCommand.symoff)
Queue.push_back({SymTabCommand.symoff, &MachOWriter::writeSymbolTable});
if (SymTabCommand.stroff)
Queue.push_back({SymTabCommand.stroff, &MachOWriter::writeStringTable});
}
if (O.DyLdInfoCommandIndex) {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
if (DyLdInfoCommand.rebase_off)
Queue.push_back(
{DyLdInfoCommand.rebase_off, &MachOWriter::writeRebaseInfo});
if (DyLdInfoCommand.bind_off)
Queue.push_back({DyLdInfoCommand.bind_off, &MachOWriter::writeBindInfo});
if (DyLdInfoCommand.weak_bind_off)
Queue.push_back(
{DyLdInfoCommand.weak_bind_off, &MachOWriter::writeWeakBindInfo});
if (DyLdInfoCommand.lazy_bind_off)
Queue.push_back(
{DyLdInfoCommand.lazy_bind_off, &MachOWriter::writeLazyBindInfo});
if (DyLdInfoCommand.export_off)
Queue.push_back(
{DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo});
}
if (O.DySymTabCommandIndex) {
const MachO::dysymtab_command &DySymTabCommand =
O.LoadCommands[*O.DySymTabCommandIndex]
.MachOLoadCommand.dysymtab_command_data;
if (DySymTabCommand.indirectsymoff)
Queue.emplace_back(DySymTabCommand.indirectsymoff,
&MachOWriter::writeIndirectSymbolTable);
}
if (O.CodeSignatureCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.CodeSignatureCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeCodeSignatureData);
}
if (O.DataInCodeCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.DataInCodeCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeDataInCodeData);
}
if (O.LinkerOptimizationHintCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeLinkerOptimizationHint);
}
if (O.FunctionStartsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.FunctionStartsCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeFunctionStartsData);
}
if (O.ChainedFixupsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.ChainedFixupsCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeChainedFixupsData);
}
if (O.ExportsTrieCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.ExportsTrieCommandIndex]
.MachOLoadCommand.linkedit_data_command_data;
if (LinkEditDataCommand.dataoff)
Queue.emplace_back(LinkEditDataCommand.dataoff,
&MachOWriter::writeExportsTrieData);
}
llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) {
return LHS.first < RHS.first;
});
for (auto WriteOp : Queue)
(this->*WriteOp.second)();
}
Error MachOWriter::finalize() { return LayoutBuilder.layout(); }
Error MachOWriter::write() {
size_t TotalSize = totalSize();
Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
if (!Buf)
return createStringError(errc::not_enough_memory,
"failed to allocate memory buffer of " +
Twine::utohexstr(TotalSize) + " bytes");
memset(Buf->getBufferStart(), 0, totalSize());
writeHeader();
writeLoadCommands();
writeSections();
writeTail();
// TODO: Implement direct writing to the output stream (without intermediate
// memory buffer Buf).
Out.write(Buf->getBufferStart(), Buf->getBufferSize());
return Error::success();
}