summaryrefslogtreecommitdiff
path: root/include/lldb/Symbol/DWARFCallFrameInfo.h
blob: 7f0f3db49a718649f8ff7e8ae4764a412f5d40d6 (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//===-- DWARFCallFrameInfo.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_DWARFCallFrameInfo_h_
#define liblldb_DWARFCallFrameInfo_h_

#include <map>
#include <mutex>

#include "lldb/Core/AddressRange.h"
#include "lldb/Utility/Flags.h"

#include "lldb/Core/RangeMap.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Utility/VMRange.h"
#include "lldb/lldb-private.h"

namespace lldb_private {

// DWARFCallFrameInfo is a class which can read eh_frame and DWARF Call Frame
// Information FDEs.  It stores little information internally. Only two APIs
// are exported - one to find the high/low pc values of a function given a text
// address via the information in the eh_frame / debug_frame, and one to
// generate an UnwindPlan based on the FDE in the eh_frame / debug_frame
// section.

class DWARFCallFrameInfo {
public:
  enum Type { EH, DWARF };

  DWARFCallFrameInfo(ObjectFile &objfile, lldb::SectionSP &section, Type type);

  ~DWARFCallFrameInfo() = default;

  // Locate an AddressRange that includes the provided Address in this object's
  // eh_frame/debug_info Returns true if a range is found to cover that
  // address.
  bool GetAddressRange(Address addr, AddressRange &range);

  // Return an UnwindPlan based on the call frame information encoded in the
  // FDE of this DWARFCallFrameInfo section.
  bool GetUnwindPlan(Address addr, UnwindPlan &unwind_plan);

  typedef RangeVector<lldb::addr_t, uint32_t> FunctionAddressAndSizeVector;

  //------------------------------------------------------------------
  // Build a vector of file address and size for all functions in this Module
  // based on the eh_frame FDE entries.
  //
  // The eh_frame information can be a useful source of file address and size
  // of the functions in a Module.  Often a binary's non-exported symbols are
  // stripped before shipping so lldb won't know the start addr / size of many
  // functions in the Module.  But the eh_frame can help to give the addresses
  // of these stripped symbols, at least.
  //
  // @param[out] function_info
  //      A vector provided by the caller is filled out.  May be empty if no
  //      FDEs/no eh_frame
  //      is present in this Module.

  void
  GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector &function_info);

  void ForEachFDEEntries(
      const std::function<bool(lldb::addr_t, uint32_t, dw_offset_t)> &callback);

private:
  enum { CFI_AUG_MAX_SIZE = 8, CFI_HEADER_SIZE = 8 };
  enum CFIVersion {
    CFI_VERSION1 = 1, // DWARF v.2
    CFI_VERSION3 = 3, // DWARF v.3
    CFI_VERSION4 = 4  // DWARF v.4, v.5
  };

  struct CIE {
    dw_offset_t cie_offset;
    uint8_t version;
    char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very
                                         // short.
    uint8_t address_size = sizeof(uint32_t); // The size of a target address.
    uint8_t segment_size = 0;                // The size of a segment selector.

    uint32_t code_align;
    int32_t data_align;
    uint32_t return_addr_reg_num;
    dw_offset_t inst_offset; // offset of CIE instructions in mCFIData
    uint32_t inst_length;    // length of CIE instructions in mCFIData
    uint8_t ptr_encoding;
    uint8_t lsda_addr_encoding;   // The encoding of the LSDA address in the FDE
                                  // augmentation data
    lldb::addr_t personality_loc; // (file) address of the pointer to the
                                  // personality routine
    lldb_private::UnwindPlan::Row initial_row;

    CIE(dw_offset_t offset)
        : cie_offset(offset), version(-1), code_align(0), data_align(0),
          return_addr_reg_num(LLDB_INVALID_REGNUM), inst_offset(0),
          inst_length(0), ptr_encoding(0), lsda_addr_encoding(DW_EH_PE_omit),
          personality_loc(LLDB_INVALID_ADDRESS), initial_row() {}
  };

  typedef std::shared_ptr<CIE> CIESP;

  typedef std::map<dw_offset_t, CIESP> cie_map_t;

  // Start address (file address), size, offset of FDE location used for
  // finding an FDE for a given File address; the start address field is an
  // offset into an individual Module.
  typedef RangeDataVector<lldb::addr_t, uint32_t, dw_offset_t> FDEEntryMap;

  bool IsEHFrame() const;

  bool GetFDEEntryByFileAddress(lldb::addr_t file_offset,
                                FDEEntryMap::Entry &fde_entry);

  void GetFDEIndex();

  bool FDEToUnwindPlan(uint32_t offset, Address startaddr,
                       UnwindPlan &unwind_plan);

  const CIE *GetCIE(dw_offset_t cie_offset);

  void GetCFIData();

  // Applies the specified DWARF opcode to the given row. This function handle
  // the commands operates only on a single row (these are the ones what can
  // appear both in
  // CIE and in FDE).
  // Returns true if the opcode is handled and false otherwise.
  bool HandleCommonDwarfOpcode(uint8_t primary_opcode, uint8_t extended_opcode,
                               int32_t data_align, lldb::offset_t &offset,
                               UnwindPlan::Row &row);

  ObjectFile &m_objfile;
  lldb::SectionSP m_section_sp;
  Flags m_flags = 0;
  cie_map_t m_cie_map;

  DataExtractor m_cfi_data;
  bool m_cfi_data_initialized = false; // only copy the section into the DE once

  FDEEntryMap m_fde_index;
  bool m_fde_index_initialized = false; // only scan the section for FDEs once
  std::mutex m_fde_index_mutex; // and isolate the thread that does it

  Type m_type;

  CIESP
  ParseCIE(const uint32_t cie_offset);

  lldb::RegisterKind GetRegisterKind() const {
    return m_type == EH ? lldb::eRegisterKindEHFrame : lldb::eRegisterKindDWARF;
  }
};

} // namespace lldb_private

#endif // liblldb_DWARFCallFrameInfo_h_