Following code is part of software - written by me - that is used to analyze memory data.
Even if this just small part of software, I think this can help understand the way of unwinding stack by using DWARF information. Following sample code is to analyze DWARF information generated from C code.
For more details about unwinding stack. See this post.

const CDwf_Dbg*
CFrameS::_Get_dwf_dbg(DvAddr pc) const
{
  CDwf_Dbg*   dwf_dbg = NULL;

  for(int i=0; i<_dwfs->Size(); i++) {
    if( (*_dwfs)[i]->Is_in_fde(pc) ) { dwf_dbg = (*_dwfs)[i]; break; }
  }
  return dwf_dbg;
}

// "frm->regs" should have valid values before call this function.
int
CFrameS::_Set_frame_info(_CFrm_Info* fi) const
{
  if(REG_VAL_UNDEF==fi->regs[_tgt->Pci()].from) { return -1; }

  const CDwf_Dbg* dwf_dbg = _Get_dwf_dbg(REG_PC(fi->regs[_tgt->Pci()].val));

  if(dwf_dbg)
  {
    if( dwf_dbg->ArangeS() ) {
      ASSERT(REG_VAL_UNDEF != fi->regs[_tgt->Pci()].from);
      fi->fdie = dwf_dbg->ArangeS()->Subprogram_die(REG_PC(fi->regs[_tgt->Pci()].val), &fi->cudie);
    }
    else {
      fi->cudie = NULL;
      fi->fdie = NULL;
    }

    if( dwf_dbg->FdeS() ) {
      ASSERT(REG_VAL_UNDEF != fi->regs[_tgt->Pci()].from)
      fi->fde = dwf_dbg->FdeS()->Get_fde(REG_PC(fi->regs[_tgt->Pci()].val), NULL, NULL);
    }
    else { fi->fde = NULL; }

    if(fi->cudie)
    {
      CLine*  ln = CLine::Create(*fi->cudie);
      ln->Lineno(REG_PC(fi->regs[_tgt->Pci()].val), &fi->line, &fi->column);
      if(ln) { MDELETE(ln); }
    }
  }
  else
  {
    // There is no debugging information about this.
    // But we have address value of PC. So, it's not fail!
    fi->cudie = NULL;
    fi->fdie = NULL;
    fi->fde = NULL;
  }

  return 0;
}

