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_dwarf.h"
12 : #include "scd_log.h"
13 : #include "stacktrace_unwind.h"
14 : #include "stacktrace_unwind_reg.h"
15 : #include "stacktrace_unwind_instr.h"
16 : #include "stacktrace_unwind_inner.h"
17 : #include "scd_maps.h"
18 :
19 : #define TRACE_MAX_AUG_STR_LEN 0x08U // max length of augmentation string in CIE
20 : // .eh_frame_hdr section
21 : typedef struct TraceUnwindEhFrameHdrInfo {
22 : uint8_t version; // eh_frame_hdr version, must be 1
23 : uint8_t ehframeptrEnc; // the encode type of eh_frame_hdr
24 : uint8_t fdeCountEnc; // the encode type of FDE count
25 : uint8_t tabEnc; // the encode type of binary search table
26 : uint8_t searchTblFlag; // the flag of table present 1:present 0:no present
27 : uint8_t reserved[3]; // the reserved
28 : uintptr_t frameAddr; // eh_frame section start addr
29 : size_t fdeCount; // FDE count in binary search table
30 : uintptr_t tblStatAddr; // the binary search table addr
31 : } TraceUnwindEhFrameHdrInfo;
32 : // FDE entry in the binary search table
33 : typedef struct FdeEntry {
34 : int32_t initLocOffset; // location addr offset relative to eh_frame_hdr addr
35 : int32_t fdeTableOffset;
36 : } FdeEntry;
37 :
38 : typedef struct FDECtrlBlock {
39 : uintptr_t funcStart;
40 : uintptr_t unwindEntryAddr;
41 : } FDECtrlBlock;
42 : // the first few filed of an FDE
43 : typedef struct TraceEhFrameFde {
44 : uint32_t fdeLengh;
45 : uint32_t cieOffset;
46 : uint8_t pcBegin[16];
47 : } TraceEhFrameFde;
48 : // the first few filed of an CIE
49 : typedef struct TraceEhFrameCie {
50 : uint32_t cieLengh; // the length of CIE except itself
51 : uint32_t cieId; // the ID of CIE, must be 0
52 : uint8_t version; // the version of CIE
53 : int8_t augmentation[15]; // the string of Augmentation
54 : } TraceEhFrameCie;
55 :
56 : /**
57 : * @brief get cie address by offset
58 : * @param [in] offset: offset address
59 : * @return cie address
60 : */
61 48 : STATIC INLINE uintptr_t GetCieAddressByOffset(uint32_t *offset)
62 : {
63 48 : return (uintptr_t)(offset) - (*offset);
64 : }
65 :
66 22 : STATIC INLINE uintptr_t GetAddrByOffset(uintptr_t addr, intptr_t offset)
67 : {
68 22 : if (offset >= 0) {
69 22 : return addr + (uintptr_t)offset;
70 : }
71 0 : uintptr_t absOffset = (uintptr_t)(-offset);
72 0 : if (absOffset > addr) {
73 0 : return 0;
74 : }
75 0 : return addr - absOffset;
76 : }
77 :
78 : /**
79 : * @brief extract eh_frame_hdr section
80 : *
81 : * @param [in] ehFrameHdrAddr eh_frame_hdr addr
82 : * @param [out] ehFrameHdrInfo eh_frame_hdr information
83 : *
84 : * @return : !=0 failure; ==0 success
85 : */
86 32 : STATIC TraStatus TraceParseFrameHdrAddr(ScdDwarf *dwarf, TraceUnwindEhFrameHdrInfo *ehFrameHdrInfo)
87 : {
88 32 : const uint8_t *addr = (uint8_t *)(dwarf->memory->data + dwarf->ehFrameHdrOffset);
89 32 : ehFrameHdrInfo->version = *addr;
90 32 : addr++;
91 32 : ehFrameHdrInfo->ehframeptrEnc = *addr;
92 32 : addr++;
93 32 : ehFrameHdrInfo->fdeCountEnc = *addr;
94 32 : addr++;
95 32 : ehFrameHdrInfo->tabEnc = *addr;
96 32 : addr++;
97 32 : if (ehFrameHdrInfo->version != 1) {
98 16 : SCD_DLOG_ERR("[ERROR] invalid version %d", ehFrameHdrInfo->version);
99 16 : return TRACE_FAILURE;
100 : }
101 16 : addr = TraceReadEncodeValue(dwarf, ehFrameHdrInfo->ehframeptrEnc, addr, &ehFrameHdrInfo->frameAddr);
102 16 : if (addr == NULL) {
103 16 : SCD_DLOG_ERR("read encode value frame addr failed");
104 16 : return TRACE_FAILURE;
105 : }
106 :
107 : uintptr_t fdeItem;
108 0 : if (TRACE_IS_HDR_TBL_PRESENT(ehFrameHdrInfo)) {
109 0 : addr = TraceReadEncodeValue(dwarf, ehFrameHdrInfo->fdeCountEnc, addr, &fdeItem);
110 0 : if ((addr == NULL) || (fdeItem == 0)) {
111 0 : SCD_DLOG_ERR("read encode value fde item failed");
112 0 : return TRACE_FAILURE;
113 : }
114 0 : ehFrameHdrInfo->fdeCount = (size_t)fdeItem;
115 0 : ehFrameHdrInfo->tblStatAddr = (uintptr_t)addr;
116 0 : ehFrameHdrInfo->searchTblFlag = 1;
117 : } else {
118 0 : ehFrameHdrInfo->searchTblFlag = 0;
119 : }
120 0 : SCD_DLOG_INF("[eh_frame_hdr info] version: %u, ehframeptrEnc: %u, fdeCountEnc: %u, tabEnc: %u, searchTblFlag: %u",
121 : ehFrameHdrInfo->version, ehFrameHdrInfo->ehframeptrEnc, ehFrameHdrInfo->fdeCountEnc,
122 : ehFrameHdrInfo->tabEnc, ehFrameHdrInfo->searchTblFlag);
123 0 : return TRACE_SUCCESS;
124 : }
125 :
126 48 : STATIC const uint8_t *TraceParseCIEAug(ScdDwarf *dwarf, const char *augStr, const uint8_t *instr, uint32_t idx,
127 : uintptr_t *retIns, TraceFrameRegStateInfo *frameRegState)
128 : {
129 : size_t encSize;
130 48 : uintptr_t retBegIns = 0;
131 : uintptr_t ulebTmp;
132 48 : const uint8_t *tmpInstr = instr;
133 48 : uint32_t indexTmp = idx;
134 :
135 : /* Check for the presence of 'z' in the augmentation string, which indicates
136 : * the presence of additional information that affects the instruction range */
137 48 : if (augStr[indexTmp] == 'z') {
138 0 : tmpInstr = TraceReadUleb128(dwarf, tmpInstr, &ulebTmp);
139 0 : SCD_CHK_EXPR_ACTION(tmpInstr == NULL, return NULL, "read uleb128 failed");
140 0 : retBegIns = (uintptr_t)tmpInstr + ulebTmp;
141 0 : frameRegState->optFlag = 1;
142 0 : indexTmp++;
143 : }
144 :
145 64 : while (augStr[indexTmp] != '\0') {
146 : // 'L' indicates a local static anonymous namespace, not processed here
147 32 : if (augStr[indexTmp] == 'L') {
148 0 : indexTmp++;
149 0 : tmpInstr++;
150 32 : } else if (augStr[indexTmp] == 'P') {
151 : // 'P' indicates a procedure linkage table (PLT) entry, not processed here
152 0 : uint8_t ucPEnc = *tmpInstr;
153 0 : tmpInstr++;
154 0 : encSize = TraceEncValueSizeGet(ucPEnc);
155 0 : SCD_CHK_EXPR_ACTION(encSize == 0, return NULL, "get enc value size failed");
156 0 : tmpInstr += encSize;
157 0 : TRACE_UNWIND_PARSE_ADDR_CHECK_OR_RETURN(tmpInstr);
158 0 : indexTmp++;
159 32 : } else if (augStr[indexTmp] == 'R') { // 'R' indicates a range,
160 16 : frameRegState->fdeEnc = *tmpInstr;
161 16 : tmpInstr++;
162 16 : frameRegState->flag = 1;
163 16 : indexTmp++;
164 16 : } else if (*(const char *)tmpInstr == 'S') { // 'S' indicates a signal frame
165 0 : frameRegState->sigFrmFlag = 1;
166 0 : indexTmp++;
167 : } else {
168 : /* If an unrecognized augmentation string is encountered, and there is no 'z',
169 : * the function returns early, indicating that the CST contains an unhandled string */
170 16 : if (retBegIns == 0) {
171 16 : return NULL;
172 : } else {
173 0 : break;
174 : }
175 : }
176 : }
177 32 : *retIns = retBegIns;
178 32 : return tmpInstr;
179 : }
180 :
181 64 : STATIC TraStatus TraceParseCie(ScdDwarf *dwarf, uintptr_t cieAddr, TraceAddrRange* initIns, TraceFrameRegStateInfo *frameRegState)
182 : {
183 64 : TraceEhFrameCie *ehFrameCIEHdr = (TraceEhFrameCie *)cieAddr;
184 64 : const uint8_t *skipAug = NULL;
185 64 : char ucAugStr[TRACE_MAX_AUG_STR_LEN + 1U] = {0};
186 64 : uint32_t idx = 0;
187 64 : skipAug = (const uint8_t *)ehFrameCIEHdr->augmentation;
188 128 : for (size_t i = 0; i < TRACE_MAX_AUG_STR_LEN; i++) {
189 128 : size_t size = TraceReadBytes(dwarf, &skipAug, &ucAugStr[i], sizeof(uint8_t));
190 128 : SCD_CHK_EXPR_ACTION(size == 0, return TRACE_FAILURE, "read byte failed");
191 112 : if (ucAugStr[i] == '\0') {
192 48 : break;
193 : }
194 : }
195 48 : if (ucAugStr[0] == 'e' && ucAugStr[1] == 'h') {
196 16 : skipAug += sizeof(void *);
197 16 : idx += 2U; // add 2 for size "eh"
198 : }
199 : uintptr_t ulebTmp;
200 : intptr_t svLebTmp;
201 48 : skipAug = TraceReadUleb128(dwarf, skipAug, &ulebTmp);
202 48 : SCD_CHK_EXPR_ACTION(skipAug == NULL, return TRACE_FAILURE, "read uleb128 failed");
203 48 : frameRegState->codeAlign = ulebTmp;
204 48 : skipAug = TraceReadLeb128(dwarf, skipAug, &svLebTmp);
205 48 : SCD_CHK_EXPR_ACTION(skipAug == NULL, return TRACE_FAILURE, "read leb128 failed");
206 48 : frameRegState->dataAlign = svLebTmp;
207 48 : if (ehFrameCIEHdr->version == 1) {
208 0 : ulebTmp = (uintptr_t)(*(uint8_t *)(uintptr_t)skipAug);
209 0 : skipAug++;
210 : } else {
211 48 : skipAug = TraceReadUleb128(dwarf, skipAug, &ulebTmp);
212 48 : SCD_CHK_EXPR_ACTION(skipAug == NULL, return TRACE_FAILURE, "read uleb128 failed");
213 : }
214 48 : frameRegState->retColumn = ulebTmp;
215 48 : uintptr_t retIns = 0;
216 48 : skipAug = TraceParseCIEAug(dwarf, ucAugStr, skipAug, idx, &retIns, frameRegState);
217 48 : SCD_CHK_EXPR_ACTION(skipAug == NULL, return TRACE_FAILURE, "parse CIE aug failed");
218 :
219 : // When 'z' appears, the unwind instruction address can be directly obtained through the ret pointer.
220 32 : initIns->start = (retIns != 0) ? retIns : (uintptr_t)skipAug;
221 32 : initIns->end = (uintptr_t)(cieAddr + ehFrameCIEHdr->cieLengh + sizeof(uint32_t));
222 32 : SCD_DLOG_INF("[CIE info] addr : 0x%lx, Length : 0x%x, Extended cieId : %u, version : %u, initIns [0x%lx-0x%lx],"
223 : "codeAlign:0x%lx, dataAlign:%ld,",
224 : cieAddr, ehFrameCIEHdr->cieLengh, ehFrameCIEHdr->cieId, ehFrameCIEHdr->version, initIns->start, initIns->end,
225 : frameRegState->codeAlign, frameRegState->dataAlign);
226 32 : return TRACE_SUCCESS;
227 : }
228 :
229 48 : STATIC TraStatus TraceParseFde(ScdDwarf *dwarf, uintptr_t fdeAddr, TraceFrameRegStateInfo *frameRegState, TraceAddrRange* initIns,
230 : TraceAddrRange* ins)
231 : {
232 : uintptr_t cieEntry;
233 48 : const uint8_t *addr = NULL;
234 : uintptr_t funStart;
235 : uintptr_t range;
236 48 : TraceEhFrameFde *pstFDEHeadInfo = (TraceEhFrameFde *)fdeAddr;
237 48 : cieEntry = GetCieAddressByOffset(&pstFDEHeadInfo->cieOffset);
238 48 : TraStatus ret = TraceParseCie(dwarf, cieEntry, initIns, frameRegState);
239 48 : if (ret != TRACE_SUCCESS) {
240 16 : SCD_DLOG_ERR("parse CIE failed, ret %d", ret);
241 16 : return ret;
242 : }
243 32 : addr = pstFDEHeadInfo->pcBegin;
244 32 : if (frameRegState->flag == 0) {
245 16 : funStart = *(uintptr_t *)(uintptr_t)addr;
246 16 : addr += sizeof(void *);
247 16 : range = *(uintptr_t *)(uintptr_t)addr;
248 16 : addr += sizeof(void *);
249 : } else {
250 : // 使用FDE编码解析PC Begin和PC Range
251 16 : uint8_t ucFdeEnc = frameRegState->fdeEnc;
252 16 : addr = TraceReadEncodeValue(dwarf, ucFdeEnc, addr, &funStart);
253 16 : if (addr == NULL) {
254 16 : SCD_DLOG_ERR("invalid addr");
255 16 : return TRACE_FAILURE;
256 : }
257 0 : addr = TraceReadEncodeValue(dwarf, TRACE_LOBIT4_ENCODE(ucFdeEnc), addr, (uintptr_t *)&range); /* 0x0f */
258 0 : if (addr == NULL) {
259 0 : SCD_DLOG_ERR("invalid addr");
260 0 : return TRACE_FAILURE;
261 : }
262 : }
263 16 : frameRegState->pc = funStart;
264 16 : frameRegState->range = range;
265 :
266 16 : uintptr_t ulebTmp = 0;
267 16 : if (frameRegState->optFlag == 1) {
268 0 : addr = TraceReadUleb128(dwarf, addr, &ulebTmp);
269 0 : SCD_CHK_EXPR_ACTION(addr == NULL, return TRACE_FAILURE, "read uleb128 failed");
270 : }
271 :
272 16 : ins->start = (uintptr_t)(addr + ulebTmp);
273 16 : ulebTmp = (uintptr_t)(fdeAddr + pstFDEHeadInfo->fdeLengh + sizeof(uint32_t));
274 16 : ins->end = ulebTmp;
275 16 : SCD_DLOG_INF("[FDE info] Length:0x%x,CIE Pointer:0x%x,pcBegin:0x%lx,funStart: 0x%lx,range: 0x%lx,ins [0x%lx-0x%lx]",
276 : pstFDEHeadInfo->fdeLengh, pstFDEHeadInfo->cieOffset, (uintptr_t)pstFDEHeadInfo->pcBegin,
277 : funStart, range, ulebTmp, ulebTmp + pstFDEHeadInfo->fdeLengh);
278 16 : return TRACE_SUCCESS;
279 : }
280 :
281 48 : STATIC void TraceGetCFAAddr(ScdDwarf *dwarf, TraceFrameRegStateInfo *frameRegState, ScdRegs *regs,
282 : const ScdDwarfStepArgs *args, uintptr_t *cfaAddrPtr)
283 : {
284 48 : uintptr_t cfaAddr = 0;
285 : intptr_t offset;
286 : uintptr_t regNum;
287 : uintptr_t insLen;
288 : uintptr_t expResult;
289 : uintptr_t storeReg;
290 48 : const uint8_t *expOpAddr = NULL;
291 :
292 48 : switch (frameRegState->frameStateInfo.cfaHow) {
293 0 : case VOS_CFA_REG_OFFSET:
294 0 : regNum = frameRegState->frameStateInfo.cfaReg;
295 0 : offset = frameRegState->frameStateInfo.cfaOffset;
296 0 : storeReg = regs->r[regNum & REG_VAILD_MASK];
297 0 : if (storeReg == 0) {
298 0 : return;
299 : } else {
300 0 : SCD_DLOG_INF("CFA Addr = reg[%ld]: %lx + offset : %ld", regNum, storeReg, offset);
301 0 : cfaAddr = storeReg + (uintptr_t)offset;
302 : }
303 0 : break;
304 32 : case VOS_CFA_EXP:
305 : /* expression 分支走dw_op指令解析流程 */
306 32 : expOpAddr = frameRegState->frameStateInfo.cfaExp;
307 32 : expOpAddr = TraceReadUleb128(dwarf, expOpAddr, &insLen);
308 32 : SCD_CHK_EXPR_ACTION(expOpAddr == NULL, return, "read uleb128 failed");
309 16 : (void)TraceStackOpExc(dwarf, expOpAddr, expOpAddr + insLen, regs, &expResult, 0, args);
310 16 : cfaAddr = expResult;
311 :
312 16 : break;
313 16 : default:
314 16 : break;
315 : }
316 32 : *cfaAddrPtr = cfaAddr;
317 32 : return;
318 : }
319 :
320 128 : STATIC TraStatus CallStackRegUpdate(ScdDwarf *dwarf, uint32_t idx, ScdRegs *pstCoreRegsOld, ScdRegs *coreRegs,
321 : uintptr_t cfaAddr, TraceStagRegInfo *pstRegInfo, const ScdDwarfStepArgs *args)
322 : {
323 : uintptr_t regNum;
324 128 : uintptr_t expResult = 0;
325 128 : const uint8_t *expOpAddr = NULL;
326 128 : uintptr_t insLen = 0;
327 : uintptr_t regAddr;
328 : size_t size;
329 : TraStatus ret;
330 128 : SCD_DLOG_DBG("CallStackRegUpdate reg[%d] %d", idx, pstRegInfo->regHow);
331 128 : switch (pstRegInfo->regHow) {
332 0 : case REG_SAVED_OFFSET:
333 0 : regAddr = GetAddrByOffset(cfaAddr, pstRegInfo->regLoc.offset);
334 0 : SCD_CHK_EXPR_ACTION(regAddr == 0, return TRACE_FAILURE,
335 : "invalid offset %llx", pstRegInfo->regLoc.offset);
336 : /* 当前使用eh_frame进行unwind推栈的有x86_64、arm64和ilp32,寄存器大小都为64位,因此使用VOS_UINT64取内容
337 : * ScdMemoryRead(NULL, ...) uses global handler (ScdMemoryRemoteRead via ptrace).
338 : * Safe here: this function is only called from dumper subprocess context
339 : * (ScdProcessDump -> ScdDwarfStep), where ptrace is available. */
340 0 : size = ScdMemoryRead(NULL, regAddr, &coreRegs->r[idx & REG_VAILD_MASK], sizeof(uint64_t));
341 0 : SCD_CHK_EXPR_ACTION(size == 0, return TRACE_FAILURE, "scd read memory failed 0x%llx", regAddr);
342 0 : SCD_DLOG_INF("REG_SAVED_OFFSET reg %u addr:%lx value:%lx offset : %ld",
343 : idx, regAddr, coreRegs->r[idx & REG_VAILD_MASK], pstRegInfo->regLoc.offset);
344 0 : break;
345 16 : case REG_SAVED_VAL_OFFSET:
346 16 : regAddr = GetAddrByOffset(cfaAddr, pstRegInfo->regLoc.offset);
347 16 : SCD_CHK_EXPR_ACTION(regAddr == 0, return TRACE_FAILURE,
348 : "invalid offset %llx", pstRegInfo->regLoc.offset);
349 16 : coreRegs->r[idx & REG_VAILD_MASK] = (uintptr_t)regAddr;
350 16 : SCD_DLOG_INF("REG_SAVED_VAL_OFFSET %u %lx", idx, coreRegs->r[idx & REG_VAILD_MASK]);
351 16 : break;
352 16 : case REG_SAVED_REG:
353 16 : regNum = pstRegInfo->regLoc.reg;
354 16 : expResult = pstCoreRegsOld->r[regNum & REG_VAILD_MASK];
355 16 : coreRegs->r[idx & REG_VAILD_MASK] = expResult;
356 16 : SCD_DLOG_INF("REG_SAVED_REG %u %lx", idx, coreRegs->r[idx & REG_VAILD_MASK]);
357 16 : break;
358 32 : case REG_SAVED_EXP:
359 32 : expOpAddr = TraceReadUleb128(dwarf, pstRegInfo->regLoc.valExp, &insLen);
360 32 : SCD_CHK_EXPR_ACTION(expOpAddr == NULL, return TRACE_FAILURE, "read uleb128 failed");
361 32 : ret = TraceStackOpExc(dwarf, expOpAddr, expOpAddr + insLen, pstCoreRegsOld,
362 : &expResult, cfaAddr, args);
363 32 : SCD_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "stack op exec for add %p failed", expOpAddr);
364 : /* 异常到正常中间转接函数的unwind指令,由本层SP求出上层所有寄存器(包括SP)
365 : * 当求sp时不要覆盖,以免无法求8-16号寄存器 最后SP可有CFA求得
366 : * ScdMemoryRead(NULL, ...) uses global handler (ScdMemoryRemoteRead via ptrace).
367 : * Safe here: this function is only called from dumper subprocess context
368 : * (ScdProcessDump -> ScdDwarfStep), where ptrace is available. */
369 32 : if ((idx != VOS_R_SP) && (expResult != 0)) {
370 16 : size = ScdMemoryRead(NULL, expResult, &coreRegs->r[idx & REG_VAILD_MASK], sizeof(uint64_t));
371 16 : SCD_CHK_EXPR_ACTION(size == 0, return TRACE_FAILURE, "scd read memory failed 0x%llx", expResult);
372 : }
373 32 : SCD_DLOG_INF("REG_SAVED_EXP %u %lx", idx, coreRegs->r[idx & REG_VAILD_MASK]);
374 32 : break;
375 16 : case REG_SAVED_VAL_EXP:
376 16 : expOpAddr = TraceReadUleb128(dwarf, pstRegInfo->regLoc.valExp, &insLen);
377 16 : ret = TraceStackOpExc(dwarf, expOpAddr, expOpAddr + insLen, pstCoreRegsOld,
378 : &expResult, cfaAddr, args);
379 16 : SCD_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "stack op exec for add %p failed", expOpAddr);
380 16 : coreRegs->r[idx & REG_VAILD_MASK] = expResult;
381 16 : SCD_DLOG_INF("REG_SAVED_VAL_EXP %u %lx", idx, coreRegs->r[idx & REG_VAILD_MASK]);
382 16 : break;
383 48 : case REG_UNSAVED:
384 : case REG_UNDEFINED:
385 : default:
386 48 : break;
387 : }
388 128 : return TRACE_SUCCESS;
389 : }
390 :
391 48 : static inline bool TraceCheckStackAddrValid(uintptr_t cfaAddr, const ScdDwarfStepArgs *args)
392 : {
393 48 : return cfaAddr >= args->stackMinAddr && cfaAddr < args->stackMaxAddr;
394 : }
395 :
396 48 : STATIC TraStatus TraceUnwinRegUpdate(ScdDwarf *dwarf, TraceFrameRegStateInfo *frameRegState,
397 : ScdRegs *regs, const ScdDwarfStepArgs *args)
398 : {
399 : ScdRegs oldCoreRegArray;
400 48 : uintptr_t cfaAddr = 0;
401 48 : TraceGetCFAAddr(dwarf, frameRegState, regs, args, &cfaAddr);
402 48 : if (!TraceCheckStackAddrValid((uintptr_t)cfaAddr, args)) {
403 48 : SCD_DLOG_ERR("[CFA Addr] : %lx", cfaAddr);
404 48 : return TRACE_FAILURE;
405 : }
406 0 : SCD_DLOG_INF("[CFA Addr] : %lx", cfaAddr);
407 0 : for (uint32_t i = 0; i < TRACE_CORE_REG_NUM; i++) {
408 0 : oldCoreRegArray.r[i] = regs->r[i];
409 : }
410 0 : for (uint32_t i = 0; i < TRACE_CORE_REG_NUM; i++) {
411 0 : TraceStagRegInfo *regInfo = &(frameRegState->frameStateInfo.regInfo[i & REG_VAILD_MASK]);
412 0 : TraStatus ret = CallStackRegUpdate(dwarf, i, &oldCoreRegArray, regs, cfaAddr, regInfo, args);
413 0 : SCD_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "call stack register update failed");
414 : }
415 0 : if (frameRegState->frameStateInfo.regInfo[frameRegState->retColumn & REG_VAILD_MASK].regHow == REG_UNDEFINED) {
416 0 : regs->r[frameRegState->retColumn & REG_VAILD_MASK] = 0;
417 : }
418 0 : regs->r[VOS_R_SP] = cfaAddr;
419 0 : return TRACE_SUCCESS;
420 : }
421 :
422 5 : STATIC TraStatus TraceCallstackParse(ScdDwarf *dwarf, uintptr_t pc, const ScdDwarfStepArgs *args,
423 : ScdRegs *regs, TraceFrameRegStateInfo *frameRegState, FDECtrlBlock *ctrlBlock)
424 : {
425 : TraStatus ret;
426 5 : uintptr_t fdeAddr = ctrlBlock->unwindEntryAddr;
427 : TraceAddrRange initIns;
428 : TraceAddrRange ins;
429 5 : frameRegState->retColumn = 0;
430 5 : bool isFDEtable = false;
431 5 : ret = TraceParseFde(dwarf, fdeAddr, frameRegState, &initIns, &ins);
432 5 : if (ret != TRACE_SUCCESS) {
433 1 : SCD_DLOG_ERR("TraceParseFde failed");
434 1 : return ret;
435 : }
436 4 : if (pc < frameRegState->pc || pc > frameRegState->pc + frameRegState->range) {
437 1 : SCD_DLOG_ERR("pc %llx is out of FDE range [%llx, %llx], possible missing CFI",
438 : pc, frameRegState->pc, frameRegState->pc + frameRegState->range);
439 1 : return TRACE_FAILURE;
440 : }
441 3 : frameRegState->ret = pc;
442 3 : ret = TraceUnwindParseFn(dwarf, &initIns, frameRegState, isFDEtable);
443 3 : if (ret != TRACE_SUCCESS) {
444 1 : SCD_DLOG_ERR("TraceUnwindParseFn failed");
445 1 : return ret;
446 : }
447 :
448 2 : isFDEtable = true;
449 2 : ret = TraceUnwindParseFn(dwarf, &ins, frameRegState, isFDEtable);
450 2 : if (ret != TRACE_SUCCESS) {
451 1 : SCD_DLOG_ERR("TraceUnwindParseFn failed");
452 1 : return ret;
453 : }
454 1 : ret = TraceUnwinRegUpdate(dwarf, frameRegState, regs, args);
455 1 : if (ret != TRACE_SUCCESS) {
456 1 : SCD_DLOG_ERR("TraceUnwinRegUpdate failed");
457 1 : return ret;
458 : }
459 0 : return TRACE_SUCCESS;
460 : }
461 :
462 0 : STATIC FdeEntry *TraceSearchFdeOffsetTable(ScdDwarf *dwarf, uintptr_t tblStatAddr, uintptr_t pc)
463 : {
464 : // pc may be smaller than enFrameAddr
465 0 : uintptr_t enFrameAddr = dwarf->memory->data + dwarf->ehFrameHdrOffset;
466 0 : FdeEntry *fdeItem = (FdeEntry *)tblStatAddr;
467 0 : size_t left = 0;
468 0 : size_t right = dwarf->fdeCount;
469 0 : size_t mid = (left + right) >> 1;
470 0 : uintptr_t midLocAddr = 0;
471 0 : SCD_DLOG_DBG("search pc 0x%llx", pc);
472 0 : while (left < right) {
473 0 : midLocAddr = GetAddrByOffset(enFrameAddr, fdeItem[mid].initLocOffset);
474 0 : SCD_DLOG_DBG("search in range [%d, %d], compare [%d] 0x%llx, offset 0x%x,",
475 : left, right, mid, midLocAddr, fdeItem[mid].initLocOffset);
476 0 : if (pc == midLocAddr) {
477 0 : SCD_DLOG_DBG("match fde[%d], start pc 0x%llx", mid, midLocAddr);
478 0 : return &fdeItem[mid];
479 : }
480 0 : if (pc < midLocAddr) {
481 0 : right = mid;
482 : } else {
483 0 : left = mid + 1U;
484 : }
485 0 : mid = (left + right) >> 1U;
486 : }
487 0 : mid = right > 0U ? right - 1U : 0U;
488 0 : SCD_DLOG_DBG("match fde[%d], start pc 0x%llx", mid, midLocAddr);
489 0 : return &fdeItem[mid];
490 : }
491 :
492 : /**
493 : * @brief get next pc by dwarf and regs
494 : *
495 : * @param [in] dwarf The dwarf information of the dynamic library where the current PC is located
496 : * @param [in] regs register information of current frame
497 : * @param [in] args step arguments
498 : * @param [in] pc current pc
499 : * @param [out] nextPc pc of next frame
500 : *
501 : * @return : !=0 failure; ==0 success
502 : */
503 5 : TraStatus ScdDwarfStep(ScdDwarf *dwarf, ScdRegs *regs, const ScdDwarfStepArgs *args, uintptr_t pc, uintptr_t *nextPc)
504 : {
505 : FDECtrlBlock ctrlBlock;
506 5 : TraceFrameRegStateInfo frameRegState = {0};
507 5 : SCD_DLOG_INF("scd dwarf step with pc 0x%llx", pc);
508 5 : SCD_DLOG_INF("[eh_frame_hdr addr] : %llx, size %zu", dwarf->memory->data, dwarf->memory->size);
509 : TraceUnwindEhFrameHdrInfo ehFrameHdrInfo;
510 5 : if (TraceParseFrameHdrAddr(dwarf, &ehFrameHdrInfo) != TRACE_SUCCESS) {
511 1 : return TRACE_FAILURE;
512 : }
513 : // get fde by search table
514 4 : if (ehFrameHdrInfo.searchTblFlag == 1U) {
515 3 : SCD_DLOG_INF("has search table");
516 3 : uintptr_t ehFramrHdrAddr = dwarf->memory->data + dwarf->ehFrameHdrOffset;
517 3 : FdeEntry *entry = TraceSearchFdeOffsetTable(dwarf, ehFrameHdrInfo.tblStatAddr, pc);
518 3 : uintptr_t tmpAddr = GetAddrByOffset(ehFramrHdrAddr, entry->fdeTableOffset);
519 3 : SCD_CHK_EXPR_ACTION(tmpAddr == 0, return TRACE_FAILURE, "invalid offset %d", entry->fdeTableOffset);
520 3 : ctrlBlock.unwindEntryAddr = tmpAddr;
521 3 : tmpAddr = GetAddrByOffset(ehFramrHdrAddr, entry->initLocOffset);
522 3 : SCD_CHK_EXPR_ACTION(tmpAddr == 0, return TRACE_FAILURE, "invalid offset %d", entry->initLocOffset);
523 3 : ctrlBlock.funcStart = tmpAddr;
524 3 : SCD_DLOG_INF("[unwindEntryAddr] : %lx, funcStart : %lx", ctrlBlock.unwindEntryAddr, ctrlBlock.funcStart);
525 : } else {
526 : // process no search table
527 1 : STACKTRACE_LOG_ERR("no search table.");
528 1 : return TRACE_FAILURE;
529 : }
530 3 : uintptr_t tempSp = regs->r[VOS_R_SP];
531 3 : TraStatus ret = TraceCallstackParse(dwarf, pc, args, regs, &frameRegState, &ctrlBlock);
532 3 : if (ret != TRACE_SUCCESS) {
533 3 : SCD_DLOG_ERR("call TraceCallstackParse failed");
534 3 : return TRACE_FAILURE;
535 : }
536 0 : *nextPc = regs->r[frameRegState.retColumn];
537 0 : if (*nextPc == 0) {
538 0 : SCD_DLOG_INF("next pc is 0, finished");
539 0 : return TRACE_SUCCESS;
540 : }
541 0 : if ((args->isFirstStack == false) && (tempSp == regs->r[VOS_R_SP])) {
542 0 : SCD_DLOG_INF("sp has not been changed");
543 0 : *nextPc = 0;
544 0 : return TRACE_SUCCESS;
545 : }
546 0 : SCD_DLOG_INF("[next pc addr] : 0x%lx", *nextPc);
547 0 : return TRACE_SUCCESS;
548 : }
|