1//===- AMDGPU.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 "lld/Common/ErrorHandler.h"
13#include "llvm/BinaryFormat/ELF.h"
14#include "llvm/Support/Endian.h"
15
16using namespace llvm::object;
17using namespace llvm::support::endian;
18using namespace llvm::ELF;
19using namespace lld;
20using namespace lld::elf;
21
22namespace {
23class AMDGPU final : public TargetInfo {
24private:
25 uint32_t calcEFlagsV3() const;
26 uint32_t calcEFlagsV4() const;
27 uint32_t calcEFlagsV6() const;
28
29public:
30 AMDGPU(Ctx &);
31 uint32_t calcEFlags() const override;
32 void relocate(uint8_t *loc, const Relocation &rel,
33 uint64_t val) const override;
34 RelExpr getRelExpr(RelType type, const Symbol &s,
35 const uint8_t *loc) const override;
36 RelType getDynRel(RelType type) const override;
37 int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
38};
39} // namespace
40
41AMDGPU::AMDGPU(Ctx &ctx) : TargetInfo(ctx) {
42 relativeRel = R_AMDGPU_RELATIVE64;
43 gotRel = R_AMDGPU_ABS64;
44 symbolicRel = R_AMDGPU_ABS64;
45}
46
47static uint32_t getEFlags(InputFile *file) {
48 return cast<ObjFile<ELF64LE>>(Val: file)->getObj().getHeader().e_flags;
49}
50
51uint32_t AMDGPU::calcEFlagsV3() const {
52 uint32_t ret = getEFlags(file: ctx.objectFiles[0]);
53
54 // Verify that all input files have the same e_flags.
55 for (InputFile *f : ArrayRef(ctx.objectFiles).slice(N: 1)) {
56 if (ret == getEFlags(file: f))
57 continue;
58 ErrAlways(ctx) << "incompatible e_flags: " << f;
59 return 0;
60 }
61 return ret;
62}
63
64uint32_t AMDGPU::calcEFlagsV4() const {
65 uint32_t retMach = getEFlags(file: ctx.objectFiles[0]) & EF_AMDGPU_MACH;
66 uint32_t retXnack =
67 getEFlags(file: ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_XNACK_V4;
68 uint32_t retSramEcc =
69 getEFlags(file: ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_SRAMECC_V4;
70
71 // Verify that all input files have compatible e_flags (same mach, all
72 // features in the same category are either ANY, ANY and ON, or ANY and OFF).
73 for (InputFile *f : ArrayRef(ctx.objectFiles).slice(N: 1)) {
74 if (retMach != (getEFlags(file: f) & EF_AMDGPU_MACH)) {
75 Err(ctx) << "incompatible mach: " << f;
76 return 0;
77 }
78
79 if (retXnack == EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4 ||
80 (retXnack != EF_AMDGPU_FEATURE_XNACK_ANY_V4 &&
81 (getEFlags(file: f) & EF_AMDGPU_FEATURE_XNACK_V4)
82 != EF_AMDGPU_FEATURE_XNACK_ANY_V4)) {
83 if (retXnack != (getEFlags(file: f) & EF_AMDGPU_FEATURE_XNACK_V4)) {
84 Err(ctx) << "incompatible xnack: " << f;
85 return 0;
86 }
87 } else {
88 if (retXnack == EF_AMDGPU_FEATURE_XNACK_ANY_V4)
89 retXnack = getEFlags(file: f) & EF_AMDGPU_FEATURE_XNACK_V4;
90 }
91
92 if (retSramEcc == EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4 ||
93 (retSramEcc != EF_AMDGPU_FEATURE_SRAMECC_ANY_V4 &&
94 (getEFlags(file: f) & EF_AMDGPU_FEATURE_SRAMECC_V4) !=
95 EF_AMDGPU_FEATURE_SRAMECC_ANY_V4)) {
96 if (retSramEcc != (getEFlags(file: f) & EF_AMDGPU_FEATURE_SRAMECC_V4)) {
97 Err(ctx) << "incompatible sramecc: " << f;
98 return 0;
99 }
100 } else {
101 if (retSramEcc == EF_AMDGPU_FEATURE_SRAMECC_ANY_V4)
102 retSramEcc = getEFlags(file: f) & EF_AMDGPU_FEATURE_SRAMECC_V4;
103 }
104 }
105
106 return retMach | retXnack | retSramEcc;
107}
108
109uint32_t AMDGPU::calcEFlagsV6() const {
110 uint32_t flags = calcEFlagsV4();
111
112 uint32_t genericVersion =
113 getEFlags(file: ctx.objectFiles[0]) & EF_AMDGPU_GENERIC_VERSION;
114
115 // Verify that all input files have compatible generic version.
116 for (InputFile *f : ArrayRef(ctx.objectFiles).slice(N: 1)) {
117 if (genericVersion != (getEFlags(file: f) & EF_AMDGPU_GENERIC_VERSION)) {
118 ErrAlways(ctx) << "incompatible generic version: " << f;
119 return 0;
120 }
121 }
122
123 flags |= genericVersion;
124 return flags;
125}
126
127uint32_t AMDGPU::calcEFlags() const {
128 if (ctx.objectFiles.empty())
129 return 0;
130
131 uint8_t abiVersion = cast<ObjFile<ELF64LE>>(Val: ctx.objectFiles[0])
132 ->getObj()
133 .getHeader()
134 .e_ident[EI_ABIVERSION];
135 switch (abiVersion) {
136 case ELFABIVERSION_AMDGPU_HSA_V2:
137 case ELFABIVERSION_AMDGPU_HSA_V3:
138 return calcEFlagsV3();
139 case ELFABIVERSION_AMDGPU_HSA_V4:
140 case ELFABIVERSION_AMDGPU_HSA_V5:
141 return calcEFlagsV4();
142 case ELFABIVERSION_AMDGPU_HSA_V6:
143 return calcEFlagsV6();
144 default:
145 Err(ctx) << "unknown abi version: " << abiVersion;
146 return 0;
147 }
148}
149
150void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
151 switch (rel.type) {
152 case R_AMDGPU_ABS32:
153 case R_AMDGPU_GOTPCREL:
154 case R_AMDGPU_GOTPCREL32_LO:
155 case R_AMDGPU_REL32:
156 case R_AMDGPU_REL32_LO:
157 write32le(P: loc, V: val);
158 break;
159 case R_AMDGPU_ABS64:
160 case R_AMDGPU_REL64:
161 write64le(P: loc, V: val);
162 break;
163 case R_AMDGPU_GOTPCREL32_HI:
164 case R_AMDGPU_REL32_HI:
165 write32le(P: loc, V: val >> 32);
166 break;
167 case R_AMDGPU_REL16: {
168 int64_t simm = (static_cast<int64_t>(val) - 4) / 4;
169 checkInt(ctx, loc, v: simm, n: 16, rel);
170 write16le(P: loc, V: simm);
171 break;
172 }
173 default:
174 llvm_unreachable("unknown relocation");
175 }
176}
177
178RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
179 const uint8_t *loc) const {
180 switch (type) {
181 case R_AMDGPU_ABS32:
182 case R_AMDGPU_ABS64:
183 return R_ABS;
184 case R_AMDGPU_REL32:
185 case R_AMDGPU_REL32_LO:
186 case R_AMDGPU_REL32_HI:
187 case R_AMDGPU_REL64:
188 case R_AMDGPU_REL16:
189 return R_PC;
190 case R_AMDGPU_GOTPCREL:
191 case R_AMDGPU_GOTPCREL32_LO:
192 case R_AMDGPU_GOTPCREL32_HI:
193 return R_GOT_PC;
194 default:
195 Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
196 << ") against symbol " << &s;
197 return R_NONE;
198 }
199}
200
201RelType AMDGPU::getDynRel(RelType type) const {
202 if (type == R_AMDGPU_ABS64)
203 return type;
204 return R_AMDGPU_NONE;
205}
206
207int64_t AMDGPU::getImplicitAddend(const uint8_t *buf, RelType type) const {
208 switch (type) {
209 case R_AMDGPU_NONE:
210 return 0;
211 case R_AMDGPU_ABS64:
212 case R_AMDGPU_RELATIVE64:
213 return read64(ctx, p: buf);
214 default:
215 InternalErr(ctx, buf) << "cannot read addend for relocation " << type;
216 return 0;
217 }
218}
219
220void elf::setAMDGPUTargetInfo(Ctx &ctx) { ctx.target.reset(p: new AMDGPU(ctx)); }
221