Path: blob/main/contrib/llvm-project/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp
35269 views
//===- MachOLayoutBuilder.cpp -----------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "MachOLayoutBuilder.h"9#include "llvm/Support/Alignment.h"10#include "llvm/Support/Errc.h"11#include "llvm/Support/ErrorHandling.h"12#include "llvm/Support/SystemZ/zOSSupport.h"1314using namespace llvm;15using namespace llvm::objcopy::macho;1617StringTableBuilder::Kind18MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) {19if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT)20return Is64Bit ? StringTableBuilder::MachO64 : StringTableBuilder::MachO;21return Is64Bit ? StringTableBuilder::MachO64Linked22: StringTableBuilder::MachOLinked;23}2425uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {26uint32_t Size = 0;27for (const LoadCommand &LC : O.LoadCommands) {28const MachO::macho_load_command &MLC = LC.MachOLoadCommand;29auto cmd = MLC.load_command_data.cmd;30switch (cmd) {31case MachO::LC_SEGMENT:32Size += sizeof(MachO::segment_command) +33sizeof(MachO::section) * LC.Sections.size();34continue;35case MachO::LC_SEGMENT_64:36Size += sizeof(MachO::segment_command_64) +37sizeof(MachO::section_64) * LC.Sections.size();38continue;39}4041switch (cmd) {42#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \43case MachO::LCName: \44Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \45break;46#include "llvm/BinaryFormat/MachO.def"47#undef HANDLE_LOAD_COMMAND48}49}5051return Size;52}5354void MachOLayoutBuilder::constructStringTable() {55for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)56StrTableBuilder.add(Sym->Name);57StrTableBuilder.finalize();58}5960void MachOLayoutBuilder::updateSymbolIndexes() {61uint32_t Index = 0;62for (auto &Symbol : O.SymTable.Symbols)63Symbol->Index = Index++;64}6566// Updates the index and the number of local/external/undefined symbols.67void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {68assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB);69// Make sure that nlist entries in the symbol table are sorted by the those70// types. The order is: local < defined external < undefined external.71assert(llvm::is_sorted(O.SymTable.Symbols,72[](const std::unique_ptr<SymbolEntry> &A,73const std::unique_ptr<SymbolEntry> &B) {74bool AL = A->isLocalSymbol(),75BL = B->isLocalSymbol();76if (AL != BL)77return AL;78return !AL && !A->isUndefinedSymbol() &&79B->isUndefinedSymbol();80}) &&81"Symbols are not sorted by their types.");8283uint32_t NumLocalSymbols = 0;84auto Iter = O.SymTable.Symbols.begin();85auto End = O.SymTable.Symbols.end();86for (; Iter != End; ++Iter) {87if ((*Iter)->isExternalSymbol())88break;8990++NumLocalSymbols;91}9293uint32_t NumExtDefSymbols = 0;94for (; Iter != End; ++Iter) {95if ((*Iter)->isUndefinedSymbol())96break;9798++NumExtDefSymbols;99}100101MLC.dysymtab_command_data.ilocalsym = 0;102MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;103MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;104MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;105MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;106MLC.dysymtab_command_data.nundefsym =107O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);108}109110// Recomputes and updates offset and size fields in load commands and sections111// since they could be modified.112uint64_t MachOLayoutBuilder::layoutSegments() {113auto HeaderSize =114Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);115const bool IsObjectFile =116O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;117uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;118for (LoadCommand &LC : O.LoadCommands) {119auto &MLC = LC.MachOLoadCommand;120StringRef Segname;121uint64_t SegmentVmAddr;122uint64_t SegmentVmSize;123switch (MLC.load_command_data.cmd) {124case MachO::LC_SEGMENT:125SegmentVmAddr = MLC.segment_command_data.vmaddr;126SegmentVmSize = MLC.segment_command_data.vmsize;127Segname = StringRef(MLC.segment_command_data.segname,128strnlen(MLC.segment_command_data.segname,129sizeof(MLC.segment_command_data.segname)));130break;131case MachO::LC_SEGMENT_64:132SegmentVmAddr = MLC.segment_command_64_data.vmaddr;133SegmentVmSize = MLC.segment_command_64_data.vmsize;134Segname = StringRef(MLC.segment_command_64_data.segname,135strnlen(MLC.segment_command_64_data.segname,136sizeof(MLC.segment_command_64_data.segname)));137break;138default:139continue;140}141142if (Segname == "__LINKEDIT") {143// We update the __LINKEDIT segment later (in layoutTail).144assert(LC.Sections.empty() && "__LINKEDIT segment has sections");145LinkEditLoadCommand = &MLC;146continue;147}148149// Update file offsets and sizes of sections.150uint64_t SegOffset = Offset;151uint64_t SegFileSize = 0;152uint64_t VMSize = 0;153for (std::unique_ptr<Section> &Sec : LC.Sections) {154assert(SegmentVmAddr <= Sec->Addr &&155"Section's address cannot be smaller than Segment's one");156uint32_t SectOffset = Sec->Addr - SegmentVmAddr;157if (IsObjectFile) {158if (!Sec->hasValidOffset()) {159Sec->Offset = 0;160} else {161uint64_t PaddingSize =162offsetToAlignment(SegFileSize, Align(1ull << Sec->Align));163Sec->Offset = SegOffset + SegFileSize + PaddingSize;164Sec->Size = Sec->Content.size();165SegFileSize += PaddingSize + Sec->Size;166}167} else {168if (!Sec->hasValidOffset()) {169Sec->Offset = 0;170} else {171Sec->Offset = SegOffset + SectOffset;172Sec->Size = Sec->Content.size();173SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size);174}175}176VMSize = std::max(VMSize, SectOffset + Sec->Size);177}178179if (IsObjectFile) {180Offset += SegFileSize;181} else {182Offset = alignTo(Offset + SegFileSize, PageSize);183SegFileSize = alignTo(SegFileSize, PageSize);184// Use the original vmsize if the segment is __PAGEZERO.185VMSize =186Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize);187}188189switch (MLC.load_command_data.cmd) {190case MachO::LC_SEGMENT:191MLC.segment_command_data.cmdsize =192sizeof(MachO::segment_command) +193sizeof(MachO::section) * LC.Sections.size();194MLC.segment_command_data.nsects = LC.Sections.size();195MLC.segment_command_data.fileoff = SegOffset;196MLC.segment_command_data.vmsize = VMSize;197MLC.segment_command_data.filesize = SegFileSize;198break;199case MachO::LC_SEGMENT_64:200MLC.segment_command_64_data.cmdsize =201sizeof(MachO::segment_command_64) +202sizeof(MachO::section_64) * LC.Sections.size();203MLC.segment_command_64_data.nsects = LC.Sections.size();204MLC.segment_command_64_data.fileoff = SegOffset;205MLC.segment_command_64_data.vmsize = VMSize;206MLC.segment_command_64_data.filesize = SegFileSize;207break;208}209}210211return Offset;212}213214uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {215for (LoadCommand &LC : O.LoadCommands)216for (std::unique_ptr<Section> &Sec : LC.Sections) {217Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset;218Sec->NReloc = Sec->Relocations.size();219Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc;220}221222return Offset;223}224225Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {226// If we are building the layout of an executable or dynamic library227// which does not have any segments other than __LINKEDIT,228// the Offset can be equal to zero by this time. It happens because of the229// convention that in such cases the file offsets specified by LC_SEGMENT230// start with zero (unlike the case of a relocatable object file).231const uint64_t HeaderSize =232Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);233assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) ||234Offset >= HeaderSize + O.Header.SizeOfCmds) &&235"Incorrect tail offset");236Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds);237238// The exports trie can be in either LC_DYLD_INFO or in239// LC_DYLD_EXPORTS_TRIE, but not both.240size_t DyldInfoExportsTrieSize = 0;241size_t DyldExportsTrieSize = 0;242for (const auto &LC : O.LoadCommands) {243switch (LC.MachOLoadCommand.load_command_data.cmd) {244case MachO::LC_DYLD_INFO:245case MachO::LC_DYLD_INFO_ONLY:246DyldInfoExportsTrieSize = O.Exports.Trie.size();247break;248case MachO::LC_DYLD_EXPORTS_TRIE:249DyldExportsTrieSize = O.Exports.Trie.size();250break;251default:252break;253}254}255assert((DyldInfoExportsTrieSize == 0 || DyldExportsTrieSize == 0) &&256"Export trie in both LCs");257258uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);259uint64_t StartOfLinkEdit = Offset;260261// The order of LINKEDIT elements is as follows:262// rebase info, binding info, weak binding info, lazy binding info, export263// trie, chained fixups, dyld exports trie, function starts, data-in-code,264// symbol table, indirect symbol table, symbol table strings,265// dylib codesign drs, and code signature.266auto updateOffset = [&Offset](size_t Size) {267uint64_t PreviousOffset = Offset;268Offset += Size;269return PreviousOffset;270};271272uint64_t StartOfRebaseInfo = updateOffset(O.Rebases.Opcodes.size());273uint64_t StartOfBindingInfo = updateOffset(O.Binds.Opcodes.size());274uint64_t StartOfWeakBindingInfo = updateOffset(O.WeakBinds.Opcodes.size());275uint64_t StartOfLazyBindingInfo = updateOffset(O.LazyBinds.Opcodes.size());276uint64_t StartOfExportTrie = updateOffset(DyldInfoExportsTrieSize);277uint64_t StartOfChainedFixups = updateOffset(O.ChainedFixups.Data.size());278uint64_t StartOfDyldExportsTrie = updateOffset(DyldExportsTrieSize);279uint64_t StartOfFunctionStarts = updateOffset(O.FunctionStarts.Data.size());280uint64_t StartOfDataInCode = updateOffset(O.DataInCode.Data.size());281uint64_t StartOfLinkerOptimizationHint =282updateOffset(O.LinkerOptimizationHint.Data.size());283uint64_t StartOfSymbols = updateOffset(NListSize * O.SymTable.Symbols.size());284uint64_t StartOfIndirectSymbols =285updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());286uint64_t StartOfSymbolStrings = updateOffset(StrTableBuilder.getSize());287uint64_t StartOfDylibCodeSignDRs = updateOffset(O.DylibCodeSignDRs.Data.size());288289uint64_t StartOfCodeSignature = Offset;290uint32_t CodeSignatureSize = 0;291if (O.CodeSignatureCommandIndex) {292StartOfCodeSignature = alignTo(StartOfCodeSignature, 16);293294// Note: These calculations are to be kept in sync with the same295// calculations performed in LLD's CodeSignatureSection.296const uint32_t AllHeadersSize =297alignTo(CodeSignature.FixedHeadersSize + OutputFileName.size() + 1,298CodeSignature.Align);299const uint32_t BlockCount =300(StartOfCodeSignature + CodeSignature.BlockSize - 1) /301CodeSignature.BlockSize;302const uint32_t Size =303alignTo(AllHeadersSize + BlockCount * CodeSignature.HashSize,304CodeSignature.Align);305306CodeSignature.StartOffset = StartOfCodeSignature;307CodeSignature.AllHeadersSize = AllHeadersSize;308CodeSignature.BlockCount = BlockCount;309CodeSignature.OutputFileName = OutputFileName;310CodeSignature.Size = Size;311CodeSignatureSize = Size;312}313uint64_t LinkEditSize =314StartOfCodeSignature + CodeSignatureSize - StartOfLinkEdit;315316// Now we have determined the layout of the contents of the __LINKEDIT317// segment. Update its load command.318if (LinkEditLoadCommand) {319MachO::macho_load_command *MLC = LinkEditLoadCommand;320switch (LinkEditLoadCommand->load_command_data.cmd) {321case MachO::LC_SEGMENT:322MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command);323MLC->segment_command_data.fileoff = StartOfLinkEdit;324MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize);325MLC->segment_command_data.filesize = LinkEditSize;326break;327case MachO::LC_SEGMENT_64:328MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64);329MLC->segment_command_64_data.fileoff = StartOfLinkEdit;330MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize);331MLC->segment_command_64_data.filesize = LinkEditSize;332break;333}334}335336for (LoadCommand &LC : O.LoadCommands) {337auto &MLC = LC.MachOLoadCommand;338auto cmd = MLC.load_command_data.cmd;339switch (cmd) {340case MachO::LC_CODE_SIGNATURE:341MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature;342MLC.linkedit_data_command_data.datasize = CodeSignatureSize;343break;344case MachO::LC_DYLIB_CODE_SIGN_DRS:345MLC.linkedit_data_command_data.dataoff = StartOfDylibCodeSignDRs;346MLC.linkedit_data_command_data.datasize = O.DylibCodeSignDRs.Data.size();347break;348case MachO::LC_SYMTAB:349MLC.symtab_command_data.symoff = StartOfSymbols;350MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();351MLC.symtab_command_data.stroff = StartOfSymbolStrings;352MLC.symtab_command_data.strsize = StrTableBuilder.getSize();353break;354case MachO::LC_DYSYMTAB: {355if (MLC.dysymtab_command_data.ntoc != 0 ||356MLC.dysymtab_command_data.nmodtab != 0 ||357MLC.dysymtab_command_data.nextrefsyms != 0 ||358MLC.dysymtab_command_data.nlocrel != 0 ||359MLC.dysymtab_command_data.nextrel != 0)360return createStringError(llvm::errc::not_supported,361"shared library is not yet supported");362363if (!O.IndirectSymTable.Symbols.empty()) {364MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols;365MLC.dysymtab_command_data.nindirectsyms =366O.IndirectSymTable.Symbols.size();367}368369updateDySymTab(MLC);370break;371}372case MachO::LC_DATA_IN_CODE:373MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;374MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();375break;376case MachO::LC_LINKER_OPTIMIZATION_HINT:377MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint;378MLC.linkedit_data_command_data.datasize =379O.LinkerOptimizationHint.Data.size();380break;381case MachO::LC_FUNCTION_STARTS:382MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;383MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();384break;385case MachO::LC_DYLD_CHAINED_FIXUPS:386MLC.linkedit_data_command_data.dataoff = StartOfChainedFixups;387MLC.linkedit_data_command_data.datasize = O.ChainedFixups.Data.size();388break;389case MachO::LC_DYLD_EXPORTS_TRIE:390MLC.linkedit_data_command_data.dataoff = StartOfDyldExportsTrie;391MLC.linkedit_data_command_data.datasize = DyldExportsTrieSize;392break;393case MachO::LC_DYLD_INFO:394case MachO::LC_DYLD_INFO_ONLY:395MLC.dyld_info_command_data.rebase_off =396O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo;397MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size();398MLC.dyld_info_command_data.bind_off =399O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo;400MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size();401MLC.dyld_info_command_data.weak_bind_off =402O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo;403MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size();404MLC.dyld_info_command_data.lazy_bind_off =405O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo;406MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size();407MLC.dyld_info_command_data.export_off =408O.Exports.Trie.empty() ? 0 : StartOfExportTrie;409MLC.dyld_info_command_data.export_size = DyldInfoExportsTrieSize;410break;411// Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in412// <mach-o/loader.h> is not an offset in the binary file, instead, it is a413// relative virtual address. At the moment modification of the __TEXT414// segment of executables isn't supported anyway (e.g. data in code entries415// are not recalculated). Moreover, in general416// LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because417// without making additional assumptions (e.g. that the entire __TEXT418// segment should be encrypted) we do not know how to recalculate the419// boundaries of the encrypted part. For now just copy over these load420// commands until we encounter a real world usecase where421// LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted.422case MachO::LC_ENCRYPTION_INFO:423case MachO::LC_ENCRYPTION_INFO_64:424case MachO::LC_LOAD_DYLINKER:425case MachO::LC_MAIN:426case MachO::LC_RPATH:427case MachO::LC_SEGMENT:428case MachO::LC_SEGMENT_64:429case MachO::LC_VERSION_MIN_MACOSX:430case MachO::LC_VERSION_MIN_IPHONEOS:431case MachO::LC_VERSION_MIN_TVOS:432case MachO::LC_VERSION_MIN_WATCHOS:433case MachO::LC_BUILD_VERSION:434case MachO::LC_ID_DYLIB:435case MachO::LC_LOAD_DYLIB:436case MachO::LC_LOAD_WEAK_DYLIB:437case MachO::LC_UUID:438case MachO::LC_SOURCE_VERSION:439case MachO::LC_THREAD:440case MachO::LC_UNIXTHREAD:441case MachO::LC_SUB_FRAMEWORK:442case MachO::LC_SUB_UMBRELLA:443case MachO::LC_SUB_CLIENT:444case MachO::LC_SUB_LIBRARY:445case MachO::LC_LINKER_OPTION:446// Nothing to update.447break;448default:449// Abort if it's unsupported in order to prevent corrupting the object.450return createStringError(llvm::errc::not_supported,451"unsupported load command (cmd=0x%x)", cmd);452}453}454455return Error::success();456}457458Error MachOLayoutBuilder::layout() {459O.Header.NCmds = O.LoadCommands.size();460O.Header.SizeOfCmds = computeSizeOfCmds();461constructStringTable();462updateSymbolIndexes();463uint64_t Offset = layoutSegments();464Offset = layoutRelocations(Offset);465return layoutTail(Offset);466}467468469