239 lines · cpp
1//===- MachOObject.cpp - Mach-O object file model ---------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "MachOObject.h"10#include "llvm/ADT/SmallPtrSet.h"11 12using namespace llvm;13using namespace llvm::objcopy::macho;14 15Section::Section(StringRef SegName, StringRef SectName)16 : Segname(SegName), Sectname(SectName),17 CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {}18 19Section::Section(StringRef SegName, StringRef SectName, StringRef Content)20 : Segname(SegName), Sectname(SectName),21 CanonicalName((Twine(SegName) + Twine(',') + SectName).str()),22 Content(Content) {}23 24const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const {25 assert(Index < Symbols.size() && "invalid symbol index");26 return Symbols[Index].get();27}28 29SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) {30 return const_cast<SymbolEntry *>(31 static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index));32}33 34void SymbolTable::updateSymbols(function_ref<void(SymbolEntry &)> Callable) {35 for (auto &Sym : Symbols)36 Callable(*Sym);37 38 // Partition symbols: local < defined external < undefined external.39 auto ExternalBegin = std::stable_partition(40 std::begin(Symbols), std::end(Symbols),41 [](const auto &Sym) { return Sym->isLocalSymbol(); });42 std::stable_partition(ExternalBegin, std::end(Symbols), [](const auto &Sym) {43 return !Sym->isUndefinedSymbol();44 });45}46 47void SymbolTable::removeSymbols(48 function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) {49 llvm::erase_if(Symbols, ToRemove);50}51 52void Object::updateLoadCommandIndexes() {53 static constexpr char TextSegmentName[] = "__TEXT";54 // Update indices of special load commands55 for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {56 LoadCommand &LC = LoadCommands[Index];57 switch (LC.MachOLoadCommand.load_command_data.cmd) {58 case MachO::LC_CODE_SIGNATURE:59 CodeSignatureCommandIndex = Index;60 break;61 case MachO::LC_SEGMENT:62 if (StringRef(LC.MachOLoadCommand.segment_command_data.segname) ==63 TextSegmentName)64 TextSegmentCommandIndex = Index;65 break;66 case MachO::LC_SEGMENT_64:67 if (StringRef(LC.MachOLoadCommand.segment_command_64_data.segname) ==68 TextSegmentName)69 TextSegmentCommandIndex = Index;70 break;71 case MachO::LC_SYMTAB:72 SymTabCommandIndex = Index;73 break;74 case MachO::LC_DYSYMTAB:75 DySymTabCommandIndex = Index;76 break;77 case MachO::LC_DYLD_INFO:78 case MachO::LC_DYLD_INFO_ONLY:79 DyLdInfoCommandIndex = Index;80 break;81 case MachO::LC_DATA_IN_CODE:82 DataInCodeCommandIndex = Index;83 break;84 case MachO::LC_LINKER_OPTIMIZATION_HINT:85 LinkerOptimizationHintCommandIndex = Index;86 break;87 case MachO::LC_FUNCTION_STARTS:88 FunctionStartsCommandIndex = Index;89 break;90 case MachO::LC_DYLIB_CODE_SIGN_DRS:91 DylibCodeSignDRsIndex = Index;92 break;93 case MachO::LC_DYLD_CHAINED_FIXUPS:94 ChainedFixupsCommandIndex = Index;95 break;96 case MachO::LC_DYLD_EXPORTS_TRIE:97 ExportsTrieCommandIndex = Index;98 break;99 }100 }101}102 103Error Object::removeLoadCommands(104 function_ref<bool(const LoadCommand &)> ToRemove) {105 auto It = std::stable_partition(106 LoadCommands.begin(), LoadCommands.end(),107 [&](const LoadCommand &LC) { return !ToRemove(LC); });108 LoadCommands.erase(It, LoadCommands.end());109 110 updateLoadCommandIndexes();111 return Error::success();112}113 114Error Object::removeSections(115 function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) {116 DenseMap<uint32_t, const Section *> OldIndexToSection;117 uint32_t NextSectionIndex = 1;118 for (LoadCommand &LC : LoadCommands) {119 auto It = std::stable_partition(120 std::begin(LC.Sections), std::end(LC.Sections),121 [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); });122 for (auto I = LC.Sections.begin(), End = It; I != End; ++I) {123 OldIndexToSection[(*I)->Index] = I->get();124 (*I)->Index = NextSectionIndex++;125 }126 LC.Sections.erase(It, LC.Sections.end());127 }128 129 auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool {130 std::optional<uint32_t> Section = S->section();131 return (Section && !OldIndexToSection.count(*Section));132 };133 134 SmallPtrSet<const SymbolEntry *, 2> DeadSymbols;135 for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols)136 if (IsDead(Sym))137 DeadSymbols.insert(Sym.get());138 139 for (const LoadCommand &LC : LoadCommands)140 for (const std::unique_ptr<Section> &Sec : LC.Sections)141 for (const RelocationInfo &R : Sec->Relocations)142 if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol))143 return createStringError(std::errc::invalid_argument,144 "symbol '%s' defined in section with index "145 "'%u' cannot be removed because it is "146 "referenced by a relocation in section '%s'",147 (*R.Symbol)->Name.c_str(),148 *((*R.Symbol)->section()),149 Sec->CanonicalName.c_str());150 SymTable.removeSymbols(IsDead);151 for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols)152 if (S->section())153 S->n_sect = OldIndexToSection[S->n_sect]->Index;154 return Error::success();155}156 157uint64_t Object::nextAvailableSegmentAddress() const {158 uint64_t HeaderSize =159 is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);160 uint64_t Addr = HeaderSize + Header.SizeOfCmds;161 for (const LoadCommand &LC : LoadCommands) {162 const MachO::macho_load_command &MLC = LC.MachOLoadCommand;163 switch (MLC.load_command_data.cmd) {164 case MachO::LC_SEGMENT:165 Addr = std::max(Addr,166 static_cast<uint64_t>(MLC.segment_command_data.vmaddr) +167 MLC.segment_command_data.vmsize);168 break;169 case MachO::LC_SEGMENT_64:170 Addr = std::max(Addr, MLC.segment_command_64_data.vmaddr +171 MLC.segment_command_64_data.vmsize);172 break;173 default:174 continue;175 }176 }177 return Addr;178}179 180template <typename SegmentType>181static void182constructSegment(SegmentType &Seg, llvm::MachO::LoadCommandType CmdType,183 StringRef SegName, uint64_t SegVMAddr, uint64_t SegVMSize) {184 assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");185 memset(&Seg, 0, sizeof(SegmentType));186 Seg.cmd = CmdType;187 strncpy(Seg.segname, SegName.data(), SegName.size());188 Seg.maxprot |=189 (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);190 Seg.initprot |=191 (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);192 Seg.vmaddr = SegVMAddr;193 Seg.vmsize = SegVMSize;194}195 196LoadCommand &Object::addSegment(StringRef SegName, uint64_t SegVMSize) {197 LoadCommand LC;198 const uint64_t SegVMAddr = nextAvailableSegmentAddress();199 if (is64Bit())200 constructSegment(LC.MachOLoadCommand.segment_command_64_data,201 MachO::LC_SEGMENT_64, SegName, SegVMAddr, SegVMSize);202 else203 constructSegment(LC.MachOLoadCommand.segment_command_data,204 MachO::LC_SEGMENT, SegName, SegVMAddr, SegVMSize);205 206 LoadCommands.push_back(std::move(LC));207 return LoadCommands.back();208}209 210/// Extracts a segment name from a string which is possibly non-null-terminated.211static StringRef extractSegmentName(const char *SegName) {212 return StringRef(SegName,213 strnlen(SegName, sizeof(MachO::segment_command::segname)));214}215 216std::optional<StringRef> LoadCommand::getSegmentName() const {217 const MachO::macho_load_command &MLC = MachOLoadCommand;218 switch (MLC.load_command_data.cmd) {219 case MachO::LC_SEGMENT:220 return extractSegmentName(MLC.segment_command_data.segname);221 case MachO::LC_SEGMENT_64:222 return extractSegmentName(MLC.segment_command_64_data.segname);223 default:224 return std::nullopt;225 }226}227 228std::optional<uint64_t> LoadCommand::getSegmentVMAddr() const {229 const MachO::macho_load_command &MLC = MachOLoadCommand;230 switch (MLC.load_command_data.cmd) {231 case MachO::LC_SEGMENT:232 return MLC.segment_command_data.vmaddr;233 case MachO::LC_SEGMENT_64:234 return MLC.segment_command_64_data.vmaddr;235 default:236 return std::nullopt;237 }238}239