322 lines · cpp
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-exception6//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/CommandLineOpts.h"16#include "bolt/Utils/Utils.h"17#include "llvm/MC/MCStreamer.h"18#include "llvm/Support/CommandLine.h"19 20#define DEBUG_TYPE "bolt"21 22using namespace llvm;23using namespace bolt;24 25namespace opts {26extern cl::opt<bool> HotData;27extern cl::opt<bool> PrintRelocations;28} // namespace opts29 30uint64_t BinarySection::Count = 0;31 32bool BinarySection::isELF() const { return BC.isELF(); }33 34bool BinarySection::isMachO() const { return BC.isMachO(); }35 36uint64_t37BinarySection::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 hash_code Hash =44 hash_combine(hash_value(BD.getSize()), hash_value(BD.getSectionName()));45 46 Cache[&BD] = Hash;47 48 if (!containsRange(BD.getAddress(), BD.getSize()))49 return Hash;50 51 uint64_t Offset = BD.getAddress() - getAddress();52 const uint64_t EndOffset = BD.getEndAddress() - getAddress();53 auto Begin = Relocations.lower_bound(Relocation{Offset, 0, 0, 0, 0});54 auto End = Relocations.upper_bound(Relocation{EndOffset, 0, 0, 0, 0});55 const StringRef Contents = getContents();56 57 while (Begin != End) {58 const Relocation &Rel = *Begin++;59 Hash = hash_combine(60 Hash, hash_value(Contents.substr(Offset, Begin->Offset - Offset)));61 if (BinaryData *RelBD = BC.getBinaryDataByName(Rel.Symbol->getName()))62 Hash = hash_combine(Hash, hash(*RelBD, Cache));63 Offset = Rel.Offset + Rel.getSize();64 }65 66 Hash = hash_combine(Hash,67 hash_value(Contents.substr(Offset, EndOffset - Offset)));68 69 Cache[&BD] = Hash;70 71 return Hash;72}73 74void BinarySection::emitAsData(MCStreamer &Streamer,75 const Twine &SectionName) const {76 StringRef SectionContents =77 isFinalized() ? getOutputContents() : getContents();78 MCSectionELF *ELFSection =79 BC.Ctx->getELFSection(SectionName, getELFType(), getELFFlags());80 81 Streamer.switchSection(ELFSection);82 Streamer.emitValueToAlignment(getAlign());83 84 if (BC.HasRelocations && opts::HotData && isReordered())85 Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_start"));86 87 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitting "88 << (isAllocatable() ? "" : "non-")89 << "allocatable data section " << SectionName << '\n');90 91 if (!hasRelocations()) {92 Streamer.emitBytes(SectionContents);93 } else {94 uint64_t SectionOffset = 0;95 for (auto RI = Relocations.begin(), RE = Relocations.end(); RI != RE;) {96 auto RelocationOffset = RI->Offset;97 assert(RelocationOffset < SectionContents.size() && "overflow detected");98 99 if (SectionOffset < RelocationOffset) {100 Streamer.emitBytes(SectionContents.substr(101 SectionOffset, RelocationOffset - SectionOffset));102 SectionOffset = RelocationOffset;103 }104 105 // Get iterators to all relocations with the same offset. Usually, there106 // is only one such relocation but there can be more for composed107 // relocations.108 auto ROI = RI;109 auto ROE = Relocations.upper_bound(RelocationOffset);110 111 // Start from the next offset on the next iteration.112 RI = ROE;113 114 // Skip undefined symbols.115 auto HasUndefSym = [](const auto &Relocation) {116 return Relocation.Symbol && Relocation.Symbol->isTemporary() &&117 Relocation.Symbol->isUndefined() &&118 !Relocation.Symbol->isRegistered();119 };120 121 if (std::any_of(ROI, ROE, HasUndefSym))122 continue;123 124#ifndef NDEBUG125 for (const auto &Relocation : make_range(ROI, ROE)) {126 LLVM_DEBUG(127 dbgs() << "BOLT-DEBUG: emitting relocation for symbol "128 << (Relocation.Symbol ? Relocation.Symbol->getName()129 : StringRef("<none>"))130 << " at offset 0x" << Twine::utohexstr(Relocation.Offset)131 << " with size "132 << Relocation::getSizeForType(Relocation.Type) << '\n');133 }134#endif135 136 size_t RelocationSize = Relocation::emit(ROI, ROE, &Streamer);137 SectionOffset += RelocationSize;138 }139 assert(SectionOffset <= SectionContents.size() && "overflow error");140 if (SectionOffset < SectionContents.size())141 Streamer.emitBytes(SectionContents.substr(SectionOffset));142 }143 144 if (BC.HasRelocations && opts::HotData && isReordered())145 Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));146}147 148uint64_t BinarySection::write(raw_ostream &OS) const {149 const uint64_t NumValidContentBytes =150 std::min<uint64_t>(getOutputContents().size(), getOutputSize());151 OS.write(getOutputContents().data(), NumValidContentBytes);152 if (getOutputSize() > NumValidContentBytes)153 OS.write_zeros(getOutputSize() - NumValidContentBytes);154 return getOutputSize();155}156 157void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,158 SymbolResolverFuncTy Resolver) {159 if (PendingRelocations.empty() && Patches.empty())160 return;161 162 const uint64_t SectionAddress = getAddress();163 164 // We apply relocations to original section contents. For allocatable sections165 // this means using their input file offsets, since the output file offset166 // could change (e.g. for new instance of .text). For non-allocatable167 // sections, the output offset should always be a valid one.168 const uint64_t SectionFileOffset =169 isAllocatable() ? getInputFileOffset() : getOutputFileOffset();170 LLVM_DEBUG(171 dbgs() << "BOLT-DEBUG: flushing pending relocations for section "172 << getName() << '\n'173 << " address: 0x" << Twine::utohexstr(SectionAddress) << '\n'174 << " offset: 0x" << Twine::utohexstr(SectionFileOffset) << '\n');175 176 for (BinaryPatch &Patch : Patches)177 OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),178 SectionFileOffset + Patch.Offset);179 180 uint64_t SkippedPendingRelocations = 0;181 for (Relocation &Reloc : PendingRelocations) {182 uint64_t Value = Reloc.Addend;183 if (Reloc.Symbol)184 Value += Resolver(Reloc.Symbol);185 186 // Safely skip any optional pending relocation that cannot be encoded.187 if (Reloc.isOptional() &&188 !Relocation::canEncodeValue(Reloc.Type, Value,189 SectionAddress + Reloc.Offset)) {190 191 ++SkippedPendingRelocations;192 continue;193 }194 Value = Relocation::encodeValue(Reloc.Type, Value,195 SectionAddress + Reloc.Offset);196 197 OS.pwrite(reinterpret_cast<const char *>(&Value),198 Relocation::getSizeForType(Reloc.Type),199 SectionFileOffset + Reloc.Offset);200 201 LLVM_DEBUG(202 dbgs() << "BOLT-DEBUG: writing value 0x" << Twine::utohexstr(Value)203 << " of size " << Relocation::getSizeForType(Reloc.Type)204 << " at section offset 0x" << Twine::utohexstr(Reloc.Offset)205 << " address 0x"206 << Twine::utohexstr(SectionAddress + Reloc.Offset)207 << " file offset 0x"208 << Twine::utohexstr(SectionFileOffset + Reloc.Offset) << '\n';);209 }210 211 clearList(PendingRelocations);212 213 if (SkippedPendingRelocations > 0 && opts::Verbosity >= 1) {214 BC.outs() << "BOLT-INFO: skipped " << SkippedPendingRelocations215 << " out-of-range optional relocations\n";216 }217}218 219BinarySection::~BinarySection() { updateContents(nullptr, 0); }220 221void BinarySection::clearRelocations() { clearList(Relocations); }222 223void BinarySection::print(raw_ostream &OS) const {224 OS << getName() << ", "225 << "0x" << Twine::utohexstr(getAddress()) << ", " << getSize() << " (0x"226 << Twine::utohexstr(getOutputAddress()) << ", " << getOutputSize() << ")"227 << ", data = " << getData() << ", output data = " << getOutputData();228 229 if (isAllocatable())230 OS << " (allocatable)";231 232 if (isVirtual())233 OS << " (virtual)";234 235 if (isTLS())236 OS << " (tls)";237 238 if (opts::PrintRelocations)239 for (const Relocation &R : relocations())240 OS << "\n " << R;241}242 243BinarySection::RelocationSetType244BinarySection::reorderRelocations(bool Inplace) const {245 assert(PendingRelocations.empty() &&246 "reordering pending relocations not supported");247 RelocationSetType NewRelocations;248 for (const Relocation &Rel : relocations()) {249 uint64_t RelAddr = Rel.Offset + getAddress();250 BinaryData *BD = BC.getBinaryDataContainingAddress(RelAddr);251 BD = BD->getAtomicRoot();252 assert(BD);253 254 if ((!BD->isMoved() && !Inplace) || BD->isJumpTable())255 continue;256 257 Relocation NewRel(Rel);258 uint64_t RelOffset = RelAddr - BD->getAddress();259 NewRel.Offset = BD->getOutputOffset() + RelOffset;260 assert(NewRel.Offset < getSize());261 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: moving " << Rel << " -> " << NewRel262 << "\n");263 NewRelocations.emplace(std::move(NewRel));264 }265 return NewRelocations;266}267 268void BinarySection::reorderContents(const std::vector<BinaryData *> &Order,269 bool Inplace) {270 IsReordered = true;271 272 Relocations = reorderRelocations(Inplace);273 274 std::string Str;275 raw_string_ostream OS(Str);276 const char *Src = Contents.data();277 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: reorderContents for " << Name << "\n");278 for (BinaryData *BD : Order) {279 assert((BD->isMoved() || !Inplace) && !BD->isJumpTable());280 assert(BD->isAtomic() && BD->isMoveable());281 const uint64_t SrcOffset = BD->getAddress() - getAddress();282 assert(SrcOffset < Contents.size());283 assert(SrcOffset == BD->getOffset());284 while (OS.tell() < BD->getOutputOffset())285 OS.write((unsigned char)0);286 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << BD->getName() << " @ " << OS.tell()287 << "\n");288 OS.write(&Src[SrcOffset], BD->getOutputSize());289 }290 if (Relocations.empty()) {291 // If there are no existing relocations, tack a phony one at the end292 // of the reordered segment to force LLVM to recognize and map this293 // section.294 MCSymbol *ZeroSym = BC.registerNameAtAddress("Zero", 0, 0, 0);295 addRelocation(OS.tell(), ZeroSym, Relocation::getAbs64(), 0xdeadbeef);296 297 uint64_t Zero = 0;298 OS.write(reinterpret_cast<const char *>(&Zero), sizeof(Zero));299 }300 auto *NewData = reinterpret_cast<char *>(copyByteArray(OS.str()));301 Contents = OutputContents = StringRef(NewData, OS.str().size());302 OutputSize = Contents.size();303}304 305std::string BinarySection::encodeELFNote(StringRef NameStr, StringRef DescStr,306 uint32_t Type) {307 std::string Str;308 raw_string_ostream OS(Str);309 const uint32_t NameSz = NameStr.size() + 1;310 const uint32_t DescSz = DescStr.size();311 OS.write(reinterpret_cast<const char *>(&(NameSz)), 4);312 OS.write(reinterpret_cast<const char *>(&(DescSz)), 4);313 OS.write(reinterpret_cast<const char *>(&(Type)), 4);314 OS << NameStr << '\0';315 for (uint64_t I = NameSz; I < alignTo(NameSz, 4); ++I)316 OS << '\0';317 OS << DescStr;318 for (uint64_t I = DescStr.size(); I < alignTo(DescStr.size(), 4); ++I)319 OS << '\0';320 return OS.str();321}322