MachOLayoutBuilder.cpp revision 360784
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-exception
6//
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
14namespace llvm {
15namespace objcopy {
16namespace macho {
17
18uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {
19  uint32_t Size = 0;
20  for (const auto &LC : O.LoadCommands) {
21    const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
22    auto cmd = MLC.load_command_data.cmd;
23    switch (cmd) {
24    case MachO::LC_SEGMENT:
25      Size += sizeof(MachO::segment_command) +
26              sizeof(MachO::section) * LC.Sections.size();
27      continue;
28    case MachO::LC_SEGMENT_64:
29      Size += sizeof(MachO::segment_command_64) +
30              sizeof(MachO::section_64) * LC.Sections.size();
31      continue;
32    }
33
34    switch (cmd) {
35#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
36  case MachO::LCName:                                                          \
37    Size += sizeof(MachO::LCStruct) + LC.Payload.size();                       \
38    break;
39#include "llvm/BinaryFormat/MachO.def"
40#undef HANDLE_LOAD_COMMAND
41    }
42  }
43
44  return Size;
45}
46
47void MachOLayoutBuilder::constructStringTable() {
48  for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)
49    StrTableBuilder.add(Sym->Name);
50  StrTableBuilder.finalize();
51}
52
53void MachOLayoutBuilder::updateSymbolIndexes() {
54  uint32_t Index = 0;
55  for (auto &Symbol : O.SymTable.Symbols)
56    Symbol->Index = Index++;
57}
58
59// Updates the index and the number of local/external/undefined symbols.
60void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {
61  assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB);
62  // Make sure that nlist entries in the symbol table are sorted by the those
63  // types. The order is: local < defined external < undefined external.
64  assert(std::is_sorted(O.SymTable.Symbols.begin(), O.SymTable.Symbols.end(),
65                        [](const std::unique_ptr<SymbolEntry> &A,
66                           const std::unique_ptr<SymbolEntry> &B) {
67                          bool AL = A->isLocalSymbol(), BL = B->isLocalSymbol();
68                          if (AL != BL)
69                            return AL;
70                          return !AL && !A->isUndefinedSymbol() &&
71                                         B->isUndefinedSymbol();
72                        }) &&
73         "Symbols are not sorted by their types.");
74
75  uint32_t NumLocalSymbols = 0;
76  auto Iter = O.SymTable.Symbols.begin();
77  auto End = O.SymTable.Symbols.end();
78  for (; Iter != End; ++Iter) {
79    if ((*Iter)->isExternalSymbol())
80      break;
81
82    ++NumLocalSymbols;
83  }
84
85  uint32_t NumExtDefSymbols = 0;
86  for (; Iter != End; ++Iter) {
87    if ((*Iter)->isUndefinedSymbol())
88      break;
89
90    ++NumExtDefSymbols;
91  }
92
93  MLC.dysymtab_command_data.ilocalsym = 0;
94  MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;
95  MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;
96  MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;
97  MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;
98  MLC.dysymtab_command_data.nundefsym =
99      O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);
100}
101
102// Recomputes and updates offset and size fields in load commands and sections
103// since they could be modified.
104uint64_t MachOLayoutBuilder::layoutSegments() {
105  auto HeaderSize =
106      Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
107  const bool IsObjectFile =
108      O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;
109  uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;
110  for (auto &LC : O.LoadCommands) {
111    auto &MLC = LC.MachOLoadCommand;
112    StringRef Segname;
113    uint64_t SegmentVmAddr;
114    uint64_t SegmentVmSize;
115    switch (MLC.load_command_data.cmd) {
116    case MachO::LC_SEGMENT:
117      SegmentVmAddr = MLC.segment_command_data.vmaddr;
118      SegmentVmSize = MLC.segment_command_data.vmsize;
119      Segname = StringRef(MLC.segment_command_data.segname,
120                          strnlen(MLC.segment_command_data.segname,
121                                  sizeof(MLC.segment_command_data.segname)));
122      break;
123    case MachO::LC_SEGMENT_64:
124      SegmentVmAddr = MLC.segment_command_64_data.vmaddr;
125      SegmentVmSize = MLC.segment_command_64_data.vmsize;
126      Segname = StringRef(MLC.segment_command_64_data.segname,
127                          strnlen(MLC.segment_command_64_data.segname,
128                                  sizeof(MLC.segment_command_64_data.segname)));
129      break;
130    default:
131      continue;
132    }
133
134    if (Segname == "__LINKEDIT") {
135      // We update the __LINKEDIT segment later (in layoutTail).
136      assert(LC.Sections.empty() && "__LINKEDIT segment has sections");
137      LinkEditLoadCommand = &MLC;
138      continue;
139    }
140
141    // Update file offsets and sizes of sections.
142    uint64_t SegOffset = Offset;
143    uint64_t SegFileSize = 0;
144    uint64_t VMSize = 0;
145    for (auto &Sec : LC.Sections) {
146      if (IsObjectFile) {
147        if (Sec.isVirtualSection()) {
148          Sec.Offset = 0;
149        } else {
150          uint64_t PaddingSize =
151              offsetToAlignment(SegFileSize, Align(1ull << Sec.Align));
152          Sec.Offset = SegOffset + SegFileSize + PaddingSize;
153          Sec.Size = Sec.Content.size();
154          SegFileSize += PaddingSize + Sec.Size;
155        }
156        VMSize = std::max(VMSize, Sec.Addr + Sec.Size);
157      } else {
158        if (Sec.isVirtualSection()) {
159          Sec.Offset = 0;
160          VMSize += Sec.Size;
161        } else {
162          uint32_t SectOffset = Sec.Addr - SegmentVmAddr;
163          Sec.Offset = SegOffset + SectOffset;
164          Sec.Size = Sec.Content.size();
165          SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size);
166          VMSize = std::max(VMSize, SegFileSize);
167        }
168      }
169    }
170
171    if (IsObjectFile) {
172      Offset += SegFileSize;
173    } else {
174      Offset = alignTo(Offset + SegFileSize, PageSize);
175      SegFileSize = alignTo(SegFileSize, PageSize);
176      // Use the original vmsize if the segment is __PAGEZERO.
177      VMSize =
178          Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize);
179    }
180
181    switch (MLC.load_command_data.cmd) {
182    case MachO::LC_SEGMENT:
183      MLC.segment_command_data.cmdsize =
184          sizeof(MachO::segment_command) +
185          sizeof(MachO::section) * LC.Sections.size();
186      MLC.segment_command_data.nsects = LC.Sections.size();
187      MLC.segment_command_data.fileoff = SegOffset;
188      MLC.segment_command_data.vmsize = VMSize;
189      MLC.segment_command_data.filesize = SegFileSize;
190      break;
191    case MachO::LC_SEGMENT_64:
192      MLC.segment_command_64_data.cmdsize =
193          sizeof(MachO::segment_command_64) +
194          sizeof(MachO::section_64) * LC.Sections.size();
195      MLC.segment_command_64_data.nsects = LC.Sections.size();
196      MLC.segment_command_64_data.fileoff = SegOffset;
197      MLC.segment_command_64_data.vmsize = VMSize;
198      MLC.segment_command_64_data.filesize = SegFileSize;
199      break;
200    }
201  }
202
203  return Offset;
204}
205
206uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {
207  for (auto &LC : O.LoadCommands)
208    for (auto &Sec : LC.Sections) {
209      Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset;
210      Sec.NReloc = Sec.Relocations.size();
211      Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc;
212    }
213
214  return Offset;
215}
216
217Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
218  // The order of LINKEDIT elements is as follows:
219  // rebase info, binding info, weak binding info, lazy binding info, export
220  // trie, data-in-code, symbol table, indirect symbol table, symbol table
221  // strings.
222  uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
223  uint64_t StartOfLinkEdit = Offset;
224  uint64_t StartOfRebaseInfo = StartOfLinkEdit;
225  uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size();
226  uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size();
227  uint64_t StartOfLazyBindingInfo =
228      StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size();
229  uint64_t StartOfExportTrie =
230      StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size();
231  uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size();
232  uint64_t StartOfDataInCode =
233      StartOfFunctionStarts + O.FunctionStarts.Data.size();
234  uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size();
235  uint64_t StartOfIndirectSymbols =
236      StartOfSymbols + NListSize * O.SymTable.Symbols.size();
237  uint64_t StartOfSymbolStrings =
238      StartOfIndirectSymbols +
239      sizeof(uint32_t) * O.IndirectSymTable.Symbols.size();
240  uint64_t LinkEditSize =
241      (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit;
242
243  // Now we have determined the layout of the contents of the __LINKEDIT
244  // segment. Update its load command.
245  if (LinkEditLoadCommand) {
246    MachO::macho_load_command *MLC = LinkEditLoadCommand;
247    switch (LinkEditLoadCommand->load_command_data.cmd) {
248    case MachO::LC_SEGMENT:
249      MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command);
250      MLC->segment_command_data.fileoff = StartOfLinkEdit;
251      MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize);
252      MLC->segment_command_data.filesize = LinkEditSize;
253      break;
254    case MachO::LC_SEGMENT_64:
255      MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64);
256      MLC->segment_command_64_data.fileoff = StartOfLinkEdit;
257      MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize);
258      MLC->segment_command_64_data.filesize = LinkEditSize;
259      break;
260    }
261  }
262
263  for (auto &LC : O.LoadCommands) {
264    auto &MLC = LC.MachOLoadCommand;
265    auto cmd = MLC.load_command_data.cmd;
266    switch (cmd) {
267    case MachO::LC_SYMTAB:
268      MLC.symtab_command_data.symoff = StartOfSymbols;
269      MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();
270      MLC.symtab_command_data.stroff = StartOfSymbolStrings;
271      MLC.symtab_command_data.strsize = StrTableBuilder.getSize();
272      break;
273    case MachO::LC_DYSYMTAB: {
274      if (MLC.dysymtab_command_data.ntoc != 0 ||
275          MLC.dysymtab_command_data.nmodtab != 0 ||
276          MLC.dysymtab_command_data.nextrefsyms != 0 ||
277          MLC.dysymtab_command_data.nlocrel != 0 ||
278          MLC.dysymtab_command_data.nextrel != 0)
279        return createStringError(llvm::errc::not_supported,
280                                 "shared library is not yet supported");
281
282      if (!O.IndirectSymTable.Symbols.empty()) {
283        MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols;
284        MLC.dysymtab_command_data.nindirectsyms =
285            O.IndirectSymTable.Symbols.size();
286      }
287
288      updateDySymTab(MLC);
289      break;
290    }
291    case MachO::LC_DATA_IN_CODE:
292      MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;
293      MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();
294      break;
295    case MachO::LC_FUNCTION_STARTS:
296      MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;
297      MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();
298      break;
299    case MachO::LC_DYLD_INFO:
300    case MachO::LC_DYLD_INFO_ONLY:
301      MLC.dyld_info_command_data.rebase_off =
302          O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo;
303      MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size();
304      MLC.dyld_info_command_data.bind_off =
305          O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo;
306      MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size();
307      MLC.dyld_info_command_data.weak_bind_off =
308          O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo;
309      MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size();
310      MLC.dyld_info_command_data.lazy_bind_off =
311          O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo;
312      MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size();
313      MLC.dyld_info_command_data.export_off =
314          O.Exports.Trie.empty() ? 0 : StartOfExportTrie;
315      MLC.dyld_info_command_data.export_size = O.Exports.Trie.size();
316      break;
317    case MachO::LC_LOAD_DYLINKER:
318    case MachO::LC_MAIN:
319    case MachO::LC_RPATH:
320    case MachO::LC_SEGMENT:
321    case MachO::LC_SEGMENT_64:
322    case MachO::LC_VERSION_MIN_MACOSX:
323    case MachO::LC_VERSION_MIN_IPHONEOS:
324    case MachO::LC_VERSION_MIN_TVOS:
325    case MachO::LC_VERSION_MIN_WATCHOS:
326    case MachO::LC_BUILD_VERSION:
327    case MachO::LC_ID_DYLIB:
328    case MachO::LC_LOAD_DYLIB:
329    case MachO::LC_UUID:
330    case MachO::LC_SOURCE_VERSION:
331      // Nothing to update.
332      break;
333    default:
334      // Abort if it's unsupported in order to prevent corrupting the object.
335      return createStringError(llvm::errc::not_supported,
336                               "unsupported load command (cmd=0x%x)", cmd);
337    }
338  }
339
340  return Error::success();
341}
342
343Error MachOLayoutBuilder::layout() {
344  O.Header.NCmds = O.LoadCommands.size();
345  O.Header.SizeOfCmds = computeSizeOfCmds();
346  constructStringTable();
347  updateSymbolIndexes();
348  uint64_t Offset = layoutSegments();
349  Offset = layoutRelocations(Offset);
350  return layoutTail(Offset);
351}
352
353} // end namespace macho
354} // end namespace objcopy
355} // end namespace llvm
356