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