LCOV - code coverage report
Current view: top level - atrace/utrace/stacktrace/stacktrace_dumper - scd_dwarf.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.9 % 308 209
Test Date: 2026-07-28 10:53:44 Functions: 92.3 % 13 12

            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              : }
        

Generated by: LCOV version 2.0-1