//===- 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 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( static_cast(this)->getSymbolByIndex(Index)); } void SymbolTable::removeSymbols( function_ref &)> 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 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 &)> ToRemove) { DenseMap 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
&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 &S) -> bool { std::optional Section = S->section(); return (Section && !OldIndexToSection.count(*Section)); }; SmallPtrSet DeadSymbols; for (const std::unique_ptr &Sym : SymTable.Symbols) if (IsDead(Sym)) DeadSymbols.insert(Sym.get()); for (const LoadCommand &LC : LoadCommands) for (const std::unique_ptr
&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 &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(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 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 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 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; } }