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