Skip to content

Commit 3b1b89c

Browse files
committed
[Xtensa] Add LLD linker support
1 parent c21ad4a commit 3b1b89c

File tree

7 files changed

+195
-1
lines changed

7 files changed

+195
-1
lines changed

lld/ELF/Arch/Xtensa.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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 &target;
170+
}

lld/ELF/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_lld_library(lldELF
1818
Arch/SPARCV9.cpp
1919
Arch/X86.cpp
2020
Arch/X86_64.cpp
21+
Arch/Xtensa.cpp
2122
ARMErrataFix.cpp
2223
CallGraphSort.cpp
2324
DWARF.cpp

lld/ELF/InputFiles.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,8 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) {
16491649
return t.isOSIAMCU() ? EM_IAMCU : EM_386;
16501650
case Triple::x86_64:
16511651
return EM_X86_64;
1652+
case Triple::xtensa:
1653+
return EM_XTENSA;
16521654
default:
16531655
error(path + ": could not infer e_machine from bitcode target triple " +
16541656
t.str());

lld/ELF/Target.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ TargetInfo *elf::getTarget() {
8787
return getSPARCV9TargetInfo();
8888
case EM_X86_64:
8989
return getX86_64TargetInfo();
90+
case EM_XTENSA:
91+
return getXtensaTargetInfo();
9092
}
9193
llvm_unreachable("unknown target machine");
9294
}

lld/ELF/Target.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ TargetInfo *getRISCVTargetInfo();
183183
TargetInfo *getSPARCV9TargetInfo();
184184
TargetInfo *getX86TargetInfo();
185185
TargetInfo *getX86_64TargetInfo();
186+
TargetInfo *getXtensaTargetInfo();
186187
template <class ELFT> TargetInfo *getMipsTargetInfo();
187188

188189
struct ErrorPlace {

lld/test/ELF/xtensa-reloc.s

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# REQUIRES: xtensa
2+
# RUN: llvm-mc -filetype=obj -triple=xtensa -mcpu=esp32 %s -o %t.o
3+
# RUN: ld.lld %t.o --defsym=a=0x2000 --section-start=.CALL=0x1000 --defsym=b=40 -o %t
4+
# RUN: llvm-objdump -d --print-imm-hex %t | FileCheck %s
5+
6+
.section .CALL,"ax",@progbits
7+
# CHECK-LABEL: section .CALL:
8+
# CHECK: call0 . +4096
9+
# CHECK-NEXT: call0 . +4096
10+
# CHECK-NEXT: call0 . +4092
11+
# CHECK-NEXT: call0 . +4088
12+
# CHECK-NEXT: call0 . -4068
13+
call0 a
14+
call0 a
15+
call0 a
16+
call0 a
17+
call0 b

lld/test/lit.cfg.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
'RISCV': 'riscv',
7777
'Sparc': 'sparc',
7878
'WebAssembly': 'wasm',
79-
'X86': 'x86'})
79+
'X86': 'x86',
80+
'Xtensa': 'xtensa'})
8081
])
8182

8283
# Set a fake constant version so that we get consistent output.

0 commit comments

Comments
 (0)