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