Line data Source code
1 : /**
2 : * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 : #ifndef KERNEL_ELF_PARSER_H
11 : #define KERNEL_ELF_PARSER_H
12 :
13 : #include <cstdlib>
14 : #include <memory>
15 : #include <stdexcept>
16 : #include <string>
17 : #include <unordered_map>
18 : #include <elf.h>
19 : #include <cxxabi.h>
20 :
21 : #include "stub_def.h"
22 :
23 : namespace AscendC {
24 :
25 : constexpr uint16_t FUNC_META_TYPE_KERNEL_TYPE = 1U;
26 : constexpr uint16_t FUNC_META_TYPE_MIX_TASK_RATION = 3U;
27 : const std::string KERNEL_SECTION_NAME_PREFIX = ".ascend.meta.";
28 : const std::string KERNEL_MIX_AIV_POSTFIX = "_mix_aiv";
29 : const std::string KERNEL_MIX_AIC_POSTFIX = "_mix_aic";
30 : const size_t PREFIX_LEN = KERNEL_SECTION_NAME_PREFIX.length();
31 : const size_t MIX_SUFFIX_LEN =
32 : KERNEL_MIX_AIV_POSTFIX.length(); // KERNEL_MIX_AIC_POSTFIX.length() == KERNEL_MIX_AIV_POSTFIX.length()
33 :
34 : typedef struct {
35 : uint16_t type;
36 : uint16_t length;
37 : } ElfTlvHead;
38 :
39 : typedef enum KernelType : unsigned int {
40 : K_TYPE_INVALID = 0,
41 : K_TYPE_AICORE = 1,
42 : K_TYPE_AIC = 2,
43 : K_TYPE_AIV = 3,
44 : K_TYPE_MIX_AIC_MAIN = 4,
45 : K_TYPE_MIX_AIV_MAIN = 5,
46 : K_TYPE_AIC_ROLLBACK = 6,
47 : K_TYPE_AIV_ROLLBACK = 7,
48 : K_TYPE_MAX
49 : } KernelTypeAsc;
50 :
51 : struct ElfKernelInfo {
52 : uint32_t kernelType = 0;
53 : uint16_t aicRation = 0;
54 : uint16_t aivRation = 0;
55 : };
56 :
57 : class KernelModeRegister {
58 : public:
59 40 : static KernelModeRegister& GetInstance()
60 : {
61 40 : static KernelModeRegister instance;
62 40 : return instance;
63 : }
64 :
65 60 : static std::string Demangle(const char* symbol)
66 : {
67 60 : if (symbol == nullptr) {
68 4 : throw std::runtime_error("Failed to demangle symbol: null symbol");
69 : }
70 :
71 56 : int status = 0;
72 : std::unique_ptr<char, decltype(&std::free)> demangled(
73 56 : abi::__cxa_demangle(symbol, nullptr, nullptr, &status), std::free);
74 : // abi::__cxa_demangle mallocs memory for the demangled name, so we use unique_ptr to ensure it gets freed
75 :
76 56 : if (status == 0 && demangled != nullptr) {
77 88 : return std::string(demangled.get());
78 : }
79 :
80 12 : if (status == -2) {
81 24 : return std::string(symbol);
82 : }
83 :
84 0 : throw std::runtime_error("Failed to demangle symbol: " + std::string(symbol));
85 56 : }
86 :
87 24 : void Register(const std::string& kernelName, KernelMode kernelMode)
88 : {
89 24 : kernelModeMap[Demangle(kernelName.c_str())] = kernelMode;
90 24 : }
91 :
92 24 : void Clear() { kernelModeMap.clear(); }
93 :
94 24 : KernelMode GetKenelMode(const char* mangling)
95 : {
96 24 : std::string kernelName = Demangle(mangling);
97 24 : auto it = kernelModeMap.find(kernelName);
98 24 : if (it != kernelModeMap.end()) {
99 40 : return it->second;
100 : }
101 4 : throw std::invalid_argument("Kernel mode not found for kernel: " + kernelName);
102 24 : }
103 :
104 : private:
105 : std::unordered_map<std::string, KernelMode> kernelModeMap;
106 : };
107 :
108 76 : inline uint64_t ByteGetBigEndian(const uint8_t field[], const int32_t size)
109 : {
110 76 : uint64_t ret = 0UL;
111 :
112 76 : switch (size) {
113 4 : case 1:
114 4 : ret = static_cast<uint64_t>(*field);
115 4 : break;
116 36 : case 2:
117 36 : ret = (static_cast<uint64_t>(field[1U])) | ((static_cast<uint64_t>(field[0U])) << 8U); // shift 8 bit
118 36 : break;
119 0 : case 3:
120 0 : ret = (static_cast<uint64_t>(field[2U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
121 0 : ((static_cast<uint64_t>(field[0U])) << 16U);
122 0 : break;
123 12 : case 4:
124 12 : ret = (static_cast<uint64_t>(field[3U])) | ((static_cast<uint64_t>(field[2U])) << 8U) |
125 12 : ((static_cast<uint64_t>(field[1U])) << 16U) | ((static_cast<uint64_t>(field[0U])) << 24U);
126 12 : break;
127 0 : case 5:
128 0 : ret = (static_cast<uint64_t>(field[4U])) | ((static_cast<uint64_t>(field[3U])) << 8U) |
129 0 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[1U])) << 24U) |
130 0 : ((static_cast<uint64_t>(field[0U])) << 32U);
131 0 : break;
132 0 : case 6:
133 0 : ret = (static_cast<uint64_t>(field[5U])) | ((static_cast<uint64_t>(field[4U])) << 8U) |
134 0 : ((static_cast<uint64_t>(field[3U])) << 16U) | ((static_cast<uint64_t>(field[2U])) << 24U) |
135 0 : ((static_cast<uint64_t>(field[1U])) << 32U) | ((static_cast<uint64_t>(field[0U])) << 40U);
136 0 : break;
137 0 : case 7:
138 0 : ret = (static_cast<uint64_t>(field[6U])) | ((static_cast<uint64_t>(field[5U])) << 8U) |
139 0 : ((static_cast<uint64_t>(field[4U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U) |
140 0 : ((static_cast<uint64_t>(field[2U])) << 32U) | ((static_cast<uint64_t>(field[1U])) << 40U) |
141 0 : ((static_cast<uint64_t>(field[0U])) << 48U);
142 0 : break;
143 16 : case 8:
144 16 : ret = (static_cast<uint64_t>(field[7U])) | ((static_cast<uint64_t>(field[6U])) << 8U) |
145 16 : ((static_cast<uint64_t>(field[5U])) << 16U) | ((static_cast<uint64_t>(field[4U])) << 24U) |
146 16 : ((static_cast<uint64_t>(field[3U])) << 32U) | ((static_cast<uint64_t>(field[2U])) << 40U) |
147 16 : ((static_cast<uint64_t>(field[1U])) << 48U) | ((static_cast<uint64_t>(field[0U])) << 56U);
148 16 : break;
149 8 : default:
150 8 : throw std::invalid_argument("Invalid data length: size = " + std::to_string(size) + ", support 1~8 only");
151 : break;
152 : }
153 :
154 68 : return ret;
155 : }
156 :
157 924 : inline uint64_t ByteGetLittleEndian(const uint8_t field[], const int32_t size)
158 : {
159 924 : uint64_t ret = 0UL;
160 :
161 924 : switch (size) {
162 4 : case 1:
163 4 : ret = static_cast<uint64_t>(*field);
164 4 : break;
165 300 : case 2:
166 300 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U); // shift 8 bit
167 300 : break;
168 0 : case 3:
169 0 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
170 0 : ((static_cast<uint64_t>(field[2U])) << 16U);
171 0 : break;
172 304 : case 4:
173 304 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
174 304 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U);
175 304 : break;
176 0 : case 5:
177 0 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
178 0 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U) |
179 0 : ((static_cast<uint64_t>(field[4U])) << 32U);
180 0 : break;
181 0 : case 6: /* Fall through. */
182 0 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
183 0 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U) |
184 0 : ((static_cast<uint64_t>(field[4U])) << 32U) | ((static_cast<uint64_t>(field[5U])) << 40U);
185 0 : break;
186 0 : case 7:
187 0 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
188 0 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U) |
189 0 : ((static_cast<uint64_t>(field[4U])) << 32U) | ((static_cast<uint64_t>(field[5U])) << 40U) |
190 0 : ((static_cast<uint64_t>(field[6U])) << 48U);
191 0 : break;
192 308 : case 8:
193 308 : ret = (static_cast<uint64_t>(field[0U])) | ((static_cast<uint64_t>(field[1U])) << 8U) |
194 308 : ((static_cast<uint64_t>(field[2U])) << 16U) | ((static_cast<uint64_t>(field[3U])) << 24U) |
195 308 : ((static_cast<uint64_t>(field[4U])) << 32U) | ((static_cast<uint64_t>(field[5U])) << 40U) |
196 308 : ((static_cast<uint64_t>(field[6U])) << 48U) | ((static_cast<uint64_t>(field[7U])) << 56U);
197 308 : break;
198 8 : default:
199 8 : throw std::invalid_argument("Invalid data length: size = " + std::to_string(size) + ", support 1~8 only");
200 : break;
201 : }
202 :
203 916 : return ret;
204 : }
205 :
206 : thread_local static uint64_t (*GetByte)(const uint8_t[], const int32_t) = nullptr;
207 :
208 44 : inline Elf64_Ehdr ParseElfHeader(const uint8_t* const elfData, size_t dataSize)
209 : {
210 44 : if (dataSize < sizeof(Elf64_Ehdr)) {
211 : throw std::invalid_argument(
212 16 : "Input data size is too small for 64-bit ELF header, get input dataSize: " + std::to_string(dataSize) +
213 24 : ", requires at least: " + std::to_string(sizeof(Elf64_Ehdr)));
214 : }
215 :
216 : /* Determine how to read the rest of the header. */
217 36 : switch (elfData[EI_DATA]) {
218 32 : case ELFDATANONE:
219 : case ELFDATA2LSB:
220 32 : GetByte = &ByteGetLittleEndian;
221 32 : break;
222 4 : case ELFDATA2MSB:
223 4 : GetByte = &ByteGetBigEndian;
224 4 : break;
225 0 : default:
226 0 : GetByte = &ByteGetLittleEndian;
227 0 : break;
228 : }
229 :
230 36 : const bool is32bitElf = (elfData[EI_CLASS] != ELFCLASS64);
231 :
232 : /* Read in the rest of the header. */
233 36 : if (is32bitElf) {
234 4 : throw std::invalid_argument("Only support input elf is 64-bit format.");
235 : }
236 :
237 : Elf64_Ehdr header;
238 32 : size_t offset = EI_NIDENT; // Skip e_ident
239 :
240 32 : header.e_type = static_cast<uint16_t>(GetByte(elfData + offset, 2));
241 32 : offset += 2;
242 32 : header.e_machine = static_cast<uint16_t>(GetByte(elfData + offset, 2));
243 32 : offset += 2;
244 32 : header.e_version = GetByte(elfData + offset, 4);
245 32 : offset += 4;
246 32 : header.e_entry = GetByte(elfData + offset, 8);
247 32 : offset += 8;
248 32 : header.e_phoff = GetByte(elfData + offset, 8);
249 32 : offset += 8;
250 32 : header.e_shoff = GetByte(elfData + offset, 8);
251 32 : offset += 8;
252 32 : header.e_flags = static_cast<uint32_t>(GetByte(elfData + offset, 4));
253 32 : offset += 4;
254 32 : header.e_ehsize = static_cast<uint16_t>(GetByte(elfData + offset, 2));
255 32 : offset += 2;
256 32 : header.e_phentsize = static_cast<uint16_t>(GetByte(elfData + offset, 2));
257 32 : offset += 2;
258 32 : header.e_phnum = static_cast<uint16_t>(GetByte(elfData + offset, 2));
259 32 : offset += 2;
260 32 : header.e_shentsize = static_cast<uint16_t>(GetByte(elfData + offset, 2));
261 32 : offset += 2;
262 32 : header.e_shnum = static_cast<uint16_t>(GetByte(elfData + offset, 2));
263 32 : offset += 2;
264 32 : header.e_shstrndx = static_cast<uint16_t>(GetByte(elfData + offset, 2));
265 32 : return header;
266 : };
267 :
268 52 : inline Elf64_Shdr GetSectionHeader(const uint8_t* const elfData, size_t dataSize, Elf64_Ehdr header, uint16_t index)
269 : {
270 52 : if (index >= header.e_shnum) {
271 : throw std::invalid_argument(
272 8 : "Invalid section index, get index: " + std::to_string(index) +
273 12 : ", but section number is: " + std::to_string(header.e_shnum));
274 : }
275 48 : size_t shOffset = header.e_shoff + index * header.e_shentsize;
276 48 : if (shOffset + sizeof(Elf64_Shdr) > dataSize) {
277 4 : throw std::invalid_argument("Data size is to small for parse section header[" + std::to_string(index) + "]");
278 : }
279 44 : const uint8_t* data = elfData + shOffset;
280 : Elf64_Shdr shdr;
281 :
282 44 : shdr.sh_name = static_cast<uint32_t>(GetByte(data, 4));
283 44 : data += 4;
284 44 : shdr.sh_type = static_cast<uint32_t>(GetByte(data, 4));
285 44 : data += 4;
286 44 : shdr.sh_flags = GetByte(data, 8);
287 44 : data += 8;
288 44 : shdr.sh_addr = GetByte(data, 8);
289 44 : data += 8;
290 44 : shdr.sh_offset = GetByte(data, 8);
291 44 : data += 8;
292 44 : shdr.sh_size = GetByte(data, 4);
293 44 : data += 8;
294 44 : shdr.sh_link = static_cast<uint32_t>(GetByte(data, 4));
295 44 : data += 4;
296 44 : shdr.sh_info = static_cast<uint32_t>(GetByte(data, 4));
297 44 : data += 4;
298 44 : shdr.sh_addralign = GetByte(data, 8);
299 44 : data += 8;
300 44 : shdr.sh_entsize = GetByte(data, 8);
301 :
302 44 : return shdr;
303 : };
304 :
305 32 : inline ElfKernelInfo GetKernelInfo(const uint8_t* const elfData, size_t dataSize, Elf64_Shdr kernelMetaSectionHead)
306 : {
307 32 : uint64_t remainLen = kernelMetaSectionHead.sh_size;
308 32 : if (remainLen + kernelMetaSectionHead.sh_offset > dataSize) {
309 4 : throw std::invalid_argument("Data size is to small for parse kernel meta section");
310 : }
311 28 : const uint8_t* curData = elfData + kernelMetaSectionHead.sh_offset;
312 28 : ElfKernelInfo kernelInfo;
313 56 : while (remainLen > sizeof(ElfTlvHead)) {
314 32 : const ElfTlvHead* tlvHead = reinterpret_cast<const ElfTlvHead*>(curData);
315 : const uint16_t tlvType =
316 32 : static_cast<uint16_t>(GetByte(reinterpret_cast<const uint8_t*>(&(tlvHead->type)), sizeof(uint16_t)));
317 : const uint16_t tlvLength =
318 32 : static_cast<uint16_t>(GetByte(reinterpret_cast<const uint8_t*>(&(tlvHead->length)), sizeof(uint16_t)));
319 32 : if ((sizeof(ElfTlvHead) + tlvLength) > remainLen) {
320 4 : throw std::invalid_argument("Invalid TLV length in kernel meta section");
321 : }
322 :
323 28 : if (tlvType == FUNC_META_TYPE_KERNEL_TYPE) {
324 24 : if (tlvLength != sizeof(uint32_t)) {
325 0 : throw std::invalid_argument("Invalid kernel type length in kernel meta section");
326 : }
327 24 : kernelInfo.kernelType = static_cast<uint32_t>(
328 24 : GetByte(reinterpret_cast<const uint8_t*>(curData + sizeof(ElfTlvHead)), sizeof(uint32_t)));
329 4 : } else if (tlvType == FUNC_META_TYPE_MIX_TASK_RATION) {
330 4 : if (tlvLength != sizeof(uint16_t) * 2) {
331 0 : throw std::invalid_argument("Invalid mix task ration length in kernel meta section");
332 : }
333 4 : kernelInfo.aicRation = static_cast<uint16_t>(
334 4 : GetByte(reinterpret_cast<const uint8_t*>(curData + sizeof(ElfTlvHead)), sizeof(uint16_t)));
335 4 : kernelInfo.aivRation = static_cast<uint16_t>(GetByte(
336 : reinterpret_cast<const uint8_t*>(curData + sizeof(ElfTlvHead) + sizeof(uint16_t)), sizeof(uint16_t)));
337 : }
338 28 : curData += sizeof(ElfTlvHead) + tlvLength;
339 28 : remainLen = remainLen - (sizeof(ElfTlvHead) + tlvLength);
340 : }
341 24 : return kernelInfo;
342 : }
343 :
344 48 : inline KernelMode ToKernelMode(ElfKernelInfo kernelInfo)
345 : {
346 48 : if (kernelInfo.kernelType == K_TYPE_INVALID) {
347 4 : throw std::invalid_argument("get invalid kernel type");
348 : }
349 44 : if (kernelInfo.kernelType == K_TYPE_MIX_AIC_MAIN) {
350 12 : if (kernelInfo.aicRation == 1 && kernelInfo.aivRation == 0) {
351 4 : return KernelMode::AIC_MODE;
352 8 : } else if (kernelInfo.aicRation == 1 && kernelInfo.aivRation == 1) {
353 4 : return KernelMode::MIX_AIC_1_1; // MIX_AIC_1_1
354 4 : } else if (kernelInfo.aicRation == 1 && kernelInfo.aivRation == 2) { // aic num 1, aiv num 2
355 4 : return KernelMode::MIX_MODE; // MIX_MODE
356 : }
357 32 : } else if (kernelInfo.kernelType == K_TYPE_AIC || kernelInfo.kernelType == K_TYPE_AIC_ROLLBACK) {
358 20 : return KernelMode::AIC_MODE; // => AIC_MODE
359 12 : } else if (
360 12 : kernelInfo.kernelType == K_TYPE_AIV || kernelInfo.kernelType == K_TYPE_AIV_ROLLBACK ||
361 0 : kernelInfo.kernelType == K_TYPE_MIX_AIV_MAIN) {
362 12 : return KernelMode::AIV_MODE; // AIV_MODE
363 : }
364 0 : return KernelMode::MIX_MODE;
365 : }
366 :
367 : // Extract kernel name from section name by stripping .ascend.meta. prefix and _mix_aiv/_mix_aic postfix
368 : // Returns empty string if sectionName is not a kernel meta section
369 60 : inline std::string ExtractKernelName(const std::string& sectionName)
370 : {
371 60 : if (sectionName.length() > PREFIX_LEN && sectionName.compare(0, PREFIX_LEN, KERNEL_SECTION_NAME_PREFIX) == 0) {
372 32 : size_t kernelNameLen = sectionName.length() - PREFIX_LEN;
373 32 : if (kernelNameLen > MIX_SUFFIX_LEN) {
374 28 : size_t suffixPos = sectionName.length() - MIX_SUFFIX_LEN;
375 48 : if (sectionName.compare(suffixPos, MIX_SUFFIX_LEN, KERNEL_MIX_AIV_POSTFIX) == 0 ||
376 20 : sectionName.compare(suffixPos, MIX_SUFFIX_LEN, KERNEL_MIX_AIC_POSTFIX) == 0) {
377 16 : kernelNameLen -= MIX_SUFFIX_LEN;
378 : }
379 : }
380 32 : return sectionName.substr(PREFIX_LEN, kernelNameLen);
381 : }
382 56 : return "";
383 : }
384 :
385 12 : inline void ParseKernelSections(
386 : const uint8_t* const elfData, size_t dataSize, Elf64_Ehdr header, Elf64_Shdr shStrTabHdr)
387 : {
388 12 : const uint8_t* shStrTab = elfData + shStrTabHdr.sh_offset;
389 12 : if (shStrTabHdr.sh_offset + shStrTabHdr.sh_size > dataSize) {
390 0 : throw std::invalid_argument("Data size is to small for parse section header string table");
391 : }
392 40 : for (int i = 0; i < header.e_shnum; ++i) {
393 28 : Elf64_Shdr shdr = GetSectionHeader(elfData, dataSize, header, i);
394 28 : std::string sectionName(reinterpret_cast<const char*>(shStrTab) + shdr.sh_name);
395 28 : std::string kernelName = ExtractKernelName(sectionName);
396 28 : if (!kernelName.empty()) {
397 : try {
398 16 : ElfKernelInfo kernelInfo = GetKernelInfo(elfData, dataSize, shdr);
399 16 : KernelMode kernelMode = ToKernelMode(kernelInfo);
400 16 : KernelModeRegister::GetInstance().Register(kernelName, kernelMode);
401 0 : } catch (std::invalid_argument& e) {
402 0 : throw std::invalid_argument("Failed to get kernel mode from section " + sectionName + ": " + e.what());
403 0 : }
404 : }
405 28 : }
406 12 : }
407 :
408 16 : inline void RegisterKernelElf(const uint8_t* const elfData, size_t dataSize)
409 : {
410 16 : Elf64_Ehdr header = ParseElfHeader(elfData, dataSize);
411 12 : Elf64_Shdr shStrTabHdr = GetSectionHeader(elfData, dataSize, header, header.e_shstrndx);
412 12 : ParseKernelSections(elfData, dataSize, header, shStrTabHdr);
413 12 : }
414 :
415 : } // namespace AscendC
416 : #endif // KERNEL_ELF_PARSER_H
|