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