|
| 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