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