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