Skip to content

Commit ce430fd

Browse files
aykevlsstefan1
authored andcommitted
[Xtensa] Add LLD linker support
Closes llvm#11
1 parent 32e6411 commit ce430fd

File tree

8 files changed

+204
-1
lines changed

8 files changed

+204
-1
lines changed

lld/ELF/Arch/Xtensa.cpp

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

lld/ELF/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_lld_library(lldELF
2222
Arch/SPARCV9.cpp
2323
Arch/X86.cpp
2424
Arch/X86_64.cpp
25+
Arch/Xtensa.cpp
2526
ARMErrataFix.cpp
2627
CallGraphSort.cpp
2728
DWARF.cpp

lld/ELF/InputFiles.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,8 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) {
15381538
return t.isOSIAMCU() ? EM_IAMCU : EM_386;
15391539
case Triple::x86_64:
15401540
return EM_X86_64;
1541+
case Triple::xtensa:
1542+
return EM_XTENSA;
15411543
default:
15421544
error(path + ": could not infer e_machine from bitcode target triple " +
15431545
t.str());

lld/ELF/Target.cpp

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

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ TargetInfo *getRISCVTargetInfo();
187187
TargetInfo *getSPARCV9TargetInfo();
188188
TargetInfo *getX86TargetInfo();
189189
TargetInfo *getX86_64TargetInfo();
190+
TargetInfo *getXtensaTargetInfo();
190191
template <class ELFT> TargetInfo *getMipsTargetInfo();
191192

192193
struct ErrorPlace {

lld/test/ELF/xtensa-reloc.s

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
'RISCV': 'riscv',
7575
'Sparc': 'sparc',
7676
'WebAssembly': 'wasm',
77-
'X86': 'x86'}),
77+
'X86': 'x86',
78+
'Xtensa': 'xtensa'}),
7879
('--assertion-mode', {'ON': 'asserts'}),
7980
])
8081

llvm/include/llvm/BinaryFormat/ELFRelocs/Xtensa.def

+6
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,9 @@ ELF_RELOC(R_XTENSA_TLS_TPOFF, 53)
5757
ELF_RELOC(R_XTENSA_TLS_FUNC, 54)
5858
ELF_RELOC(R_XTENSA_TLS_ARG, 55)
5959
ELF_RELOC(R_XTENSA_TLS_CALL, 56)
60+
ELF_RELOC(R_XTENSA_PDIFF8, 57)
61+
ELF_RELOC(R_XTENSA_PDIFF16, 58)
62+
ELF_RELOC(R_XTENSA_PDIFF32, 59)
63+
ELF_RELOC(R_XTENSA_NDIFF8, 60)
64+
ELF_RELOC(R_XTENSA_NDIFF16, 61)
65+
ELF_RELOC(R_XTENSA_NDIFF32, 62)

0 commit comments

Comments
 (0)