| 1 | //===- bolt/Core/Relocation.h - Object file relocations ---------*- C++ -*-===// |
| 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 contains the declaration of Relocation class, which represents a |
| 10 | // relocation in an object or a binary file. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #ifndef BOLT_CORE_RELOCATION_H |
| 15 | #define BOLT_CORE_RELOCATION_H |
| 16 | |
| 17 | #include "llvm/MC/MCExpr.h" |
| 18 | #include "llvm/MC/MCStreamer.h" |
| 19 | #include "llvm/TargetParser/Triple.h" |
| 20 | |
| 21 | namespace llvm { |
| 22 | class MCSymbol; |
| 23 | |
| 24 | namespace object { |
| 25 | class RelocationRef; |
| 26 | } // namespace object |
| 27 | |
| 28 | class raw_ostream; |
| 29 | |
| 30 | namespace ELF { |
| 31 | /// Relocation type mask that was accidentally output by bfd 2.30 linker. |
| 32 | enum { R_X86_64_converted_reloc_bit = 0x80 }; |
| 33 | } // namespace ELF |
| 34 | |
| 35 | namespace bolt { |
| 36 | |
| 37 | /// Relocation class. |
| 38 | class Relocation { |
| 39 | public: |
| 40 | Relocation(uint64_t Offset, MCSymbol *Symbol, uint32_t Type, uint64_t Addend, |
| 41 | uint64_t Value) |
| 42 | : Offset(Offset), Symbol(Symbol), Type(Type), Optional(false), |
| 43 | Addend(Addend), Value(Value) {} |
| 44 | |
| 45 | Relocation() |
| 46 | : Offset(0), Symbol(0), Type(0), Optional(0), Addend(0), Value(0) {} |
| 47 | |
| 48 | static Triple::ArchType Arch; /// set by BinaryContext ctor. |
| 49 | |
| 50 | /// The offset of this relocation in the object it is contained in. |
| 51 | uint64_t Offset; |
| 52 | |
| 53 | /// The symbol this relocation is referring to. |
| 54 | MCSymbol *Symbol; |
| 55 | |
| 56 | /// Relocation type. |
| 57 | uint32_t Type; |
| 58 | |
| 59 | private: |
| 60 | /// Relocations added by optimizations can be optional, meaning they can be |
| 61 | /// omitted under certain circumstances. |
| 62 | bool Optional = false; |
| 63 | |
| 64 | public: |
| 65 | /// The offset from the \p Symbol base used to compute the final |
| 66 | /// value of this relocation. |
| 67 | uint64_t Addend; |
| 68 | |
| 69 | /// The computed relocation value extracted from the binary file. |
| 70 | /// Used to validate relocation correctness. |
| 71 | uint64_t Value; |
| 72 | |
| 73 | /// Return size in bytes of the given relocation \p Type. |
| 74 | static size_t getSizeForType(uint32_t Type); |
| 75 | |
| 76 | void setOptional() { Optional = true; } |
| 77 | |
| 78 | bool isOptional() { return Optional; } |
| 79 | |
| 80 | /// Return size of this relocation. |
| 81 | size_t getSize() const { return getSizeForType(Type); } |
| 82 | |
| 83 | /// Skip relocations that we don't want to handle in BOLT |
| 84 | static bool skipRelocationType(uint32_t Type); |
| 85 | |
| 86 | /// Adjust value depending on relocation type (make it PC relative or not). |
| 87 | static uint64_t encodeValue(uint32_t Type, uint64_t Value, uint64_t PC); |
| 88 | |
| 89 | /// Return true if there are enough bits to encode the relocation value. |
| 90 | static bool canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC); |
| 91 | |
| 92 | /// Extract current relocated value from binary contents. This is used for |
| 93 | /// RISC architectures where values are encoded in specific bits depending |
| 94 | /// on the relocation value. For X86, we limit to sign extending the value |
| 95 | /// if necessary. |
| 96 | static uint64_t (uint32_t Type, uint64_t Contents, uint64_t PC); |
| 97 | |
| 98 | /// Return true if relocation type is PC-relative. Return false otherwise. |
| 99 | static bool isPCRelative(uint32_t Type); |
| 100 | |
| 101 | /// Check if \p Type is a supported relocation type. |
| 102 | static bool isSupported(uint32_t Type); |
| 103 | |
| 104 | /// Return true if relocation type implies the creation of a GOT entry |
| 105 | static bool isGOT(uint32_t Type); |
| 106 | |
| 107 | /// Special relocation type that allows the linker to modify the instruction. |
| 108 | static bool isX86GOTPCRELX(uint32_t Type); |
| 109 | static bool isX86GOTPC64(uint32_t Type); |
| 110 | |
| 111 | /// Return true if relocation type is NONE |
| 112 | static bool isNone(uint32_t Type); |
| 113 | |
| 114 | /// Return true if relocation type is RELATIVE |
| 115 | static bool isRelative(uint32_t Type); |
| 116 | |
| 117 | /// Return true if relocation type is IRELATIVE |
| 118 | static bool isIRelative(uint32_t Type); |
| 119 | |
| 120 | /// Return true if relocation type is for thread local storage. |
| 121 | static bool isTLS(uint32_t Type); |
| 122 | |
| 123 | /// Return true of relocation type is for referencing a specific instruction |
| 124 | /// (as opposed to a function, basic block, etc). |
| 125 | static bool isInstructionReference(uint32_t Type); |
| 126 | |
| 127 | /// Return the relocation type of \p Rel from llvm::object. It checks for |
| 128 | /// overflows as BOLT uses 32 bits for the type. |
| 129 | static uint32_t getType(const object::RelocationRef &Rel); |
| 130 | |
| 131 | /// Return code for a NONE relocation |
| 132 | static uint32_t getNone(); |
| 133 | |
| 134 | /// Return code for a PC-relative 4-byte relocation |
| 135 | static uint32_t getPC32(); |
| 136 | |
| 137 | /// Return code for a PC-relative 8-byte relocation |
| 138 | static uint32_t getPC64(); |
| 139 | |
| 140 | /// Return code for a ABS 8-byte relocation |
| 141 | static uint32_t getAbs64(); |
| 142 | |
| 143 | /// Return code for a RELATIVE relocation |
| 144 | static uint32_t getRelative(); |
| 145 | |
| 146 | /// Return true if this relocation is PC-relative. Return false otherwise. |
| 147 | bool isPCRelative() const { return isPCRelative(Type); } |
| 148 | |
| 149 | /// Return true if this relocation is R_*_RELATIVE type. Return false |
| 150 | /// otherwise. |
| 151 | bool isRelative() const { return isRelative(Type); } |
| 152 | |
| 153 | /// Return true if this relocation is R_*_IRELATIVE type. Return false |
| 154 | /// otherwise. |
| 155 | bool isIRelative() const { return isIRelative(Type); } |
| 156 | |
| 157 | /// Emit relocation at a current \p Streamer' position. The caller is |
| 158 | /// responsible for setting the position correctly. |
| 159 | size_t emit(MCStreamer *Streamer) const; |
| 160 | |
| 161 | /// Emit a group of composed relocations. All relocations must have the same |
| 162 | /// offset. If std::distance(Begin, End) == 1, this is equivalent to |
| 163 | /// Begin->emit(Streamer). |
| 164 | template <typename RelocIt> |
| 165 | static size_t emit(RelocIt Begin, RelocIt End, MCStreamer *Streamer) { |
| 166 | if (Begin == End) |
| 167 | return 0; |
| 168 | |
| 169 | const MCExpr *Value = nullptr; |
| 170 | |
| 171 | for (auto RI = Begin; RI != End; ++RI) { |
| 172 | assert(RI->Offset == Begin->Offset && |
| 173 | "emitting composed relocations with different offsets" ); |
| 174 | Value = RI->createExpr(Streamer, Value); |
| 175 | } |
| 176 | |
| 177 | assert(Value && "failed to create relocation value" ); |
| 178 | auto Size = std::prev(End)->getSize(); |
| 179 | Streamer->emitValue(Value, Size); |
| 180 | return Size; |
| 181 | } |
| 182 | |
| 183 | /// Print a relocation to \p OS. |
| 184 | void print(raw_ostream &OS) const; |
| 185 | |
| 186 | private: |
| 187 | const MCExpr *createExpr(MCStreamer *Streamer) const; |
| 188 | const MCExpr *createExpr(MCStreamer *Streamer, |
| 189 | const MCExpr *RetainedValue) const; |
| 190 | static MCBinaryExpr::Opcode getComposeOpcodeFor(uint32_t Type); |
| 191 | }; |
| 192 | |
| 193 | /// Relocation ordering by offset. |
| 194 | inline bool operator<(const Relocation &A, const Relocation &B) { |
| 195 | return A.Offset < B.Offset; |
| 196 | } |
| 197 | |
| 198 | inline bool operator<(const Relocation &A, uint64_t B) { return A.Offset < B; } |
| 199 | |
| 200 | inline bool operator<(uint64_t A, const Relocation &B) { return A < B.Offset; } |
| 201 | |
| 202 | inline raw_ostream &operator<<(raw_ostream &OS, const Relocation &Rel) { |
| 203 | Rel.print(OS); |
| 204 | return OS; |
| 205 | } |
| 206 | |
| 207 | } // namespace bolt |
| 208 | } // namespace llvm |
| 209 | |
| 210 | #endif |
| 211 | |