| //===- MachOLayoutBuilder.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 "MachOLayoutBuilder.h" |
| #include "llvm/Support/Alignment.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| namespace llvm { |
| namespace objcopy { |
| namespace macho { |
| |
| uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { |
| uint32_t Size = 0; |
| for (const auto &LC : O.LoadCommands) { |
| const MachO::macho_load_command &MLC = LC.MachOLoadCommand; |
| auto cmd = MLC.load_command_data.cmd; |
| switch (cmd) { |
| case MachO::LC_SEGMENT: |
| Size += sizeof(MachO::segment_command) + |
| sizeof(MachO::section) * LC.Sections.size(); |
| continue; |
| case MachO::LC_SEGMENT_64: |
| Size += sizeof(MachO::segment_command_64) + |
| sizeof(MachO::section_64) * LC.Sections.size(); |
| continue; |
| } |
| |
| switch (cmd) { |
| #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ |
| case MachO::LCName: \ |
| Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \ |
| break; |
| #include "llvm/BinaryFormat/MachO.def" |
| #undef HANDLE_LOAD_COMMAND |
| } |
| } |
| |
| return Size; |
| } |
| |
| void MachOLayoutBuilder::constructStringTable() { |
| for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) |
| StrTableBuilder.add(Sym->Name); |
| StrTableBuilder.finalize(); |
| } |
| |
| void MachOLayoutBuilder::updateSymbolIndexes() { |
| uint32_t Index = 0; |
| for (auto &Symbol : O.SymTable.Symbols) |
| Symbol->Index = Index++; |
| } |
| |
| // Updates the index and the number of local/external/undefined symbols. |
| void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) { |
| assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB); |
| // Make sure that nlist entries in the symbol table are sorted by the those |
| // types. The order is: local < defined external < undefined external. |
| assert(std::is_sorted(O.SymTable.Symbols.begin(), O.SymTable.Symbols.end(), |
| [](const std::unique_ptr<SymbolEntry> &A, |
| const std::unique_ptr<SymbolEntry> &B) { |
| bool AL = A->isLocalSymbol(), BL = B->isLocalSymbol(); |
| if (AL != BL) |
| return AL; |
| return !AL && !A->isUndefinedSymbol() && |
| B->isUndefinedSymbol(); |
| }) && |
| "Symbols are not sorted by their types."); |
| |
| uint32_t NumLocalSymbols = 0; |
| auto Iter = O.SymTable.Symbols.begin(); |
| auto End = O.SymTable.Symbols.end(); |
| for (; Iter != End; ++Iter) { |
| if ((*Iter)->isExternalSymbol()) |
| break; |
| |
| ++NumLocalSymbols; |
| } |
| |
| uint32_t NumExtDefSymbols = 0; |
| for (; Iter != End; ++Iter) { |
| if ((*Iter)->isUndefinedSymbol()) |
| break; |
| |
| ++NumExtDefSymbols; |
| } |
| |
| MLC.dysymtab_command_data.ilocalsym = 0; |
| MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; |
| MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; |
| MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; |
| MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; |
| MLC.dysymtab_command_data.nundefsym = |
| O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); |
| } |
| |
| // Recomputes and updates offset and size fields in load commands and sections |
| // since they could be modified. |
| uint64_t MachOLayoutBuilder::layoutSegments() { |
| auto HeaderSize = |
| Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); |
| const bool IsObjectFile = |
| O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; |
| uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; |
| for (auto &LC : O.LoadCommands) { |
| auto &MLC = LC.MachOLoadCommand; |
| StringRef Segname; |
| uint64_t SegmentVmAddr; |
| uint64_t SegmentVmSize; |
| switch (MLC.load_command_data.cmd) { |
| case MachO::LC_SEGMENT: |
| SegmentVmAddr = MLC.segment_command_data.vmaddr; |
| SegmentVmSize = MLC.segment_command_data.vmsize; |
| Segname = StringRef(MLC.segment_command_data.segname, |
| strnlen(MLC.segment_command_data.segname, |
| sizeof(MLC.segment_command_data.segname))); |
| break; |
| case MachO::LC_SEGMENT_64: |
| SegmentVmAddr = MLC.segment_command_64_data.vmaddr; |
| SegmentVmSize = MLC.segment_command_64_data.vmsize; |
| Segname = StringRef(MLC.segment_command_64_data.segname, |
| strnlen(MLC.segment_command_64_data.segname, |
| sizeof(MLC.segment_command_64_data.segname))); |
| break; |
| default: |
| continue; |
| } |
| |
| if (Segname == "__LINKEDIT") { |
| // We update the __LINKEDIT segment later (in layoutTail). |
| assert(LC.Sections.empty() && "__LINKEDIT segment has sections"); |
| LinkEditLoadCommand = &MLC; |
| continue; |
| } |
| |
| // Update file offsets and sizes of sections. |
| uint64_t SegOffset = Offset; |
| uint64_t SegFileSize = 0; |
| uint64_t VMSize = 0; |
| for (auto &Sec : LC.Sections) { |
| if (IsObjectFile) { |
| if (Sec.isVirtualSection()) { |
| Sec.Offset = 0; |
| } else { |
| uint64_t PaddingSize = |
| offsetToAlignment(SegFileSize, Align(1ull << Sec.Align)); |
| Sec.Offset = SegOffset + SegFileSize + PaddingSize; |
| Sec.Size = Sec.Content.size(); |
| SegFileSize += PaddingSize + Sec.Size; |
| } |
| VMSize = std::max(VMSize, Sec.Addr + Sec.Size); |
| } else { |
| if (Sec.isVirtualSection()) { |
| Sec.Offset = 0; |
| VMSize += Sec.Size; |
| } else { |
| uint32_t SectOffset = Sec.Addr - SegmentVmAddr; |
| Sec.Offset = SegOffset + SectOffset; |
| Sec.Size = Sec.Content.size(); |
| SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size); |
| VMSize = std::max(VMSize, SegFileSize); |
| } |
| } |
| } |
| |
| if (IsObjectFile) { |
| Offset += SegFileSize; |
| } else { |
| Offset = alignTo(Offset + SegFileSize, PageSize); |
| SegFileSize = alignTo(SegFileSize, PageSize); |
| // Use the original vmsize if the segment is __PAGEZERO. |
| VMSize = |
| Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize); |
| } |
| |
| switch (MLC.load_command_data.cmd) { |
| case MachO::LC_SEGMENT: |
| MLC.segment_command_data.cmdsize = |
| sizeof(MachO::segment_command) + |
| sizeof(MachO::section) * LC.Sections.size(); |
| MLC.segment_command_data.nsects = LC.Sections.size(); |
| MLC.segment_command_data.fileoff = SegOffset; |
| MLC.segment_command_data.vmsize = VMSize; |
| MLC.segment_command_data.filesize = SegFileSize; |
| break; |
| case MachO::LC_SEGMENT_64: |
| MLC.segment_command_64_data.cmdsize = |
| sizeof(MachO::segment_command_64) + |
| sizeof(MachO::section_64) * LC.Sections.size(); |
| MLC.segment_command_64_data.nsects = LC.Sections.size(); |
| MLC.segment_command_64_data.fileoff = SegOffset; |
| MLC.segment_command_64_data.vmsize = VMSize; |
| MLC.segment_command_64_data.filesize = SegFileSize; |
| break; |
| } |
| } |
| |
| return Offset; |
| } |
| |
| uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) { |
| for (auto &LC : O.LoadCommands) |
| for (auto &Sec : LC.Sections) { |
| Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset; |
| Sec.NReloc = Sec.Relocations.size(); |
| Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc; |
| } |
| |
| return Offset; |
| } |
| |
| Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { |
| // The order of LINKEDIT elements is as follows: |
| // rebase info, binding info, weak binding info, lazy binding info, export |
| // trie, data-in-code, symbol table, indirect symbol table, symbol table |
| // strings. |
| uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); |
| uint64_t StartOfLinkEdit = Offset; |
| uint64_t StartOfRebaseInfo = StartOfLinkEdit; |
| uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size(); |
| uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size(); |
| uint64_t StartOfLazyBindingInfo = |
| StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size(); |
| uint64_t StartOfExportTrie = |
| StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size(); |
| uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); |
| uint64_t StartOfDataInCode = |
| StartOfFunctionStarts + O.FunctionStarts.Data.size(); |
| uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); |
| uint64_t StartOfIndirectSymbols = |
| StartOfSymbols + NListSize * O.SymTable.Symbols.size(); |
| uint64_t StartOfSymbolStrings = |
| StartOfIndirectSymbols + |
| sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); |
| uint64_t LinkEditSize = |
| (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit; |
| |
| // Now we have determined the layout of the contents of the __LINKEDIT |
| // segment. Update its load command. |
| if (LinkEditLoadCommand) { |
| MachO::macho_load_command *MLC = LinkEditLoadCommand; |
| switch (LinkEditLoadCommand->load_command_data.cmd) { |
| case MachO::LC_SEGMENT: |
| MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command); |
| MLC->segment_command_data.fileoff = StartOfLinkEdit; |
| MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize); |
| MLC->segment_command_data.filesize = LinkEditSize; |
| break; |
| case MachO::LC_SEGMENT_64: |
| MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64); |
| MLC->segment_command_64_data.fileoff = StartOfLinkEdit; |
| MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize); |
| MLC->segment_command_64_data.filesize = LinkEditSize; |
| break; |
| } |
| } |
| |
| for (auto &LC : O.LoadCommands) { |
| auto &MLC = LC.MachOLoadCommand; |
| auto cmd = MLC.load_command_data.cmd; |
| switch (cmd) { |
| case MachO::LC_SYMTAB: |
| MLC.symtab_command_data.symoff = StartOfSymbols; |
| MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); |
| MLC.symtab_command_data.stroff = StartOfSymbolStrings; |
| MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); |
| break; |
| case MachO::LC_DYSYMTAB: { |
| if (MLC.dysymtab_command_data.ntoc != 0 || |
| MLC.dysymtab_command_data.nmodtab != 0 || |
| MLC.dysymtab_command_data.nextrefsyms != 0 || |
| MLC.dysymtab_command_data.nlocrel != 0 || |
| MLC.dysymtab_command_data.nextrel != 0) |
| return createStringError(llvm::errc::not_supported, |
| "shared library is not yet supported"); |
| |
| if (!O.IndirectSymTable.Symbols.empty()) { |
| MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols; |
| MLC.dysymtab_command_data.nindirectsyms = |
| O.IndirectSymTable.Symbols.size(); |
| } |
| |
| updateDySymTab(MLC); |
| break; |
| } |
| case MachO::LC_DATA_IN_CODE: |
| MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; |
| MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); |
| break; |
| case MachO::LC_FUNCTION_STARTS: |
| MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; |
| MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); |
| break; |
| case MachO::LC_DYLD_INFO: |
| case MachO::LC_DYLD_INFO_ONLY: |
| MLC.dyld_info_command_data.rebase_off = |
| O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo; |
| MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size(); |
| MLC.dyld_info_command_data.bind_off = |
| O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo; |
| MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size(); |
| MLC.dyld_info_command_data.weak_bind_off = |
| O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo; |
| MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size(); |
| MLC.dyld_info_command_data.lazy_bind_off = |
| O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo; |
| MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size(); |
| MLC.dyld_info_command_data.export_off = |
| O.Exports.Trie.empty() ? 0 : StartOfExportTrie; |
| MLC.dyld_info_command_data.export_size = O.Exports.Trie.size(); |
| break; |
| case MachO::LC_LOAD_DYLINKER: |
| case MachO::LC_MAIN: |
| case MachO::LC_RPATH: |
| case MachO::LC_SEGMENT: |
| case MachO::LC_SEGMENT_64: |
| case MachO::LC_VERSION_MIN_MACOSX: |
| case MachO::LC_VERSION_MIN_IPHONEOS: |
| case MachO::LC_VERSION_MIN_TVOS: |
| case MachO::LC_VERSION_MIN_WATCHOS: |
| case MachO::LC_BUILD_VERSION: |
| case MachO::LC_ID_DYLIB: |
| case MachO::LC_LOAD_DYLIB: |
| case MachO::LC_UUID: |
| case MachO::LC_SOURCE_VERSION: |
| // Nothing to update. |
| break; |
| default: |
| // Abort if it's unsupported in order to prevent corrupting the object. |
| return createStringError(llvm::errc::not_supported, |
| "unsupported load command (cmd=0x%x)", cmd); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| Error MachOLayoutBuilder::layout() { |
| O.Header.NCmds = O.LoadCommands.size(); |
| O.Header.SizeOfCmds = computeSizeOfCmds(); |
| constructStringTable(); |
| updateSymbolIndexes(); |
| uint64_t Offset = layoutSegments(); |
| Offset = layoutRelocations(Offset); |
| return layoutTail(Offset); |
| } |
| |
| } // end namespace macho |
| } // end namespace objcopy |
| } // end namespace llvm |