summaryrefslogtreecommitdiff
path: root/sys/lib/libunwind/UnwindCursor.hpp
blob: 32052f1befffae4602591327a732a0d511671a9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//===------------------------- UnwindCursor.hpp ---------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//
// C++ interface to lower levels of libuwind
//===----------------------------------------------------------------------===//

#ifndef __UNWINDCURSOR_HPP__
#define __UNWINDCURSOR_HPP__

#include <stdint.h>
#include <stdlib.h>
#if !defined(__minix)
#include <pthread.h>
#endif /* !defined(__minix) */

#include "AddressSpace.hpp"
#include "DwarfInstructions.hpp"
#include "Registers.hpp"

namespace _Unwind {

template <typename A, typename R> class UnwindCursor {
public:
  UnwindCursor(R &regs, A &as)
      : fRegisters(regs), fAddressSpace(as), fUnwindInfoMissing(false),
        fIsSignalFrame(false) {
    memset(&fInfo, 0, sizeof(fInfo));
  }

  uint64_t getIP() const { return fRegisters.getIP(); }

  void setIP(uint64_t value) { return fRegisters.setIP(value); }

  uint64_t getSP() const { return fRegisters.getSP(); }

  void setSP(uint64_t value) { return fRegisters.setSP(value); }

  bool validReg(int regNum) { return fRegisters.validRegister(regNum); }

  uint64_t getReg(int regNum) { return fRegisters.getRegister(regNum); }

  void setReg(int regNum, uint64_t value) {
    fRegisters.setRegister(regNum, value);
  }

  step_result step() {
    // Bottom of stack is defined as having no more unwind info.
    if (fUnwindInfoMissing)
      return UNW_STEP_END;

    // Apply unwinding to register set.
    switch (this->stepWithDwarfFDE()) {
    case UNW_STEP_FAILED:
      return UNW_STEP_FAILED;
    case UNW_STEP_END:
      return UNW_STEP_END;
    case UNW_STEP_SUCCESS:
      this->setInfoBasedOnIPRegister(true);
      if (fUnwindInfoMissing)
        return UNW_STEP_END;

      if (fInfo.extra_args)
        setSP(getSP() + fInfo.extra_args);
      return UNW_STEP_SUCCESS;
    }
    __builtin_unreachable();
  }

  void getInfo(unw_proc_info_t *info) { *info = fInfo; }

  bool isSignalFrame() { return fIsSignalFrame; }
  void setInfoBasedOnIPRegister(bool isReturnAddress = false);

  void jumpto() { fRegisters.jumpto(); }

private:
  typedef typename A::pint_t pint_t;
  typedef uint32_t EncodedUnwindInfo;

  bool getInfoFromDwarfSection(pint_t, pint_t, uint32_t, uint32_t);

  step_result stepWithDwarfFDE() {
    return DwarfInstructions<A, R>::stepWithDwarf(
        fAddressSpace, this->getIP(), fInfo.unwind_info, fRegisters, &fInfo);
  }

  unw_proc_info_t fInfo;
  R fRegisters;
  A &fAddressSpace;
  bool fUnwindInfoMissing;
  bool fIsSignalFrame;
};

template <typename A, typename R>
void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
  pint_t pc = this->getIP();

  // If the last line of a function is a "throw", the compiler sometimes
  // emits no instructions after the call to __cxa_throw.  This means
  // the return address is actually the start of the next function.
  // To disambiguate this, back up the PC when we know it is a return
  // address.
  if (isReturnAddress)
    --pc;

  pint_t fdeStart, data_base;
  if (!fAddressSpace.findFDE(pc, fdeStart, data_base)) {
    fUnwindInfoMissing = true;
    return;
  }
  fInfo.data_base = data_base;

  typename CFI_Parser<A, R>::FDE_Info fdeInfo;
  typename CFI_Parser<A, R>::CIE_Info cieInfo;
  CFI_Parser<A, R>::decodeFDE(fAddressSpace, fdeStart, &fdeInfo, &cieInfo,
                              &fInfo);
  if (pc < fdeInfo.pcStart || pc > fdeInfo.pcEnd) {
    fUnwindInfoMissing = true;
    return;
  }
  fInfo.start_ip = fdeInfo.pcStart;

  typename CFI_Parser<A, R>::PrologInfo prolog;
  if (!CFI_Parser<A, R>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo,
                                              pc, &prolog, &fInfo)) {
    fUnwindInfoMissing = true;
    return;
  }
  // Save off parsed FDE info
  fInfo.end_ip = fdeInfo.pcEnd;
  fInfo.lsda = fdeInfo.lsda;
  fInfo.handler = cieInfo.personality;
  fInfo.extra_args = prolog.spExtraArgSize;
  fInfo.unwind_info = fdeInfo.fdeStart;
}

}; // namespace _Unwind

#endif // __UNWINDCURSOR_HPP__