brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.5 KiB · 8660c90 Raw
464 lines · cpp
1//===- MachOLayoutBuilder.cpp -----------------------------------*- 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 "MachOLayoutBuilder.h"10#include "llvm/Support/Alignment.h"11#include "llvm/Support/Errc.h"12#include "llvm/Support/ErrorHandling.h"13 14using namespace llvm;15using namespace llvm::objcopy::macho;16 17StringTableBuilder::Kind18MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) {19  if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT)20    return Is64Bit ? StringTableBuilder::MachO64 : StringTableBuilder::MachO;21  return Is64Bit ? StringTableBuilder::MachO64Linked22                 : StringTableBuilder::MachOLinked;23}24 25uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {26  uint32_t Size = 0;27  for (const LoadCommand &LC : O.LoadCommands) {28    const MachO::macho_load_command &MLC = LC.MachOLoadCommand;29    auto cmd = MLC.load_command_data.cmd;30    switch (cmd) {31    case MachO::LC_SEGMENT:32      Size += sizeof(MachO::segment_command) +33              sizeof(MachO::section) * LC.Sections.size();34      continue;35    case MachO::LC_SEGMENT_64:36      Size += sizeof(MachO::segment_command_64) +37              sizeof(MachO::section_64) * LC.Sections.size();38      continue;39    }40 41    switch (cmd) {42#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \43  case MachO::LCName:                                                          \44    Size += sizeof(MachO::LCStruct) + LC.Payload.size();                       \45    break;46#include "llvm/BinaryFormat/MachO.def"47#undef HANDLE_LOAD_COMMAND48    }49  }50 51  return Size;52}53 54void MachOLayoutBuilder::constructStringTable() {55  for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)56    StrTableBuilder.add(Sym->Name);57  StrTableBuilder.finalize();58}59 60void MachOLayoutBuilder::updateSymbolIndexes() {61  uint32_t Index = 0;62  for (auto &Symbol : O.SymTable.Symbols)63    Symbol->Index = Index++;64}65 66// Updates the index and the number of local/external/undefined symbols.67void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {68  assert(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.71  assert(llvm::is_sorted(O.SymTable.Symbols,72                         [](const std::unique_ptr<SymbolEntry> &A,73                            const std::unique_ptr<SymbolEntry> &B) {74                           bool AL = A->isLocalSymbol(),75                                BL = B->isLocalSymbol();76                           if (AL != BL)77                             return AL;78                           return !AL && !A->isUndefinedSymbol() &&79                                  B->isUndefinedSymbol();80                         }) &&81         "Symbols are not sorted by their types.");82 83  uint32_t NumLocalSymbols = 0;84  auto Iter = O.SymTable.Symbols.begin();85  auto End = O.SymTable.Symbols.end();86  for (; Iter != End; ++Iter) {87    if ((*Iter)->isExternalSymbol())88      break;89 90    ++NumLocalSymbols;91  }92 93  uint32_t NumExtDefSymbols = 0;94  for (; Iter != End; ++Iter) {95    if ((*Iter)->isUndefinedSymbol())96      break;97 98    ++NumExtDefSymbols;99  }100 101  MLC.dysymtab_command_data.ilocalsym = 0;102  MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;103  MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;104  MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;105  MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;106  MLC.dysymtab_command_data.nundefsym =107      O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);108}109 110// Recomputes and updates offset and size fields in load commands and sections111// since they could be modified.112uint64_t MachOLayoutBuilder::layoutSegments() {113  auto HeaderSize =114      Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);115  const bool IsObjectFile =116      O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;117  uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;118  for (LoadCommand &LC : O.LoadCommands) {119    auto &MLC = LC.MachOLoadCommand;120    StringRef Segname;121    uint64_t SegmentVmAddr;122    uint64_t SegmentVmSize;123    switch (MLC.load_command_data.cmd) {124    case MachO::LC_SEGMENT:125      SegmentVmAddr = MLC.segment_command_data.vmaddr;126      SegmentVmSize = MLC.segment_command_data.vmsize;127      Segname = StringRef(MLC.segment_command_data.segname,128                          strnlen(MLC.segment_command_data.segname,129                                  sizeof(MLC.segment_command_data.segname)));130      break;131    case MachO::LC_SEGMENT_64:132      SegmentVmAddr = MLC.segment_command_64_data.vmaddr;133      SegmentVmSize = MLC.segment_command_64_data.vmsize;134      Segname = StringRef(MLC.segment_command_64_data.segname,135                          strnlen(MLC.segment_command_64_data.segname,136                                  sizeof(MLC.segment_command_64_data.segname)));137      break;138    default:139      continue;140    }141 142    if (Segname == "__LINKEDIT") {143      // We update the __LINKEDIT segment later (in layoutTail).144      assert(LC.Sections.empty() && "__LINKEDIT segment has sections");145      LinkEditLoadCommand = &MLC;146      continue;147    }148 149    // Update file offsets and sizes of sections.150    uint64_t SegOffset = Offset;151    uint64_t SegFileSize = 0;152    uint64_t VMSize = 0;153    for (std::unique_ptr<Section> &Sec : LC.Sections) {154      assert(SegmentVmAddr <= Sec->Addr &&155             "Section's address cannot be smaller than Segment's one");156      uint32_t SectOffset = Sec->Addr - SegmentVmAddr;157      if (IsObjectFile) {158        if (!Sec->hasValidOffset()) {159          Sec->Offset = 0;160        } else {161          uint64_t PaddingSize =162              offsetToAlignment(SegFileSize, Align(1ull << Sec->Align));163          Sec->Offset = SegOffset + SegFileSize + PaddingSize;164          Sec->Size = Sec->Content.size();165          SegFileSize += PaddingSize + Sec->Size;166        }167      } else {168        if (!Sec->hasValidOffset()) {169          Sec->Offset = 0;170        } else {171          Sec->Offset = SegOffset + SectOffset;172          Sec->Size = Sec->Content.size();173          SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size);174        }175      }176      VMSize = std::max(VMSize, SectOffset + Sec->Size);177    }178 179    if (IsObjectFile) {180      Offset += SegFileSize;181    } else {182      Offset = alignTo(Offset + SegFileSize, PageSize);183      SegFileSize = alignTo(SegFileSize, PageSize);184      // Use the original vmsize if the segment is __PAGEZERO.185      VMSize =186          Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize);187    }188 189    switch (MLC.load_command_data.cmd) {190    case MachO::LC_SEGMENT:191      MLC.segment_command_data.cmdsize =192          sizeof(MachO::segment_command) +193          sizeof(MachO::section) * LC.Sections.size();194      MLC.segment_command_data.nsects = LC.Sections.size();195      MLC.segment_command_data.fileoff = SegOffset;196      MLC.segment_command_data.vmsize = VMSize;197      MLC.segment_command_data.filesize = SegFileSize;198      break;199    case MachO::LC_SEGMENT_64:200      MLC.segment_command_64_data.cmdsize =201          sizeof(MachO::segment_command_64) +202          sizeof(MachO::section_64) * LC.Sections.size();203      MLC.segment_command_64_data.nsects = LC.Sections.size();204      MLC.segment_command_64_data.fileoff = SegOffset;205      MLC.segment_command_64_data.vmsize = VMSize;206      MLC.segment_command_64_data.filesize = SegFileSize;207      break;208    }209  }210 211  return Offset;212}213 214uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {215  for (LoadCommand &LC : O.LoadCommands)216    for (std::unique_ptr<Section> &Sec : LC.Sections) {217      Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset;218      Sec->NReloc = Sec->Relocations.size();219      Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc;220    }221 222  return Offset;223}224 225Error 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).231  const uint64_t HeaderSize =232      Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);233  assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) ||234          Offset >= HeaderSize + O.Header.SizeOfCmds) &&235         "Incorrect tail offset");236  Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds);237 238  // The exports trie can be in either LC_DYLD_INFO or in239  // LC_DYLD_EXPORTS_TRIE, but not both.240  size_t DyldInfoExportsTrieSize = 0;241  size_t DyldExportsTrieSize = 0;242  for (const auto &LC : O.LoadCommands) {243    switch (LC.MachOLoadCommand.load_command_data.cmd) {244    case MachO::LC_DYLD_INFO:245    case MachO::LC_DYLD_INFO_ONLY:246      DyldInfoExportsTrieSize = O.Exports.Trie.size();247      break;248    case MachO::LC_DYLD_EXPORTS_TRIE:249      DyldExportsTrieSize = O.Exports.Trie.size();250      break;251    default:252      break;253    }254  }255  assert((DyldInfoExportsTrieSize == 0 || DyldExportsTrieSize == 0) &&256         "Export trie in both LCs");257 258  uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);259  uint64_t StartOfLinkEdit = Offset;260 261  // 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.266  auto updateOffset = [&Offset](size_t Size) {267    uint64_t PreviousOffset = Offset;268    Offset += Size;269    return PreviousOffset;270  };271 272  uint64_t StartOfRebaseInfo = updateOffset(O.Rebases.Opcodes.size());273  uint64_t StartOfBindingInfo = updateOffset(O.Binds.Opcodes.size());274  uint64_t StartOfWeakBindingInfo = updateOffset(O.WeakBinds.Opcodes.size());275  uint64_t StartOfLazyBindingInfo = updateOffset(O.LazyBinds.Opcodes.size());276  uint64_t StartOfExportTrie = updateOffset(DyldInfoExportsTrieSize);277  uint64_t StartOfChainedFixups = updateOffset(O.ChainedFixups.Data.size());278  uint64_t StartOfDyldExportsTrie = updateOffset(DyldExportsTrieSize);279  uint64_t StartOfFunctionStarts = updateOffset(O.FunctionStarts.Data.size());280  uint64_t StartOfDataInCode = updateOffset(O.DataInCode.Data.size());281  uint64_t StartOfLinkerOptimizationHint =282      updateOffset(O.LinkerOptimizationHint.Data.size());283  uint64_t StartOfSymbols = updateOffset(NListSize * O.SymTable.Symbols.size());284  uint64_t StartOfIndirectSymbols =285      updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());286  uint64_t StartOfSymbolStrings = updateOffset(StrTableBuilder.getSize());287  uint64_t StartOfDylibCodeSignDRs = updateOffset(O.DylibCodeSignDRs.Data.size());288 289  uint64_t StartOfCodeSignature = Offset;290  uint32_t CodeSignatureSize = 0;291  if (O.CodeSignatureCommandIndex) {292    StartOfCodeSignature = alignTo(StartOfCodeSignature, 16);293 294    // Note: These calculations are to be kept in sync with the same295    // calculations performed in LLD's CodeSignatureSection.296    const uint32_t AllHeadersSize =297        alignTo(CodeSignature.FixedHeadersSize + OutputFileName.size() + 1,298                CodeSignature.Align);299    const uint32_t BlockCount =300        (StartOfCodeSignature + CodeSignature.BlockSize - 1) /301        CodeSignature.BlockSize;302    const uint32_t Size =303        alignTo(AllHeadersSize + BlockCount * CodeSignature.HashSize,304                CodeSignature.Align);305 306    CodeSignature.StartOffset = StartOfCodeSignature;307    CodeSignature.AllHeadersSize = AllHeadersSize;308    CodeSignature.BlockCount = BlockCount;309    CodeSignature.OutputFileName = OutputFileName;310    CodeSignature.Size = Size;311    CodeSignatureSize = Size;312  }313  uint64_t LinkEditSize =314      StartOfCodeSignature + CodeSignatureSize - StartOfLinkEdit;315 316  // Now we have determined the layout of the contents of the __LINKEDIT317  // segment. Update its load command.318  if (LinkEditLoadCommand) {319    MachO::macho_load_command *MLC = LinkEditLoadCommand;320    switch (LinkEditLoadCommand->load_command_data.cmd) {321    case MachO::LC_SEGMENT:322      MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command);323      MLC->segment_command_data.fileoff = StartOfLinkEdit;324      MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize);325      MLC->segment_command_data.filesize = LinkEditSize;326      break;327    case MachO::LC_SEGMENT_64:328      MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64);329      MLC->segment_command_64_data.fileoff = StartOfLinkEdit;330      MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize);331      MLC->segment_command_64_data.filesize = LinkEditSize;332      break;333    }334  }335 336  for (LoadCommand &LC : O.LoadCommands) {337    auto &MLC = LC.MachOLoadCommand;338    auto cmd = MLC.load_command_data.cmd;339    switch (cmd) {340    case MachO::LC_CODE_SIGNATURE:341      MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature;342      MLC.linkedit_data_command_data.datasize = CodeSignatureSize;343      break;344    case MachO::LC_DYLIB_CODE_SIGN_DRS:345      MLC.linkedit_data_command_data.dataoff = StartOfDylibCodeSignDRs;346      MLC.linkedit_data_command_data.datasize = O.DylibCodeSignDRs.Data.size();347      break;348    case MachO::LC_SYMTAB:349      MLC.symtab_command_data.symoff = StartOfSymbols;350      MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();351      MLC.symtab_command_data.stroff = StartOfSymbolStrings;352      MLC.symtab_command_data.strsize = StrTableBuilder.getSize();353      break;354    case MachO::LC_DYSYMTAB: {355      if (MLC.dysymtab_command_data.ntoc != 0 ||356          MLC.dysymtab_command_data.nmodtab != 0 ||357          MLC.dysymtab_command_data.nextrefsyms != 0 ||358          MLC.dysymtab_command_data.nlocrel != 0 ||359          MLC.dysymtab_command_data.nextrel != 0)360        return createStringError(llvm::errc::not_supported,361                                 "shared library is not yet supported");362      MLC.dysymtab_command_data.indirectsymoff =363          O.IndirectSymTable.Symbols.size() ? StartOfIndirectSymbols : 0;364      MLC.dysymtab_command_data.nindirectsyms =365          O.IndirectSymTable.Symbols.size();366      updateDySymTab(MLC);367      break;368    }369    case MachO::LC_DATA_IN_CODE:370      MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;371      MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();372      break;373    case MachO::LC_LINKER_OPTIMIZATION_HINT:374      MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint;375      MLC.linkedit_data_command_data.datasize =376          O.LinkerOptimizationHint.Data.size();377      break;378    case MachO::LC_FUNCTION_STARTS:379      MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;380      MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();381      break;382    case MachO::LC_DYLD_CHAINED_FIXUPS:383      MLC.linkedit_data_command_data.dataoff = StartOfChainedFixups;384      MLC.linkedit_data_command_data.datasize = O.ChainedFixups.Data.size();385      break;386    case MachO::LC_DYLD_EXPORTS_TRIE:387      MLC.linkedit_data_command_data.dataoff = StartOfDyldExportsTrie;388      MLC.linkedit_data_command_data.datasize = DyldExportsTrieSize;389      break;390    case MachO::LC_DYLD_INFO:391    case MachO::LC_DYLD_INFO_ONLY:392      MLC.dyld_info_command_data.rebase_off =393          O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo;394      MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size();395      MLC.dyld_info_command_data.bind_off =396          O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo;397      MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size();398      MLC.dyld_info_command_data.weak_bind_off =399          O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo;400      MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size();401      MLC.dyld_info_command_data.lazy_bind_off =402          O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo;403      MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size();404      MLC.dyld_info_command_data.export_off =405          O.Exports.Trie.empty() ? 0 : StartOfExportTrie;406      MLC.dyld_info_command_data.export_size = DyldInfoExportsTrieSize;407      break;408    // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in409    // <mach-o/loader.h> is not an offset in the binary file, instead, it is a410    // relative virtual address. At the moment modification of the __TEXT411    // segment of executables isn't supported anyway (e.g. data in code entries412    // are not recalculated). Moreover, in general413    // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because414    // without making additional assumptions (e.g. that the entire __TEXT415    // segment should be encrypted) we do not know how to recalculate the416    // boundaries of the encrypted part. For now just copy over these load417    // commands until we encounter a real world usecase where418    // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted.419    case MachO::LC_ENCRYPTION_INFO:420    case MachO::LC_ENCRYPTION_INFO_64:421    case MachO::LC_LOAD_DYLINKER:422    case MachO::LC_MAIN:423    case MachO::LC_RPATH:424    case MachO::LC_SEGMENT:425    case MachO::LC_SEGMENT_64:426    case MachO::LC_VERSION_MIN_MACOSX:427    case MachO::LC_VERSION_MIN_IPHONEOS:428    case MachO::LC_VERSION_MIN_TVOS:429    case MachO::LC_VERSION_MIN_WATCHOS:430    case MachO::LC_BUILD_VERSION:431    case MachO::LC_ID_DYLIB:432    case MachO::LC_LOAD_DYLIB:433    case MachO::LC_LOAD_WEAK_DYLIB:434    case MachO::LC_UUID:435    case MachO::LC_SOURCE_VERSION:436    case MachO::LC_THREAD:437    case MachO::LC_UNIXTHREAD:438    case MachO::LC_SUB_FRAMEWORK:439    case MachO::LC_SUB_UMBRELLA:440    case MachO::LC_SUB_CLIENT:441    case MachO::LC_SUB_LIBRARY:442    case MachO::LC_LINKER_OPTION:443      // Nothing to update.444      break;445    default:446      // Abort if it's unsupported in order to prevent corrupting the object.447      return createStringError(llvm::errc::not_supported,448                               "unsupported load command (cmd=0x%x)", cmd);449    }450  }451 452  return Error::success();453}454 455Error MachOLayoutBuilder::layout() {456  O.Header.NCmds = O.LoadCommands.size();457  O.Header.SizeOfCmds = computeSizeOfCmds();458  constructStringTable();459  updateSymbolIndexes();460  uint64_t Offset = layoutSegments();461  Offset = layoutRelocations(Offset);462  return layoutTail(Offset);463}464