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