|  | 
|  | 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 | +  default: | 
|  | 72 | +    error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + | 
|  | 73 | +          ") against symbol " + toString(s)); | 
|  | 74 | +    return R_NONE; | 
|  | 75 | +  } | 
|  | 76 | +} | 
|  | 77 | + | 
|  | 78 | +void Xtensa::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { | 
|  | 79 | +  switch (rel.type) { | 
|  | 80 | +  case R_XTENSA_32: | 
|  | 81 | +    write32le(loc, val); | 
|  | 82 | +    break; | 
|  | 83 | +  case R_XTENSA_SLOT0_OP: { | 
|  | 84 | +    // HACK: calculate the instruction location based on the PC-relative | 
|  | 85 | +    // relocation value. | 
|  | 86 | +    uint64_t dest = rel.sym->getVA(rel.addend); | 
|  | 87 | +    uint64_t p = dest - val; | 
|  | 88 | + | 
|  | 89 | +    // This relocation is used for various instructions. | 
|  | 90 | +    // Look at the instruction to determine how to do the relocation. | 
|  | 91 | +    uint8_t opcode = loc[0] & 0x0f; | 
|  | 92 | +    if (opcode == 0b0001) { // l32r | 
|  | 93 | +      uint64_t val = dest - ((p + 3) & (uint64_t)0xfffffffc); | 
|  | 94 | +      checkInt(loc, static_cast<int64_t>(val) >> 2, 16, rel); | 
|  | 95 | +      checkAlignment(loc, val, 4, rel); | 
|  | 96 | +      write16le(loc + 1, static_cast<int64_t>(val) >> 2); | 
|  | 97 | +    } else if (opcode == 0b0101) { // call0, call4, call8, call12 | 
|  | 98 | +      uint64_t val = dest - ((p + 4) & (uint64_t)0xfffffffc); | 
|  | 99 | +      checkInt(loc, static_cast<int64_t>(val) >> 2, 18, rel); | 
|  | 100 | +      checkAlignment(loc, val, 4, rel); | 
|  | 101 | +      const int64_t target = static_cast<int64_t>(val) >> 2; | 
|  | 102 | +      loc[0] = (loc[0] & 0b0011'1111) | ((target & 0b0000'0011) << 6); | 
|  | 103 | +      loc[1] = target >> 2; | 
|  | 104 | +      loc[2] = target >> 10; | 
|  | 105 | +    } else if ((loc[0] & 0x3f) == 0b00'0110) { // j | 
|  | 106 | +      uint64_t val = dest - p + 4; | 
|  | 107 | +      checkInt(loc, static_cast<int64_t>(val), 18, rel); | 
|  | 108 | +      loc[0] = (loc[0] & 0b0011'1111) | ((val & 0b0000'0011) << 6); | 
|  | 109 | +      loc[1] = val >> 2; | 
|  | 110 | +      loc[2] = val >> 10; | 
|  | 111 | +    } else { | 
|  | 112 | +      error(getErrorLocation(loc) + | 
|  | 113 | +            "unknown opcode for relocation: " + std::to_string(opcode)); | 
|  | 114 | +    } | 
|  | 115 | +    break; | 
|  | 116 | +  } | 
|  | 117 | +  default: | 
|  | 118 | +    llvm_unreachable("unknown relocation"); | 
|  | 119 | +  } | 
|  | 120 | +} | 
|  | 121 | + | 
|  | 122 | +TargetInfo *elf::getXtensaTargetInfo() { | 
|  | 123 | +  static Xtensa target; | 
|  | 124 | +  return ⌖ | 
|  | 125 | +} | 
0 commit comments