int
CFrameS::_Create_previous_frame_info(_CFrm_Info* outfi, const _CFrm_Info* fi) const
{
  ASSERT(outfi && fi);

  bool          b_exact = true;
#ifdef __RT_DEBUG__
  DvAddr        fde_lopc, fdie_lopc;
#endif // __RT_DEBUG__

  if( (REG_VAL_UNDEF == fi->regs[_tgt->Pci()].from)
      || (REG_VAL_UNDEF == fi->regs[_tgt->Spi()].from) ) { goto no_info; }

  const CDwf_Dbg* dwf_dbg = _Get_dwf_dbg(REG_PC(fi->regs[_tgt->Pci()].val));

  if(dwf_dbg && dwf_dbg->FdeS() && fi->fde)
  {
    DvHalf      lri = dwf_dbg->FdeS()->Get_return_addr_register(fi->fde);

    // initial reg values of this frame
    if(0 > dwf_dbg->FdeS()->Get_all_regs(outfi->regs, fi->regs,
                                          fi->fde, REG_PC(fi->regs[_tgt->Pci()].val)))
    { goto fail; }

    if(REG_VAL_UNDEF == outfi->regs[lri].from) { goto fail; }

    outfi->regs[_tgt->Pci()] = outfi->regs[lri];
    COMPENSATE_PC_AHEAD_OF_LR(outfi->regs[_tgt->Pci()].val);

    if(fi->fdie && fi->cudie)
    {
#ifdef __RT_DEBUG__
      fde_lopc = dwf_dbg->FdeS()->Fde_lopc(fi->fde);
      if(0 > fi->fdie->LoPC(&fdie_lopc)) { goto fail; }

      if(fde_lopc != fdie_lopc) { ASSERT(FALSE); goto fail; }
#endif // __RT_DEBUG__

      if(fi->fdie->Has(DW_AT_frame_base))
      {
        DvUSign             val;
        int                 val_type;
        DvAddr              culo;
        bool                b_exact = false;
        CArr<CLoc_Desc*> locarr;
        if(0 > fi->fdie->Frame_base(&locarr) ) {goto fail; }
        ASSERT(REG_VAL_UNDEF != fi->regs[_tgt->Pci()].from);
        if(0 > fi->cudie->LoPC(&culo)) { goto fail; }

        if(0 > CLocS::Interpret(&val_type, &val, &b_exact,_tgt,
                                REG_PC(fi->regs[_tgt->Pci()].val) - culo,
                                INVALID_FRAME_BASE, fi->regs, locarr) ) { goto fail; }
        if(!b_exact){ goto fail; } // this should be exact value.!

        //ASSERT( (LOC_VAL_REG == val_type) || (LOC_VAL_UCONST == val_type) );
        outfi->regs[_tgt->Spi()].val = val;
        outfi->regs[_tgt->Spi()].from = REG_VAL_FROM_REG;
      } // if(fi->fdie->Has(DW_AT_frame_base))
      else if(fi->fdie->Has(DW_AT_abstract_origin) )
      { // this may be inlined function
        CDieI* ofdie = fi->fdie->Original_die(ORI_PURE);

#ifdef  __RT_DEBUG__
        ODPRINTF(("========*********=========\n"));
        ODPRINTF(("Tag: %s\n", dwarf_get_tagCN(fi->fdie->Tag()) ));
        fi->fdie->ODPrint_attributes();
        ODPRINTF(("=========*********============\n"));
        ODPRINTF(("Tag:%s\n", dwarf_get_tagCN(ofdie->Tag()) ));
        ofdie->ODPrint_attributes();
        //ASSERT(FALSE);
#endif // __RT_DEBUG__
        if(ofdie->Has(DW_AT_inline)) {
          outfi->regs[_tgt->Spi()] = fi->regs[_tgt->Spi()];
        }
        else { ASSERT(FALSE); }
        MDELETE(ofdie);
      }
      else { ASSERT(FALSE); }
    }
    else
    {
#ifdef COMPILER_ADS12
      DvSign off;
      if( 0 <= dwf_dbg->FdeS()->Get_cfa_offset_via_sp(&off, fi->fde, fi->regs[_tgt->Pci()].val) )
      {
        if(0 == off) { goto fail; } // If value of stack pointer is not changed, than we can assume that virtual unwinding fails.
        else {
          outfi->regs[_tgt->Spi()].val = fi->regs[_tgt->Spi()].val+off;
          outfi->regs[_tgt->Spi()].from = fi->regs[_tgt->Spi()].from;
        }
      }
      else
#endif // COMPILER_ADS12
      { goto no_info; }
    }
  }
  else  // if(dwf_dbg && dwf_dbg->FdeS() && fi->fde)
  {
    static const int  __UNW_INST_CNT  = 1000;

    bool b_success = false;
    // try to unwind stack with raw machine instruction!!
    if(_unw)
    {
      UnwRT*    unwr;
      MMNEW(unwr, , UnwRT, _tgt->Num_regs());
      for(int i=0; i<_tgt->Num_regs(); i++) {
        unwr[i].v = fi->regs[i].val;
        unwr[i].o = (REG_VAL_UNDEF==fi->regs[i].from)? UNWR_INVALID: UNWR_VALID;
      }

      if( UNW_OK == _unw->Unw(unwr, __UNW_INST_CNT, _stk_base))
      {
        for(int i=0; i<_tgt->Num_regs(); i++) {
          ASSERT( (UNWR_VALID==unwr[_tgt->Pci()].o)&&(UNWR_VALID==unwr[_tgt->Spi()].o) );
          outfi->regs[i].val = unwr[i].v;
          // We can assume that valid register value comes from stack.
          outfi->regs[i].from = (UNWR_VALID==unwr[i].o)? REG_VAL_FROM_ADDR: REG_VAL_UNDEF;
        }
        b_success = true;;
      }
      MMDELETE(unwr);
      COMPENSATE_PC_AHEAD_OF_LR(outfi->regs[_tgt->Pci()].val);
    } // if(_unw)
    if(!b_success) { goto fail; }
  } // else  // if(dwf_dbg && dwf_dbg->FdeS() && fi->fde)

  // Unexpected case. - infinite loop!! if this happened
  // If PC is on 'very strange position' this can be happened
  // previous frame is exactly same with current frame! This cannot happened in normal situation!
  if( (REG_VAL_UNDEF != outfi->regs[_tgt->Pci()].from)
      && (outfi->regs[_tgt->Spi()].val == fi->regs[_tgt->Spi()].val)
      && (outfi->regs[_tgt->Pci()].val == fi->regs[_tgt->Pci()].val)
      && (outfi->regs[_tgt->Lri()].val == fi->regs[_tgt->Lri()].val) )
  { goto fail; }

  if(0 > _Set_frame_info(outfi) ) { goto fail; }

  return (b_exact)? FRAME_OK: FRAME_NOT_EXACT;

fail:
  return FRAME_FAIL;

no_info:
  return FRAME_NO_DBG_INFO;

}

