Line data Source code
1 : /**
2 : * Copyright (c) 2025 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 :
11 : #include "scd_elf.h"
12 : #include <elf.h>
13 : #include <link.h>
14 : #include <sys/types.h>
15 : #include "adiag_utils.h"
16 : #include "scd_log.h"
17 : #include "scd_dwarf.h"
18 :
19 : #define SCD_ELF_FDE_ENTRY_SIZE 8U
20 : #define SCD_ELF_SH_NAME_LENGTH 128U
21 : #define SCD_UINTPTR_MAX ((uintptr_t)(-1))
22 : #define SCD_PF_X ((uint32_t)(PF_X))
23 :
24 18 : uintptr_t ScdElfGetLoadBias(const ScdElf* elf) { return elf->loadBias; }
25 :
26 4 : size_t ScdElfGetFdeNum(const ScdElf* elf) { return elf->ehFrameHdrSize / SCD_ELF_FDE_ENTRY_SIZE; }
27 :
28 : /**
29 : * @brief get function info based on pc
30 : * @param [in] elf: elf info
31 : * @param [in] pc: program counter
32 : * @param [out] funcName: function name
33 : * @param [in] len: function name buffer length
34 : * @param [out] funcOffset: function offset in file
35 : * @return NA
36 : */
37 10 : void ScdElfGetFunctionInfo(ScdElf* elf, uintptr_t pc, char* funcName, size_t len, size_t* funcOffset)
38 : {
39 20 : SCD_CHK_PTR_ACTION(elf, return);
40 10 : SCD_CHK_PTR_ACTION(funcName, return);
41 10 : SCD_CHK_PTR_ACTION(funcOffset, return);
42 10 : struct ListHead* pos = NULL;
43 10 : struct AdiagListNode* node = NULL;
44 10 : ScdDlSymbol* symbols = NULL;
45 10 : char buf[SCD_FUNC_NAME_LENGTH] = {0};
46 : // traverse all symbol section to find function symbol
47 10 : LIST_FOR_EACH(pos, &elf->symbolList.list)
48 : {
49 10 : node = LIST_ENTRY(pos, struct AdiagListNode, list);
50 10 : if (node == NULL) {
51 0 : return;
52 : }
53 10 : symbols = (ScdDlSymbol*)node->data;
54 6570 : for (size_t offset = symbols->symOffset; offset < symbols->symEnd; offset += symbols->symEntrySize) {
55 6570 : ElfW(Sym)* sym = (ElfW(Sym)*)ScdMemoryGetAddr(elf->memory, offset, sizeof(ElfW(Sym)));
56 6570 : if (sym == NULL) {
57 0 : break;
58 : }
59 : // filter function symbol
60 6570 : if ((sym->st_shndx == SHN_UNDEF) || (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)) {
61 1070 : continue;
62 : }
63 :
64 5500 : size_t startOffset = sym->st_value + elf->memory->data + elf->loadBias;
65 5500 : size_t endOffset = startOffset + sym->st_size;
66 5500 : if ((pc < startOffset) || (pc >= endOffset)) {
67 5490 : continue;
68 : }
69 : // st_name is the offset of symbol name in string table
70 10 : size_t strOffset = symbols->strOffset + sym->st_name;
71 10 : if (strOffset >= symbols->strEnd) {
72 0 : continue;
73 : }
74 :
75 10 : if (ScdMemoryReadString(
76 10 : elf->memory, strOffset, buf, SCD_MIN(SCD_FUNC_NAME_LENGTH, symbols->strEnd - strOffset)) == 0) {
77 0 : continue;
78 : }
79 :
80 10 : errno_t err = memcpy_s(funcName, len, buf, SCD_MIN(strlen(buf) + 1U, len));
81 10 : if (err != EOK) {
82 2 : SCD_DLOG_ERR("memcpy_s failed, err=%d.", (int32_t)err);
83 2 : return;
84 : }
85 8 : *funcOffset = pc - startOffset;
86 8 : return;
87 : }
88 : }
89 : }
90 :
91 4 : TraStatus ScdElfStep(ScdElf* elf, uintptr_t pc, ScdRegs* regs, bool isFirstStack)
92 : {
93 4 : if (elf->ehFrameHdrOffset == 0) {
94 0 : STACKTRACE_LOG_RUN("elf of pc(0x%llx) does not have eh_frame_hdr section.", pc);
95 0 : return TRACE_FAILURE;
96 : }
97 4 : ScdDwarf dwarf = {0};
98 4 : const ScdDwarfStepArgs args = {ScdRegsGetSp(regs), SCD_UINTPTR_MAX, isFirstStack};
99 4 : dwarf.memory = elf->memory;
100 4 : dwarf.ehFrameHdrOffset = elf->ehFrameHdrOffset;
101 4 : dwarf.loadBias = elf->loadBias;
102 4 : SCD_DLOG_DBG("dwarf.ehFrameHdrOffset = 0x%llx, dwarf.loadBias = 0x%llx.", dwarf.ehFrameHdrOffset, dwarf.loadBias);
103 4 : dwarf.fdeCount = ScdElfGetFdeNum(elf);
104 : uintptr_t nextPc;
105 : // when the address read from the elf file is compared with the address in the memory, bias offset is required
106 : // pc is the address in the memory, so it needs to be subtracted by the bias offset
107 4 : TraStatus ret = ScdDwarfStep(&dwarf, regs, &args, pc - dwarf.loadBias, &nextPc);
108 4 : ScdRegsSetPc(regs, nextPc);
109 4 : return ret;
110 : }
111 :
112 24 : STATIC TraStatus ScdElfParseProgramHeaders(ScdElf* elf, ElfW(Ehdr) * ehdr)
113 : {
114 24 : size_t phSize = (size_t)ehdr->e_phnum * (size_t)ehdr->e_phentsize;
115 108 : for (size_t i = 0; i < phSize; i += ehdr->e_phentsize) {
116 102 : ElfW(Phdr)* phdr = (ElfW(Phdr)*)ScdMemoryGetAddr(elf->memory, ehdr->e_phoff + i, sizeof(ElfW(Phdr)));
117 102 : SCD_CHK_PTR_ACTION(phdr, return TRACE_FAILURE);
118 :
119 84 : switch (phdr->p_type) {
120 24 : case PT_LOAD:
121 24 : if ((phdr->p_flags & SCD_PF_X) == 0) {
122 18 : continue;
123 : }
124 6 : if (elf->loadBiasSave == false) {
125 6 : elf->loadBias = phdr->p_vaddr - phdr->p_offset;
126 6 : elf->loadBiasSave = true;
127 : }
128 6 : break;
129 6 : case PT_GNU_EH_FRAME:
130 6 : elf->ehFrameHdrOffset = phdr->p_offset;
131 6 : elf->ehFrameHdrSize = phdr->p_memsz;
132 6 : elf->ehFrameHdrLoadBias = phdr->p_vaddr - phdr->p_offset;
133 6 : break;
134 6 : case PT_DYNAMIC:
135 6 : elf->dynamicOffset = phdr->p_offset;
136 6 : elf->dynamicSize = phdr->p_memsz;
137 6 : break;
138 48 : default:
139 48 : break;
140 : }
141 : }
142 6 : return TRACE_SUCCESS;
143 : }
144 :
145 6 : STATIC TraStatus ScdElfGetStringSectionHeader(ScdElf* elf, ElfW(Ehdr) * ehdr, size_t* secOffset, size_t* secSize)
146 : {
147 6 : ElfW(Shdr)* shdr = ScdMemoryGetAddr(
148 6 : elf->memory, ehdr->e_shoff + (uintptr_t)ehdr->e_shstrndx * (uintptr_t)ehdr->e_shentsize, sizeof(ElfW(Shdr)));
149 6 : if (shdr == NULL) {
150 0 : SCD_DLOG_ERR("read memory failed, get string table section header failed.");
151 0 : return TRACE_FAILURE;
152 : }
153 6 : *secOffset = shdr->sh_offset;
154 6 : *secSize = shdr->sh_size;
155 6 : return TRACE_SUCCESS;
156 : }
157 :
158 24 : STATIC void ScdElfHandleNote(ScdElf* elf, ElfW(Shdr) * shdr, size_t secOffset, size_t secSize)
159 : {
160 24 : if (shdr->sh_name >= secSize) {
161 0 : return;
162 : }
163 24 : char name[SCD_ELF_SH_NAME_LENGTH] = {0};
164 24 : if (ScdMemoryReadString(elf->memory, secOffset + shdr->sh_name, name, sizeof(name)) == 0) {
165 0 : return;
166 : }
167 24 : if (strcmp(name, ".note.gnu.build-id") == 0) {
168 6 : elf->buildIdOffset = shdr->sh_offset;
169 6 : elf->buildIdSize = shdr->sh_size;
170 : }
171 24 : return;
172 : }
173 :
174 6 : STATIC TraStatus ScdElfHandleDynsym(ScdElf* elf, ElfW(Ehdr) * ehdr, ElfW(Shdr) * shdr)
175 : {
176 6 : if (shdr->sh_link >= ehdr->e_shnum) {
177 0 : return TRACE_SUCCESS;
178 : }
179 6 : ElfW(Shdr)* strShdr = (ElfW(Shdr)*)ScdMemoryGetAddr(
180 6 : elf->memory, ehdr->e_shoff + (uintptr_t)shdr->sh_link * (uintptr_t)ehdr->e_shentsize, sizeof(ElfW(Shdr)));
181 6 : SCD_CHK_PTR_ACTION(strShdr, return TRACE_FAILURE);
182 :
183 6 : if (strShdr->sh_type != SHT_STRTAB) {
184 0 : return TRACE_SUCCESS;
185 : }
186 6 : ScdDlSymbol* symbol = (ScdDlSymbol*)AdiagMalloc(sizeof(ScdDlSymbol));
187 6 : SCD_CHK_PTR_ACTION(symbol, return TRACE_FAILURE);
188 :
189 6 : symbol->symOffset = shdr->sh_offset;
190 6 : symbol->symEnd = shdr->sh_offset + shdr->sh_size;
191 6 : symbol->symEntrySize = shdr->sh_entsize;
192 6 : symbol->strOffset = strShdr->sh_offset;
193 6 : symbol->strEnd = strShdr->sh_offset + strShdr->sh_size;
194 :
195 6 : AdiagStatus adiagRet = AdiagListInsert(&elf->symbolList, symbol);
196 6 : if (adiagRet != ADIAG_SUCCESS) {
197 0 : SCD_DLOG_ERR("add symbol to list failed.");
198 0 : ADIAG_SAFE_FREE(symbol);
199 0 : return TRACE_FAILURE;
200 : }
201 6 : return TRACE_SUCCESS;
202 : }
203 :
204 264 : STATIC TraStatus ScdElfHandleProgbits(ScdElf* elf, ElfW(Shdr) * shdr, size_t secOffset, size_t secSize)
205 : {
206 264 : if (shdr->sh_name >= secSize) {
207 0 : return TRACE_SUCCESS;
208 : }
209 264 : char name[SCD_ELF_SH_NAME_LENGTH] = {0};
210 264 : if (ScdMemoryReadString(elf->memory, secOffset + shdr->sh_name, name, sizeof(name)) == 0) {
211 0 : SCD_DLOG_ERR("read memory failed, get SHT_PROGBITS section failed.");
212 0 : return TRACE_FAILURE;
213 : }
214 264 : if (strcmp(name, ".eh_frame") == 0) {
215 6 : elf->ehFrameOffset = shdr->sh_offset;
216 6 : elf->ehFrameSize = shdr->sh_size;
217 6 : elf->ehFrameLoadBias = shdr->sh_addr - shdr->sh_offset;
218 258 : } else if (strcmp(name, ".eh_frame_hdr") == 0 && elf->ehFrameHdrOffset == 0) {
219 0 : elf->ehFrameHdrOffset = shdr->sh_offset;
220 0 : elf->ehFrameHdrSize = shdr->sh_size;
221 0 : elf->ehFrameHdrLoadBias = shdr->sh_addr - shdr->sh_offset;
222 : } else {
223 : ;
224 : }
225 264 : return TRACE_SUCCESS;
226 : }
227 :
228 6 : STATIC TraStatus ScdElfParseSectionHeaders(ScdElf* elf, ElfW(Ehdr) * ehdr)
229 : {
230 6 : size_t secOffset = 0;
231 6 : size_t secSize = 0;
232 : // get section header entry that contains the section names
233 6 : if (ehdr->e_shstrndx < ehdr->e_shnum) {
234 6 : if (ScdElfGetStringSectionHeader(elf, ehdr, &secOffset, &secSize) != TRACE_SUCCESS) {
235 0 : return TRACE_FAILURE;
236 : }
237 : }
238 6 : size_t ehSize = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize;
239 384 : for (size_t i = ehdr->e_shentsize; i < ehSize; i += ehdr->e_shentsize) {
240 378 : ElfW(Shdr)* shdr = (ElfW(Shdr)*)ScdMemoryGetAddr(elf->memory, ehdr->e_shoff + i, sizeof(ElfW(Shdr)));
241 378 : if (shdr == NULL) {
242 0 : SCD_DLOG_ERR("read memory failed, get section[%zu] failed.", i);
243 0 : return TRACE_FAILURE;
244 : }
245 378 : switch (shdr->sh_type) {
246 24 : case SHT_NOTE: {
247 24 : ScdElfHandleNote(elf, shdr, secOffset, secSize);
248 24 : break;
249 : }
250 6 : case SHT_SYMTAB:
251 : case SHT_DYNSYM: {
252 6 : if (ScdElfHandleDynsym(elf, ehdr, shdr) != TRACE_SUCCESS) {
253 0 : return TRACE_FAILURE;
254 : }
255 6 : break;
256 : }
257 264 : case SHT_PROGBITS: {
258 264 : if (ScdElfHandleProgbits(elf, shdr, secOffset, secSize) != TRACE_SUCCESS) {
259 0 : return TRACE_FAILURE;
260 : }
261 264 : break;
262 : }
263 84 : default:
264 84 : break;
265 : }
266 : }
267 6 : return TRACE_SUCCESS;
268 : }
269 :
270 42 : STATIC TraStatus ScdElfCheckValid(ElfW(Ehdr) * ehdr)
271 : {
272 : // check magic
273 42 : if (strncmp((const char*)ehdr->e_ident, (const char*)ELFMAG, SELFMAG) != 0) {
274 18 : SCD_DLOG_ERR("elf valid check failed, ehdr->e_ident = %s.", ehdr->e_ident);
275 18 : return TRACE_FAILURE;
276 : }
277 :
278 24 : return TRACE_SUCCESS;
279 : }
280 :
281 60 : STATIC TraStatus ScdElfParse(ScdElf* elf)
282 : {
283 : // get ELF header
284 60 : ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*)ScdMemoryGetAddr(elf->memory, 0, sizeof(ElfW(Ehdr)));
285 60 : SCD_CHK_PTR_ACTION(ehdr, return TRACE_FAILURE);
286 :
287 42 : TraStatus ret = ScdElfCheckValid(ehdr);
288 42 : if (ret != TRACE_SUCCESS) {
289 18 : return TRACE_FAILURE;
290 : }
291 :
292 24 : ret = ScdElfParseProgramHeaders(elf, ehdr);
293 24 : if (ret != TRACE_SUCCESS) {
294 18 : return TRACE_FAILURE;
295 : }
296 :
297 6 : ret = ScdElfParseSectionHeaders(elf, ehdr);
298 6 : if (ret != TRACE_SUCCESS) {
299 0 : return TRACE_FAILURE;
300 : }
301 :
302 6 : return TRACE_SUCCESS;
303 : }
304 :
305 : /**
306 : * @brief load elf info from memory
307 : * @param [in] elf: elf info
308 : * @param [in] pid: process id
309 : * @param [in] memory: dynamic library memory
310 : * @return TraStatus
311 : */
312 60 : TraStatus ScdElfLoad(ScdElf* elf, int32_t pid, ScdMemory* memory)
313 : {
314 60 : SCD_CHK_PTR_ACTION(elf, return TRACE_FAILURE);
315 60 : elf->pid = pid;
316 60 : elf->memory = memory;
317 :
318 60 : TraStatus ret = ScdElfParse(elf);
319 60 : if (ret != TRACE_SUCCESS) {
320 54 : return TRACE_FAILURE;
321 : }
322 :
323 6 : return TRACE_SUCCESS;
324 : }
325 :
326 : /**
327 : * @brief init elf info
328 : * @param [in] elf: elf info
329 : * @return TraStatus
330 : */
331 1183 : TraStatus ScdElfInit(ScdElf* elf)
332 : {
333 1183 : (void)memset_s(elf, sizeof(ScdElf), 0, sizeof(ScdElf));
334 1183 : elf->loadBiasSave = false;
335 1183 : return AdiagListInit(&elf->symbolList);
336 : }
337 :
338 : /**
339 : * @brief uinit elf info
340 : * @param [in] elf: elf info
341 : * @return NA
342 : */
343 24 : void ScdElfUninit(ScdElf* elf)
344 : {
345 24 : ScdDlSymbol* node = (ScdDlSymbol*)AdiagListTakeOut(&elf->symbolList);
346 30 : while (node != NULL) {
347 6 : ADIAG_SAFE_FREE(node);
348 6 : node = (ScdDlSymbol*)AdiagListTakeOut(&elf->symbolList);
349 : }
350 24 : }
|