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