xref: /aosp_15_r20/external/cronet/base/profiler/chrome_unwinder_android.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2021 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "base/profiler/chrome_unwinder_android.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <algorithm>
8*6777b538SAndroid Build Coastguard Worker 
9*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/memory/aligned_memory.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/numerics/checked_math.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/profiler/chrome_unwind_info_android.h"
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker namespace base {
16*6777b538SAndroid Build Coastguard Worker namespace {
17*6777b538SAndroid Build Coastguard Worker 
GetRegisterPointer(RegisterContext * context,uint8_t register_index)18*6777b538SAndroid Build Coastguard Worker uintptr_t* GetRegisterPointer(RegisterContext* context,
19*6777b538SAndroid Build Coastguard Worker                               uint8_t register_index) {
20*6777b538SAndroid Build Coastguard Worker   DCHECK_LE(register_index, 15);
21*6777b538SAndroid Build Coastguard Worker   static unsigned long RegisterContext::*const registers[16] = {
22*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r0,  &RegisterContext::arm_r1,
23*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r2,  &RegisterContext::arm_r3,
24*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r4,  &RegisterContext::arm_r5,
25*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r6,  &RegisterContext::arm_r7,
26*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r8,  &RegisterContext::arm_r9,
27*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_r10, &RegisterContext::arm_fp,
28*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_ip,  &RegisterContext::arm_sp,
29*6777b538SAndroid Build Coastguard Worker       &RegisterContext::arm_lr,  &RegisterContext::arm_pc,
30*6777b538SAndroid Build Coastguard Worker   };
31*6777b538SAndroid Build Coastguard Worker   return reinterpret_cast<uintptr_t*>(&(context->*registers[register_index]));
32*6777b538SAndroid Build Coastguard Worker }
33*6777b538SAndroid Build Coastguard Worker 
34*6777b538SAndroid Build Coastguard Worker // Pops the value on the top of stack out and assign it to target register.
35*6777b538SAndroid Build Coastguard Worker // This is equivalent to arm instruction `Pop r[n]` where n = `register_index`.
36*6777b538SAndroid Build Coastguard Worker // Returns whether the pop is successful.
PopRegister(RegisterContext * context,uint8_t register_index)37*6777b538SAndroid Build Coastguard Worker bool PopRegister(RegisterContext* context, uint8_t register_index) {
38*6777b538SAndroid Build Coastguard Worker   const uintptr_t sp = RegisterContextStackPointer(context);
39*6777b538SAndroid Build Coastguard Worker   const uintptr_t stacktop_value = *reinterpret_cast<uintptr_t*>(sp);
40*6777b538SAndroid Build Coastguard Worker   const auto new_sp = CheckedNumeric<uintptr_t>(sp) + sizeof(uintptr_t);
41*6777b538SAndroid Build Coastguard Worker   const bool success =
42*6777b538SAndroid Build Coastguard Worker       new_sp.AssignIfValid(&RegisterContextStackPointer(context));
43*6777b538SAndroid Build Coastguard Worker   if (success)
44*6777b538SAndroid Build Coastguard Worker     *GetRegisterPointer(context, register_index) = stacktop_value;
45*6777b538SAndroid Build Coastguard Worker   return success;
46*6777b538SAndroid Build Coastguard Worker }
47*6777b538SAndroid Build Coastguard Worker 
48*6777b538SAndroid Build Coastguard Worker // Decodes the given bytes as an ULEB128 format number and advances the bytes
49*6777b538SAndroid Build Coastguard Worker // pointer by the size of ULEB128.
50*6777b538SAndroid Build Coastguard Worker //
51*6777b538SAndroid Build Coastguard Worker // This function assumes the given bytes are in valid ULEB128
52*6777b538SAndroid Build Coastguard Worker // format and the decoded number should not overflow `uintptr_t` type.
DecodeULEB128(const uint8_t * & bytes)53*6777b538SAndroid Build Coastguard Worker uintptr_t DecodeULEB128(const uint8_t*& bytes) {
54*6777b538SAndroid Build Coastguard Worker   uintptr_t value = 0;
55*6777b538SAndroid Build Coastguard Worker   unsigned shift = 0;
56*6777b538SAndroid Build Coastguard Worker   do {
57*6777b538SAndroid Build Coastguard Worker     DCHECK_LE(shift, sizeof(uintptr_t) * 8);  // ULEB128 must not overflow.
58*6777b538SAndroid Build Coastguard Worker     value += (*bytes & 0x7fu) << shift;
59*6777b538SAndroid Build Coastguard Worker     shift += 7;
60*6777b538SAndroid Build Coastguard Worker   } while (*bytes++ & 0x80);
61*6777b538SAndroid Build Coastguard Worker   return value;
62*6777b538SAndroid Build Coastguard Worker }
63*6777b538SAndroid Build Coastguard Worker 
GetTopBits(uint8_t byte,unsigned bits)64*6777b538SAndroid Build Coastguard Worker uint8_t GetTopBits(uint8_t byte, unsigned bits) {
65*6777b538SAndroid Build Coastguard Worker   DCHECK_LE(bits, 8u);
66*6777b538SAndroid Build Coastguard Worker   return byte >> (8 - bits);
67*6777b538SAndroid Build Coastguard Worker }
68*6777b538SAndroid Build Coastguard Worker 
69*6777b538SAndroid Build Coastguard Worker }  // namespace
70*6777b538SAndroid Build Coastguard Worker 
ChromeUnwinderAndroid(const ChromeUnwindInfoAndroid & unwind_info,uintptr_t chrome_module_base_address,uintptr_t text_section_start_address)71*6777b538SAndroid Build Coastguard Worker ChromeUnwinderAndroid::ChromeUnwinderAndroid(
72*6777b538SAndroid Build Coastguard Worker     const ChromeUnwindInfoAndroid& unwind_info,
73*6777b538SAndroid Build Coastguard Worker     uintptr_t chrome_module_base_address,
74*6777b538SAndroid Build Coastguard Worker     uintptr_t text_section_start_address)
75*6777b538SAndroid Build Coastguard Worker     : unwind_info_(unwind_info),
76*6777b538SAndroid Build Coastguard Worker       chrome_module_base_address_(chrome_module_base_address),
77*6777b538SAndroid Build Coastguard Worker       text_section_start_address_(text_section_start_address) {
78*6777b538SAndroid Build Coastguard Worker   DCHECK_GT(text_section_start_address_, chrome_module_base_address_);
79*6777b538SAndroid Build Coastguard Worker }
80*6777b538SAndroid Build Coastguard Worker 
CanUnwindFrom(const Frame & current_frame) const81*6777b538SAndroid Build Coastguard Worker bool ChromeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const {
82*6777b538SAndroid Build Coastguard Worker   return current_frame.module &&
83*6777b538SAndroid Build Coastguard Worker          current_frame.module->GetBaseAddress() == chrome_module_base_address_;
84*6777b538SAndroid Build Coastguard Worker }
85*6777b538SAndroid Build Coastguard Worker 
TryUnwind(RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)86*6777b538SAndroid Build Coastguard Worker UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
87*6777b538SAndroid Build Coastguard Worker                                               uintptr_t stack_top,
88*6777b538SAndroid Build Coastguard Worker                                               std::vector<Frame>* stack) {
89*6777b538SAndroid Build Coastguard Worker   DCHECK(CanUnwindFrom(stack->back()));
90*6777b538SAndroid Build Coastguard Worker   uintptr_t frame_initial_sp = RegisterContextStackPointer(thread_context);
91*6777b538SAndroid Build Coastguard Worker   const uintptr_t unwind_initial_pc =
92*6777b538SAndroid Build Coastguard Worker       RegisterContextInstructionPointer(thread_context);
93*6777b538SAndroid Build Coastguard Worker 
94*6777b538SAndroid Build Coastguard Worker   do {
95*6777b538SAndroid Build Coastguard Worker     const uintptr_t pc = RegisterContextInstructionPointer(thread_context);
96*6777b538SAndroid Build Coastguard Worker     const uintptr_t instruction_byte_offset_from_text_section_start =
97*6777b538SAndroid Build Coastguard Worker         pc - text_section_start_address_;
98*6777b538SAndroid Build Coastguard Worker 
99*6777b538SAndroid Build Coastguard Worker     const std::optional<FunctionOffsetTableIndex> function_offset_table_index =
100*6777b538SAndroid Build Coastguard Worker         GetFunctionTableIndexFromInstructionOffset(
101*6777b538SAndroid Build Coastguard Worker             unwind_info_.page_table, unwind_info_.function_table,
102*6777b538SAndroid Build Coastguard Worker             instruction_byte_offset_from_text_section_start);
103*6777b538SAndroid Build Coastguard Worker 
104*6777b538SAndroid Build Coastguard Worker     if (!function_offset_table_index) {
105*6777b538SAndroid Build Coastguard Worker       return UnwindResult::kAborted;
106*6777b538SAndroid Build Coastguard Worker     }
107*6777b538SAndroid Build Coastguard Worker 
108*6777b538SAndroid Build Coastguard Worker     const uint32_t current_unwind_instruction_index =
109*6777b538SAndroid Build Coastguard Worker         GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
110*6777b538SAndroid Build Coastguard Worker             &unwind_info_
111*6777b538SAndroid Build Coastguard Worker                  .function_offset_table[function_offset_table_index
112*6777b538SAndroid Build Coastguard Worker                                             ->function_offset_table_byte_index],
113*6777b538SAndroid Build Coastguard Worker             function_offset_table_index
114*6777b538SAndroid Build Coastguard Worker                 ->instruction_offset_from_function_start);
115*6777b538SAndroid Build Coastguard Worker 
116*6777b538SAndroid Build Coastguard Worker     const uint8_t* current_unwind_instruction =
117*6777b538SAndroid Build Coastguard Worker         &unwind_info_
118*6777b538SAndroid Build Coastguard Worker              .unwind_instruction_table[current_unwind_instruction_index];
119*6777b538SAndroid Build Coastguard Worker 
120*6777b538SAndroid Build Coastguard Worker     UnwindInstructionResult instruction_result;
121*6777b538SAndroid Build Coastguard Worker     bool pc_was_updated = false;
122*6777b538SAndroid Build Coastguard Worker 
123*6777b538SAndroid Build Coastguard Worker     do {
124*6777b538SAndroid Build Coastguard Worker       instruction_result = ExecuteUnwindInstruction(
125*6777b538SAndroid Build Coastguard Worker           current_unwind_instruction, pc_was_updated, thread_context);
126*6777b538SAndroid Build Coastguard Worker       const uintptr_t sp = RegisterContextStackPointer(thread_context);
127*6777b538SAndroid Build Coastguard Worker       if (sp > stack_top || sp < frame_initial_sp ||
128*6777b538SAndroid Build Coastguard Worker           !IsAligned(sp, sizeof(uintptr_t))) {
129*6777b538SAndroid Build Coastguard Worker         return UnwindResult::kAborted;
130*6777b538SAndroid Build Coastguard Worker       }
131*6777b538SAndroid Build Coastguard Worker     } while (instruction_result ==
132*6777b538SAndroid Build Coastguard Worker              UnwindInstructionResult::kInstructionPending);
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker     if (instruction_result == UnwindInstructionResult::kAborted) {
135*6777b538SAndroid Build Coastguard Worker       return UnwindResult::kAborted;
136*6777b538SAndroid Build Coastguard Worker     }
137*6777b538SAndroid Build Coastguard Worker 
138*6777b538SAndroid Build Coastguard Worker     DCHECK_EQ(instruction_result, UnwindInstructionResult::kCompleted);
139*6777b538SAndroid Build Coastguard Worker 
140*6777b538SAndroid Build Coastguard Worker     const uintptr_t new_sp = RegisterContextStackPointer(thread_context);
141*6777b538SAndroid Build Coastguard Worker     // Validate SP is properly aligned across frames.
142*6777b538SAndroid Build Coastguard Worker     // See
143*6777b538SAndroid Build Coastguard Worker     // https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/using-the-stack-in-aarch32-and-aarch64
144*6777b538SAndroid Build Coastguard Worker     // for SP alignment rules.
145*6777b538SAndroid Build Coastguard Worker     if (!IsAligned(new_sp, 2 * sizeof(uintptr_t))) {
146*6777b538SAndroid Build Coastguard Worker       return UnwindResult::kAborted;
147*6777b538SAndroid Build Coastguard Worker     }
148*6777b538SAndroid Build Coastguard Worker     // Validate that SP does not decrease across frames.
149*6777b538SAndroid Build Coastguard Worker     const bool is_leaf_frame = stack->size() == 1;
150*6777b538SAndroid Build Coastguard Worker     // Each frame unwind is expected to only pop from stack memory, which will
151*6777b538SAndroid Build Coastguard Worker     // cause sp to increase.
152*6777b538SAndroid Build Coastguard Worker     // Non-Leaf frames are expected to at least pop lr off stack, so sp is
153*6777b538SAndroid Build Coastguard Worker     // expected to strictly increase for non-leaf frames.
154*6777b538SAndroid Build Coastguard Worker     if (new_sp <= (is_leaf_frame ? frame_initial_sp - 1 : frame_initial_sp)) {
155*6777b538SAndroid Build Coastguard Worker       return UnwindResult::kAborted;
156*6777b538SAndroid Build Coastguard Worker     }
157*6777b538SAndroid Build Coastguard Worker 
158*6777b538SAndroid Build Coastguard Worker     // For leaf functions, if SP does not change, PC must change, otherwise,
159*6777b538SAndroid Build Coastguard Worker     // the overall execution state will be the same before/after the frame
160*6777b538SAndroid Build Coastguard Worker     // unwind.
161*6777b538SAndroid Build Coastguard Worker     if (is_leaf_frame && new_sp == frame_initial_sp &&
162*6777b538SAndroid Build Coastguard Worker         RegisterContextInstructionPointer(thread_context) ==
163*6777b538SAndroid Build Coastguard Worker             unwind_initial_pc) {
164*6777b538SAndroid Build Coastguard Worker       return UnwindResult::kAborted;
165*6777b538SAndroid Build Coastguard Worker     }
166*6777b538SAndroid Build Coastguard Worker 
167*6777b538SAndroid Build Coastguard Worker     frame_initial_sp = new_sp;
168*6777b538SAndroid Build Coastguard Worker 
169*6777b538SAndroid Build Coastguard Worker     stack->emplace_back(RegisterContextInstructionPointer(thread_context),
170*6777b538SAndroid Build Coastguard Worker                         module_cache()->GetModuleForAddress(
171*6777b538SAndroid Build Coastguard Worker                             RegisterContextInstructionPointer(thread_context)));
172*6777b538SAndroid Build Coastguard Worker   } while (CanUnwindFrom(stack->back()));
173*6777b538SAndroid Build Coastguard Worker   return UnwindResult::kUnrecognizedFrame;
174*6777b538SAndroid Build Coastguard Worker }
175*6777b538SAndroid Build Coastguard Worker 
ExecuteUnwindInstruction(const uint8_t * & instruction,bool & pc_was_updated,RegisterContext * thread_context)176*6777b538SAndroid Build Coastguard Worker UnwindInstructionResult ExecuteUnwindInstruction(
177*6777b538SAndroid Build Coastguard Worker     const uint8_t*& instruction,
178*6777b538SAndroid Build Coastguard Worker     bool& pc_was_updated,
179*6777b538SAndroid Build Coastguard Worker     RegisterContext* thread_context) {
180*6777b538SAndroid Build Coastguard Worker   if (GetTopBits(*instruction, 2) == 0b00) {
181*6777b538SAndroid Build Coastguard Worker     // 00xxxxxx
182*6777b538SAndroid Build Coastguard Worker     // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive.
183*6777b538SAndroid Build Coastguard Worker     const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4;
184*6777b538SAndroid Build Coastguard Worker 
185*6777b538SAndroid Build Coastguard Worker     const auto new_sp =
186*6777b538SAndroid Build Coastguard Worker         CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
187*6777b538SAndroid Build Coastguard Worker         offset;
188*6777b538SAndroid Build Coastguard Worker     if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
189*6777b538SAndroid Build Coastguard Worker       return UnwindInstructionResult::kAborted;
190*6777b538SAndroid Build Coastguard Worker     }
191*6777b538SAndroid Build Coastguard Worker   } else if (GetTopBits(*instruction, 2) == 0b01) {
192*6777b538SAndroid Build Coastguard Worker     // 01xxxxxx
193*6777b538SAndroid Build Coastguard Worker     // vsp = vsp - (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive.
194*6777b538SAndroid Build Coastguard Worker     const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4;
195*6777b538SAndroid Build Coastguard Worker     const auto new_sp =
196*6777b538SAndroid Build Coastguard Worker         CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) -
197*6777b538SAndroid Build Coastguard Worker         offset;
198*6777b538SAndroid Build Coastguard Worker     if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
199*6777b538SAndroid Build Coastguard Worker       return UnwindInstructionResult::kAborted;
200*6777b538SAndroid Build Coastguard Worker     }
201*6777b538SAndroid Build Coastguard Worker   } else if (GetTopBits(*instruction, 4) == 0b1001) {
202*6777b538SAndroid Build Coastguard Worker     // 1001nnnn (nnnn != 13,15)
203*6777b538SAndroid Build Coastguard Worker     // Set vsp = r[nnnn].
204*6777b538SAndroid Build Coastguard Worker     const uint8_t register_index = *instruction++ & 0b00001111;
205*6777b538SAndroid Build Coastguard Worker     DCHECK_NE(register_index, 13);  // Must not set sp to sp.
206*6777b538SAndroid Build Coastguard Worker     DCHECK_NE(register_index, 15);  // Must not set sp to pc.
207*6777b538SAndroid Build Coastguard Worker     // Note: We shouldn't have cases that are setting caller-saved registers
208*6777b538SAndroid Build Coastguard Worker     // using this instruction.
209*6777b538SAndroid Build Coastguard Worker     DCHECK_GE(register_index, 4);
210*6777b538SAndroid Build Coastguard Worker 
211*6777b538SAndroid Build Coastguard Worker     RegisterContextStackPointer(thread_context) =
212*6777b538SAndroid Build Coastguard Worker         *GetRegisterPointer(thread_context, register_index);
213*6777b538SAndroid Build Coastguard Worker   } else if (GetTopBits(*instruction, 5) == 0b10101) {
214*6777b538SAndroid Build Coastguard Worker     // 10101nnn
215*6777b538SAndroid Build Coastguard Worker     // Pop r4-r[4+nnn], r14
216*6777b538SAndroid Build Coastguard Worker     const uint8_t max_register_index = (*instruction++ & 0b00000111u) + 4;
217*6777b538SAndroid Build Coastguard Worker     for (uint8_t n = 4; n <= max_register_index; n++) {
218*6777b538SAndroid Build Coastguard Worker       if (!PopRegister(thread_context, n)) {
219*6777b538SAndroid Build Coastguard Worker         return UnwindInstructionResult::kAborted;
220*6777b538SAndroid Build Coastguard Worker       }
221*6777b538SAndroid Build Coastguard Worker     }
222*6777b538SAndroid Build Coastguard Worker     if (!PopRegister(thread_context, 14)) {
223*6777b538SAndroid Build Coastguard Worker       return UnwindInstructionResult::kAborted;
224*6777b538SAndroid Build Coastguard Worker     }
225*6777b538SAndroid Build Coastguard Worker   } else if (*instruction == 0b10000000 && *(instruction + 1) == 0) {
226*6777b538SAndroid Build Coastguard Worker     // 10000000 00000000
227*6777b538SAndroid Build Coastguard Worker     // Refuse to unwind.
228*6777b538SAndroid Build Coastguard Worker     instruction += 2;
229*6777b538SAndroid Build Coastguard Worker     return UnwindInstructionResult::kAborted;
230*6777b538SAndroid Build Coastguard Worker   } else if (GetTopBits(*instruction, 4) == 0b1000) {
231*6777b538SAndroid Build Coastguard Worker     const uint32_t register_bitmask =
232*6777b538SAndroid Build Coastguard Worker         ((*instruction & 0xfu) << 8) + *(instruction + 1);
233*6777b538SAndroid Build Coastguard Worker     instruction += 2;
234*6777b538SAndroid Build Coastguard Worker     // 1000iiii iiiiiiii
235*6777b538SAndroid Build Coastguard Worker     // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
236*6777b538SAndroid Build Coastguard Worker     for (uint8_t register_index = 4; register_index < 16; register_index++) {
237*6777b538SAndroid Build Coastguard Worker       if (register_bitmask & (1 << (register_index - 4))) {
238*6777b538SAndroid Build Coastguard Worker         if (!PopRegister(thread_context, register_index)) {
239*6777b538SAndroid Build Coastguard Worker           return UnwindInstructionResult::kAborted;
240*6777b538SAndroid Build Coastguard Worker         }
241*6777b538SAndroid Build Coastguard Worker       }
242*6777b538SAndroid Build Coastguard Worker     }
243*6777b538SAndroid Build Coastguard Worker     // If we set pc (r15) with value on stack, we should no longer copy lr to
244*6777b538SAndroid Build Coastguard Worker     // pc on COMPLETE.
245*6777b538SAndroid Build Coastguard Worker     pc_was_updated |= register_bitmask & (1 << (15 - 4));
246*6777b538SAndroid Build Coastguard Worker   } else if (*instruction == 0b10110000) {
247*6777b538SAndroid Build Coastguard Worker     // Finish
248*6777b538SAndroid Build Coastguard Worker     // Code 0xb0, Finish, copies VRS[r14] to VRS[r15] and also
249*6777b538SAndroid Build Coastguard Worker     // indicates that no further instructions are to be processed for this
250*6777b538SAndroid Build Coastguard Worker     // frame.
251*6777b538SAndroid Build Coastguard Worker 
252*6777b538SAndroid Build Coastguard Worker     instruction++;
253*6777b538SAndroid Build Coastguard Worker     // Only copy lr to pc when pc is not updated by other instructions before.
254*6777b538SAndroid Build Coastguard Worker     if (!pc_was_updated)
255*6777b538SAndroid Build Coastguard Worker       thread_context->arm_pc = thread_context->arm_lr;
256*6777b538SAndroid Build Coastguard Worker 
257*6777b538SAndroid Build Coastguard Worker     return UnwindInstructionResult::kCompleted;
258*6777b538SAndroid Build Coastguard Worker   } else if (*instruction == 0b10110010) {
259*6777b538SAndroid Build Coastguard Worker     // 10110010 uleb128
260*6777b538SAndroid Build Coastguard Worker     // vsp = vsp + 0x204 + (uleb128 << 2)
261*6777b538SAndroid Build Coastguard Worker     // (for vsp increments of 0x104-0x200, use 00xxxxxx twice)
262*6777b538SAndroid Build Coastguard Worker     instruction++;
263*6777b538SAndroid Build Coastguard Worker     const auto new_sp =
264*6777b538SAndroid Build Coastguard Worker         CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
265*6777b538SAndroid Build Coastguard Worker         (CheckedNumeric<uintptr_t>(DecodeULEB128(instruction)) << 2) + 0x204;
266*6777b538SAndroid Build Coastguard Worker 
267*6777b538SAndroid Build Coastguard Worker     if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
268*6777b538SAndroid Build Coastguard Worker       return UnwindInstructionResult::kAborted;
269*6777b538SAndroid Build Coastguard Worker     }
270*6777b538SAndroid Build Coastguard Worker   } else {
271*6777b538SAndroid Build Coastguard Worker     NOTREACHED();
272*6777b538SAndroid Build Coastguard Worker   }
273*6777b538SAndroid Build Coastguard Worker   return UnwindInstructionResult::kInstructionPending;
274*6777b538SAndroid Build Coastguard Worker }
275*6777b538SAndroid Build Coastguard Worker 
GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(const uint8_t * function_offset_table_entry,int instruction_offset_from_function_start)276*6777b538SAndroid Build Coastguard Worker uintptr_t GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
277*6777b538SAndroid Build Coastguard Worker     const uint8_t* function_offset_table_entry,
278*6777b538SAndroid Build Coastguard Worker     int instruction_offset_from_function_start) {
279*6777b538SAndroid Build Coastguard Worker   DCHECK_GE(instruction_offset_from_function_start, 0);
280*6777b538SAndroid Build Coastguard Worker   const uint8_t* current_function_offset_table_position =
281*6777b538SAndroid Build Coastguard Worker       function_offset_table_entry;
282*6777b538SAndroid Build Coastguard Worker 
283*6777b538SAndroid Build Coastguard Worker   do {
284*6777b538SAndroid Build Coastguard Worker     const uintptr_t function_offset =
285*6777b538SAndroid Build Coastguard Worker         DecodeULEB128(current_function_offset_table_position);
286*6777b538SAndroid Build Coastguard Worker 
287*6777b538SAndroid Build Coastguard Worker     const uintptr_t unwind_table_index =
288*6777b538SAndroid Build Coastguard Worker         DecodeULEB128(current_function_offset_table_position);
289*6777b538SAndroid Build Coastguard Worker 
290*6777b538SAndroid Build Coastguard Worker     // Each function always ends at 0 offset. It is guaranteed to find an entry
291*6777b538SAndroid Build Coastguard Worker     // as long as the function offset table is well-structured.
292*6777b538SAndroid Build Coastguard Worker     if (function_offset <=
293*6777b538SAndroid Build Coastguard Worker         static_cast<uint32_t>(instruction_offset_from_function_start))
294*6777b538SAndroid Build Coastguard Worker       return unwind_table_index;
295*6777b538SAndroid Build Coastguard Worker 
296*6777b538SAndroid Build Coastguard Worker   } while (true);
297*6777b538SAndroid Build Coastguard Worker 
298*6777b538SAndroid Build Coastguard Worker   NOTREACHED();
299*6777b538SAndroid Build Coastguard Worker   return 0;
300*6777b538SAndroid Build Coastguard Worker }
301*6777b538SAndroid Build Coastguard Worker 
302*6777b538SAndroid Build Coastguard Worker const std::optional<FunctionOffsetTableIndex>
GetFunctionTableIndexFromInstructionOffset(span<const uint32_t> page_start_instructions,span<const FunctionTableEntry> function_offset_table_indices,uint32_t instruction_byte_offset_from_text_section_start)303*6777b538SAndroid Build Coastguard Worker GetFunctionTableIndexFromInstructionOffset(
304*6777b538SAndroid Build Coastguard Worker     span<const uint32_t> page_start_instructions,
305*6777b538SAndroid Build Coastguard Worker     span<const FunctionTableEntry> function_offset_table_indices,
306*6777b538SAndroid Build Coastguard Worker     uint32_t instruction_byte_offset_from_text_section_start) {
307*6777b538SAndroid Build Coastguard Worker   DCHECK(!page_start_instructions.empty());
308*6777b538SAndroid Build Coastguard Worker   DCHECK(!function_offset_table_indices.empty());
309*6777b538SAndroid Build Coastguard Worker   // First function on first page should always start from 0 offset.
310*6777b538SAndroid Build Coastguard Worker   DCHECK_EQ(function_offset_table_indices.front()
311*6777b538SAndroid Build Coastguard Worker                 .function_start_address_page_instruction_offset,
312*6777b538SAndroid Build Coastguard Worker             0ul);
313*6777b538SAndroid Build Coastguard Worker 
314*6777b538SAndroid Build Coastguard Worker   const uint16_t page_number =
315*6777b538SAndroid Build Coastguard Worker       instruction_byte_offset_from_text_section_start >> 17;
316*6777b538SAndroid Build Coastguard Worker   const uint16_t page_instruction_offset =
317*6777b538SAndroid Build Coastguard Worker       (instruction_byte_offset_from_text_section_start >> 1) &
318*6777b538SAndroid Build Coastguard Worker       0xffff;  // 16 bits.
319*6777b538SAndroid Build Coastguard Worker 
320*6777b538SAndroid Build Coastguard Worker   // Invalid instruction_byte_offset_from_text_section_start:
321*6777b538SAndroid Build Coastguard Worker   // instruction_byte_offset_from_text_section_start falls after the last page.
322*6777b538SAndroid Build Coastguard Worker   if (page_number >= page_start_instructions.size()) {
323*6777b538SAndroid Build Coastguard Worker     return std::nullopt;
324*6777b538SAndroid Build Coastguard Worker   }
325*6777b538SAndroid Build Coastguard Worker 
326*6777b538SAndroid Build Coastguard Worker   const span<const FunctionTableEntry>::iterator function_table_entry_start =
327*6777b538SAndroid Build Coastguard Worker       function_offset_table_indices.begin() +
328*6777b538SAndroid Build Coastguard Worker       checked_cast<ptrdiff_t>(page_start_instructions[page_number]);
329*6777b538SAndroid Build Coastguard Worker   const span<const FunctionTableEntry>::iterator function_table_entry_end =
330*6777b538SAndroid Build Coastguard Worker       page_number == page_start_instructions.size() - 1
331*6777b538SAndroid Build Coastguard Worker           ? function_offset_table_indices.end()
332*6777b538SAndroid Build Coastguard Worker           : function_offset_table_indices.begin() +
333*6777b538SAndroid Build Coastguard Worker                 checked_cast<ptrdiff_t>(
334*6777b538SAndroid Build Coastguard Worker                     page_start_instructions[page_number + 1]);
335*6777b538SAndroid Build Coastguard Worker 
336*6777b538SAndroid Build Coastguard Worker   // `std::upper_bound` finds first element that > target in range
337*6777b538SAndroid Build Coastguard Worker   // [function_table_entry_start, function_table_entry_end).
338*6777b538SAndroid Build Coastguard Worker   const auto first_larger_entry_location = std::upper_bound(
339*6777b538SAndroid Build Coastguard Worker       function_table_entry_start, function_table_entry_end,
340*6777b538SAndroid Build Coastguard Worker       page_instruction_offset,
341*6777b538SAndroid Build Coastguard Worker       [](uint16_t page_instruction_offset, const FunctionTableEntry& entry) {
342*6777b538SAndroid Build Coastguard Worker         return page_instruction_offset <
343*6777b538SAndroid Build Coastguard Worker                entry.function_start_address_page_instruction_offset;
344*6777b538SAndroid Build Coastguard Worker       });
345*6777b538SAndroid Build Coastguard Worker 
346*6777b538SAndroid Build Coastguard Worker   // Offsets the element found by 1 to get the biggest element that <= target.
347*6777b538SAndroid Build Coastguard Worker   const auto entry_location = first_larger_entry_location - 1;
348*6777b538SAndroid Build Coastguard Worker 
349*6777b538SAndroid Build Coastguard Worker   // When all offsets in current range > page_instruction_offset (including when
350*6777b538SAndroid Build Coastguard Worker   // there is no entry in current range), the `FunctionTableEntry` we are
351*6777b538SAndroid Build Coastguard Worker   // looking for is not within the function_offset_table_indices range we are
352*6777b538SAndroid Build Coastguard Worker   // inspecting, because the function is too long that it spans multiple pages.
353*6777b538SAndroid Build Coastguard Worker   //
354*6777b538SAndroid Build Coastguard Worker   // We need to locate the previous entry on function_offset_table_indices and
355*6777b538SAndroid Build Coastguard Worker   // find its corresponding page_table index.
356*6777b538SAndroid Build Coastguard Worker   //
357*6777b538SAndroid Build Coastguard Worker   // Example:
358*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+
359*6777b538SAndroid Build Coastguard Worker   // | <-----2 byte-----> | <-----2 byte-----> |
360*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+
361*6777b538SAndroid Build Coastguard Worker   // | Page Offset        | Offset Table Index |
362*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+-----
363*6777b538SAndroid Build Coastguard Worker   // | 10                 | XXX                |  |
364*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+  |
365*6777b538SAndroid Build Coastguard Worker   // | ...                | ...                |Page 0x100
366*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+  |
367*6777b538SAndroid Build Coastguard Worker   // | 65500              | ZZZ                |  |
368*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+----- Page 0x101 is empty
369*6777b538SAndroid Build Coastguard Worker   // | 200                | AAA                |  |
370*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+  |
371*6777b538SAndroid Build Coastguard Worker   // | ...                | ...                |Page 0x102
372*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+  |
373*6777b538SAndroid Build Coastguard Worker   // | 65535              | BBB                |  |
374*6777b538SAndroid Build Coastguard Worker   // +--------------------+--------------------+-----
375*6777b538SAndroid Build Coastguard Worker   //
376*6777b538SAndroid Build Coastguard Worker   // Example:
377*6777b538SAndroid Build Coastguard Worker   // For
378*6777b538SAndroid Build Coastguard Worker   // - page_number = 0x100, page_instruction_offset >= 65535
379*6777b538SAndroid Build Coastguard Worker   // - page_number = 0x101, all page_instruction_offset
380*6777b538SAndroid Build Coastguard Worker   // - page_number = 0x102, page_instruction_offset < 200
381*6777b538SAndroid Build Coastguard Worker   // We should be able to map them all to entry [65500, ZZZ] in page 0x100.
382*6777b538SAndroid Build Coastguard Worker 
383*6777b538SAndroid Build Coastguard Worker   // Finds the page_number that corresponds to `entry_location`. The page
384*6777b538SAndroid Build Coastguard Worker   // might not be the page we are inspecting, when the function spans over
385*6777b538SAndroid Build Coastguard Worker   // multiple pages.
386*6777b538SAndroid Build Coastguard Worker   uint16_t function_start_page_number = page_number;
387*6777b538SAndroid Build Coastguard Worker   while (function_offset_table_indices.begin() +
388*6777b538SAndroid Build Coastguard Worker              checked_cast<ptrdiff_t>(
389*6777b538SAndroid Build Coastguard Worker                  page_start_instructions[function_start_page_number]) >
390*6777b538SAndroid Build Coastguard Worker          entry_location) {
391*6777b538SAndroid Build Coastguard Worker     // First page in page table must not be empty.
392*6777b538SAndroid Build Coastguard Worker     DCHECK_NE(function_start_page_number, 0);
393*6777b538SAndroid Build Coastguard Worker     function_start_page_number--;
394*6777b538SAndroid Build Coastguard Worker   };
395*6777b538SAndroid Build Coastguard Worker 
396*6777b538SAndroid Build Coastguard Worker   const uint32_t function_start_address_instruction_offset =
397*6777b538SAndroid Build Coastguard Worker       (uint32_t{function_start_page_number} << 16) +
398*6777b538SAndroid Build Coastguard Worker       entry_location->function_start_address_page_instruction_offset;
399*6777b538SAndroid Build Coastguard Worker 
400*6777b538SAndroid Build Coastguard Worker   const int instruction_offset_from_function_start =
401*6777b538SAndroid Build Coastguard Worker       static_cast<int>((instruction_byte_offset_from_text_section_start >> 1) -
402*6777b538SAndroid Build Coastguard Worker                        function_start_address_instruction_offset);
403*6777b538SAndroid Build Coastguard Worker 
404*6777b538SAndroid Build Coastguard Worker   DCHECK_GE(instruction_offset_from_function_start, 0);
405*6777b538SAndroid Build Coastguard Worker   return FunctionOffsetTableIndex{
406*6777b538SAndroid Build Coastguard Worker       instruction_offset_from_function_start,
407*6777b538SAndroid Build Coastguard Worker       entry_location->function_offset_table_byte_index,
408*6777b538SAndroid Build Coastguard Worker   };
409*6777b538SAndroid Build Coastguard Worker }
410*6777b538SAndroid Build Coastguard Worker 
411*6777b538SAndroid Build Coastguard Worker }  // namespace base
412