int
CFrameS::Construct_frame_stack(const RegT* regs)
{
  for(int i=0; i<_frame.Size(); i++) { MDELETE(_frame[i]); }
  _frame.Reset();

  int         frm_ret;
  bool        b_end = false;

  // flag to retry frame unwind
  bool        b_retry = true;
  _CFrm_Info* prev_fi = NULL;
  _CFrm_Info* next_fi = NULL;

  MNEW(prev_fi, , _CFrm_Info, (_num_regs));
  for(int i=0; i<_num_regs; i++) {
    prev_fi->regs[i].from = regs[i].from;
    prev_fi->regs[i].val = regs[i].val;
  }

  if(0 > _Set_frame_info(prev_fi) ) { goto fail; }
  _frame.Append(prev_fi);
  _ODPrint_frame_name(prev_fi);
  prev_fi = prev_fi->Clone();

  for(;;)
  {
    ASSERT(REG_VAL_UNDEF != prev_fi->regs[_tgt->Pci()].from);

    MNEW(next_fi, , _CFrm_Info, (_num_regs));
    frm_ret = _Create_previous_frame_info(next_fi, prev_fi);

    switch(frm_ret)
    {
      case FRAME_NOT_EXACT:
      case FRAME_OK:
        ASSERT(prev_fi && next_fi);
        _frame.Append(next_fi);
        MDELETE(prev_fi);
        prev_fi = next_fi->Clone();
        next_fi = NULL;
#ifdef __RT_DEBUG__
        if(prev_fi->fdie) {
          _ODPrint_frame_name(prev_fi);
        }
        else if(REG_VAL_UNDEF != prev_fi->regs[_tgt->Pci()].from) {
          ODPRINTF(("-- [0x%8x]\n", prev_fi->regs[_tgt->Pci()].val));
        }
#endif // __RT_DEBUG__

#ifdef ARCHITECTURE_ARM
        // In ARM, stack grows in negative direction.
        if( (REG_VAL_UNDEF != prev_fi->regs[_tgt->Spi()].from)
          && (prev_fi->regs[_tgt->Spi()].val >= _stk_base ) ) {
          frm_ret = FRAME_END;
          goto end_of_loop;
        }
#endif // ARCHITECTURE_ARM
      break;

      case FRAME_FAIL:
      case FRAME_NO_DBG_INFO:
        ASSERT(prev_fi);
        MDELETE(next_fi); 

        // Check whether previous frame is top of frame or not.
        if( (1 == _frame.Size()) && b_retry )
        {
          b_retry = false;
          ///////////////////////////////////
          // Let's try again with LR!! if previous frame is top of stack!!
          // (There are lots of cases that pc is corrupted in the top of stack!! (ex. prefetch abort))

          /////////////////////////////////////////////////////////////
          // CHECK IT! : "Try with LR" is good for approximation?!
          //  - Careful investigation is needed!
          /////////////////////////////////////////////////////////////
          DvAddr  lrpc = prev_fi->regs[_tgt->Lri()].val;
          COMPENSATE_PC_AHEAD_OF_LR(lrpc);

          if( (REG_VAL_UNDEF != prev_fi->regs[_tgt->Lri()].from)
              && (lrpc != prev_fi->regs[_tgt->Pci()].val) )
          {
            prev_fi->regs[_tgt->Pci()].val = lrpc;
            if(0 > _Set_frame_info(prev_fi) ) { goto fail; }  // set new frame info for retrying with LR.
          }
          else { goto end_of_loop; }
        }
        else { goto end_of_loop; }
      break;

      default:
        ASSERT(FALSE);
    } // switch
    continue;

end_of_loop:
    break;
  } // for(;;)

  if(prev_fi) { MDELETE(prev_fi); }
  if(next_fi) { MDELETE(next_fi); }

  _frame_status = frm_ret;
  return 0;

fail:
  if(prev_fi) { MDELETE(prev_fi); }
  if(next_fi) { MDELETE(next_fi); }
  return -1;
}

