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