1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/profiler/arm_cfi_table.h"
6 
7 #include "base/ranges/algorithm.h"
8 
9 namespace base {
10 
11 namespace {
12 
13 // The value of index when the function does not have unwind information.
14 constexpr uint32_t kNoUnwindInformation = 0xFFFF;
15 
16 // The mask on the CFI row data that is used to get the high 14 bits and
17 // multiply it by 4 to get CFA offset. Since the last 2 bits are masked out, a
18 // shift is not necessary.
19 constexpr uint16_t kCFAMask = 0xfffc;
20 
21 // The mask on the CFI row data that is used to get the low 2 bits and multiply
22 // it by 4 to get the return address offset.
23 constexpr uint16_t kReturnAddressMask = 0x3;
24 constexpr uint16_t kReturnAddressShift = 2;
25 
26 // The CFI data in UNW_DATA table starts with number of rows (N) encoded as
27 // uint16_t, followed by N 4 byte rows. The CFIDataRow represents a single row
28 // of CFI data of a function in the table. Since we cast the memory at the
29 // address after the address of number of rows into an array of CFIDataRow, the
30 // size of the struct should be 4 bytes and the order of the members is fixed
31 // according to the given format. The first 2 bytes is the address of function
32 // and last 2 bytes is the CFI data for the offset.
33 struct CFIDataRow {
34   // The address of the instruction as an offset from the start of the
35   // function.
36   uint16_t addr_offset;
37   // Represents the CFA and RA offsets to get information about next stack
38   // frame. This is the CFI data at the point before executing the instruction
39   // at |addr_offset| from the start of the function.
40   uint16_t cfi_data;
41 
42   // Helper functions to convert the to ArmCFITable::FrameEntry
ra_offsetbase::__anonc6a7384d0111::CFIDataRow43   size_t ra_offset() const {
44     return (cfi_data & kReturnAddressMask) << kReturnAddressShift;
45   }
cfa_offsetbase::__anonc6a7384d0111::CFIDataRow46   size_t cfa_offset() const { return cfi_data & kCFAMask; }
47 };
48 
49 static_assert(sizeof(CFIDataRow) == 4,
50               "The CFIDataEntry struct must be exactly 4 bytes to ensure "
51               "correct parsing of input data");
52 
53 }  // namespace
54 
55 // static
Parse(span<const uint8_t> cfi_data)56 std::unique_ptr<ArmCFITable> ArmCFITable::Parse(span<const uint8_t> cfi_data) {
57   BufferIterator<const uint8_t> cfi_iterator(cfi_data);
58 
59   const uint32_t* unw_index_count = cfi_iterator.Object<uint32_t>();
60   if (unw_index_count == nullptr || *unw_index_count == 0U)
61     return nullptr;
62 
63   auto function_addresses = cfi_iterator.Span<uint32_t>(*unw_index_count);
64   auto entry_data_indices = cfi_iterator.Span<uint16_t>(*unw_index_count);
65   if (function_addresses.size() != *unw_index_count ||
66       entry_data_indices.size() != *unw_index_count)
67     return nullptr;
68 
69   // The UNW_DATA table data is right after the end of UNW_INDEX table.
70   auto entry_data = cfi_iterator.Span<uint8_t>(
71       (cfi_iterator.total_size() - cfi_iterator.position()) / sizeof(uint8_t));
72   return std::make_unique<ArmCFITable>(function_addresses, entry_data_indices,
73                                        entry_data);
74 }
75 
ArmCFITable(span<const uint32_t> function_addresses,span<const uint16_t> entry_data_indices,span<const uint8_t> entry_data)76 ArmCFITable::ArmCFITable(span<const uint32_t> function_addresses,
77                          span<const uint16_t> entry_data_indices,
78                          span<const uint8_t> entry_data)
79     : function_addresses_(function_addresses),
80       entry_data_indices_(entry_data_indices),
81       entry_data_(entry_data) {
82   DCHECK_EQ(function_addresses.size(), entry_data_indices.size());
83 }
84 
85 ArmCFITable::~ArmCFITable() = default;
86 
FindEntryForAddress(uintptr_t address) const87 Optional<ArmCFITable::FrameEntry> ArmCFITable::FindEntryForAddress(
88     uintptr_t address) const {
89   DCHECK(!function_addresses_.empty());
90 
91   // Find the required function address in UNW_INDEX as the last function lower
92   // or equal to |address| (the value right before the result of upper_bound(),
93   // if any).
94   auto func_it = ranges::upper_bound(function_addresses_, address);
95   // If no function comes before |address|, no CFI entry  is returned.
96   if (func_it == function_addresses_.begin())
97     return nullopt;
98   --func_it;
99 
100   uint32_t func_start_addr = *func_it;
101   size_t row_num = func_it - function_addresses_.begin();
102   uint16_t index = entry_data_indices_[row_num];
103   DCHECK_LE(func_start_addr, address);
104 
105   if (index == kNoUnwindInformation)
106     return nullopt;
107 
108   // The unwind data for the current function is at a 2 bytes offset of the
109   // index found in UNW_INDEX table.
110   if (entry_data_.size() <= index * sizeof(uint16_t))
111     return nullopt;
112   BufferIterator<const uint8_t> entry_iterator(entry_data_);
113   entry_iterator.Seek(index * sizeof(uint16_t));
114 
115   // The value of first 2 bytes is the CFI data row count for the function.
116   const uint16_t* row_count = entry_iterator.Object<uint16_t>();
117   if (row_count == nullptr)
118     return nullopt;
119   // And the actual CFI rows start after 2 bytes from the |unwind_data|. Cast
120   // the data into an array of CFIUnwindDataRow since the struct is designed to
121   // represent each row. We should be careful to read only |row_count| number of
122   // elements in the array.
123   auto function_cfi = entry_iterator.Span<CFIDataRow>(*row_count);
124   if (function_cfi.size() != *row_count)
125     return nullopt;
126 
127   FrameEntry last_frame_entry = {0, 0};
128   // Iterate through all function entries to find a range covering |address|.
129   // In practice, the majority of functions contain very few entries.
130   for (const auto& entry : function_cfi) {
131     // The return address of the function is the instruction that is not yet
132     // been executed. The CFI row specifies the unwind info before executing the
133     // given instruction. If the given address is equal to the instruction
134     // offset, then use the current row. Or use the row with highest address
135     // less than the given address.
136     if (func_start_addr + entry.addr_offset > address)
137       break;
138 
139     uint32_t cfa_offset = entry.cfa_offset();
140     if (cfa_offset == 0)
141       return nullopt;
142     last_frame_entry.cfa_offset = cfa_offset;
143 
144     uint32_t ra_offset = entry.ra_offset();
145     // The RA offset of the last specified row should be used, if unspecified.
146     // Update |last_ra_offset| only if valid for this row. Otherwise, tthe last
147     // valid |last_ra_offset| is used. TODO(ssid): This should be fixed in the
148     // format and we should always output ra offset.
149     if (ra_offset)
150       last_frame_entry.ra_offset = ra_offset;
151 
152     if (last_frame_entry.ra_offset == 0)
153       return nullopt;
154   }
155 
156   return last_frame_entry;
157 }
158 
159 }  // namespace base
160