'Domain > ARM' 카테고리의 다른 글

[ARM] Multi-core optimization test...  (0) 2011.05.03
[ARM] Sample code for unwinding stack in Thumb mode.  (0) 2010.04.07
[ARM] Unwinding Stack.  (0) 2009.09.15
[ARM] Long jump.  (0) 2007.06.05
[ARM] .init_array section  (0) 2007.03.18
[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

[From DWARF Specification Version 2.0]

* Ambiguity in the Spec.
At first, let's see followings.

[Extracted from spec. page 66]
DW_CFA_def_cfa_offset takes a single unsigned LEB128 argument representing an offset -- (*1). The required action is to define the current CFA rule to use the provided offset (but to keep the old register).

[Extracted from spec. page 105]
fde + 17 DW_CFA_def_cfa_offset (<fsize>/4) -- (*2) ; assuming <fsize> < 512

As you can see above, in (*1) argument is just an offset. But in (*2), <fsize> is divided by '4'. So, I think there is conflict in those part.

In 'libdwarf', as (*1), it uses offset value itself. But, dwarf information generated by ADS1.2 compiler is obeys rule (*2) - offset * 4 (data alignment factor).

* How can we get Die from PC without 'Arrange Section'.

In this case, we can get CU Die from CU Header. And then, the result of sorting them into 'lowpc' order, can be used.

* Optimizing out local stack variable.

Unlike static or global variable, dwarf defines valid range for the local stack variable. That is, stack variable is valid only in this range. This may make mismatch between code and dwarf information; Sometimes, we seem to know value of variable when we look at the code, but in actual dwarf information, the variable is not valid yet, or optimized out. So, we cannot know it. Yes, this is totally compiler dependent.

Here is example.

funcA(...)
{
    int a = 8;
    ...
    a = 10;   // --- (*)
    ...
}

"int a=8" is stored at NVRAM. So, compiler don't need to keep this value in memory. Therefore, before "a=10;", this value may not be assigned into the stack - that is, there is not relative dwarf information. As a result, we cannot read value "a" by reading register of memory. (In practice, this kind of stuffs often happens in ADS1.2 compiler.)

* Handling included source.
See follows.

a.c
    ...

b.c
    ...
    #include "a.c"
    ...

(ASSUMPTION : 'a.c' is not compiled separately)

In ADS1.2, 10th line of 'a.c' is regarded as 10th line of 'b.c'. So, there are two 10th lines in the dwarf information. I think some compiler may handle this case correctly. But, in ADS1.2, dwarf information is generated unexpectedly.

+ Recent posts