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 
145     Value = Relocation::adjustValue(Reloc.Type, Value,
146                                     SectionAddress + Reloc.Offset);
147 
148     OS.pwrite(reinterpret_cast<const char *>(&Value),
149               Relocation::getSizeForType(Reloc.Type),
150               SectionFileOffset + Reloc.Offset);
151 
152     LLVM_DEBUG(
153         dbgs() << "BOLT-DEBUG: writing value 0x" << Twine::utohexstr(Value)
154                << " of size " << Relocation::getSizeForType(Reloc.Type)
155                << " at section offset 0x" << Twine::utohexstr(Reloc.Offset)
156                << " address 0x"
157                << Twine::utohexstr(SectionAddress + Reloc.Offset)
158                << " file offset 0x"
159                << Twine::utohexstr(SectionFileOffset + Reloc.Offset) << '\n';);
160   }
161 
162   clearList(PendingRelocations);
163 }
164 
165 BinarySection::~BinarySection() {
166   if (isReordered()) {
167     delete[] getData();
168     return;
169   }
170 
171   if (!isAllocatable() &&
172       (!hasSectionRef() ||
173        OutputContents.data() != getContents(Section).data())) {
174     delete[] getOutputData();
175   }
176 }
177 
178 void BinarySection::clearRelocations() { clearList(Relocations); }
179 
180 void BinarySection::print(raw_ostream &OS) const {
181   OS << getName() << ", "
182      << "0x" << Twine::utohexstr(getAddress()) << ", " << getSize() << " (0x"
183      << Twine::utohexstr(getOutputAddress()) << ", " << getOutputSize() << ")"
184      << ", data = " << getData() << ", output data = " << getOutputData();
185 
186   if (isAllocatable())
187     OS << " (allocatable)";
188 
189   if (isVirtual())
190     OS << " (virtual)";
191 
192   if (isTLS())
193     OS << " (tls)";
194 
195   if (opts::PrintRelocations) {
196     for (const Relocation &R : relocations())
197       OS << "\n  " << R;
198   }
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     }
247     LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << BD->getName() << " @ " << OS.tell()
248                       << "\n");
249     OS.write(&Src[SrcOffset], BD->getOutputSize());
250   }
251   if (Relocations.empty()) {
252     // If there are no existing relocations, tack a phony one at the end
253     // of the reordered segment to force LLVM to recognize and map this
254     // section.
255     MCSymbol *ZeroSym = BC.registerNameAtAddress("Zero", 0, 0, 0);
256     addRelocation(OS.tell(), ZeroSym, ELF::R_X86_64_64, 0xdeadbeef);
257 
258     uint64_t Zero = 0;
259     OS.write(reinterpret_cast<const char *>(&Zero), sizeof(Zero));
260   }
261   auto *NewData = reinterpret_cast<char *>(copyByteArray(OS.str()));
262   Contents = OutputContents = StringRef(NewData, OS.str().size());
263   OutputSize = Contents.size();
264 }
265 
266 std::string BinarySection::encodeELFNote(StringRef NameStr, StringRef DescStr,
267                                          uint32_t Type) {
268   std::string Str;
269   raw_string_ostream OS(Str);
270   const uint32_t NameSz = NameStr.size() + 1;
271   const uint32_t DescSz = DescStr.size();
272   OS.write(reinterpret_cast<const char *>(&(NameSz)), 4);
273   OS.write(reinterpret_cast<const char *>(&(DescSz)), 4);
274   OS.write(reinterpret_cast<const char *>(&(Type)), 4);
275   OS << NameStr << '\0';
276   for (uint64_t I = NameSz; I < alignTo(NameSz, 4); ++I) {
277     OS << '\0';
278   }
279   OS << DescStr;
280   for (uint64_t I = DescStr.size(); I < alignTo(DescStr.size(), 4); ++I) {
281     OS << '\0';
282   }
283   return OS.str();
284 }
285