|  | 
|  | 1 | +//===- Xtensa.cpp ---------------------------------------------------------===// | 
|  | 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 | +#include "InputFiles.h" | 
|  | 10 | +#include "Symbols.h" | 
|  | 11 | +#include "Target.h" | 
|  | 12 | + | 
|  | 13 | +using namespace llvm; | 
|  | 14 | +using namespace llvm::object; | 
|  | 15 | +using namespace llvm::support::endian; | 
|  | 16 | +using namespace llvm::ELF; | 
|  | 17 | +using namespace lld; | 
|  | 18 | +using namespace lld::elf; | 
|  | 19 | + | 
|  | 20 | +namespace { | 
|  | 21 | + | 
|  | 22 | +class Xtensa final : public TargetInfo { | 
|  | 23 | +public: | 
|  | 24 | +  Xtensa(); | 
|  | 25 | +  RelExpr getRelExpr(RelType type, const Symbol &s, | 
|  | 26 | +                     const uint8_t *loc) const override; | 
|  | 27 | +  void relocate(uint8_t *loc, const Relocation &rel, | 
|  | 28 | +                uint64_t val) const override; | 
|  | 29 | +}; | 
|  | 30 | + | 
|  | 31 | +} // namespace | 
|  | 32 | + | 
|  | 33 | +Xtensa::Xtensa() { noneRel = R_XTENSA_NONE; } | 
|  | 34 | + | 
|  | 35 | +RelExpr Xtensa::getRelExpr(RelType type, const Symbol &s, | 
|  | 36 | +                           const uint8_t *loc) const { | 
|  | 37 | +  switch (type) { | 
|  | 38 | +  case R_XTENSA_32: | 
|  | 39 | +    return R_ABS; | 
|  | 40 | +  case R_XTENSA_SLOT0_OP: | 
|  | 41 | +    // This relocation is used for various instructions, with varying ways to | 
|  | 42 | +    // calculate the relocation value. This is unlike most ELF architectures, | 
|  | 43 | +    // and is arguably bad design (see the comment on R_386_GOT32 in X86.cpp). | 
|  | 44 | +    // But that's what compilers emit, so it needs to be supported. | 
|  | 45 | +    // | 
|  | 46 | +    // We work around this by returning R_PC here and calculating the PC address | 
|  | 47 | +    // in Xtensa::relocate based on the relative value. That's ugly. A better | 
|  | 48 | +    // solution would be to look at the instruction here and emit various | 
|  | 49 | +    // Xtensa-specific RelTypes, but that has another problem: the RelExpr enum | 
|  | 50 | +    // is at its maximum size of 64. This will need to be fixed eventually, but | 
|  | 51 | +    // for now hack around it and return R_PC. | 
|  | 52 | +    return R_PC; | 
|  | 53 | +  case R_XTENSA_ASM_EXPAND: | 
|  | 54 | +    // This relocation appears to be emitted by the GNU Xtensa compiler as a | 
|  | 55 | +    // linker relaxation hint. For example, for the following code: | 
|  | 56 | +    // | 
|  | 57 | +    //   .section .foo | 
|  | 58 | +    //   .align  4 | 
|  | 59 | +    //   foo: | 
|  | 60 | +    //       nop | 
|  | 61 | +    //       nop | 
|  | 62 | +    //       call0 bar | 
|  | 63 | +    //   .align  4 | 
|  | 64 | +    //       bar: | 
|  | 65 | +    // | 
|  | 66 | +    // The call0 instruction is compiled to a l32r and callx0 instruction. | 
|  | 67 | +    // The LLVM Xtensa backend does not emit this relocation. | 
|  | 68 | +    // Because it's a relaxation hint, this relocation can be ignored for now | 
|  | 69 | +    // until linker relaxations are implemented. | 
|  | 70 | +    return R_NONE; | 
|  | 71 | +  case R_XTENSA_PDIFF8: | 
|  | 72 | +  case R_XTENSA_PDIFF16: | 
|  | 73 | +  case R_XTENSA_PDIFF32: | 
|  | 74 | +  case R_XTENSA_NDIFF8: | 
|  | 75 | +  case R_XTENSA_NDIFF16: | 
|  | 76 | +  case R_XTENSA_NDIFF32: | 
|  | 77 | +    // > Xtensa relocations to mark the difference of two local symbols. | 
|  | 78 | +    // > These are only needed to support linker relaxation and can be ignored | 
|  | 79 | +    // > when not relaxing. | 
|  | 80 | +    // Source: | 
|  | 81 | +    // https://github.com/espressif/binutils-gdb/commit/30ce8e47fad9b057b6d7af9e1d43061126d34d20: | 
|  | 82 | +    // Because we don't do linker relaxation, we can ignore these relocations. | 
|  | 83 | +    return R_NONE; | 
|  | 84 | +  default: | 
|  | 85 | +    error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + | 
|  | 86 | +          ") against symbol " + toString(s)); | 
|  | 87 | +    return R_NONE; | 
|  | 88 | +  } | 
|  | 89 | +} | 
|  | 90 | + | 
|  | 91 | +static inline bool isRRI8Branch(uint8_t *loc) { | 
|  | 92 | +  if ((loc[0] & 0x0f) == 0b0111) { | 
|  | 93 | +    // instructions: ball, bany, bbc, bbci, bbs, bbsi, beq, bge, bgeu, blt, | 
|  | 94 | +    // bltu, bnall, bne, bnone | 
|  | 95 | +    return true; | 
|  | 96 | +  } | 
|  | 97 | +  if ((loc[0] & 0b11'1111) == 0b10'0110) { | 
|  | 98 | +    // instructions: beqi, bgei, bnei, blti | 
|  | 99 | +    return true; | 
|  | 100 | +  } | 
|  | 101 | +  if ((loc[0] & 0b1011'1111) == 0b1011'0110) { | 
|  | 102 | +    // instructions: bgeui, bltui | 
|  | 103 | +    return true; | 
|  | 104 | +  } | 
|  | 105 | +  // some other instruction | 
|  | 106 | +  return false; | 
|  | 107 | +} | 
|  | 108 | + | 
|  | 109 | +void Xtensa::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { | 
|  | 110 | +  switch (rel.type) { | 
|  | 111 | +  case R_XTENSA_32: | 
|  | 112 | +    write32le(loc, val); | 
|  | 113 | +    break; | 
|  | 114 | +  case R_XTENSA_SLOT0_OP: { | 
|  | 115 | +    // HACK: calculate the instruction location based on the PC-relative | 
|  | 116 | +    // relocation value. | 
|  | 117 | +    uint64_t dest = rel.sym->getVA(rel.addend); | 
|  | 118 | +    uint64_t p = dest - val; | 
|  | 119 | + | 
|  | 120 | +    // This relocation is used for various instructions. | 
|  | 121 | +    // Look at the instruction to determine how to do the relocation. | 
|  | 122 | +    uint8_t opcode = loc[0] & 0x0f; | 
|  | 123 | +    if (opcode == 0b0001) { // RI16 format: l32r | 
|  | 124 | +      uint64_t val = dest - ((p + 3) & (uint64_t)0xfffffffc); | 
|  | 125 | +      checkInt(loc, static_cast<int64_t>(val) >> 2, 16, rel); | 
|  | 126 | +      checkAlignment(loc, val, 4, rel); | 
|  | 127 | +      write16le(loc + 1, static_cast<int64_t>(val) >> 2); | 
|  | 128 | +    } else if (opcode == 0b0101) { // call0, call4, call8, call12 (CALL format) | 
|  | 129 | +      uint64_t val = dest - ((p + 4) & (uint64_t)0xfffffffc); | 
|  | 130 | +      checkInt(loc, static_cast<int64_t>(val) >> 2, 18, rel); | 
|  | 131 | +      checkAlignment(loc, val, 4, rel); | 
|  | 132 | +      const int64_t target = static_cast<int64_t>(val) >> 2; | 
|  | 133 | +      loc[0] = (loc[0] & 0b0011'1111) | ((target & 0b0000'0011) << 6); | 
|  | 134 | +      loc[1] = target >> 2; | 
|  | 135 | +      loc[2] = target >> 10; | 
|  | 136 | +    } else if ((loc[0] & 0x3f) == 0b00'0110) { // j (CALL format) | 
|  | 137 | +      uint64_t val = dest - p + 4; | 
|  | 138 | +      checkInt(loc, static_cast<int64_t>(val), 18, rel); | 
|  | 139 | +      loc[0] = (loc[0] & 0b0011'1111) | ((val & 0b0000'0011) << 6); | 
|  | 140 | +      loc[1] = val >> 2; | 
|  | 141 | +      loc[2] = val >> 10; | 
|  | 142 | +    } else if (isRRI8Branch(loc)) { // RRI8 format (various branch instructions) | 
|  | 143 | +      uint64_t v = val - 4; | 
|  | 144 | +      checkInt(loc, static_cast<int64_t>(v), 8, rel); | 
|  | 145 | +      loc[2] = v & 0xff; | 
|  | 146 | +    } else if ((loc[0] & 0b1000'1111) == 0b1000'1100) { // RI16 format: beqz.n, bnez.n | 
|  | 147 | +      uint64_t v = val - 4; | 
|  | 148 | +      checkUInt(loc, v, 6, rel); | 
|  | 149 | +      loc[0] = (loc[0] & 0xcf) | (v & 0x30); | 
|  | 150 | +      loc[1] = (loc[1] & 0x0f) | ((v & 0x0f) << 4); | 
|  | 151 | +    } else if ((loc[0] & 0b0011'1111) == 0b0001'0110) { // BRI12 format: beqz, bgez, bltz, bnez | 
|  | 152 | +      uint64_t v = val - 4; | 
|  | 153 | +      checkInt(loc, static_cast<int64_t>(v), 12, rel); | 
|  | 154 | +      loc[1] = ((loc[1] & 0x0f)) | ((v & 0x0f) << 4); | 
|  | 155 | +      loc[2] = (v >> 4) & 0xff; | 
|  | 156 | +    } else { | 
|  | 157 | +      error(getErrorLocation(loc) + | 
|  | 158 | +            "unknown opcode for relocation: " + std::to_string(loc[0])); | 
|  | 159 | +    } | 
|  | 160 | +    break; | 
|  | 161 | +  } | 
|  | 162 | +  default: | 
|  | 163 | +    llvm_unreachable("unknown relocation"); | 
|  | 164 | +  } | 
|  | 165 | +} | 
|  | 166 | + | 
|  | 167 | +TargetInfo *elf::getXtensaTargetInfo() { | 
|  | 168 | +  static Xtensa target; | 
|  | 169 | +  return ⌖ | 
|  | 170 | +} | 
0 commit comments