[go: nahoru, domu]

blob: 8995b9c130e2d07756c7ca94b1069d47721c1a8d [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2019 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 Hudaf45ec2022-04-05 17:40:277#include "base/memory/aligned_memory.h"
Charlie Hu027f8a12021-09-23 23:25:198#include "base/profiler/chrome_unwind_info_android.h"
Leonard Grey412c54862022-02-04 19:37:369#include "base/profiler/stack_sampling_profiler_test_util.h"
Peter Kasting50039f32022-10-28 16:49:2210#include "base/ranges/algorithm.h"
Charlie Hud5c14032021-08-26 21:45:5911#include "base/test/gtest_util.h"
12#include "build/build_config.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15namespace base {
16
17TEST(ChromeAndroidUnwindInstructionTest,
18 TestSmallStackPointerIncrementMinValue) {
19 RegisterContext thread_context = {};
20 const uint8_t instruction = 0b00000000;
21 const uint8_t* current_instruction = &instruction;
22 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:3623 bool pc_was_updated = false;
24 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
25 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:5526 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:3627 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:5928 ASSERT_EQ(current_instruction, &instruction + 1);
29 EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
30}
31
32TEST(ChromeAndroidUnwindInstructionTest,
33 TestSmallStackPointerIncrementMidValue) {
34 // xxxxxx = 4; vsp = vsp + (4 << 2) + 4 = vsp + 16 + 4 = vsp + 0x14.
35 RegisterContext thread_context = {};
36 const uint8_t instruction = 0b00000100;
37 const uint8_t* current_instruction = &instruction;
38 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:3639 bool pc_was_updated = false;
40 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
41 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:5542 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:3643 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:5944 ASSERT_EQ(current_instruction, &instruction + 1);
45 EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
46}
47
48TEST(ChromeAndroidUnwindInstructionTest,
49 TestSmallStackPointerIncrementMaxValue) {
50 RegisterContext thread_context = {};
51 const uint8_t instruction = 0b00111111;
52 const uint8_t* current_instruction = &instruction;
53 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:3654 bool pc_was_updated = false;
55 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
56 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:5557 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:3658 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:5959 ASSERT_EQ(current_instruction, &instruction + 1);
60 EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
61}
62
63TEST(ChromeAndroidUnwindInstructionTest,
64 TestSmallStackPointerIncrementOverflow) {
65 RegisterContext thread_context = {};
66 const uint8_t instruction = 0b00111111;
67 const uint8_t* current_instruction = &instruction;
68 thread_context.arm_sp = 0xffffffff;
Charlie Hu01b7e662021-09-13 14:51:3669 bool pc_was_updated = false;
70 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
71 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:5572 UnwindInstructionResult::kAborted);
Charlie Hud5c14032021-08-26 21:45:5973 ASSERT_EQ(current_instruction, &instruction + 1);
74 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
75}
76
77TEST(ChromeAndroidUnwindInstructionTest,
78 TestSmallStackPointerDecrementMinValue) {
79 RegisterContext thread_context = {};
80 const uint8_t instruction = 0b01000000;
81 const uint8_t* current_instruction = &instruction;
82 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:3683 bool pc_was_updated = false;
84 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
85 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:5586 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:3687 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:5988 ASSERT_EQ(current_instruction, &instruction + 1);
89 EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
90}
91
92TEST(ChromeAndroidUnwindInstructionTest,
93 TestSmallStackPointerDecrementMidValue) {
94 // xxxxxx = 4; vsp = vsp - (4 << 2) - 4 = vsp - 16 - 4 = vsp - 0x14.
95 RegisterContext thread_context = {};
96 const uint8_t instruction = 0b01000100;
97 const uint8_t* current_instruction = &instruction;
98 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:3699 bool pc_was_updated = false;
100 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
101 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55102 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36103 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59104 ASSERT_EQ(current_instruction, &instruction + 1);
105 EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
106}
107
108TEST(ChromeAndroidUnwindInstructionTest,
109 TestSmallStackPointerDecrementMaxValue) {
110 RegisterContext thread_context = {};
111 const uint8_t instruction = 0b01111111;
112 const uint8_t* current_instruction = &instruction;
113 thread_context.arm_sp = 0x10000000;
Charlie Hu01b7e662021-09-13 14:51:36114 bool pc_was_updated = false;
115 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
116 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55117 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36118 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59119 ASSERT_EQ(current_instruction, &instruction + 1);
120 EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
121}
122
123TEST(ChromeAndroidUnwindInstructionTest,
124 TestSmallStackPointerDecrementUnderflow) {
125 RegisterContext thread_context = {};
126 const uint8_t instruction = 0b01111111;
127 const uint8_t* current_instruction = &instruction;
128 thread_context.arm_sp = 0x00000000;
Charlie Hu01b7e662021-09-13 14:51:36129 bool pc_was_updated = false;
130 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
131 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55132 UnwindInstructionResult::kAborted);
Charlie Hu01b7e662021-09-13 14:51:36133 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59134 ASSERT_EQ(current_instruction, &instruction + 1);
135 EXPECT_EQ(0x0ul, thread_context.arm_sp);
136}
137
138using ChromeAndroidUnwindSetStackPointerFromRegisterValueTest =
139 ::testing::TestWithParam<uint8_t>;
140
141INSTANTIATE_TEST_SUITE_P(
142 All,
143 ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
144 // The function should set all registers except
145 // - callee saved registers (r0, r1, r2, r3)
146 // - sp (r13)
147 // - pc (r15)
148 ::testing::Values(4, 5, 6, 7, 8, 9, 10, 11, 12, 14));
149
150TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
151 TestSetStackPointerFromRegisterValue) {
152 const uint8_t register_index = GetParam();
153
154 RegisterContext thread_context = {};
155 thread_context.arm_r0 = 100;
156 thread_context.arm_r1 = 101;
157 thread_context.arm_r2 = 102;
158 thread_context.arm_r3 = 103;
159 thread_context.arm_r4 = 104;
160 thread_context.arm_r5 = 105;
161 thread_context.arm_r6 = 106;
162 thread_context.arm_r7 = 107;
163 thread_context.arm_r8 = 108;
164 thread_context.arm_r9 = 109;
165 thread_context.arm_r10 = 110;
166 thread_context.arm_fp = 111; // r11
167 thread_context.arm_ip = 112; // r12
168 thread_context.arm_lr = 114; // r14
169
170 const uint8_t instruction = 0b10010000 + register_index;
171 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36172 bool pc_was_updated = false;
173 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
174 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55175 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36176 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59177 ASSERT_EQ(current_instruction, &instruction + 1);
178 EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
179}
180
Charlie Hu01b7e662021-09-13 14:51:36181TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
Charlie Hud5c14032021-08-26 21:45:59182 RegisterContext thread_context = {};
183 thread_context.arm_lr = 114; // r14
184 thread_context.arm_pc = 115; // r15
Charlie Hud5c14032021-08-26 21:45:59185 const uint8_t instruction = 0b10110000;
186 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36187 bool pc_was_updated = false;
188 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
189 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55190 UnwindInstructionResult::kCompleted);
Charlie Hud5c14032021-08-26 21:45:59191 ASSERT_EQ(current_instruction, &instruction + 1);
192 EXPECT_EQ(114ul, thread_context.arm_pc);
193}
194
Charlie Hu01b7e662021-09-13 14:51:36195TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
196 RegisterContext thread_context = {};
197 thread_context.arm_lr = 114; // r14
198 thread_context.arm_pc = 115; // r15
199 const uint8_t instruction = 0b10110000;
200 const uint8_t* current_instruction = &instruction;
201 bool pc_was_updated = true;
202 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
203 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55204 UnwindInstructionResult::kCompleted);
Charlie Hu01b7e662021-09-13 14:51:36205 ASSERT_EQ(current_instruction, &instruction + 1);
206 EXPECT_EQ(115ul, thread_context.arm_pc);
207}
208
209TEST(ChromeAndroidUnwindInstructionTest,
210 TestPopDiscontinuousRegistersIncludingPC) {
211 RegisterContext thread_context = {};
212
213 thread_context.arm_r0 = 100;
214 thread_context.arm_r1 = 101;
215 thread_context.arm_r2 = 102;
216 thread_context.arm_r3 = 103;
217 thread_context.arm_r4 = 104;
218 thread_context.arm_r5 = 105;
219 thread_context.arm_r6 = 106;
220 thread_context.arm_r7 = 107;
221 thread_context.arm_r8 = 108;
222 thread_context.arm_r9 = 109;
223 thread_context.arm_r10 = 110;
224 thread_context.arm_fp = 111;
225 thread_context.arm_ip = 112;
226 thread_context.arm_lr = 113;
227 thread_context.arm_pc = 114;
228
229 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
230 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
231
232 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
233 // Pop r15, r12, r8, r4.
234 const uint8_t instruction[] = {0b10001001, 0b00010001};
235 const uint8_t* current_instruction = instruction;
236
237 bool pc_was_updated = false;
238 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
239 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55240 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36241 EXPECT_TRUE(pc_was_updated);
242 ASSERT_EQ(current_instruction, instruction + 2);
243 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4), thread_context.arm_sp);
244
245 EXPECT_EQ(100ul, thread_context.arm_r0);
246 EXPECT_EQ(101ul, thread_context.arm_r1);
247 EXPECT_EQ(102ul, thread_context.arm_r2);
248 EXPECT_EQ(103ul, thread_context.arm_r3);
249 EXPECT_EQ(1ul, thread_context.arm_r4);
250 EXPECT_EQ(105ul, thread_context.arm_r5);
251 EXPECT_EQ(106ul, thread_context.arm_r6);
252 EXPECT_EQ(107ul, thread_context.arm_r7);
253 EXPECT_EQ(2ul, thread_context.arm_r8);
254 EXPECT_EQ(109ul, thread_context.arm_r9);
255 EXPECT_EQ(110ul, thread_context.arm_r10);
256 EXPECT_EQ(111ul, thread_context.arm_fp);
257 EXPECT_EQ(3ul, thread_context.arm_ip);
258 EXPECT_EQ(113ul, thread_context.arm_lr);
259 EXPECT_EQ(4ul, thread_context.arm_pc);
260}
261
262TEST(ChromeAndroidUnwindInstructionTest, TestPopDiscontinuousRegisters) {
263 RegisterContext thread_context = {};
264
265 thread_context.arm_r0 = 100;
266 thread_context.arm_r1 = 101;
267 thread_context.arm_r2 = 102;
268 thread_context.arm_r3 = 103;
269 thread_context.arm_r4 = 104;
270 thread_context.arm_r5 = 105;
271 thread_context.arm_r6 = 106;
272 thread_context.arm_r7 = 107;
273 thread_context.arm_r8 = 108;
274 thread_context.arm_r9 = 109;
275 thread_context.arm_r10 = 110;
276 thread_context.arm_fp = 111;
277 thread_context.arm_ip = 112;
278 thread_context.arm_lr = 113;
279 thread_context.arm_pc = 114;
280
281 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
282 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
283
284 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
285 // Pop r12, r8, r4.
286 const uint8_t instruction[] = {0b10000001, 0b00010001};
287 const uint8_t* current_instruction = instruction;
288
289 bool pc_was_updated = false;
290 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
291 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55292 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36293 EXPECT_FALSE(pc_was_updated);
294 ASSERT_EQ(current_instruction, instruction + 2);
295 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3), thread_context.arm_sp);
296
297 EXPECT_EQ(100ul, thread_context.arm_r0);
298 EXPECT_EQ(101ul, thread_context.arm_r1);
299 EXPECT_EQ(102ul, thread_context.arm_r2);
300 EXPECT_EQ(103ul, thread_context.arm_r3);
301 EXPECT_EQ(1ul, thread_context.arm_r4);
302 EXPECT_EQ(105ul, thread_context.arm_r5);
303 EXPECT_EQ(106ul, thread_context.arm_r6);
304 EXPECT_EQ(107ul, thread_context.arm_r7);
305 EXPECT_EQ(2ul, thread_context.arm_r8);
306 EXPECT_EQ(109ul, thread_context.arm_r9);
307 EXPECT_EQ(110ul, thread_context.arm_r10);
308 EXPECT_EQ(111ul, thread_context.arm_fp);
309 EXPECT_EQ(3ul, thread_context.arm_ip);
310 EXPECT_EQ(113ul, thread_context.arm_lr);
311 EXPECT_EQ(114ul, thread_context.arm_pc);
312}
313
314TEST(ChromeAndroidUnwindInstructionTest,
315 TestPopDiscontinuousRegistersOverflow) {
316 RegisterContext thread_context = {};
317
318 thread_context.arm_r0 = 100;
319 thread_context.arm_r1 = 101;
320 thread_context.arm_r2 = 102;
321 thread_context.arm_r3 = 103;
322 thread_context.arm_r4 = 104;
323 thread_context.arm_r5 = 105;
324 thread_context.arm_r6 = 106;
325 thread_context.arm_r7 = 107;
326 thread_context.arm_r8 = 108;
327 thread_context.arm_r9 = 109;
328 thread_context.arm_r10 = 110;
329 thread_context.arm_fp = 111;
330 thread_context.arm_ip = 112;
331 thread_context.arm_lr = 113;
332 thread_context.arm_pc = 114;
333
334 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
335 thread_context.arm_sp = 0xffffffff;
336 // Pop r15, r12, r8, r4.
337 const uint8_t instruction[] = {0b10001001, 0b00010001};
338 const uint8_t* current_instruction = instruction;
339
340 bool pc_was_updated = false;
341 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
342 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55343 UnwindInstructionResult::kAborted);
Charlie Hu01b7e662021-09-13 14:51:36344 EXPECT_FALSE(pc_was_updated);
345 ASSERT_EQ(current_instruction, instruction + 2);
346 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
347
348 EXPECT_EQ(100ul, thread_context.arm_r0);
349 EXPECT_EQ(101ul, thread_context.arm_r1);
350 EXPECT_EQ(102ul, thread_context.arm_r2);
351 EXPECT_EQ(103ul, thread_context.arm_r3);
352 EXPECT_EQ(104ul, thread_context.arm_r4);
353 EXPECT_EQ(105ul, thread_context.arm_r5);
354 EXPECT_EQ(106ul, thread_context.arm_r6);
355 EXPECT_EQ(107ul, thread_context.arm_r7);
356 EXPECT_EQ(108ul, thread_context.arm_r8);
357 EXPECT_EQ(109ul, thread_context.arm_r9);
358 EXPECT_EQ(110ul, thread_context.arm_r10);
359 EXPECT_EQ(111ul, thread_context.arm_fp);
360 EXPECT_EQ(112ul, thread_context.arm_ip);
361 EXPECT_EQ(113ul, thread_context.arm_lr);
362 EXPECT_EQ(114ul, thread_context.arm_pc);
363}
364
Charlie Hu82c2ed352021-09-14 17:58:55365TEST(ChromeAndroidUnwindInstructionTest, TestRefuseToUnwind) {
366 RegisterContext thread_context = {};
367
368 const uint8_t instruction[] = {0b10000000, 0b0};
369 const uint8_t* current_instruction = instruction;
370
371 bool pc_was_updated = false;
372 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
373 &thread_context),
374 UnwindInstructionResult::kAborted);
375 EXPECT_FALSE(pc_was_updated);
376 ASSERT_EQ(current_instruction, instruction + 2);
377}
378
Charlie Hud5c14032021-08-26 21:45:59379TEST(ChromeAndroidUnwindInstructionTest,
380 TestPopRegistersIncludingR14MinRegisters) {
381 RegisterContext thread_context = {};
382
383 thread_context.arm_r0 = 100;
384 thread_context.arm_r1 = 101;
385 thread_context.arm_r2 = 102;
386 thread_context.arm_r3 = 103;
387 thread_context.arm_r4 = 104;
388 thread_context.arm_r5 = 105;
389 thread_context.arm_r6 = 106;
390 thread_context.arm_r7 = 107;
391 thread_context.arm_r8 = 108;
392 thread_context.arm_r9 = 109;
393 thread_context.arm_r10 = 110;
394 thread_context.arm_fp = 111;
395 thread_context.arm_ip = 112;
396 thread_context.arm_lr = 113;
397
398 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
399 // r14 = lr
400 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
401
402 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
403 const uint8_t instruction = 0b10101000;
404 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36405 bool pc_was_updated = false;
406 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
407 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55408 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36409 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59410 ASSERT_EQ(current_instruction, &instruction + 1);
411 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2), thread_context.arm_sp);
412
413 EXPECT_EQ(100ul, thread_context.arm_r0);
414 EXPECT_EQ(101ul, thread_context.arm_r1);
415 EXPECT_EQ(102ul, thread_context.arm_r2);
416 EXPECT_EQ(103ul, thread_context.arm_r3);
417 EXPECT_EQ(1ul, thread_context.arm_r4);
418 EXPECT_EQ(105ul, thread_context.arm_r5);
419 EXPECT_EQ(106ul, thread_context.arm_r6);
420 EXPECT_EQ(107ul, thread_context.arm_r7);
421 EXPECT_EQ(108ul, thread_context.arm_r8);
422 EXPECT_EQ(109ul, thread_context.arm_r9);
423 EXPECT_EQ(110ul, thread_context.arm_r10);
424 EXPECT_EQ(111ul, thread_context.arm_fp);
425 EXPECT_EQ(112ul, thread_context.arm_ip);
426 EXPECT_EQ(2ul, thread_context.arm_lr);
427}
428
429TEST(ChromeAndroidUnwindInstructionTest,
430 TestPopRegistersIncludingR14MidRegisters) {
431 RegisterContext thread_context = {};
432
433 thread_context.arm_r0 = 100;
434 thread_context.arm_r1 = 101;
435 thread_context.arm_r2 = 102;
436 thread_context.arm_r3 = 103;
437 thread_context.arm_r4 = 104;
438 thread_context.arm_r5 = 105;
439 thread_context.arm_r6 = 106;
440 thread_context.arm_r7 = 107;
441 thread_context.arm_r8 = 108;
442 thread_context.arm_r9 = 109;
443 thread_context.arm_r10 = 110;
444 thread_context.arm_fp = 111;
445 thread_context.arm_ip = 112;
446 thread_context.arm_lr = 113;
447
448 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
449 // r14 = lr
450 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
451
452 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
453 const uint8_t instruction = 0b10101100; // Pop r4-r8, r14.
454 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36455 bool pc_was_updated = false;
456 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
457 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55458 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36459 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59460 ASSERT_EQ(current_instruction, &instruction + 1);
461 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6), thread_context.arm_sp);
462
463 EXPECT_EQ(100ul, thread_context.arm_r0);
464 EXPECT_EQ(101ul, thread_context.arm_r1);
465 EXPECT_EQ(102ul, thread_context.arm_r2);
466 EXPECT_EQ(103ul, thread_context.arm_r3);
467 EXPECT_EQ(1ul, thread_context.arm_r4);
468 EXPECT_EQ(2ul, thread_context.arm_r5);
469 EXPECT_EQ(3ul, thread_context.arm_r6);
470 EXPECT_EQ(4ul, thread_context.arm_r7);
471 EXPECT_EQ(5ul, thread_context.arm_r8);
472 EXPECT_EQ(109ul, thread_context.arm_r9);
473 EXPECT_EQ(110ul, thread_context.arm_r10);
474 EXPECT_EQ(111ul, thread_context.arm_fp);
475 EXPECT_EQ(112ul, thread_context.arm_ip);
476 EXPECT_EQ(6ul, thread_context.arm_lr);
477}
478
479TEST(ChromeAndroidUnwindInstructionTest,
480 TestPopRegistersIncludingR14MaxRegisters) {
481 RegisterContext thread_context = {};
482
483 thread_context.arm_r0 = 100;
484 thread_context.arm_r1 = 101;
485 thread_context.arm_r2 = 102;
486 thread_context.arm_r3 = 103;
487 thread_context.arm_r4 = 104;
488 thread_context.arm_r5 = 105;
489 thread_context.arm_r6 = 106;
490 thread_context.arm_r7 = 107;
491 thread_context.arm_r8 = 108;
492 thread_context.arm_r9 = 109;
493 thread_context.arm_r10 = 110;
494 thread_context.arm_fp = 111;
495 thread_context.arm_ip = 112;
496 thread_context.arm_lr = 113;
497
498 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
499 // r14 = lr
500 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
501
502 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
503 const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
504 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36505 bool pc_was_updated = false;
506 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
507 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55508 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36509 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59510 ASSERT_EQ(current_instruction, &instruction + 1);
511 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9), thread_context.arm_sp);
512
513 EXPECT_EQ(100ul, thread_context.arm_r0);
514 EXPECT_EQ(101ul, thread_context.arm_r1);
515 EXPECT_EQ(102ul, thread_context.arm_r2);
516 EXPECT_EQ(103ul, thread_context.arm_r3);
517 EXPECT_EQ(1ul, thread_context.arm_r4);
518 EXPECT_EQ(2ul, thread_context.arm_r5);
519 EXPECT_EQ(3ul, thread_context.arm_r6);
520 EXPECT_EQ(4ul, thread_context.arm_r7);
521 EXPECT_EQ(5ul, thread_context.arm_r8);
522 EXPECT_EQ(6ul, thread_context.arm_r9);
523 EXPECT_EQ(7ul, thread_context.arm_r10);
524 EXPECT_EQ(8ul, thread_context.arm_fp);
525 EXPECT_EQ(112ul, thread_context.arm_ip);
526 EXPECT_EQ(9ul, thread_context.arm_lr);
527}
528
529TEST(ChromeAndroidUnwindInstructionTest, TestPopRegistersIncludingR14Overflow) {
530 RegisterContext thread_context = {};
531
532 thread_context.arm_r0 = 100;
533 thread_context.arm_r1 = 101;
534 thread_context.arm_r2 = 102;
535 thread_context.arm_r3 = 103;
536 thread_context.arm_r4 = 104;
537 thread_context.arm_r5 = 105;
538 thread_context.arm_r6 = 106;
539 thread_context.arm_r7 = 107;
540 thread_context.arm_r8 = 108;
541 thread_context.arm_r9 = 109;
542 thread_context.arm_r10 = 110;
543 thread_context.arm_fp = 111;
544 thread_context.arm_ip = 112;
545 thread_context.arm_lr = 113;
546
547 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
548 // r14 = lr
549 thread_context.arm_sp = 0xffffffff;
550 const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
551 const uint8_t* current_instruction = &instruction;
Charlie Hu01b7e662021-09-13 14:51:36552 bool pc_was_updated = false;
553 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
554 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55555 UnwindInstructionResult::kAborted);
Charlie Hu01b7e662021-09-13 14:51:36556 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59557 ASSERT_EQ(current_instruction, &instruction + 1);
558 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
559
560 EXPECT_EQ(100ul, thread_context.arm_r0);
561 EXPECT_EQ(101ul, thread_context.arm_r1);
562 EXPECT_EQ(102ul, thread_context.arm_r2);
563 EXPECT_EQ(103ul, thread_context.arm_r3);
564 EXPECT_EQ(104ul, thread_context.arm_r4);
565 EXPECT_EQ(105ul, thread_context.arm_r5);
566 EXPECT_EQ(106ul, thread_context.arm_r6);
567 EXPECT_EQ(107ul, thread_context.arm_r7);
568 EXPECT_EQ(108ul, thread_context.arm_r8);
569 EXPECT_EQ(109ul, thread_context.arm_r9);
570 EXPECT_EQ(110ul, thread_context.arm_r10);
571 EXPECT_EQ(111ul, thread_context.arm_fp);
572 EXPECT_EQ(112ul, thread_context.arm_ip);
573 EXPECT_EQ(113ul, thread_context.arm_lr);
574}
575
576TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMinValue) {
577 RegisterContext thread_context = {};
578 thread_context.arm_sp = 0x10000000;
579
580 const uint8_t increment_0[] = {
581 0b10110010,
582 0b00000000,
583 };
584 const uint8_t* current_instruction = &increment_0[0];
Charlie Hu01b7e662021-09-13 14:51:36585 bool pc_was_updated = false;
586 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
587 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55588 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36589 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59590 ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0));
591 // vsp + 0x204 + (0 << 2)
592 // = vsp + 0x204
593 EXPECT_EQ(0x10000204ul, thread_context.arm_sp);
594}
595
596TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMidValue) {
597 RegisterContext thread_context = {};
598 thread_context.arm_sp = 0x10000000;
599
600 const uint8_t increment_4[] = {
601 0b10110010,
602 0b00000100,
603 };
604 const uint8_t* current_instruction = &increment_4[0];
605
606 // vsp + 0x204 + (4 << 2)
607 // = vsp + 0x204 + 0x10
608 // = vsp + 0x214
Charlie Hu01b7e662021-09-13 14:51:36609 bool pc_was_updated = false;
610 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
611 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55612 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36613 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59614 ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4));
615 EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
616}
617
618TEST(ChromeAndroidUnwindInstructionTest,
619 TestBigStackPointerIncrementLargeValue) {
620 RegisterContext thread_context = {};
621 thread_context.arm_sp = 0x10000000;
622
623 const uint8_t increment_128[] = {
624 0b10110010,
625 0b10000000,
626 0b00000001,
627 };
628 const uint8_t* current_instruction = &increment_128[0];
629 // vsp + 0x204 + (128 << 2)
630 // = vsp + 0x204 + 512
631 // = vsp + 0x204 + 0x200
632 // = vsp + 0x404
Charlie Hu01b7e662021-09-13 14:51:36633 bool pc_was_updated = false;
634 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
635 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55636 UnwindInstructionResult::kInstructionPending);
Charlie Hu01b7e662021-09-13 14:51:36637 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59638 ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128));
639 EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
640}
641
642TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementOverflow) {
643 RegisterContext thread_context = {};
644 thread_context.arm_sp = 0xffffffff;
645
646 const uint8_t increment_overflow[] = {
647 0b10110010,
648 0b10000000,
649 0b00000001,
650 }; // ULEB128 = 128
651 const uint8_t* current_instruction = &increment_overflow[0];
Charlie Hu01b7e662021-09-13 14:51:36652 bool pc_was_updated = false;
653 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
654 &thread_context),
Charlie Hu82c2ed352021-09-14 17:58:55655 UnwindInstructionResult::kAborted);
Charlie Hu01b7e662021-09-13 14:51:36656 EXPECT_FALSE(pc_was_updated);
Charlie Hud5c14032021-08-26 21:45:59657 ASSERT_EQ(current_instruction,
658 increment_overflow + sizeof(increment_overflow));
659 EXPECT_EQ(0xfffffffful, thread_context.arm_sp);
660}
661
Tushar Agarwaldcafb622022-11-30 17:32:27662TEST(ChromeUnwinderAndroidTest,
Charlie Huf36b2f62021-09-01 22:31:20663 TestFunctionOffsetTableLookupExactMatchingOffset) {
Charlie Huf36b2f62021-09-01 22:31:20664 const uint8_t function_offset_table[] = {
665 // Function 1: [(130, 2), (128, 3), (0, 4)]
666 // offset = 130
667 0b10000010,
668 0b00000001,
669 // unwind index = 2
670 0b00000010,
671 // offset = 128
672 0b10000000,
673 0b00000001,
674 // unwind index = 3
675 0b00000011,
676 // offset = 0
677 0b00000000,
678 // unwind index = 4
679 0b00000100,
680 };
681
Charlie Hu78e00402021-10-13 06:10:43682 EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
683 &function_offset_table[0],
684 /* instruction_offset_from_function_start */ 128));
Charlie Huf36b2f62021-09-01 22:31:20685}
686
Tushar Agarwaldcafb622022-11-30 17:32:27687TEST(ChromeUnwinderAndroidTest,
Charlie Huf36b2f62021-09-01 22:31:20688 TestFunctionOffsetTableLookupNonExactMatchingOffset) {
Charlie Huf36b2f62021-09-01 22:31:20689 const uint8_t function_offset_table[] = {
690 // Function 1: [(130, 2), (128, 3), (0, 4)]
691 // offset = 130
692 0b10000010,
693 0b00000001,
694 // unwind index = 2
695 0b00000010,
696 // offset = 128
697 0b10000000,
698 0b00000001,
699 // unwind index = 3
700 0b00000011,
701 // offset = 0
702 0b00000000,
703 // unwind index = 4
704 0b00000100,
705 };
706
Charlie Hu78e00402021-10-13 06:10:43707 EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
708 &function_offset_table[0],
709 /* instruction_offset_from_function_start */ 129));
Charlie Huf36b2f62021-09-01 22:31:20710}
711
Tushar Agarwaldcafb622022-11-30 17:32:27712TEST(ChromeUnwinderAndroidTest, TestFunctionOffsetTableLookupZeroOffset) {
Charlie Huf36b2f62021-09-01 22:31:20713 const uint8_t function_offset_table[] = {
714 // Function 1: [(130, 2), (128, 3), (0, 4)]
715 // offset = 130
716 0b10000010,
717 0b00000001,
718 // unwind index = 2
719 0b00000010,
720 // offset = 128
721 0b10000000,
722 0b00000001,
723 // unwind index = 3
724 0b00000011,
725 // offset = 0
726 0b00000000,
727 // unwind index = 4
728 0b00000100,
729 };
730
Charlie Hu78e00402021-10-13 06:10:43731 EXPECT_EQ(4ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
732 &function_offset_table[0],
733 /* instruction_offset_from_function_start */ 0));
Charlie Huf36b2f62021-09-01 22:31:20734}
735
Tushar Agarwaldcafb622022-11-30 17:32:27736TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupEntryInPage) {
Charlie Hu235c6b72021-09-13 20:50:01737 const uint32_t page_start_instructions[] = {0, 2};
738 const FunctionTableEntry function_offset_table_indices[] = {
739 // Page 0
740 {
741 /* function_start_address_page_instruction_offset */ 0,
742 /* function_offset_table_byte_index */ 20,
743 },
744 {
745 /* function_start_address_page_instruction_offset */ 4,
746 /* function_offset_table_byte_index */ 40,
747 },
748 // Page 1
749 {
750 /* function_start_address_page_instruction_offset */ 6,
751 /* function_offset_table_byte_index */ 70,
752 },
753 };
754
755 {
756 const uint32_t page_number = 0;
757 const uint32_t page_instruction_offset = 4;
758 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
759 page_start_instructions, function_offset_table_indices,
760 /* instruction_offset */ (page_instruction_offset << 1) +
761 (page_number << 17));
762 ASSERT_NE(absl::nullopt, entry_found);
763 EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
764 EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
765 }
766
767 {
768 const uint32_t page_number = 0;
769 const uint32_t page_instruction_offset = 50;
770 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
771 page_start_instructions, function_offset_table_indices,
772 /* instruction_offset */ (page_instruction_offset << 1) +
773 (page_number << 17));
774 ASSERT_NE(absl::nullopt, entry_found);
775 EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
776 EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
777 }
778
779 // Lookup last instruction in last function.
780 {
781 const uint32_t page_number = 1;
782 const uint32_t page_instruction_offset = 0xffff;
783 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
784 page_start_instructions, function_offset_table_indices,
785 /* instruction_offset */ (page_instruction_offset << 1) +
786 (page_number << 17));
787 ASSERT_NE(absl::nullopt, entry_found);
788 // 0xffff - 6 = 0xfff9.
789 EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
790 EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
791 }
792}
793
Tushar Agarwaldcafb622022-11-30 17:32:27794TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupEmptyPage) {
Charlie Hu235c6b72021-09-13 20:50:01795 const uint32_t page_start_instructions[] = {0, 1, 1};
796 const FunctionTableEntry function_offset_table_indices[] = {
797 // Page 0
798 {
799 /* function_start_address_page_instruction_offset */ 0,
800 /* function_offset_table_byte_index */ 20,
801 },
802 // Page 1 is empty
803 // Page 2
804 {
805 /* function_start_address_page_instruction_offset */ 6,
806 /* function_offset_table_byte_index */ 70,
807 },
808 };
809
810 const uint32_t page_number = 1;
811 const uint32_t page_instruction_offset = 4;
812 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
813 page_start_instructions, function_offset_table_indices,
814 /* instruction_offset */ (page_instruction_offset << 1) +
815 (page_number << 17));
816 ASSERT_NE(absl::nullopt, entry_found);
817 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
818 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
819}
820
Tushar Agarwaldcafb622022-11-30 17:32:27821TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupInvalidIntructionOffset) {
Charlie Hu235c6b72021-09-13 20:50:01822 const uint32_t page_start_instructions[] = {0, 1};
823 const FunctionTableEntry function_offset_table_indices[] = {
824 // Page 0
825 // This function spans from page 0 offset 0 to page 1 offset 5.
826 {
827 /* function_start_address_page_instruction_offset */ 0,
828 /* function_offset_table_byte_index */ 20,
829 },
830 // Page 1
831 {
832 /* function_start_address_page_instruction_offset */ 6,
833 /* function_offset_table_byte_index */ 70,
834 },
835 };
836
837 // Instruction offset lies after last page on page table.
838 {
839 const uint32_t page_number = 50;
840 const uint32_t page_instruction_offset = 6;
841 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
842 page_start_instructions, function_offset_table_indices,
843 /* instruction_offset */ (page_instruction_offset << 1) +
844 (page_number << 17));
845 ASSERT_EQ(absl::nullopt, entry_found);
846 }
847 {
848 const uint32_t page_number = 2;
849 const uint32_t page_instruction_offset = 0;
850 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
851 page_start_instructions, function_offset_table_indices,
852 /* instruction_offset */ (page_instruction_offset << 1) +
853 (page_number << 17));
854 ASSERT_EQ(absl::nullopt, entry_found);
855 }
856}
857
Tushar Agarwaldcafb622022-11-30 17:32:27858TEST(ChromeUnwinderAndroidTest,
Charlie Hu235c6b72021-09-13 20:50:01859 TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
860 const uint32_t page_start_instructions[] = {0, 1, 2};
861 const FunctionTableEntry function_offset_table_indices[] = {
862 // Page 0
863 {
864 /* function_start_address_page_instruction_offset */ 0,
865 /* function_offset_table_byte_index */ 20,
866 },
867 // Page 1
868 {
869 /* function_start_address_page_instruction_offset */ 6,
870 /* function_offset_table_byte_index */ 70,
871 },
872 // Page 2
873 {
874 /* function_start_address_page_instruction_offset */ 10,
875 /* function_offset_table_byte_index */ 80,
876 }};
877
878 const uint32_t page_number = 1;
879 const uint32_t page_instruction_offset = 4;
880 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
881 page_start_instructions, function_offset_table_indices,
882 /* instruction_offset */ (page_instruction_offset << 1) +
883 (page_number << 17));
884 ASSERT_NE(absl::nullopt, entry_found);
885 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
886 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
887}
888
Tushar Agarwaldcafb622022-11-30 17:32:27889TEST(ChromeUnwinderAndroidTest,
Charlie Hu235c6b72021-09-13 20:50:01890 TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
891 const uint32_t page_start_instructions[] = {0, 1, 1, 1};
892 const FunctionTableEntry function_offset_table_indices[] = {
893 // Page 0
894 // This function spans from page 0 offset 0 to page 3 offset 5.
895 {
896 /* function_start_address_page_instruction_offset */ 0,
897 /* function_offset_table_byte_index */ 20,
898 },
899 // Page 1 is empty
900 // Page 2 is empty
901 // Page 3
902 {
903 /* function_start_address_page_instruction_offset */ 6,
904 /* function_offset_table_byte_index */ 70,
905 },
906 };
907
908 {
909 const uint32_t page_number = 0;
910 const uint32_t page_instruction_offset = 4;
911 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
912 page_start_instructions, function_offset_table_indices,
913 /* instruction_offset */ (page_instruction_offset << 1) +
914 (page_number << 17));
915 ASSERT_NE(absl::nullopt, entry_found);
916 EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
917 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
918 }
919 {
920 const uint32_t page_number = 1;
921 const uint32_t page_instruction_offset = 4;
922 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
923 page_start_instructions, function_offset_table_indices,
924 /* instruction_offset */ (page_instruction_offset << 1) +
925 (page_number << 17));
926 ASSERT_NE(absl::nullopt, entry_found);
927 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
928 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
929 }
930 {
931 const uint32_t page_number = 2;
932 const uint32_t page_instruction_offset = 4;
933 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
934 page_start_instructions, function_offset_table_indices,
935 /* instruction_offset */ (page_instruction_offset << 1) +
936 (page_number << 17));
937 ASSERT_NE(absl::nullopt, entry_found);
938 EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
939 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
940 }
941 {
942 const uint32_t page_number = 3;
943 const uint32_t page_instruction_offset = 4;
944 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
945 page_start_instructions, function_offset_table_indices,
946 /* instruction_offset */ (page_instruction_offset << 1) +
947 (page_number << 17));
948 ASSERT_NE(absl::nullopt, entry_found);
949 EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
950 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
951 }
952}
Charlie Hu78e00402021-10-13 06:10:43953
Charlie Hu78e00402021-10-13 06:10:43954// Utility function to add a single native module during test setup. Returns
955// a pointer to the provided module.
956const ModuleCache::Module* AddNativeModule(
957 ModuleCache* cache,
958 std::unique_ptr<const ModuleCache::Module> module) {
959 const ModuleCache::Module* module_ptr = module.get();
960 cache->AddCustomNativeModule(std::move(module));
961 return module_ptr;
962}
963
Tushar Agarwaldcafb622022-11-30 17:32:27964TEST(ChromeUnwinderAndroidTest, CanUnwindFrom) {
Charlie Hu78e00402021-10-13 06:10:43965 const uint32_t page_table[] = {0};
966 const FunctionTableEntry function_table[] = {{0, 0}};
967 const uint8_t function_offset_table[] = {0};
968 const uint8_t unwind_instruction_table[] = {0};
969 auto dummy_unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:43970 unwind_instruction_table,
971 function_offset_table,
972 function_table,
973 page_table,
Charlie Hu78e00402021-10-13 06:10:43974 };
975
Leonard Grey412c54862022-02-04 19:37:36976 auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
977 auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
Charlie Hu78e00402021-10-13 06:10:43978
979 ModuleCache module_cache;
Tushar Agarwaldcafb622022-11-30 17:32:27980 ChromeUnwinderAndroid unwinder(dummy_unwind_info,
981 chrome_module->GetBaseAddress(),
982 /* text_section_start_address */
983 chrome_module->GetBaseAddress() + 4);
Charlie Hu78e00402021-10-13 06:10:43984 unwinder.Initialize(&module_cache);
985
986 EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
987 EXPECT_TRUE(unwinder.CanUnwindFrom({0x1000, chrome_module.get()}));
988 EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
989 EXPECT_FALSE(unwinder.CanUnwindFrom({0x400, nullptr}));
990}
991
Charlie Hudaf45ec2022-04-05 17:40:27992namespace {
993void ExpectFramesEq(const std::vector<Frame>& expected,
994 const std::vector<Frame>& actual) {
Charlie Hu78e00402021-10-13 06:10:43995 EXPECT_EQ(actual.size(), expected.size());
996 if (actual.size() != expected.size())
997 return;
998
999 for (size_t i = 0; i < actual.size(); i++) {
Charlie Hudaf45ec2022-04-05 17:40:271000 EXPECT_EQ(expected[i].module, actual[i].module);
1001 EXPECT_EQ(expected[i].instruction_pointer, actual[i].instruction_pointer);
Charlie Hu78e00402021-10-13 06:10:431002 }
1003}
1004
Charlie Hudaf45ec2022-04-05 17:40:271005class AlignedStackMemory {
1006 public:
1007 AlignedStackMemory(std::initializer_list<uintptr_t> values)
1008 : size_(values.size()),
1009 stack_memory_(static_cast<uintptr_t*>(
1010 AlignedAlloc(size_ * sizeof(uintptr_t), 2 * sizeof(uintptr_t)))) {
1011 DCHECK_EQ(size_ % 2, 0u);
Peter Kasting50039f32022-10-28 16:49:221012 ranges::copy(values, stack_memory_.get());
Charlie Hudaf45ec2022-04-05 17:40:271013 }
1014
1015 uintptr_t stack_start_address() const {
1016 return reinterpret_cast<uintptr_t>(stack_memory_.get());
1017 }
1018
1019 uintptr_t stack_end_address() const {
1020 return reinterpret_cast<uintptr_t>(stack_memory_.get() + size_);
1021 }
1022
1023 private:
1024 const uintptr_t size_;
1025 const std::unique_ptr<uintptr_t, AlignedFreeDeleter> stack_memory_;
1026};
1027
1028} // namespace
1029
Tushar Agarwaldcafb622022-11-30 17:32:271030TEST(ChromeUnwinderAndroidTest, TryUnwind) {
Charlie Hu78e00402021-10-13 06:10:431031 const uint32_t page_table[] = {0, 2};
Daniel Chengf45f47602022-02-28 22:38:321032 const size_t number_of_pages = std::size(page_table);
Charlie Hu78e00402021-10-13 06:10:431033 const size_t page_size = 1 << 17;
1034
1035 const FunctionTableEntry function_table[] = {
1036 // Page 0.
1037 {0, 0}, // Function 0.
1038 {0x10, 4}, // Function 1. The function to unwind 2 times.
1039 // Page 1.
1040 {0x5, 8}, // Function 2.
1041 {0x20, 12}, // Function 3.
1042 };
1043 const uint8_t function_offset_table[] = {
1044 // Function 0.
1045 0x2,
1046 0,
1047 0x0,
Charlie Hudaf45ec2022-04-05 17:40:271048 1,
Charlie Hu78e00402021-10-13 06:10:431049 // Function 1.
1050 0x7f,
1051 0,
1052 0x0,
Charlie Hudaf45ec2022-04-05 17:40:271053 1,
Charlie Hu78e00402021-10-13 06:10:431054 // Function 2.
1055 0x78,
1056 0,
1057 0x0,
Charlie Hudaf45ec2022-04-05 17:40:271058 1,
Charlie Hu78e00402021-10-13 06:10:431059 // Function 3.
1060 0x2,
1061 0,
1062 0x0,
Charlie Hudaf45ec2022-04-05 17:40:271063 1,
Charlie Hu78e00402021-10-13 06:10:431064 };
1065 const uint8_t unwind_instruction_table[] = {
Charlie Hudaf45ec2022-04-05 17:40:271066 // Offset 0: Pop r4, r14 from stack top.
1067 // Need to pop 2 registers to keep SP aligned.
1068 0b10101000,
1069 // Offset 1: COMPLETE.
Charlie Hu78e00402021-10-13 06:10:431070 0b10110000,
1071 };
1072
1073 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431074 unwind_instruction_table,
1075 function_offset_table,
1076 function_table,
1077 page_table,
Charlie Hu78e00402021-10-13 06:10:431078 };
1079
1080 ModuleCache module_cache;
1081 const ModuleCache::Module* chrome_module = AddNativeModule(
1082 &module_cache, std::make_unique<TestModule>(
1083 0x1000, number_of_pages * page_size, "ChromeModule"));
1084
1085 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271086 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1087 text_section_start_address);
Charlie Hu78e00402021-10-13 06:10:431088
1089 unwinder.Initialize(&module_cache);
1090
1091 // Both first_pc and second_pc lie in Function 1's address range.
1092 uintptr_t first_pc = text_section_start_address + 0x20;
1093 uintptr_t second_pc = text_section_start_address + page_size + 0x4;
1094 // third_pc lies outside chrome_module's address range.
1095 uintptr_t third_pc = text_section_start_address + 3 * page_size;
1096
Charlie Hudaf45ec2022-04-05 17:40:271097 AlignedStackMemory stack_memory = {
1098 0x0,
Charlie Hu78e00402021-10-13 06:10:431099 third_pc,
1100 0xFFFF,
Charlie Hudaf45ec2022-04-05 17:40:271101 0xFFFF,
Charlie Hu78e00402021-10-13 06:10:431102 };
Charlie Hu78e00402021-10-13 06:10:431103
1104 std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1105 RegisterContext context;
1106 RegisterContextInstructionPointer(&context) = first_pc;
Charlie Hudaf45ec2022-04-05 17:40:271107 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
Charlie Hu78e00402021-10-13 06:10:431108 context.arm_lr = second_pc;
1109
Charlie Hua1d07b922021-11-02 16:55:131110 EXPECT_EQ(UnwindResult::kUnrecognizedFrame,
Charlie Hudaf45ec2022-04-05 17:40:271111 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1112 &unwound_frames));
Charlie Hu78e00402021-10-13 06:10:431113 ExpectFramesEq(std::vector<Frame>({{first_pc, chrome_module},
1114 {second_pc, chrome_module},
1115 {third_pc, nullptr}}),
1116 unwound_frames);
1117}
1118
Tushar Agarwaldcafb622022-11-30 17:32:271119TEST(ChromeUnwinderAndroidTest, TryUnwindInfiniteLoopSingleFrame) {
Charlie Hudaf45ec2022-04-05 17:40:271120 const uint32_t page_table[] = {0, 2};
1121 const size_t number_of_pages = std::size(page_table);
1122 const size_t page_size = 1 << 17;
1123
1124 const FunctionTableEntry function_table[] = {
1125 // Page 0.
1126 {0x0, 0}, // Refuse to unwind filler function.
1127 {0x10, 2}, // Function 0. The function to unwind.
1128 // Page 1.
1129 {0x5, 0}, // Refuse to unwind filler function.
1130 };
1131 const uint8_t function_offset_table[] = {
1132 // Refuse to unwind filler function.
1133 0x0,
1134 0,
1135 // Function 0.
1136 0x0,
1137 2,
1138 };
1139 const uint8_t unwind_instruction_table[] = {
1140 // Offset 0: REFUSE_TO_UNWIND.
1141 0b10000000,
1142 0b00000000,
1143 // Offset 2: COMPLETE.
1144 0b10110000,
1145 };
1146
1147 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431148 unwind_instruction_table,
1149 function_offset_table,
1150 function_table,
1151 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271152 };
1153
1154 ModuleCache module_cache;
1155 const ModuleCache::Module* chrome_module = AddNativeModule(
1156 &module_cache, std::make_unique<TestModule>(
1157 0x1000, number_of_pages * page_size, "ChromeModule"));
1158
1159 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271160 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1161 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271162
1163 unwinder.Initialize(&module_cache);
1164 uintptr_t pc = text_section_start_address + 0x20;
1165
1166 AlignedStackMemory stack_memory = {
1167 0xFFFF,
1168 0xFFFF,
1169 };
1170
1171 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1172 RegisterContext context;
1173 RegisterContextInstructionPointer(&context) = pc;
1174 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1175
1176 // Set lr = pc so that both sp and pc stays the same after first round of
1177 // unwind.
1178 context.arm_lr = pc;
1179
1180 EXPECT_EQ(UnwindResult::kAborted,
1181 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1182 &unwound_frames));
1183 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1184}
1185
Tushar Agarwaldcafb622022-11-30 17:32:271186TEST(ChromeUnwinderAndroidTest, TryUnwindInfiniteLoopMultipleFrames) {
Charlie Hudaf45ec2022-04-05 17:40:271187 // This test aims to produce a scenario, where after the unwind of a number
1188 // of frames, the sp and pc get to their original state before the unwind.
1189
1190 // Function 1 (pc1, sp1):
1191 // - set pc = lr(pc2)
1192 // Function 2 (pc2, sp1):
1193 // - pop r14(pc2), r15(pc1) off stack
1194 // - vsp = r4 (reset vsp to frame initial vsp)
1195
1196 const uint32_t page_table[] = {0, 3};
1197 const size_t number_of_pages = std::size(page_table);
1198 const size_t page_size = 1 << 17;
1199
1200 const FunctionTableEntry function_table[] = {
1201 // Page 0.
1202 {0x0, 0}, // Refuse to unwind filler function.
1203 {0x10, 2}, // Function 1. The function to unwind.
1204 {0x100, 2}, // Function 2. The function to unwind.
1205 // Page 1.
1206 {0x5, 0}, // Refuse to unwind filler function.
1207 };
1208 const uint8_t function_offset_table[] = {
1209 // Refuse to unwind filler function.
1210 0x0,
1211 0,
1212 // Function 0.
1213 0x0,
1214 2,
1215 // Function 1.
1216 0x2,
1217 3,
1218 0x1,
1219 5,
1220 0x0,
1221 6,
1222 };
1223 const uint8_t unwind_instruction_table[] = {
1224 // Offset 0: REFUSE_TO_UNWIND.
1225 0b10000000,
1226 0b00000000,
1227 // Offset 2: COMPLETE.
1228 0b10110000,
1229 // Offset 3: POP r14, r15 off the stack.
1230 0b10001100,
1231 0b00000000,
1232 // Offset 5: vsp = r4.
1233 0b10010100,
1234 // Offset 6: COMPLETE.
1235 0b10110000,
1236 };
1237
1238 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431239 unwind_instruction_table,
1240 function_offset_table,
1241 function_table,
1242 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271243 };
1244
1245 ModuleCache module_cache;
1246 const ModuleCache::Module* chrome_module = AddNativeModule(
1247 &module_cache, std::make_unique<TestModule>(
1248 0x1000, number_of_pages * page_size, "ChromeModule"));
1249
1250 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271251 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1252 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271253
1254 unwinder.Initialize(&module_cache);
1255 uintptr_t first_pc = text_section_start_address + 0x20; // Function 1.
1256 uintptr_t second_pc = text_section_start_address + 0x110; // Function 2.
1257
1258 AlignedStackMemory stack_memory = {
1259 second_pc,
1260 first_pc,
1261 0xFFFF,
1262 0xFFFF,
1263 };
1264
1265 std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1266 RegisterContext context;
1267 RegisterContextInstructionPointer(&context) = first_pc;
1268 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1269
1270 context.arm_lr = second_pc;
1271 context.arm_r4 = stack_memory.stack_start_address();
1272
1273 EXPECT_EQ(UnwindResult::kAborted,
1274 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1275 &unwound_frames));
1276 ExpectFramesEq(std::vector<Frame>(
1277 {{first_pc, chrome_module}, {second_pc, chrome_module}}),
1278 unwound_frames);
1279}
1280
Tushar Agarwaldcafb622022-11-30 17:32:271281TEST(ChromeUnwinderAndroidTest, TryUnwindUnalignedSPFrameUnwind) {
Charlie Hudaf45ec2022-04-05 17:40:271282 // SP should be 2-uintptr_t aligned before/after each frame unwind.
1283 const uint32_t page_table[] = {0, 2};
1284 const size_t number_of_pages = std::size(page_table);
1285 const size_t page_size = 1 << 17;
1286
1287 const FunctionTableEntry function_table[] = {
1288 // Page 0.
1289 {0x0, 0}, // Refuse to unwind filler function.
1290 {0x10, 2}, // Function 0. The function to unwind.
1291 // Page 1.
1292 {0x5, 0}, // Refuse to unwind filler function.
1293 };
1294 const uint8_t function_offset_table[] = {
1295 // Refuse to unwind filler function.
1296 0x0,
1297 0,
1298 // Function 0.
1299 0x0,
1300 2,
1301 };
1302 const uint8_t unwind_instruction_table[] = {
1303 // Offset 0: REFUSE_TO_UNWIND.
1304 0b10000000,
1305 0b00000000,
1306 // Offset 2: COMPLETE.
1307 0b10110000,
1308 };
1309
1310 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431311 unwind_instruction_table,
1312 function_offset_table,
1313 function_table,
1314 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271315 };
1316
1317 ModuleCache module_cache;
1318 const ModuleCache::Module* chrome_module = AddNativeModule(
1319 &module_cache, std::make_unique<TestModule>(
1320 0x1000, number_of_pages * page_size, "ChromeModule"));
1321
1322 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271323 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1324 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271325
1326 unwinder.Initialize(&module_cache);
1327 uintptr_t pc = text_section_start_address + 0x20;
1328
1329 AlignedStackMemory stack_memory = {
1330 0xFFFF,
1331 0xFFFF,
1332 };
1333
1334 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1335 RegisterContext context;
1336 RegisterContextInstructionPointer(&context) = pc;
1337 // Make stack memory not aligned to 2 * sizeof(uintptr_t);
1338 RegisterContextStackPointer(&context) =
1339 stack_memory.stack_start_address() + sizeof(uintptr_t);
1340
1341 // The address is outside chrome module, which will result the unwind to
1342 // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1343 context.arm_lr =
1344 text_section_start_address + (number_of_pages + 1) * page_size;
1345
1346 EXPECT_EQ(UnwindResult::kAborted,
1347 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1348 &unwound_frames));
1349 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1350}
1351
Tushar Agarwaldcafb622022-11-30 17:32:271352TEST(ChromeUnwinderAndroidTest, TryUnwindUnalignedSPInstructionUnwind) {
Charlie Hudaf45ec2022-04-05 17:40:271353 // SP should be uintptr_t aligned before/after each unwind instruction
1354 // execution.
1355
1356 const uint32_t page_table[] = {0, 2};
1357 const size_t number_of_pages = std::size(page_table);
1358 const size_t page_size = 1 << 17;
1359
1360 const FunctionTableEntry function_table[] = {
1361 // Page 0.
1362 {0x0, 0}, // Refuse to unwind filler function.
1363 {0x10, 2}, // Function 0. The function to unwind.
1364 // Page 1.
1365 {0x5, 0}, // Refuse to unwind filler function.
1366 };
1367 const uint8_t function_offset_table[] = {
1368 // Refuse to unwind filler function.
1369 0x0,
1370 0,
1371 // Function 0.
1372 0x0,
1373 2,
1374 };
1375 const uint8_t unwind_instruction_table[] = {
1376 // Offset 0: REFUSE_TO_UNWIND.
1377 0b10000000, 0b00000000,
1378 // Offset 2:
1379 0b10010100, // vsp = r4, where r4 = stack + (sizeof(uintptr_t) / 2)
1380 0b10110000, // COMPLETE.
1381 };
1382
1383 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431384 unwind_instruction_table,
1385 function_offset_table,
1386 function_table,
1387 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271388 };
1389
1390 ModuleCache module_cache;
1391 const ModuleCache::Module* chrome_module = AddNativeModule(
1392 &module_cache, std::make_unique<TestModule>(
1393 0x1000, number_of_pages * page_size, "ChromeModule"));
1394
1395 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271396 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1397 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271398
1399 unwinder.Initialize(&module_cache);
1400 uintptr_t pc = text_section_start_address + 0x20;
1401
1402 AlignedStackMemory stack_memory = {
1403 0xFFFF,
1404 0xFFFF,
1405 };
1406
1407 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1408 RegisterContext context;
1409 RegisterContextInstructionPointer(&context) = pc;
1410 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1411
1412 // The address is outside chrome module, which will result the unwind to
1413 // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1414 context.arm_lr =
1415 text_section_start_address + (number_of_pages + 1) * page_size;
1416
1417 context.arm_r4 = stack_memory.stack_start_address() + sizeof(uintptr_t) / 2;
1418
1419 EXPECT_EQ(UnwindResult::kAborted,
1420 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1421 &unwound_frames));
1422 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1423}
1424
Tushar Agarwaldcafb622022-11-30 17:32:271425TEST(ChromeUnwinderAndroidTest, TryUnwindSPOverflow) {
Charlie Hudaf45ec2022-04-05 17:40:271426 const uint32_t page_table[] = {0, 2};
1427 const size_t number_of_pages = std::size(page_table);
1428 const size_t page_size = 1 << 17;
1429
1430 const FunctionTableEntry function_table[] = {
1431 // Page 0.
1432 {0x0, 0}, // Refuse to unwind filler function.
1433 {0x10, 2}, // Function 0. The function to unwind.
1434 // Page 1.
1435 {0x5, 0}, // Refuse to unwind filler function.
1436 };
1437 const uint8_t function_offset_table[] = {
1438 // Refuse to unwind filler function.
1439 0x0,
1440 0,
1441 // Function 0.
1442 0x0,
1443 2,
1444 };
1445 const uint8_t unwind_instruction_table[] = {
1446 // Offset 0: REFUSE_TO_UNWIND.
1447 0b10000000, 0b00000000,
1448 // Offset 2.
1449 0b10010100, // vsp = r4.
1450 0b10101000, // Pop r4, r14.
1451 0b10110000, // COMPLETE.
1452 };
1453
1454 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431455 unwind_instruction_table,
1456 function_offset_table,
1457 function_table,
1458 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271459 };
1460
1461 ModuleCache module_cache;
1462 const ModuleCache::Module* chrome_module = AddNativeModule(
1463 &module_cache, std::make_unique<TestModule>(
1464 0x1000, number_of_pages * page_size, "ChromeModule"));
1465
1466 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271467 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1468 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271469
1470 unwinder.Initialize(&module_cache);
1471 uintptr_t pc = text_section_start_address + 0x20;
1472
1473 AlignedStackMemory stack_memory = {
1474 0xFFFF,
1475 0xFFFF,
1476 };
1477 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1478 RegisterContext context;
1479 RegisterContextInstructionPointer(&context) = pc;
1480 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1481
1482 // Setting vsp = 0xffffffff should cause SP overflow.
1483 context.arm_r4 = 0xffffffff;
1484
1485 // The address is outside chrome module, which will result the unwind to
1486 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1487 // reasons.
1488 context.arm_lr =
1489 text_section_start_address + (number_of_pages + 1) * page_size;
1490
1491 EXPECT_EQ(UnwindResult::kAborted,
1492 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1493 &unwound_frames));
1494 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1495}
1496
Tushar Agarwaldcafb622022-11-30 17:32:271497TEST(ChromeUnwinderAndroidTest, TryUnwindNullSP) {
Charlie Hudaf45ec2022-04-05 17:40:271498 const uint32_t page_table[] = {0, 2};
1499 const size_t number_of_pages = std::size(page_table);
1500 const size_t page_size = 1 << 17;
1501
1502 const FunctionTableEntry function_table[] = {
1503 // Page 0.
1504 {0x0, 0}, // Refuse to unwind filler function.
1505 {0x10, 2}, // Function 0. The function to unwind.
1506 // Page 1.
1507 {0x5, 0}, // Refuse to unwind filler function.
1508 };
1509 const uint8_t function_offset_table[] = {
1510 // Refuse to unwind filler function.
1511 0x0,
1512 0,
1513 // Function 0.
1514 0x0,
1515 2,
1516 };
1517 const uint8_t unwind_instruction_table[] = {
1518 // Offset 0: REFUSE_TO_UNWIND.
1519 0b10000000, 0b00000000,
1520 // Offset 2.
1521 0b10010100, // vsp = r4.
1522 0b10101000, // Pop r4, r14.
1523 0b10110000, // COMPLETE.
1524 };
1525
1526 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431527 unwind_instruction_table,
1528 function_offset_table,
1529 function_table,
1530 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271531 };
1532
1533 ModuleCache module_cache;
1534 const ModuleCache::Module* chrome_module = AddNativeModule(
1535 &module_cache, std::make_unique<TestModule>(
1536 0x1000, number_of_pages * page_size, "ChromeModule"));
1537
1538 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271539 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1540 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271541
1542 unwinder.Initialize(&module_cache);
1543 uintptr_t pc = text_section_start_address + 0x20;
1544
1545 AlignedStackMemory stack_memory = {
1546 0xFFFF,
1547 0xFFFF,
1548 };
1549 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1550 RegisterContext context;
1551 RegisterContextInstructionPointer(&context) = pc;
1552 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1553
1554 // Setting vsp = 0x0 should cause the unwinder to abort.
1555 context.arm_r4 = 0x0;
1556
1557 // The address is outside chrome module, which will result the unwind to
1558 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1559 // reasons.
1560 context.arm_lr =
1561 text_section_start_address + (number_of_pages + 1) * page_size;
1562
1563 EXPECT_EQ(UnwindResult::kAborted,
1564 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1565 &unwound_frames));
1566 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1567}
1568
Tushar Agarwaldcafb622022-11-30 17:32:271569TEST(ChromeUnwinderAndroidTest, TryUnwindInvalidSPOperation) {
Charlie Hudaf45ec2022-04-05 17:40:271570 // This test aims to verify that for each unwind instruction executed, it is
1571 // always true that sp > frame initial sp.
1572
1573 const uint32_t page_table[] = {0, 2};
1574 const size_t number_of_pages = std::size(page_table);
1575 const size_t page_size = 1 << 17;
1576
1577 const FunctionTableEntry function_table[] = {
1578 // Page 0.
1579 {0x0, 0}, // Refuse to unwind filler function.
1580 {0x10, 2}, // Function 0. The function to unwind.
1581 // Page 1.
1582 {0x5, 0}, // Refuse to unwind filler function.
1583 };
1584 const uint8_t function_offset_table[] = {
1585 // Refuse to unwind filler function.
1586 0x0,
1587 0,
1588 // Function 0.
1589 0x0,
1590 2,
1591 };
1592 const uint8_t unwind_instruction_table[] = {
1593 // Offset 0: REFUSE_TO_UNWIND.
1594 0b10000000, 0b00000000,
1595 // Offset 2.
1596 0b10010100, // vsp = r4 (r4 < frame initial sp).
1597 0b10010101, // vsp = r5 (r5 > frame initial sp).
1598 0b10110000, // COMPLETE.
1599 };
1600
1601 auto unwind_info = ChromeUnwindInfoAndroid{
Tom Sepez10f7df1d782024-01-12 17:22:431602 unwind_instruction_table,
1603 function_offset_table,
1604 function_table,
1605 page_table,
Charlie Hudaf45ec2022-04-05 17:40:271606 };
1607
1608 ModuleCache module_cache;
1609 const ModuleCache::Module* chrome_module = AddNativeModule(
1610 &module_cache, std::make_unique<TestModule>(
1611 0x1000, number_of_pages * page_size, "ChromeModule"));
1612
1613 uintptr_t text_section_start_address = 0x1100;
Tushar Agarwaldcafb622022-11-30 17:32:271614 ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1615 text_section_start_address);
Charlie Hudaf45ec2022-04-05 17:40:271616
1617 unwinder.Initialize(&module_cache);
1618 uintptr_t pc = text_section_start_address + 0x20;
1619
1620 AlignedStackMemory stack_memory = {
1621 0xFFFF,
1622 0xFFFF,
1623 };
1624 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1625 RegisterContext context;
1626 RegisterContextInstructionPointer(&context) = pc;
1627 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1628
1629 context.arm_r4 = stack_memory.stack_start_address() - 2 * sizeof(uintptr_t);
1630 context.arm_r5 = stack_memory.stack_start_address() + 2 * sizeof(uintptr_t);
1631
1632 // The address is outside chrome module, which will result the unwind to
1633 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1634 // reasons.
1635 context.arm_lr =
1636 text_section_start_address + (number_of_pages + 1) * page_size;
1637
1638 EXPECT_EQ(UnwindResult::kAborted,
1639 unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1640 &unwound_frames));
1641 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1642}
1643
Tom Sepez10f7df1d782024-01-12 17:22:431644} // namespace base