forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy patharm_cfi_table.cc
159 lines (130 loc) · 6.27 KB
/
arm_cfi_table.cc
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
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/arm_cfi_table.h"
#include "base/ranges/algorithm.h"
namespace base {
namespace {
// The value of index when the function does not have unwind information.
constexpr uint32_t kNoUnwindInformation = 0xFFFF;
// The mask on the CFI row data that is used to get the high 14 bits and
// multiply it by 4 to get CFA offset. Since the last 2 bits are masked out, a
// shift is not necessary.
constexpr uint16_t kCFAMask = 0xfffc;
// The mask on the CFI row data that is used to get the low 2 bits and multiply
// it by 4 to get the return address offset.
constexpr uint16_t kReturnAddressMask = 0x3;
constexpr uint16_t kReturnAddressShift = 2;
// The CFI data in UNW_DATA table starts with number of rows (N) encoded as
// uint16_t, followed by N 4 byte rows. The CFIDataRow represents a single row
// of CFI data of a function in the table. Since we cast the memory at the
// address after the address of number of rows into an array of CFIDataRow, the
// size of the struct should be 4 bytes and the order of the members is fixed
// according to the given format. The first 2 bytes is the address of function
// and last 2 bytes is the CFI data for the offset.
struct CFIDataRow {
// The address of the instruction as an offset from the start of the
// function.
uint16_t addr_offset;
// Represents the CFA and RA offsets to get information about next stack
// frame. This is the CFI data at the point before executing the instruction
// at |addr_offset| from the start of the function.
uint16_t cfi_data;
// Helper functions to convert the to ArmCFITable::FrameEntry
size_t ra_offset() const {
return (cfi_data & kReturnAddressMask) << kReturnAddressShift;
}
size_t cfa_offset() const { return cfi_data & kCFAMask; }
};
static_assert(sizeof(CFIDataRow) == 4,
"The CFIDataEntry struct must be exactly 4 bytes to ensure "
"correct parsing of input data");
} // namespace
// static
std::unique_ptr<ArmCFITable> ArmCFITable::Parse(span<const uint8_t> cfi_data) {
BufferIterator<const uint8_t> cfi_iterator(cfi_data);
const uint32_t* unw_index_count = cfi_iterator.Object<uint32_t>();
if (unw_index_count == nullptr || *unw_index_count == 0U)
return nullptr;
auto function_addresses = cfi_iterator.Span<uint32_t>(*unw_index_count);
auto entry_data_indices = cfi_iterator.Span<uint16_t>(*unw_index_count);
if (function_addresses.size() != *unw_index_count ||
entry_data_indices.size() != *unw_index_count)
return nullptr;
// The UNW_DATA table data is right after the end of UNW_INDEX table.
auto entry_data = cfi_iterator.Span<uint8_t>(
(cfi_iterator.total_size() - cfi_iterator.position()) / sizeof(uint8_t));
return std::make_unique<ArmCFITable>(function_addresses, entry_data_indices,
entry_data);
}
ArmCFITable::ArmCFITable(span<const uint32_t> function_addresses,
span<const uint16_t> entry_data_indices,
span<const uint8_t> entry_data)
: function_addresses_(function_addresses),
entry_data_indices_(entry_data_indices),
entry_data_(entry_data) {
DCHECK_EQ(function_addresses.size(), entry_data_indices.size());
}
ArmCFITable::~ArmCFITable() = default;
Optional<ArmCFITable::FrameEntry> ArmCFITable::FindEntryForAddress(
uintptr_t address) const {
DCHECK(!function_addresses_.empty());
// Find the required function address in UNW_INDEX as the last function lower
// or equal to |address| (the value right before the result of upper_bound(),
// if any).
auto func_it = ranges::upper_bound(function_addresses_, address);
// If no function comes before |address|, no CFI entry is returned.
if (func_it == function_addresses_.begin())
return nullopt;
--func_it;
uint32_t func_start_addr = *func_it;
size_t row_num = func_it - function_addresses_.begin();
uint16_t index = entry_data_indices_[row_num];
DCHECK_LE(func_start_addr, address);
if (index == kNoUnwindInformation)
return nullopt;
// The unwind data for the current function is at a 2 bytes offset of the
// index found in UNW_INDEX table.
if (entry_data_.size() <= index * sizeof(uint16_t))
return nullopt;
BufferIterator<const uint8_t> entry_iterator(entry_data_);
entry_iterator.Seek(index * sizeof(uint16_t));
// The value of first 2 bytes is the CFI data row count for the function.
const uint16_t* row_count = entry_iterator.Object<uint16_t>();
if (row_count == nullptr)
return nullopt;
// And the actual CFI rows start after 2 bytes from the |unwind_data|. Cast
// the data into an array of CFIUnwindDataRow since the struct is designed to
// represent each row. We should be careful to read only |row_count| number of
// elements in the array.
auto function_cfi = entry_iterator.Span<CFIDataRow>(*row_count);
if (function_cfi.size() != *row_count)
return nullopt;
FrameEntry last_frame_entry = {0, 0};
// Iterate through all function entries to find a range covering |address|.
// In practice, the majority of functions contain very few entries.
for (const auto& entry : function_cfi) {
// The return address of the function is the instruction that is not yet
// been executed. The CFI row specifies the unwind info before executing the
// given instruction. If the given address is equal to the instruction
// offset, then use the current row. Or use the row with highest address
// less than the given address.
if (func_start_addr + entry.addr_offset > address)
break;
uint32_t cfa_offset = entry.cfa_offset();
if (cfa_offset == 0)
return nullopt;
last_frame_entry.cfa_offset = cfa_offset;
uint32_t ra_offset = entry.ra_offset();
// The RA offset of the last specified row should be used, if unspecified.
// Update |last_ra_offset| only if valid for this row. Otherwise, tthe last
// valid |last_ra_offset| is used. TODO(ssid): This should be fixed in the
// format and we should always output ra offset.
if (ra_offset)
last_frame_entry.ra_offset = ra_offset;
if (last_frame_entry.ra_offset == 0)
return nullopt;
}
return last_frame_entry;
}
} // namespace base