blob: 9a4abadc8710a4d42b822b62e927b4aaf155c379 [file] [log] [blame]
//===- MachOObject.cpp - Mach-O object file model ---------------*- 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 "MachOObject.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <unordered_set>
using namespace llvm;
using namespace llvm::objcopy::macho;
Section::Section(StringRef SegName, StringRef SectName)
: Segname(SegName), Sectname(SectName),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {}
Section::Section(StringRef SegName, StringRef SectName, StringRef Content)
: Segname(SegName), Sectname(SectName),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()),
Content(Content) {}
const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const {
assert(Index < Symbols.size() && "invalid symbol index");
return Symbols[Index].get();
}
SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) {
return const_cast<SymbolEntry *>(
static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index));
}
void SymbolTable::removeSymbols(
function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) {
llvm::erase_if(Symbols, ToRemove);
}
void Object::updateLoadCommandIndexes() {
static constexpr char TextSegmentName[] = "__TEXT";
// Update indices of special load commands
for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
LoadCommand &LC = LoadCommands[Index];
switch (LC.MachOLoadCommand.load_command_data.cmd) {
case MachO::LC_CODE_SIGNATURE:
CodeSignatureCommandIndex = Index;
break;
case MachO::LC_SEGMENT:
if (StringRef(LC.MachOLoadCommand.segment_command_data.segname) ==
TextSegmentName)
TextSegmentCommandIndex = Index;
break;
case MachO::LC_SEGMENT_64:
if (StringRef(LC.MachOLoadCommand.segment_command_64_data.segname) ==
TextSegmentName)
TextSegmentCommandIndex = Index;
break;
case MachO::LC_SYMTAB:
SymTabCommandIndex = Index;
break;
case MachO::LC_DYSYMTAB:
DySymTabCommandIndex = Index;
break;
case MachO::LC_DYLD_INFO:
case MachO::LC_DYLD_INFO_ONLY:
DyLdInfoCommandIndex = Index;
break;
case MachO::LC_DATA_IN_CODE:
DataInCodeCommandIndex = Index;
break;
case MachO::LC_LINKER_OPTIMIZATION_HINT:
LinkerOptimizationHintCommandIndex = Index;
break;
case MachO::LC_FUNCTION_STARTS:
FunctionStartsCommandIndex = Index;
break;
case MachO::LC_DYLIB_CODE_SIGN_DRS:
DylibCodeSignDRsIndex = Index;
break;
case MachO::LC_DYLD_CHAINED_FIXUPS:
ChainedFixupsCommandIndex = Index;
break;
case MachO::LC_DYLD_EXPORTS_TRIE:
ExportsTrieCommandIndex = Index;
break;
}
}
}
Error Object::removeLoadCommands(
function_ref<bool(const LoadCommand &)> ToRemove) {
auto It = std::stable_partition(
LoadCommands.begin(), LoadCommands.end(),
[&](const LoadCommand &LC) { return !ToRemove(LC); });
LoadCommands.erase(It, LoadCommands.end());
updateLoadCommandIndexes();
return Error::success();
}
Error Object::removeSections(
function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) {
DenseMap<uint32_t, const Section *> OldIndexToSection;
uint32_t NextSectionIndex = 1;
for (LoadCommand &LC : LoadCommands) {
auto It = std::stable_partition(
std::begin(LC.Sections), std::end(LC.Sections),
[&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); });
for (auto I = LC.Sections.begin(), End = It; I != End; ++I) {
OldIndexToSection[(*I)->Index] = I->get();
(*I)->Index = NextSectionIndex++;
}
LC.Sections.erase(It, LC.Sections.end());
}
auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool {
std::optional<uint32_t> Section = S->section();
return (Section && !OldIndexToSection.count(*Section));
};
SmallPtrSet<const SymbolEntry *, 2> DeadSymbols;
for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols)
if (IsDead(Sym))
DeadSymbols.insert(Sym.get());
for (const LoadCommand &LC : LoadCommands)
for (const std::unique_ptr<Section> &Sec : LC.Sections)
for (const RelocationInfo &R : Sec->Relocations)
if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol))
return createStringError(std::errc::invalid_argument,
"symbol '%s' defined in section with index "
"'%u' cannot be removed because it is "
"referenced by a relocation in section '%s'",
(*R.Symbol)->Name.c_str(),
*((*R.Symbol)->section()),
Sec->CanonicalName.c_str());
SymTable.removeSymbols(IsDead);
for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols)
if (S->section())
S->n_sect = OldIndexToSection[S->n_sect]->Index;
return Error::success();
}
uint64_t Object::nextAvailableSegmentAddress() const {
uint64_t HeaderSize =
is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
uint64_t Addr = HeaderSize + Header.SizeOfCmds;
for (const LoadCommand &LC : LoadCommands) {
const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
Addr = std::max(Addr,
static_cast<uint64_t>(MLC.segment_command_data.vmaddr) +
MLC.segment_command_data.vmsize);
break;
case MachO::LC_SEGMENT_64:
Addr = std::max(Addr, MLC.segment_command_64_data.vmaddr +
MLC.segment_command_64_data.vmsize);
break;
default:
continue;
}
}
return Addr;
}
template <typename SegmentType>
static void
constructSegment(SegmentType &Seg, llvm::MachO::LoadCommandType CmdType,
StringRef SegName, uint64_t SegVMAddr, uint64_t SegVMSize) {
assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");
memset(&Seg, 0, sizeof(SegmentType));
Seg.cmd = CmdType;
strncpy(Seg.segname, SegName.data(), SegName.size());
Seg.maxprot |=
(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
Seg.initprot |=
(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
Seg.vmaddr = SegVMAddr;
Seg.vmsize = SegVMSize;
}
LoadCommand &Object::addSegment(StringRef SegName, uint64_t SegVMSize) {
LoadCommand LC;
const uint64_t SegVMAddr = nextAvailableSegmentAddress();
if (is64Bit())
constructSegment(LC.MachOLoadCommand.segment_command_64_data,
MachO::LC_SEGMENT_64, SegName, SegVMAddr, SegVMSize);
else
constructSegment(LC.MachOLoadCommand.segment_command_data,
MachO::LC_SEGMENT, SegName, SegVMAddr, SegVMSize);
LoadCommands.push_back(std::move(LC));
return LoadCommands.back();
}
/// Extracts a segment name from a string which is possibly non-null-terminated.
static StringRef extractSegmentName(const char *SegName) {
return StringRef(SegName,
strnlen(SegName, sizeof(MachO::segment_command::segname)));
}
std::optional<StringRef> LoadCommand::getSegmentName() const {
const MachO::macho_load_command &MLC = MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return extractSegmentName(MLC.segment_command_data.segname);
case MachO::LC_SEGMENT_64:
return extractSegmentName(MLC.segment_command_64_data.segname);
default:
return std::nullopt;
}
}
std::optional<uint64_t> LoadCommand::getSegmentVMAddr() const {
const MachO::macho_load_command &MLC = MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return MLC.segment_command_data.vmaddr;
case MachO::LC_SEGMENT_64:
return MLC.segment_command_64_data.vmaddr;
default:
return std::nullopt;
}
}