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