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 Symbols.erase(
30 std::remove_if(std::begin(Symbols), std::end(Symbols), ToRemove),
31 std::end(Symbols));
32 }
33
updateLoadCommandIndexes()34 void Object::updateLoadCommandIndexes() {
35 // Update indices of special load commands
36 for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
37 LoadCommand &LC = LoadCommands[Index];
38 switch (LC.MachOLoadCommand.load_command_data.cmd) {
39 case MachO::LC_SYMTAB:
40 SymTabCommandIndex = Index;
41 break;
42 case MachO::LC_DYSYMTAB:
43 DySymTabCommandIndex = Index;
44 break;
45 case MachO::LC_DYLD_INFO:
46 case MachO::LC_DYLD_INFO_ONLY:
47 DyLdInfoCommandIndex = Index;
48 break;
49 case MachO::LC_DATA_IN_CODE:
50 DataInCodeCommandIndex = 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
196 } // end namespace macho
197 } // end namespace objcopy
198 } // end namespace llvm
199