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