1 //===- bolt/Core/BinarySection.cpp - Section in a binary file -------------===//
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 // This file implements the BinarySection class.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "bolt/Core/BinarySection.h"
14 #include "bolt/Core/BinaryContext.h"
15 #include "bolt/Utils/Utils.h"
16 #include "llvm/MC/MCStreamer.h"
17 #include "llvm/Support/CommandLine.h"
18
19 #define DEBUG_TYPE "bolt"
20
21 using namespace llvm;
22 using namespace bolt;
23
24 namespace opts {
25 extern cl::opt<bool> PrintRelocations;
26 extern cl::opt<bool> HotData;
27 } // namespace opts
28
isELF() const29 bool BinarySection::isELF() const { return BC.isELF(); }
30
isMachO() const31 bool BinarySection::isMachO() const { return BC.isMachO(); }
32
33 uint64_t
hash(const BinaryData & BD,std::map<const BinaryData *,uint64_t> & Cache) const34 BinarySection::hash(const BinaryData &BD,
35 std::map<const BinaryData *, uint64_t> &Cache) const {
36 auto Itr = Cache.find(&BD);
37 if (Itr != Cache.end())
38 return Itr->second;
39
40 Cache[&BD] = 0;
41
42 uint64_t Offset = BD.getAddress() - getAddress();
43 const uint64_t EndOffset = BD.getEndAddress() - getAddress();
44 auto Begin = Relocations.lower_bound(Relocation{Offset, 0, 0, 0, 0});
45 auto End = Relocations.upper_bound(Relocation{EndOffset, 0, 0, 0, 0});
46 const StringRef Contents = getContents();
47
48 hash_code Hash =
49 hash_combine(hash_value(BD.getSize()), hash_value(BD.getSectionName()));
50
51 while (Begin != End) {
52 const Relocation &Rel = *Begin++;
53 Hash = hash_combine(
54 Hash, hash_value(Contents.substr(Offset, Begin->Offset - Offset)));
55 if (BinaryData *RelBD = BC.getBinaryDataByName(Rel.Symbol->getName()))
56 Hash = hash_combine(Hash, hash(*RelBD, Cache));
57 Offset = Rel.Offset + Rel.getSize();
58 }
59
60 Hash = hash_combine(Hash,
61 hash_value(Contents.substr(Offset, EndOffset - Offset)));
62
63 Cache[&BD] = Hash;
64
65 return Hash;
66 }
67
emitAsData(MCStreamer & Streamer,StringRef NewName) const68 void BinarySection::emitAsData(MCStreamer &Streamer, StringRef NewName) const {
69 StringRef SectionName = !NewName.empty() ? NewName : getName();
70 StringRef SectionContents = getContents();
71 MCSectionELF *ELFSection =
72 BC.Ctx->getELFSection(SectionName, getELFType(), getELFFlags());
73
74 Streamer.switchSection(ELFSection);
75 Streamer.emitValueToAlignment(getAlignment());
76
77 if (BC.HasRelocations && opts::HotData && isReordered())
78 Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_start"));
79
80 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting "
81 << (isAllocatable() ? "" : "non-")
82 << "allocatable data section " << SectionName << '\n');
83
84 if (!hasRelocations()) {
85 Streamer.emitBytes(SectionContents);
86 } else {
87 uint64_t SectionOffset = 0;
88 for (const Relocation &Relocation : relocations()) {
89 assert(Relocation.Offset < SectionContents.size() && "overflow detected");
90 // Skip undefined symbols.
91 if (BC.UndefinedSymbols.count(Relocation.Symbol))
92 continue;
93 if (SectionOffset < Relocation.Offset) {
94 Streamer.emitBytes(SectionContents.substr(
95 SectionOffset, Relocation.Offset - SectionOffset));
96 SectionOffset = Relocation.Offset;
97 }
98 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting relocation for symbol "
99 << (Relocation.Symbol ? Relocation.Symbol->getName()
100 : StringRef("<none>"))
101 << " at offset 0x"
102 << Twine::utohexstr(Relocation.Offset) << " with size "
103 << Relocation::getSizeForType(Relocation.Type) << '\n');
104 size_t RelocationSize = Relocation.emit(&Streamer);
105 SectionOffset += RelocationSize;
106 }
107 assert(SectionOffset <= SectionContents.size() && "overflow error");
108 if (SectionOffset < SectionContents.size())
109 Streamer.emitBytes(SectionContents.substr(SectionOffset));
110 }
111
112 if (BC.HasRelocations && opts::HotData && isReordered())
113 Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));
114 }
115
flushPendingRelocations(raw_pwrite_stream & OS,SymbolResolverFuncTy Resolver)116 void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
117 SymbolResolverFuncTy Resolver) {
118 if (PendingRelocations.empty() && Patches.empty())
119 return;
120
121 const uint64_t SectionAddress = getAddress();
122
123 // We apply relocations to original section contents. For allocatable sections
124 // this means using their input file offsets, since the output file offset
125 // could change (e.g. for new instance of .text). For non-allocatable
126 // sections, the output offset should always be a valid one.
127 const uint64_t SectionFileOffset =
128 isAllocatable() ? getInputFileOffset() : getOutputFileOffset();
129 LLVM_DEBUG(
130 dbgs() << "BOLT-DEBUG: flushing pending relocations for section "
131 << getName() << '\n'
132 << " address: 0x" << Twine::utohexstr(SectionAddress) << '\n'
133 << " offset: 0x" << Twine::utohexstr(SectionFileOffset) << '\n');
134
135 for (BinaryPatch &Patch : Patches)
136 OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),
137 SectionFileOffset + Patch.Offset);
138
139 for (Relocation &Reloc : PendingRelocations) {
140 uint64_t Value = Reloc.Addend;
141 if (Reloc.Symbol)
142 Value += Resolver(Reloc.Symbol);
143
144 Value = Relocation::adjustValue(Reloc.Type, Value,
145 SectionAddress + Reloc.Offset);
146
147 OS.pwrite(reinterpret_cast<const char *>(&Value),
148 Relocation::getSizeForType(Reloc.Type),
149 SectionFileOffset + Reloc.Offset);
150
151 LLVM_DEBUG(
152 dbgs() << "BOLT-DEBUG: writing value 0x" << Twine::utohexstr(Value)
153 << " of size " << Relocation::getSizeForType(Reloc.Type)
154 << " at section offset 0x" << Twine::utohexstr(Reloc.Offset)
155 << " address 0x"
156 << Twine::utohexstr(SectionAddress + Reloc.Offset)
157 << " file offset 0x"
158 << Twine::utohexstr(SectionFileOffset + Reloc.Offset) << '\n';);
159 }
160
161 clearList(PendingRelocations);
162 }
163
~BinarySection()164 BinarySection::~BinarySection() {
165 if (isReordered()) {
166 delete[] getData();
167 return;
168 }
169
170 if (!isAllocatable() &&
171 (!hasSectionRef() ||
172 OutputContents.data() != getContents(Section).data())) {
173 delete[] getOutputData();
174 }
175 }
176
clearRelocations()177 void BinarySection::clearRelocations() { clearList(Relocations); }
178
print(raw_ostream & OS) const179 void BinarySection::print(raw_ostream &OS) const {
180 OS << getName() << ", "
181 << "0x" << Twine::utohexstr(getAddress()) << ", " << getSize() << " (0x"
182 << Twine::utohexstr(getOutputAddress()) << ", " << getOutputSize() << ")"
183 << ", data = " << getData() << ", output data = " << getOutputData();
184
185 if (isAllocatable())
186 OS << " (allocatable)";
187
188 if (isVirtual())
189 OS << " (virtual)";
190
191 if (isTLS())
192 OS << " (tls)";
193
194 if (opts::PrintRelocations)
195 for (const Relocation &R : relocations())
196 OS << "\n " << R;
197 }
198
199 BinarySection::RelocationSetType
reorderRelocations(bool Inplace) const200 BinarySection::reorderRelocations(bool Inplace) const {
201 assert(PendingRelocations.empty() &&
202 "reodering pending relocations not supported");
203 RelocationSetType NewRelocations;
204 for (const Relocation &Rel : relocations()) {
205 uint64_t RelAddr = Rel.Offset + getAddress();
206 BinaryData *BD = BC.getBinaryDataContainingAddress(RelAddr);
207 BD = BD->getAtomicRoot();
208 assert(BD);
209
210 if ((!BD->isMoved() && !Inplace) || BD->isJumpTable())
211 continue;
212
213 Relocation NewRel(Rel);
214 uint64_t RelOffset = RelAddr - BD->getAddress();
215 NewRel.Offset = BD->getOutputOffset() + RelOffset;
216 assert(NewRel.Offset < getSize());
217 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: moving " << Rel << " -> " << NewRel
218 << "\n");
219 auto Res = NewRelocations.emplace(std::move(NewRel));
220 (void)Res;
221 assert(Res.second && "Can't overwrite existing relocation");
222 }
223 return NewRelocations;
224 }
225
reorderContents(const std::vector<BinaryData * > & Order,bool Inplace)226 void BinarySection::reorderContents(const std::vector<BinaryData *> &Order,
227 bool Inplace) {
228 IsReordered = true;
229
230 Relocations = reorderRelocations(Inplace);
231
232 std::string Str;
233 raw_string_ostream OS(Str);
234 const char *Src = Contents.data();
235 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: reorderContents for " << Name << "\n");
236 for (BinaryData *BD : Order) {
237 assert((BD->isMoved() || !Inplace) && !BD->isJumpTable());
238 assert(BD->isAtomic() && BD->isMoveable());
239 const uint64_t SrcOffset = BD->getAddress() - getAddress();
240 assert(SrcOffset < Contents.size());
241 assert(SrcOffset == BD->getOffset());
242 while (OS.tell() < BD->getOutputOffset())
243 OS.write((unsigned char)0);
244 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << BD->getName() << " @ " << OS.tell()
245 << "\n");
246 OS.write(&Src[SrcOffset], BD->getOutputSize());
247 }
248 if (Relocations.empty()) {
249 // If there are no existing relocations, tack a phony one at the end
250 // of the reordered segment to force LLVM to recognize and map this
251 // section.
252 MCSymbol *ZeroSym = BC.registerNameAtAddress("Zero", 0, 0, 0);
253 addRelocation(OS.tell(), ZeroSym, ELF::R_X86_64_64, 0xdeadbeef);
254
255 uint64_t Zero = 0;
256 OS.write(reinterpret_cast<const char *>(&Zero), sizeof(Zero));
257 }
258 auto *NewData = reinterpret_cast<char *>(copyByteArray(OS.str()));
259 Contents = OutputContents = StringRef(NewData, OS.str().size());
260 OutputSize = Contents.size();
261 }
262
encodeELFNote(StringRef NameStr,StringRef DescStr,uint32_t Type)263 std::string BinarySection::encodeELFNote(StringRef NameStr, StringRef DescStr,
264 uint32_t Type) {
265 std::string Str;
266 raw_string_ostream OS(Str);
267 const uint32_t NameSz = NameStr.size() + 1;
268 const uint32_t DescSz = DescStr.size();
269 OS.write(reinterpret_cast<const char *>(&(NameSz)), 4);
270 OS.write(reinterpret_cast<const char *>(&(DescSz)), 4);
271 OS.write(reinterpret_cast<const char *>(&(Type)), 4);
272 OS << NameStr << '\0';
273 for (uint64_t I = NameSz; I < alignTo(NameSz, 4); ++I)
274 OS << '\0';
275 OS << DescStr;
276 for (uint64_t I = DescStr.size(); I < alignTo(DescStr.size(), 4); ++I)
277 OS << '\0';
278 return OS.str();
279 }
280