brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.4 KiB · e803d17 Raw
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