blob: da959ce8e651ee558dfb52333cf8ba4904c3441c [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2021 The Chromium Authors
Charlie Hud5c14032021-08-26 21:45:592// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Tushar Agarwaldcafb622022-11-30 17:32:275#include "base/profiler/chrome_unwinder_android.h"
Charlie Hud5c14032021-08-26 21:45:596
Charlie Hu235c6b72021-09-13 20:50:017#include <algorithm>
8
Charlie Hud5c14032021-08-26 21:45:599#include "base/check_op.h"
Charlie Hudaf45ec2022-04-05 17:40:2710#include "base/memory/aligned_memory.h"
Charlie Hud5c14032021-08-26 21:45:5911#include "base/notreached.h"
12#include "base/numerics/checked_math.h"
Charlie Hu027f8a12021-09-23 23:25:1913#include "base/profiler/chrome_unwind_info_android.h"
Charlie Hud5c14032021-08-26 21:45:5914
15namespace base {
16namespace {
17
18uintptr_t* GetRegisterPointer(RegisterContext* context,
19 uint8_t register_index) {
Charlie Hu01b7e662021-09-13 14:51:3620 DCHECK_LE(register_index, 15);
21 static unsigned long RegisterContext::*const registers[16] = {
Charlie Hud5c14032021-08-26 21:45:5922 &RegisterContext::arm_r0, &RegisterContext::arm_r1,
23 &RegisterContext::arm_r2, &RegisterContext::arm_r3,
24 &RegisterContext::arm_r4, &RegisterContext::arm_r5,
25 &RegisterContext::arm_r6, &RegisterContext::arm_r7,
26 &RegisterContext::arm_r8, &RegisterContext::arm_r9,
27 &RegisterContext::arm_r10, &RegisterContext::arm_fp,
28 &RegisterContext::arm_ip, &RegisterContext::arm_sp,
Charlie Hu01b7e662021-09-13 14:51:3629 &RegisterContext::arm_lr, &RegisterContext::arm_pc,
Charlie Hud5c14032021-08-26 21:45:5930 };
31 return reinterpret_cast<uintptr_t*>(&(context->*registers[register_index]));
32}
33
34// Pops the value on the top of stack out and assign it to target register.
35// This is equivalent to arm instruction `Pop r[n]` where n = `register_index`.
36// Returns whether the pop is successful.
37bool PopRegister(RegisterContext* context, uint8_t register_index) {
38 const uintptr_t sp = RegisterContextStackPointer(context);
39 const uintptr_t stacktop_value = *reinterpret_cast<uintptr_t*>(sp);
40 const auto new_sp = CheckedNumeric<uintptr_t>(sp) + sizeof(uintptr_t);
41 const bool success =
42 new_sp.AssignIfValid(&RegisterContextStackPointer(context));
43 if (success)
44 *GetRegisterPointer(context, register_index) = stacktop_value;
45 return success;
46}
47
48// Decodes the given bytes as an ULEB128 format number and advances the bytes
49// pointer by the size of ULEB128.
50//
51// This function assumes the given bytes are in valid ULEB128
52// format and the decoded number should not overflow `uintptr_t` type.
53uintptr_t DecodeULEB128(const uint8_t*& bytes) {
54 uintptr_t value = 0;
55 unsigned shift = 0;
56 do {
Charlie Hu25cce7d2021-09-02 19:29:5557 DCHECK_LE(shift, sizeof(uintptr_t) * 8); // ULEB128 must not overflow.
Peter Kasting6a4bf14c2022-07-13 14:53:3358 value += (*bytes & 0x7fu) << shift;
Charlie Hud5c14032021-08-26 21:45:5959 shift += 7;
60 } while (*bytes++ & 0x80);
61 return value;
62}
63
64uint8_t GetTopBits(uint8_t byte, unsigned bits) {
65 DCHECK_LE(bits, 8u);
66 return byte >> (8 - bits);
67}
68
69} // namespace
70
Tushar Agarwaldcafb622022-11-30 17:32:2771ChromeUnwinderAndroid::ChromeUnwinderAndroid(
Charlie Hu78e00402021-10-13 06:10:4372 const ChromeUnwindInfoAndroid& unwind_info,
73 uintptr_t chrome_module_base_address,
74 uintptr_t text_section_start_address)
75 : unwind_info_(unwind_info),
76 chrome_module_base_address_(chrome_module_base_address),
77 text_section_start_address_(text_section_start_address) {
78 DCHECK_GT(text_section_start_address_, chrome_module_base_address_);
79}
80
Tushar Agarwaldcafb622022-11-30 17:32:2781bool ChromeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const {
Charlie Hu78e00402021-10-13 06:10:4382 return current_frame.module &&
83 current_frame.module->GetBaseAddress() == chrome_module_base_address_;
84}
85
Tushar Agarwaldcafb622022-11-30 17:32:2786UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
87 uintptr_t stack_top,
88 std::vector<Frame>* stack) {
Charlie Hu78e00402021-10-13 06:10:4389 DCHECK(CanUnwindFrom(stack->back()));
Charlie Hudaf45ec2022-04-05 17:40:2790 uintptr_t frame_initial_sp = RegisterContextStackPointer(thread_context);
91 const uintptr_t unwind_initial_pc =
92 RegisterContextInstructionPointer(thread_context);
93
Charlie Hu78e00402021-10-13 06:10:4394 do {
95 const uintptr_t pc = RegisterContextInstructionPointer(thread_context);
Charlie Hud3028512021-10-13 18:34:2196 const uintptr_t instruction_byte_offset_from_text_section_start =
Charlie Hu78e00402021-10-13 06:10:4397 pc - text_section_start_address_;
98
99 const absl::optional<FunctionOffsetTableIndex> function_offset_table_index =
100 GetFunctionTableIndexFromInstructionOffset(
101 unwind_info_.page_table, unwind_info_.function_table,
Charlie Hud3028512021-10-13 18:34:21102 instruction_byte_offset_from_text_section_start);
Charlie Hu78e00402021-10-13 06:10:43103
104 if (!function_offset_table_index) {
Charlie Hua1d07b922021-11-02 16:55:13105 return UnwindResult::kAborted;
Charlie Hu78e00402021-10-13 06:10:43106 }
107
108 const uint32_t current_unwind_instruction_index =
109 GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
110 &unwind_info_
111 .function_offset_table[function_offset_table_index
112 ->function_offset_table_byte_index],
113 function_offset_table_index
114 ->instruction_offset_from_function_start);
115
116 const uint8_t* current_unwind_instruction =
117 &unwind_info_
118 .unwind_instruction_table[current_unwind_instruction_index];
119
120 UnwindInstructionResult instruction_result;
121 bool pc_was_updated = false;
122
123 do {
124 instruction_result = ExecuteUnwindInstruction(
125 current_unwind_instruction, pc_was_updated, thread_context);
Charlie Hudaf45ec2022-04-05 17:40:27126 const uintptr_t sp = RegisterContextStackPointer(thread_context);
127 if (sp > stack_top || sp < frame_initial_sp ||
128 !IsAligned(sp, sizeof(uintptr_t))) {
Charlie Hua1d07b922021-11-02 16:55:13129 return UnwindResult::kAborted;
Charlie Hu78e00402021-10-13 06:10:43130 }
131 } while (instruction_result ==
132 UnwindInstructionResult::kInstructionPending);
133
134 if (instruction_result == UnwindInstructionResult::kAborted) {
Charlie Hua1d07b922021-11-02 16:55:13135 return UnwindResult::kAborted;
Charlie Hu78e00402021-10-13 06:10:43136 }
137
138 DCHECK_EQ(instruction_result, UnwindInstructionResult::kCompleted);
Charlie Hudaf45ec2022-04-05 17:40:27139
140 const uintptr_t new_sp = RegisterContextStackPointer(thread_context);
141 // Validate SP is properly aligned across frames.
142 // See
143 // https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/using-the-stack-in-aarch32-and-aarch64
144 // for SP alignment rules.
145 if (!IsAligned(new_sp, 2 * sizeof(uintptr_t))) {
146 return UnwindResult::kAborted;
147 }
148 // Validate that SP does not decrease across frames.
149 const bool is_leaf_frame = stack->size() == 1;
150 // Each frame unwind is expected to only pop from stack memory, which will
151 // cause sp to increase.
152 // Non-Leaf frames are expected to at least pop lr off stack, so sp is
153 // expected to strictly increase for non-leaf frames.
154 if (new_sp <= (is_leaf_frame ? frame_initial_sp - 1 : frame_initial_sp)) {
155 return UnwindResult::kAborted;
156 }
157
158 // For leaf functions, if SP does not change, PC must change, otherwise,
159 // the overall execution state will be the same before/after the frame
160 // unwind.
161 if (is_leaf_frame && new_sp == frame_initial_sp &&
162 RegisterContextInstructionPointer(thread_context) ==
163 unwind_initial_pc) {
164 return UnwindResult::kAborted;
165 }
166
167 frame_initial_sp = new_sp;
168
Charlie Hu78e00402021-10-13 06:10:43169 stack->emplace_back(RegisterContextInstructionPointer(thread_context),
170 module_cache()->GetModuleForAddress(
171 RegisterContextInstructionPointer(thread_context)));
172 } while (CanUnwindFrom(stack->back()));
Charlie Hua1d07b922021-11-02 16:55:13173 return UnwindResult::kUnrecognizedFrame;
Charlie Hu78e00402021-10-13 06:10:43174}
175
Charlie Hud5c14032021-08-26 21:45:59176UnwindInstructionResult ExecuteUnwindInstruction(
177 const uint8_t*& instruction,
Charlie Hu01b7e662021-09-13 14:51:36178 bool& pc_was_updated,
Charlie Hud5c14032021-08-26 21:45:59179 RegisterContext* thread_context) {
180 if (GetTopBits(*instruction, 2) == 0b00) {
181 // 00xxxxxx
182 // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive.
Peter Kasting6a4bf14c2022-07-13 14:53:33183 const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4;
Charlie Hud5c14032021-08-26 21:45:59184
185 const auto new_sp =
186 CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
187 offset;
188 if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
Charlie Hu82c2ed352021-09-14 17:58:55189 return UnwindInstructionResult::kAborted;
Charlie Hud5c14032021-08-26 21:45:59190 }
191 } else if (GetTopBits(*instruction, 2) == 0b01) {
192 // 01xxxxxx
193 // vsp = vsp - (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive.
Peter Kasting6a4bf14c2022-07-13 14:53:33194 const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4;
Charlie Hud5c14032021-08-26 21:45:59195 const auto new_sp =
196 CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) -
197 offset;
198 if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
Charlie Hu82c2ed352021-09-14 17:58:55199 return UnwindInstructionResult::kAborted;
Charlie Hud5c14032021-08-26 21:45:59200 }
201 } else if (GetTopBits(*instruction, 4) == 0b1001) {
202 // 1001nnnn (nnnn != 13,15)
203 // Set vsp = r[nnnn].
204 const uint8_t register_index = *instruction++ & 0b00001111;
Charlie Hu25cce7d2021-09-02 19:29:55205 DCHECK_NE(register_index, 13); // Must not set sp to sp.
206 DCHECK_NE(register_index, 15); // Must not set sp to pc.
Charlie Hud5c14032021-08-26 21:45:59207 // Note: We shouldn't have cases that are setting caller-saved registers
208 // using this instruction.
Charlie Hu25cce7d2021-09-02 19:29:55209 DCHECK_GE(register_index, 4);
Charlie Hud5c14032021-08-26 21:45:59210
211 RegisterContextStackPointer(thread_context) =
212 *GetRegisterPointer(thread_context, register_index);
213 } else if (GetTopBits(*instruction, 5) == 0b10101) {
214 // 10101nnn
215 // Pop r4-r[4+nnn], r14
Peter Kasting6a4bf14c2022-07-13 14:53:33216 const uint8_t max_register_index = (*instruction++ & 0b00000111u) + 4;
217 for (uint8_t n = 4; n <= max_register_index; n++) {
Charlie Hu01b7e662021-09-13 14:51:36218 if (!PopRegister(thread_context, n)) {
Charlie Hu82c2ed352021-09-14 17:58:55219 return UnwindInstructionResult::kAborted;
Charlie Hu01b7e662021-09-13 14:51:36220 }
Charlie Hud5c14032021-08-26 21:45:59221 }
Charlie Hu01b7e662021-09-13 14:51:36222 if (!PopRegister(thread_context, 14)) {
Charlie Hu82c2ed352021-09-14 17:58:55223 return UnwindInstructionResult::kAborted;
Charlie Hu01b7e662021-09-13 14:51:36224 }
Charlie Hu82c2ed352021-09-14 17:58:55225 } else if (*instruction == 0b10000000 && *(instruction + 1) == 0) {
226 // 10000000 00000000
227 // Refuse to unwind.
228 instruction += 2;
229 return UnwindInstructionResult::kAborted;
Charlie Hu01b7e662021-09-13 14:51:36230 } else if (GetTopBits(*instruction, 4) == 0b1000) {
Charlie Hu01b7e662021-09-13 14:51:36231 const uint32_t register_bitmask =
Peter Kasting6a4bf14c2022-07-13 14:53:33232 ((*instruction & 0xfu) << 8) + *(instruction + 1);
Charlie Hu01b7e662021-09-13 14:51:36233 instruction += 2;
Charlie Hu82c2ed352021-09-14 17:58:55234 // 1000iiii iiiiiiii
235 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
Peter Kasting6a4bf14c2022-07-13 14:53:33236 for (uint8_t register_index = 4; register_index < 16; register_index++) {
Charlie Hu01b7e662021-09-13 14:51:36237 if (register_bitmask & (1 << (register_index - 4))) {
238 if (!PopRegister(thread_context, register_index)) {
Charlie Hu82c2ed352021-09-14 17:58:55239 return UnwindInstructionResult::kAborted;
Charlie Hu01b7e662021-09-13 14:51:36240 }
241 }
242 }
243 // If we set pc (r15) with value on stack, we should no longer copy lr to
244 // pc on COMPLETE.
245 pc_was_updated |= register_bitmask & (1 << (15 - 4));
Charlie Hud5c14032021-08-26 21:45:59246 } else if (*instruction == 0b10110000) {
247 // Finish
248 // Code 0xb0, Finish, copies VRS[r14] to VRS[r15] and also
249 // indicates that no further instructions are to be processed for this
250 // frame.
Charlie Hu01b7e662021-09-13 14:51:36251
Charlie Hud5c14032021-08-26 21:45:59252 instruction++;
Charlie Hu01b7e662021-09-13 14:51:36253 // Only copy lr to pc when pc is not updated by other instructions before.
254 if (!pc_was_updated)
255 thread_context->arm_pc = thread_context->arm_lr;
256
Charlie Hu82c2ed352021-09-14 17:58:55257 return UnwindInstructionResult::kCompleted;
Charlie Hud5c14032021-08-26 21:45:59258 } else if (*instruction == 0b10110010) {
259 // 10110010 uleb128
260 // vsp = vsp + 0x204 + (uleb128 << 2)
261 // (for vsp increments of 0x104-0x200, use 00xxxxxx twice)
262 instruction++;
263 const auto new_sp =
264 CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
265 (CheckedNumeric<uintptr_t>(DecodeULEB128(instruction)) << 2) + 0x204;
266
267 if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
Charlie Hu82c2ed352021-09-14 17:58:55268 return UnwindInstructionResult::kAborted;
Charlie Hud5c14032021-08-26 21:45:59269 }
270 } else {
271 NOTREACHED();
272 }
Charlie Hu82c2ed352021-09-14 17:58:55273 return UnwindInstructionResult::kInstructionPending;
Charlie Hud5c14032021-08-26 21:45:59274}
275
Charlie Hu78e00402021-10-13 06:10:43276uintptr_t GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
277 const uint8_t* function_offset_table_entry,
278 int instruction_offset_from_function_start) {
279 DCHECK_GE(instruction_offset_from_function_start, 0);
Charlie Huf36b2f62021-09-01 22:31:20280 const uint8_t* current_function_offset_table_position =
Charlie Hu78e00402021-10-13 06:10:43281 function_offset_table_entry;
Charlie Huf36b2f62021-09-01 22:31:20282
283 do {
284 const uintptr_t function_offset =
285 DecodeULEB128(current_function_offset_table_position);
286
287 const uintptr_t unwind_table_index =
288 DecodeULEB128(current_function_offset_table_position);
289
290 // Each function always ends at 0 offset. It is guaranteed to find an entry
291 // as long as the function offset table is well-structured.
Charlie Hu235c6b72021-09-13 20:50:01292 if (function_offset <=
Charlie Hu78e00402021-10-13 06:10:43293 static_cast<uint32_t>(instruction_offset_from_function_start))
294 return unwind_table_index;
Charlie Huf36b2f62021-09-01 22:31:20295
296 } while (true);
297
298 NOTREACHED();
Charlie Hu78e00402021-10-13 06:10:43299 return 0;
Charlie Huf36b2f62021-09-01 22:31:20300}
301
Charlie Hu235c6b72021-09-13 20:50:01302const absl::optional<FunctionOffsetTableIndex>
303GetFunctionTableIndexFromInstructionOffset(
304 span<const uint32_t> page_start_instructions,
305 span<const FunctionTableEntry> function_offset_table_indices,
Charlie Hud3028512021-10-13 18:34:21306 uint32_t instruction_byte_offset_from_text_section_start) {
Charlie Hu235c6b72021-09-13 20:50:01307 DCHECK(!page_start_instructions.empty());
308 DCHECK(!function_offset_table_indices.empty());
309 // First function on first page should always start from 0 offset.
310 DCHECK_EQ(function_offset_table_indices.front()
311 .function_start_address_page_instruction_offset,
312 0ul);
313
Charlie Hud3028512021-10-13 18:34:21314 const uint16_t page_number =
315 instruction_byte_offset_from_text_section_start >> 17;
Charlie Hu235c6b72021-09-13 20:50:01316 const uint16_t page_instruction_offset =
Charlie Hud3028512021-10-13 18:34:21317 (instruction_byte_offset_from_text_section_start >> 1) &
318 0xffff; // 16 bits.
Charlie Hu235c6b72021-09-13 20:50:01319
Charlie Hud3028512021-10-13 18:34:21320 // Invalid instruction_byte_offset_from_text_section_start:
321 // instruction_byte_offset_from_text_section_start falls after the last page.
Charlie Hu235c6b72021-09-13 20:50:01322 if (page_number >= page_start_instructions.size()) {
323 return absl::nullopt;
324 }
325
326 const span<const FunctionTableEntry>::const_iterator
Peter Kasting6a4bf14c2022-07-13 14:53:33327 function_table_entry_start =
328 function_offset_table_indices.begin() +
329 checked_cast<ptrdiff_t>(page_start_instructions[page_number]);
Charlie Hu235c6b72021-09-13 20:50:01330 const span<const FunctionTableEntry>::const_iterator
331 function_table_entry_end =
332 page_number == page_start_instructions.size() - 1
333 ? function_offset_table_indices.end()
334 : function_offset_table_indices.begin() +
Peter Kasting6a4bf14c2022-07-13 14:53:33335 checked_cast<ptrdiff_t>(
336 page_start_instructions[page_number + 1]);
Charlie Hu235c6b72021-09-13 20:50:01337
338 // `std::upper_bound` finds first element that > target in range
339 // [function_table_entry_start, function_table_entry_end).
340 const auto first_larger_entry_location = std::upper_bound(
341 function_table_entry_start, function_table_entry_end,
342 page_instruction_offset,
343 [](uint16_t page_instruction_offset, const FunctionTableEntry& entry) {
344 return page_instruction_offset <
345 entry.function_start_address_page_instruction_offset;
346 });
347
348 // Offsets the element found by 1 to get the biggest element that <= target.
349 const auto entry_location = first_larger_entry_location - 1;
350
351 // When all offsets in current range > page_instruction_offset (including when
352 // there is no entry in current range), the `FunctionTableEntry` we are
353 // looking for is not within the function_offset_table_indices range we are
354 // inspecting, because the function is too long that it spans multiple pages.
355 //
356 // We need to locate the previous entry on function_offset_table_indices and
357 // find its corresponding page_table index.
358 //
359 // Example:
360 // +--------------------+--------------------+
361 // | <-----2 byte-----> | <-----2 byte-----> |
362 // +--------------------+--------------------+
363 // | Page Offset | Offset Table Index |
364 // +--------------------+--------------------+-----
365 // | 10 | XXX | |
366 // +--------------------+--------------------+ |
367 // | ... | ... |Page 0x100
368 // +--------------------+--------------------+ |
369 // | 65500 | ZZZ | |
370 // +--------------------+--------------------+----- Page 0x101 is empty
371 // | 200 | AAA | |
372 // +--------------------+--------------------+ |
373 // | ... | ... |Page 0x102
374 // +--------------------+--------------------+ |
375 // | 65535 | BBB | |
376 // +--------------------+--------------------+-----
377 //
378 // Example:
379 // For
380 // - page_number = 0x100, page_instruction_offset >= 65535
381 // - page_number = 0x101, all page_instruction_offset
382 // - page_number = 0x102, page_instruction_offset < 200
383 // We should be able to map them all to entry [65500, ZZZ] in page 0x100.
384
385 // Finds the page_number that corresponds to `entry_location`. The page
386 // might not be the page we are inspecting, when the function spans over
387 // multiple pages.
388 uint16_t function_start_page_number = page_number;
389 while (function_offset_table_indices.begin() +
Peter Kasting6a4bf14c2022-07-13 14:53:33390 checked_cast<ptrdiff_t>(
391 page_start_instructions[function_start_page_number]) >
Charlie Hu235c6b72021-09-13 20:50:01392 entry_location) {
393 // First page in page table must not be empty.
394 DCHECK_NE(function_start_page_number, 0);
395 function_start_page_number--;
396 };
397
398 const uint32_t function_start_address_instruction_offset =
Peter Kasting6a4bf14c2022-07-13 14:53:33399 (uint32_t{function_start_page_number} << 16) +
Charlie Hu235c6b72021-09-13 20:50:01400 entry_location->function_start_address_page_instruction_offset;
401
402 const int instruction_offset_from_function_start =
Peter Kasting6a4bf14c2022-07-13 14:53:33403 static_cast<int>((instruction_byte_offset_from_text_section_start >> 1) -
404 function_start_address_instruction_offset);
Charlie Hu235c6b72021-09-13 20:50:01405
406 DCHECK_GE(instruction_offset_from_function_start, 0);
407 return FunctionOffsetTableIndex{
408 instruction_offset_from_function_start,
409 entry_location->function_offset_table_byte_index,
410 };
411}
412
Charlie Hu78e00402021-10-13 06:10:43413} // namespace base