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