Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2021 The Chromium Authors |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Tushar Agarwal | dcafb62 | 2022-11-30 17:32:27 | [diff] [blame^] | 5 | #include "base/profiler/chrome_unwinder_android.h" |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 6 | |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 9 | #include "base/check_op.h" |
Charlie Hu | daf45ec | 2022-04-05 17:40:27 | [diff] [blame] | 10 | #include "base/memory/aligned_memory.h" |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 11 | #include "base/notreached.h" |
| 12 | #include "base/numerics/checked_math.h" |
Charlie Hu | 027f8a1 | 2021-09-23 23:25:19 | [diff] [blame] | 13 | #include "base/profiler/chrome_unwind_info_android.h" |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 14 | |
| 15 | namespace base { |
| 16 | namespace { |
| 17 | |
| 18 | uintptr_t* GetRegisterPointer(RegisterContext* context, |
| 19 | uint8_t register_index) { |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 20 | DCHECK_LE(register_index, 15); |
| 21 | static unsigned long RegisterContext::*const registers[16] = { |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 22 | &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 Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 29 | &RegisterContext::arm_lr, &RegisterContext::arm_pc, |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 30 | }; |
| 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. |
| 37 | bool 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. |
| 53 | uintptr_t DecodeULEB128(const uint8_t*& bytes) { |
| 54 | uintptr_t value = 0; |
| 55 | unsigned shift = 0; |
| 56 | do { |
Charlie Hu | 25cce7d | 2021-09-02 19:29:55 | [diff] [blame] | 57 | DCHECK_LE(shift, sizeof(uintptr_t) * 8); // ULEB128 must not overflow. |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 58 | value += (*bytes & 0x7fu) << shift; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 59 | shift += 7; |
| 60 | } while (*bytes++ & 0x80); |
| 61 | return value; |
| 62 | } |
| 63 | |
| 64 | uint8_t GetTopBits(uint8_t byte, unsigned bits) { |
| 65 | DCHECK_LE(bits, 8u); |
| 66 | return byte >> (8 - bits); |
| 67 | } |
| 68 | |
| 69 | } // namespace |
| 70 | |
Tushar Agarwal | dcafb62 | 2022-11-30 17:32:27 | [diff] [blame^] | 71 | ChromeUnwinderAndroid::ChromeUnwinderAndroid( |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 72 | 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 Agarwal | dcafb62 | 2022-11-30 17:32:27 | [diff] [blame^] | 81 | bool ChromeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const { |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 82 | return current_frame.module && |
| 83 | current_frame.module->GetBaseAddress() == chrome_module_base_address_; |
| 84 | } |
| 85 | |
Tushar Agarwal | dcafb62 | 2022-11-30 17:32:27 | [diff] [blame^] | 86 | UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context, |
| 87 | uintptr_t stack_top, |
| 88 | std::vector<Frame>* stack) { |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 89 | DCHECK(CanUnwindFrom(stack->back())); |
Charlie Hu | daf45ec | 2022-04-05 17:40:27 | [diff] [blame] | 90 | uintptr_t frame_initial_sp = RegisterContextStackPointer(thread_context); |
| 91 | const uintptr_t unwind_initial_pc = |
| 92 | RegisterContextInstructionPointer(thread_context); |
| 93 | |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 94 | do { |
| 95 | const uintptr_t pc = RegisterContextInstructionPointer(thread_context); |
Charlie Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 96 | const uintptr_t instruction_byte_offset_from_text_section_start = |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 97 | 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 Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 102 | instruction_byte_offset_from_text_section_start); |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 103 | |
| 104 | if (!function_offset_table_index) { |
Charlie Hu | a1d07b92 | 2021-11-02 16:55:13 | [diff] [blame] | 105 | return UnwindResult::kAborted; |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 106 | } |
| 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 Hu | daf45ec | 2022-04-05 17:40:27 | [diff] [blame] | 126 | const uintptr_t sp = RegisterContextStackPointer(thread_context); |
| 127 | if (sp > stack_top || sp < frame_initial_sp || |
| 128 | !IsAligned(sp, sizeof(uintptr_t))) { |
Charlie Hu | a1d07b92 | 2021-11-02 16:55:13 | [diff] [blame] | 129 | return UnwindResult::kAborted; |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 130 | } |
| 131 | } while (instruction_result == |
| 132 | UnwindInstructionResult::kInstructionPending); |
| 133 | |
| 134 | if (instruction_result == UnwindInstructionResult::kAborted) { |
Charlie Hu | a1d07b92 | 2021-11-02 16:55:13 | [diff] [blame] | 135 | return UnwindResult::kAborted; |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | DCHECK_EQ(instruction_result, UnwindInstructionResult::kCompleted); |
Charlie Hu | daf45ec | 2022-04-05 17:40:27 | [diff] [blame] | 139 | |
| 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 Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 169 | stack->emplace_back(RegisterContextInstructionPointer(thread_context), |
| 170 | module_cache()->GetModuleForAddress( |
| 171 | RegisterContextInstructionPointer(thread_context))); |
| 172 | } while (CanUnwindFrom(stack->back())); |
Charlie Hu | a1d07b92 | 2021-11-02 16:55:13 | [diff] [blame] | 173 | return UnwindResult::kUnrecognizedFrame; |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 174 | } |
| 175 | |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 176 | UnwindInstructionResult ExecuteUnwindInstruction( |
| 177 | const uint8_t*& instruction, |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 178 | bool& pc_was_updated, |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 179 | RegisterContext* thread_context) { |
| 180 | if (GetTopBits(*instruction, 2) == 0b00) { |
| 181 | // 00xxxxxx |
| 182 | // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive. |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 183 | const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 184 | |
| 185 | const auto new_sp = |
| 186 | CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) + |
| 187 | offset; |
| 188 | if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) { |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 189 | return UnwindInstructionResult::kAborted; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 190 | } |
| 191 | } else if (GetTopBits(*instruction, 2) == 0b01) { |
| 192 | // 01xxxxxx |
| 193 | // vsp = vsp - (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive. |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 194 | const uintptr_t offset = ((*instruction++ & 0b00111111u) << 2) + 4; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 195 | const auto new_sp = |
| 196 | CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) - |
| 197 | offset; |
| 198 | if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) { |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 199 | return UnwindInstructionResult::kAborted; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 200 | } |
| 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 Hu | 25cce7d | 2021-09-02 19:29:55 | [diff] [blame] | 205 | DCHECK_NE(register_index, 13); // Must not set sp to sp. |
| 206 | DCHECK_NE(register_index, 15); // Must not set sp to pc. |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 207 | // Note: We shouldn't have cases that are setting caller-saved registers |
| 208 | // using this instruction. |
Charlie Hu | 25cce7d | 2021-09-02 19:29:55 | [diff] [blame] | 209 | DCHECK_GE(register_index, 4); |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 210 | |
| 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 Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 216 | const uint8_t max_register_index = (*instruction++ & 0b00000111u) + 4; |
| 217 | for (uint8_t n = 4; n <= max_register_index; n++) { |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 218 | if (!PopRegister(thread_context, n)) { |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 219 | return UnwindInstructionResult::kAborted; |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 220 | } |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 221 | } |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 222 | if (!PopRegister(thread_context, 14)) { |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 223 | return UnwindInstructionResult::kAborted; |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 224 | } |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 225 | } else if (*instruction == 0b10000000 && *(instruction + 1) == 0) { |
| 226 | // 10000000 00000000 |
| 227 | // Refuse to unwind. |
| 228 | instruction += 2; |
| 229 | return UnwindInstructionResult::kAborted; |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 230 | } else if (GetTopBits(*instruction, 4) == 0b1000) { |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 231 | const uint32_t register_bitmask = |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 232 | ((*instruction & 0xfu) << 8) + *(instruction + 1); |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 233 | instruction += 2; |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 234 | // 1000iiii iiiiiiii |
| 235 | // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 236 | for (uint8_t register_index = 4; register_index < 16; register_index++) { |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 237 | if (register_bitmask & (1 << (register_index - 4))) { |
| 238 | if (!PopRegister(thread_context, register_index)) { |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 239 | return UnwindInstructionResult::kAborted; |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 240 | } |
| 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 Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 246 | } 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 Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 251 | |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 252 | instruction++; |
Charlie Hu | 01b7e66 | 2021-09-13 14:51:36 | [diff] [blame] | 253 | // 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 Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 257 | return UnwindInstructionResult::kCompleted; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 258 | } 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 Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 268 | return UnwindInstructionResult::kAborted; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 269 | } |
| 270 | } else { |
| 271 | NOTREACHED(); |
| 272 | } |
Charlie Hu | 82c2ed35 | 2021-09-14 17:58:55 | [diff] [blame] | 273 | return UnwindInstructionResult::kInstructionPending; |
Charlie Hu | d5c1403 | 2021-08-26 21:45:59 | [diff] [blame] | 274 | } |
| 275 | |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 276 | uintptr_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 Hu | f36b2f6 | 2021-09-01 22:31:20 | [diff] [blame] | 280 | const uint8_t* current_function_offset_table_position = |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 281 | function_offset_table_entry; |
Charlie Hu | f36b2f6 | 2021-09-01 22:31:20 | [diff] [blame] | 282 | |
| 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 Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 292 | if (function_offset <= |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 293 | static_cast<uint32_t>(instruction_offset_from_function_start)) |
| 294 | return unwind_table_index; |
Charlie Hu | f36b2f6 | 2021-09-01 22:31:20 | [diff] [blame] | 295 | |
| 296 | } while (true); |
| 297 | |
| 298 | NOTREACHED(); |
Charlie Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 299 | return 0; |
Charlie Hu | f36b2f6 | 2021-09-01 22:31:20 | [diff] [blame] | 300 | } |
| 301 | |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 302 | const absl::optional<FunctionOffsetTableIndex> |
| 303 | GetFunctionTableIndexFromInstructionOffset( |
| 304 | span<const uint32_t> page_start_instructions, |
| 305 | span<const FunctionTableEntry> function_offset_table_indices, |
Charlie Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 306 | uint32_t instruction_byte_offset_from_text_section_start) { |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 307 | 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 Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 314 | const uint16_t page_number = |
| 315 | instruction_byte_offset_from_text_section_start >> 17; |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 316 | const uint16_t page_instruction_offset = |
Charlie Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 317 | (instruction_byte_offset_from_text_section_start >> 1) & |
| 318 | 0xffff; // 16 bits. |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 319 | |
Charlie Hu | d302851 | 2021-10-13 18:34:21 | [diff] [blame] | 320 | // Invalid instruction_byte_offset_from_text_section_start: |
| 321 | // instruction_byte_offset_from_text_section_start falls after the last page. |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 322 | if (page_number >= page_start_instructions.size()) { |
| 323 | return absl::nullopt; |
| 324 | } |
| 325 | |
| 326 | const span<const FunctionTableEntry>::const_iterator |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 327 | function_table_entry_start = |
| 328 | function_offset_table_indices.begin() + |
| 329 | checked_cast<ptrdiff_t>(page_start_instructions[page_number]); |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 330 | 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 Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 335 | checked_cast<ptrdiff_t>( |
| 336 | page_start_instructions[page_number + 1]); |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 337 | |
| 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 Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 390 | checked_cast<ptrdiff_t>( |
| 391 | page_start_instructions[function_start_page_number]) > |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 392 | 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 Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 399 | (uint32_t{function_start_page_number} << 16) + |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 400 | entry_location->function_start_address_page_instruction_offset; |
| 401 | |
| 402 | const int instruction_offset_from_function_start = |
Peter Kasting | 6a4bf14c | 2022-07-13 14:53:33 | [diff] [blame] | 403 | static_cast<int>((instruction_byte_offset_from_text_section_start >> 1) - |
| 404 | function_start_address_instruction_offset); |
Charlie Hu | 235c6b7 | 2021-09-13 20:50:01 | [diff] [blame] | 405 | |
| 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 Hu | 78e0040 | 2021-10-13 06:10:43 | [diff] [blame] | 413 | } // namespace base |