xref: /llvm-project/bolt/lib/Core/BinarySection.cpp (revision 2f09f445b2d6b3ef197aecd8d1e06d08140380f3)
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 bool BinarySection::isELF() const { return BC.isELF(); }
30 
31 bool BinarySection::isMachO() const { return BC.isMachO(); }
32 
33 uint64_t
34 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     }
58     Offset = Rel.Offset + Rel.getSize();
59   }
60 
61   Hash = hash_combine(Hash,
62                       hash_value(Contents.substr(Offset, EndOffset - Offset)));
63 
64   Cache[&BD] = Hash;
65 
66   return Hash;
67 }
68 
69 void BinarySection::emitAsData(MCStreamer &Streamer, StringRef NewName) const {
70   StringRef SectionName = !NewName.empty() ? NewName : getName();
71   StringRef SectionContents = getContents();
72   MCSectionELF *ELFSection =
73       BC.Ctx->getELFSection(SectionName, getELFType(), getELFFlags());
74 
75   Streamer.SwitchSection(ELFSection);
76   Streamer.emitValueToAlignment(getAlignment());
77 
78   if (BC.HasRelocations && opts::HotData && isReordered())
79     Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_start"));
80 
81   LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting "
82                     << (isAllocatable() ? "" : "non-")
83                     << "allocatable data section " << SectionName << '\n');
84 
85   if (!hasRelocations()) {
86     Streamer.emitBytes(SectionContents);
87   } else {
88     uint64_t SectionOffset = 0;
89     for (const Relocation &Relocation : relocations()) {
90       assert(Relocation.Offset < SectionContents.size() && "overflow detected");
91       // Skip undefined symbols.
92       if (BC.UndefinedSymbols.count(Relocation.Symbol))
93         continue;
94       if (SectionOffset < Relocation.Offset) {
95         Streamer.emitBytes(SectionContents.substr(
96             SectionOffset, Relocation.Offset - SectionOffset));
97         SectionOffset = Relocation.Offset;
98       }
99       LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting relocation for symbol "
100                         << (Relocation.Symbol ? Relocation.Symbol->getName()
101                                               : StringRef("<none>"))
102                         << " at offset 0x"
103                         << Twine::utohexstr(Relocation.Offset) << " with size "
104                         << Relocation::getSizeForType(Relocation.Type) << '\n');
105       size_t RelocationSize = Relocation.emit(&Streamer);
106       SectionOffset += RelocationSize;
107     }
108     assert(SectionOffset <= SectionContents.size() && "overflow error");
109     if (SectionOffset < SectionContents.size()) {
110       Streamer.emitBytes(SectionContents.substr(SectionOffset));
111     }
112   }
113 
114   if (BC.HasRelocations && opts::HotData && isReordered())
115     Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));
116 }
117 
118 void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
119                                             SymbolResolverFuncTy Resolver) {
120   if (PendingRelocations.empty() && Patches.empty())
121     return;
122 
123   const uint64_t SectionAddress = getAddress();
124 
125   // We apply relocations to original section contents. For allocatable sections
126   // this means using their input file offsets, since the output file offset
127   // could change (e.g. for new instance of .text). For non-allocatable
128   // sections, the output offset should always be a valid one.
129   const uint64_t SectionFileOffset =
130       isAllocatable() ? getInputFileOffset() : getOutputFileOffset();
131   LLVM_DEBUG(
132       dbgs() << "BOLT-DEBUG: flushing pending relocations for section "
133              << getName() << '\n'
134              << "  address: 0x" << Twine::utohexstr(SectionAddress) << '\n'
135              << "  offset: 0x" << Twine::utohexstr(SectionFileOffset) << '\n');
136 
137   for (BinaryPatch &Patch : Patches) {
138     OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),
139               SectionFileOffset + Patch.Offset);
140   }
141 
142   for (Relocation &Reloc : PendingRelocations) {
143     uint64_t Value = Reloc.Addend;
144     if (Reloc.Symbol)
145       Value += Resolver(Reloc.Symbol);
146 
147     Value = Relocation::adjustValue(Reloc.Type, Value,
148                                     SectionAddress + Reloc.Offset);
149 
150     OS.pwrite(reinterpret_cast<const char *>(&Value),
151               Relocation::getSizeForType(Reloc.Type),
152               SectionFileOffset + Reloc.Offset);
153 
154     LLVM_DEBUG(
155         dbgs() << "BOLT-DEBUG: writing value 0x" << Twine::utohexstr(Value)
156                << " of size " << Relocation::getSizeForType(Reloc.Type)
157                << " at section offset 0x" << Twine::utohexstr(Reloc.Offset)
158                << " address 0x"
159                << Twine::utohexstr(SectionAddress + Reloc.Offset)
160                << " file offset 0x"
161                << Twine::utohexstr(SectionFileOffset + Reloc.Offset) << '\n';);
162   }
163 
164   clearList(PendingRelocations);
165 }
166 
167 BinarySection::~BinarySection() {
168   if (isReordered()) {
169     delete[] getData();
170     return;
171   }
172 
173   if (!isAllocatable() &&
174       (!hasSectionRef() ||
175        OutputContents.data() != getContents(Section).data())) {
176     delete[] getOutputData();
177   }
178 }
179 
180 void BinarySection::clearRelocations() { clearList(Relocations); }
181 
182 void BinarySection::print(raw_ostream &OS) const {
183   OS << getName() << ", "
184      << "0x" << Twine::utohexstr(getAddress()) << ", " << getSize() << " (0x"
185      << Twine::utohexstr(getOutputAddress()) << ", " << getOutputSize() << ")"
186      << ", data = " << getData() << ", output data = " << getOutputData();
187 
188   if (isAllocatable())
189     OS << " (allocatable)";
190 
191   if (isVirtual())
192     OS << " (virtual)";
193 
194   if (isTLS())
195     OS << " (tls)";
196 
197   if (opts::PrintRelocations) {
198     for (const Relocation &R : relocations())
199       OS << "\n  " << R;
200   }
201 }
202 
203 BinarySection::RelocationSetType
204 BinarySection::reorderRelocations(bool Inplace) const {
205   assert(PendingRelocations.empty() &&
206          "reodering pending relocations not supported");
207   RelocationSetType NewRelocations;
208   for (const Relocation &Rel : relocations()) {
209     uint64_t RelAddr = Rel.Offset + getAddress();
210     BinaryData *BD = BC.getBinaryDataContainingAddress(RelAddr);
211     BD = BD->getAtomicRoot();
212     assert(BD);
213 
214     if ((!BD->isMoved() && !Inplace) || BD->isJumpTable())
215       continue;
216 
217     Relocation NewRel(Rel);
218     uint64_t RelOffset = RelAddr - BD->getAddress();
219     NewRel.Offset = BD->getOutputOffset() + RelOffset;
220     assert(NewRel.Offset < getSize());
221     LLVM_DEBUG(dbgs() << "BOLT-DEBUG: moving " << Rel << " -> " << NewRel
222                       << "\n");
223     auto Res = NewRelocations.emplace(std::move(NewRel));
224     (void)Res;
225     assert(Res.second && "Can't overwrite existing relocation");
226   }
227   return NewRelocations;
228 }
229 
230 void BinarySection::reorderContents(const std::vector<BinaryData *> &Order,
231                                     bool Inplace) {
232   IsReordered = true;
233 
234   Relocations = reorderRelocations(Inplace);
235 
236   std::string Str;
237   raw_string_ostream OS(Str);
238   const char *Src = Contents.data();
239   LLVM_DEBUG(dbgs() << "BOLT-DEBUG: reorderContents for " << Name << "\n");
240   for (BinaryData *BD : Order) {
241     assert((BD->isMoved() || !Inplace) && !BD->isJumpTable());
242     assert(BD->isAtomic() && BD->isMoveable());
243     const uint64_t SrcOffset = BD->getAddress() - getAddress();
244     assert(SrcOffset < Contents.size());
245     assert(SrcOffset == BD->getOffset());
246     while (OS.tell() < BD->getOutputOffset()) {
247       OS.write((unsigned char)0);
248     }
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, ELF::R_X86_64_64, 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   }
281   OS << DescStr;
282   for (uint64_t I = DescStr.size(); I < alignTo(DescStr.size(), 4); ++I) {
283     OS << '\0';
284   }
285   return OS.str();
286 }
287