xref: /freebsd/contrib/llvm-project/llvm/lib/ObjCopy/MachO/MachOObject.cpp (revision e32fecd0c2c3ee37c47ee100f169e7eb0282a873)
1 //===- MachOObject.cpp - Mach-O object file model ---------------*- 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 "MachOObject.h"
10 #include "llvm/ADT/SmallPtrSet.h"
11 #include <unordered_set>
12 
13 using namespace llvm;
14 using namespace llvm::objcopy::macho;
15 
16 const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const {
17   assert(Index < Symbols.size() && "invalid symbol index");
18   return Symbols[Index].get();
19 }
20 
21 SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) {
22   return const_cast<SymbolEntry *>(
23       static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index));
24 }
25 
26 void SymbolTable::removeSymbols(
27     function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) {
28   llvm::erase_if(Symbols, ToRemove);
29 }
30 
31 void Object::updateLoadCommandIndexes() {
32   static constexpr char TextSegmentName[] = "__TEXT";
33   // Update indices of special load commands
34   for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
35     LoadCommand &LC = LoadCommands[Index];
36     switch (LC.MachOLoadCommand.load_command_data.cmd) {
37     case MachO::LC_CODE_SIGNATURE:
38       CodeSignatureCommandIndex = Index;
39       break;
40     case MachO::LC_SEGMENT:
41       if (StringRef(LC.MachOLoadCommand.segment_command_data.segname) ==
42           TextSegmentName)
43         TextSegmentCommandIndex = Index;
44       break;
45     case MachO::LC_SEGMENT_64:
46       if (StringRef(LC.MachOLoadCommand.segment_command_64_data.segname) ==
47           TextSegmentName)
48         TextSegmentCommandIndex = Index;
49       break;
50     case MachO::LC_SYMTAB:
51       SymTabCommandIndex = Index;
52       break;
53     case MachO::LC_DYSYMTAB:
54       DySymTabCommandIndex = Index;
55       break;
56     case MachO::LC_DYLD_INFO:
57     case MachO::LC_DYLD_INFO_ONLY:
58       DyLdInfoCommandIndex = Index;
59       break;
60     case MachO::LC_DATA_IN_CODE:
61       DataInCodeCommandIndex = Index;
62       break;
63     case MachO::LC_LINKER_OPTIMIZATION_HINT:
64       LinkerOptimizationHintCommandIndex = Index;
65       break;
66     case MachO::LC_FUNCTION_STARTS:
67       FunctionStartsCommandIndex = Index;
68       break;
69     case MachO::LC_DYLD_CHAINED_FIXUPS:
70       ChainedFixupsCommandIndex = Index;
71       break;
72     case MachO::LC_DYLD_EXPORTS_TRIE:
73       ExportsTrieCommandIndex = Index;
74       break;
75     }
76   }
77 }
78 
79 Error Object::removeLoadCommands(
80     function_ref<bool(const LoadCommand &)> ToRemove) {
81   auto It = std::stable_partition(
82       LoadCommands.begin(), LoadCommands.end(),
83       [&](const LoadCommand &LC) { return !ToRemove(LC); });
84   LoadCommands.erase(It, LoadCommands.end());
85 
86   updateLoadCommandIndexes();
87   return Error::success();
88 }
89 
90 Error Object::removeSections(
91     function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) {
92   DenseMap<uint32_t, const Section *> OldIndexToSection;
93   uint32_t NextSectionIndex = 1;
94   for (LoadCommand &LC : LoadCommands) {
95     auto It = std::stable_partition(
96         std::begin(LC.Sections), std::end(LC.Sections),
97         [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); });
98     for (auto I = LC.Sections.begin(), End = It; I != End; ++I) {
99       OldIndexToSection[(*I)->Index] = I->get();
100       (*I)->Index = NextSectionIndex++;
101     }
102     LC.Sections.erase(It, LC.Sections.end());
103   }
104 
105   auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool {
106     Optional<uint32_t> Section = S->section();
107     return (Section && !OldIndexToSection.count(*Section));
108   };
109 
110   SmallPtrSet<const SymbolEntry *, 2> DeadSymbols;
111   for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols)
112     if (IsDead(Sym))
113       DeadSymbols.insert(Sym.get());
114 
115   for (const LoadCommand &LC : LoadCommands)
116     for (const std::unique_ptr<Section> &Sec : LC.Sections)
117       for (const RelocationInfo &R : Sec->Relocations)
118         if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol))
119           return createStringError(std::errc::invalid_argument,
120                                    "symbol '%s' defined in section with index "
121                                    "'%u' cannot be removed because it is "
122                                    "referenced by a relocation in section '%s'",
123                                    (*R.Symbol)->Name.c_str(),
124                                    *((*R.Symbol)->section()),
125                                    Sec->CanonicalName.c_str());
126   SymTable.removeSymbols(IsDead);
127   for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols)
128     if (S->section())
129       S->n_sect = OldIndexToSection[S->n_sect]->Index;
130   return Error::success();
131 }
132 
133 uint64_t Object::nextAvailableSegmentAddress() const {
134   uint64_t HeaderSize =
135       is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
136   uint64_t Addr = HeaderSize + Header.SizeOfCmds;
137   for (const LoadCommand &LC : LoadCommands) {
138     const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
139     switch (MLC.load_command_data.cmd) {
140     case MachO::LC_SEGMENT:
141       Addr = std::max(Addr,
142                       static_cast<uint64_t>(MLC.segment_command_data.vmaddr) +
143                           MLC.segment_command_data.vmsize);
144       break;
145     case MachO::LC_SEGMENT_64:
146       Addr = std::max(Addr, MLC.segment_command_64_data.vmaddr +
147                                 MLC.segment_command_64_data.vmsize);
148       break;
149     default:
150       continue;
151     }
152   }
153   return Addr;
154 }
155 
156 template <typename SegmentType>
157 static void
158 constructSegment(SegmentType &Seg, llvm::MachO::LoadCommandType CmdType,
159                  StringRef SegName, uint64_t SegVMAddr, uint64_t SegVMSize) {
160   assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");
161   memset(&Seg, 0, sizeof(SegmentType));
162   Seg.cmd = CmdType;
163   strncpy(Seg.segname, SegName.data(), SegName.size());
164   Seg.maxprot |=
165       (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
166   Seg.initprot |=
167       (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
168   Seg.vmaddr = SegVMAddr;
169   Seg.vmsize = SegVMSize;
170 }
171 
172 LoadCommand &Object::addSegment(StringRef SegName, uint64_t SegVMSize) {
173   LoadCommand LC;
174   const uint64_t SegVMAddr = nextAvailableSegmentAddress();
175   if (is64Bit())
176     constructSegment(LC.MachOLoadCommand.segment_command_64_data,
177                      MachO::LC_SEGMENT_64, SegName, SegVMAddr, SegVMSize);
178   else
179     constructSegment(LC.MachOLoadCommand.segment_command_data,
180                      MachO::LC_SEGMENT, SegName, SegVMAddr, SegVMSize);
181 
182   LoadCommands.push_back(std::move(LC));
183   return LoadCommands.back();
184 }
185 
186 /// Extracts a segment name from a string which is possibly non-null-terminated.
187 static StringRef extractSegmentName(const char *SegName) {
188   return StringRef(SegName,
189                    strnlen(SegName, sizeof(MachO::segment_command::segname)));
190 }
191 
192 Optional<StringRef> LoadCommand::getSegmentName() const {
193   const MachO::macho_load_command &MLC = MachOLoadCommand;
194   switch (MLC.load_command_data.cmd) {
195   case MachO::LC_SEGMENT:
196     return extractSegmentName(MLC.segment_command_data.segname);
197   case MachO::LC_SEGMENT_64:
198     return extractSegmentName(MLC.segment_command_64_data.segname);
199   default:
200     return None;
201   }
202 }
203 
204 Optional<uint64_t> LoadCommand::getSegmentVMAddr() const {
205   const MachO::macho_load_command &MLC = MachOLoadCommand;
206   switch (MLC.load_command_data.cmd) {
207   case MachO::LC_SEGMENT:
208     return MLC.segment_command_data.vmaddr;
209   case MachO::LC_SEGMENT_64:
210     return MLC.segment_command_64_data.vmaddr;
211   default:
212     return None;
213   }
214 }
215