1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and use spans.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/profiler/chrome_unwinder_android_32.h"
11
12 #include "base/memory/aligned_memory.h"
13 #include "base/profiler/chrome_unwind_info_android_32.h"
14 #include "base/profiler/stack_sampling_profiler_test_util.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/test/gtest_util.h"
17 #include "build/build_config.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace base {
21
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerIncrementMinValue)22 TEST(ChromeAndroid32UnwindInstructionTest,
23 TestSmallStackPointerIncrementMinValue) {
24 RegisterContext thread_context = {};
25 const uint8_t instruction = 0b00000000;
26 const uint8_t* current_instruction = &instruction;
27 thread_context.arm_sp = 0x10000000;
28 bool pc_was_updated = false;
29 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
30 &thread_context),
31 UnwindInstructionResult::kInstructionPending);
32 EXPECT_FALSE(pc_was_updated);
33 ASSERT_EQ(current_instruction, &instruction + 1);
34 EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
35 }
36
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerIncrementMidValue)37 TEST(ChromeAndroid32UnwindInstructionTest,
38 TestSmallStackPointerIncrementMidValue) {
39 // xxxxxx = 4; vsp = vsp + (4 << 2) + 4 = vsp + 16 + 4 = vsp + 0x14.
40 RegisterContext thread_context = {};
41 const uint8_t instruction = 0b00000100;
42 const uint8_t* current_instruction = &instruction;
43 thread_context.arm_sp = 0x10000000;
44 bool pc_was_updated = false;
45 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
46 &thread_context),
47 UnwindInstructionResult::kInstructionPending);
48 EXPECT_FALSE(pc_was_updated);
49 ASSERT_EQ(current_instruction, &instruction + 1);
50 EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
51 }
52
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerIncrementMaxValue)53 TEST(ChromeAndroid32UnwindInstructionTest,
54 TestSmallStackPointerIncrementMaxValue) {
55 RegisterContext thread_context = {};
56 const uint8_t instruction = 0b00111111;
57 const uint8_t* current_instruction = &instruction;
58 thread_context.arm_sp = 0x10000000;
59 bool pc_was_updated = false;
60 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
61 &thread_context),
62 UnwindInstructionResult::kInstructionPending);
63 EXPECT_FALSE(pc_was_updated);
64 ASSERT_EQ(current_instruction, &instruction + 1);
65 EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
66 }
67
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerIncrementOverflow)68 TEST(ChromeAndroid32UnwindInstructionTest,
69 TestSmallStackPointerIncrementOverflow) {
70 RegisterContext thread_context = {};
71 const uint8_t instruction = 0b00111111;
72 const uint8_t* current_instruction = &instruction;
73 thread_context.arm_sp = 0xffffffff;
74 bool pc_was_updated = false;
75 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
76 &thread_context),
77 UnwindInstructionResult::kAborted);
78 ASSERT_EQ(current_instruction, &instruction + 1);
79 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
80 }
81
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerDecrementMinValue)82 TEST(ChromeAndroid32UnwindInstructionTest,
83 TestSmallStackPointerDecrementMinValue) {
84 RegisterContext thread_context = {};
85 const uint8_t instruction = 0b01000000;
86 const uint8_t* current_instruction = &instruction;
87 thread_context.arm_sp = 0x10000000;
88 bool pc_was_updated = false;
89 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
90 &thread_context),
91 UnwindInstructionResult::kInstructionPending);
92 EXPECT_FALSE(pc_was_updated);
93 ASSERT_EQ(current_instruction, &instruction + 1);
94 EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
95 }
96
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerDecrementMidValue)97 TEST(ChromeAndroid32UnwindInstructionTest,
98 TestSmallStackPointerDecrementMidValue) {
99 // xxxxxx = 4; vsp = vsp - (4 << 2) - 4 = vsp - 16 - 4 = vsp - 0x14.
100 RegisterContext thread_context = {};
101 const uint8_t instruction = 0b01000100;
102 const uint8_t* current_instruction = &instruction;
103 thread_context.arm_sp = 0x10000000;
104 bool pc_was_updated = false;
105 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
106 &thread_context),
107 UnwindInstructionResult::kInstructionPending);
108 EXPECT_FALSE(pc_was_updated);
109 ASSERT_EQ(current_instruction, &instruction + 1);
110 EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
111 }
112
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerDecrementMaxValue)113 TEST(ChromeAndroid32UnwindInstructionTest,
114 TestSmallStackPointerDecrementMaxValue) {
115 RegisterContext thread_context = {};
116 const uint8_t instruction = 0b01111111;
117 const uint8_t* current_instruction = &instruction;
118 thread_context.arm_sp = 0x10000000;
119 bool pc_was_updated = false;
120 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
121 &thread_context),
122 UnwindInstructionResult::kInstructionPending);
123 EXPECT_FALSE(pc_was_updated);
124 ASSERT_EQ(current_instruction, &instruction + 1);
125 EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
126 }
127
TEST(ChromeAndroid32UnwindInstructionTest,TestSmallStackPointerDecrementUnderflow)128 TEST(ChromeAndroid32UnwindInstructionTest,
129 TestSmallStackPointerDecrementUnderflow) {
130 RegisterContext thread_context = {};
131 const uint8_t instruction = 0b01111111;
132 const uint8_t* current_instruction = &instruction;
133 thread_context.arm_sp = 0x00000000;
134 bool pc_was_updated = false;
135 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
136 &thread_context),
137 UnwindInstructionResult::kAborted);
138 EXPECT_FALSE(pc_was_updated);
139 ASSERT_EQ(current_instruction, &instruction + 1);
140 EXPECT_EQ(0x0ul, thread_context.arm_sp);
141 }
142
143 using ChromeAndroidUnwindSetStackPointerFromRegisterValueTest =
144 ::testing::TestWithParam<uint8_t>;
145
146 INSTANTIATE_TEST_SUITE_P(
147 All,
148 ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
149 // The function should set all registers except
150 // - callee saved registers (r0, r1, r2, r3)
151 // - sp (r13)
152 // - pc (r15)
153 ::testing::Values(4, 5, 6, 7, 8, 9, 10, 11, 12, 14));
154
TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,TestSetStackPointerFromRegisterValue)155 TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
156 TestSetStackPointerFromRegisterValue) {
157 const uint8_t register_index = GetParam();
158
159 RegisterContext thread_context = {};
160 thread_context.arm_r0 = 100;
161 thread_context.arm_r1 = 101;
162 thread_context.arm_r2 = 102;
163 thread_context.arm_r3 = 103;
164 thread_context.arm_r4 = 104;
165 thread_context.arm_r5 = 105;
166 thread_context.arm_r6 = 106;
167 thread_context.arm_r7 = 107;
168 thread_context.arm_r8 = 108;
169 thread_context.arm_r9 = 109;
170 thread_context.arm_r10 = 110;
171 thread_context.arm_fp = 111; // r11
172 thread_context.arm_ip = 112; // r12
173 thread_context.arm_lr = 114; // r14
174
175 const uint8_t instruction = 0b10010000 + register_index;
176 const uint8_t* current_instruction = &instruction;
177 bool pc_was_updated = false;
178 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
179 &thread_context),
180 UnwindInstructionResult::kInstructionPending);
181 EXPECT_FALSE(pc_was_updated);
182 ASSERT_EQ(current_instruction, &instruction + 1);
183 EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
184 }
185
TEST(ChromeAndroid32UnwindInstructionTest,TestCompleteWithNoPriorPCUpdate)186 TEST(ChromeAndroid32UnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
187 RegisterContext thread_context = {};
188 thread_context.arm_lr = 114; // r14
189 thread_context.arm_pc = 115; // r15
190 const uint8_t instruction = 0b10110000;
191 const uint8_t* current_instruction = &instruction;
192 bool pc_was_updated = false;
193 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
194 &thread_context),
195 UnwindInstructionResult::kCompleted);
196 ASSERT_EQ(current_instruction, &instruction + 1);
197 EXPECT_EQ(114ul, thread_context.arm_pc);
198 }
199
TEST(ChromeAndroid32UnwindInstructionTest,TestCompleteWithPriorPCUpdate)200 TEST(ChromeAndroid32UnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
201 RegisterContext thread_context = {};
202 thread_context.arm_lr = 114; // r14
203 thread_context.arm_pc = 115; // r15
204 const uint8_t instruction = 0b10110000;
205 const uint8_t* current_instruction = &instruction;
206 bool pc_was_updated = true;
207 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
208 &thread_context),
209 UnwindInstructionResult::kCompleted);
210 ASSERT_EQ(current_instruction, &instruction + 1);
211 EXPECT_EQ(115ul, thread_context.arm_pc);
212 }
213
TEST(ChromeAndroid32UnwindInstructionTest,TestPopDiscontinuousRegistersIncludingPC)214 TEST(ChromeAndroid32UnwindInstructionTest,
215 TestPopDiscontinuousRegistersIncludingPC) {
216 RegisterContext thread_context = {};
217
218 thread_context.arm_r0 = 100;
219 thread_context.arm_r1 = 101;
220 thread_context.arm_r2 = 102;
221 thread_context.arm_r3 = 103;
222 thread_context.arm_r4 = 104;
223 thread_context.arm_r5 = 105;
224 thread_context.arm_r6 = 106;
225 thread_context.arm_r7 = 107;
226 thread_context.arm_r8 = 108;
227 thread_context.arm_r9 = 109;
228 thread_context.arm_r10 = 110;
229 thread_context.arm_fp = 111;
230 thread_context.arm_ip = 112;
231 thread_context.arm_lr = 113;
232 thread_context.arm_pc = 114;
233
234 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
235 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
236
237 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
238 // Pop r15, r12, r8, r4.
239 const uint8_t instruction[] = {0b10001001, 0b00010001};
240 const uint8_t* current_instruction = instruction;
241
242 bool pc_was_updated = false;
243 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
244 &thread_context),
245 UnwindInstructionResult::kInstructionPending);
246 EXPECT_TRUE(pc_was_updated);
247 ASSERT_EQ(current_instruction, instruction + 2);
248 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4), thread_context.arm_sp);
249
250 EXPECT_EQ(100ul, thread_context.arm_r0);
251 EXPECT_EQ(101ul, thread_context.arm_r1);
252 EXPECT_EQ(102ul, thread_context.arm_r2);
253 EXPECT_EQ(103ul, thread_context.arm_r3);
254 EXPECT_EQ(1ul, thread_context.arm_r4);
255 EXPECT_EQ(105ul, thread_context.arm_r5);
256 EXPECT_EQ(106ul, thread_context.arm_r6);
257 EXPECT_EQ(107ul, thread_context.arm_r7);
258 EXPECT_EQ(2ul, thread_context.arm_r8);
259 EXPECT_EQ(109ul, thread_context.arm_r9);
260 EXPECT_EQ(110ul, thread_context.arm_r10);
261 EXPECT_EQ(111ul, thread_context.arm_fp);
262 EXPECT_EQ(3ul, thread_context.arm_ip);
263 EXPECT_EQ(113ul, thread_context.arm_lr);
264 EXPECT_EQ(4ul, thread_context.arm_pc);
265 }
266
TEST(ChromeAndroid32UnwindInstructionTest,TestPopDiscontinuousRegisters)267 TEST(ChromeAndroid32UnwindInstructionTest, TestPopDiscontinuousRegisters) {
268 RegisterContext thread_context = {};
269
270 thread_context.arm_r0 = 100;
271 thread_context.arm_r1 = 101;
272 thread_context.arm_r2 = 102;
273 thread_context.arm_r3 = 103;
274 thread_context.arm_r4 = 104;
275 thread_context.arm_r5 = 105;
276 thread_context.arm_r6 = 106;
277 thread_context.arm_r7 = 107;
278 thread_context.arm_r8 = 108;
279 thread_context.arm_r9 = 109;
280 thread_context.arm_r10 = 110;
281 thread_context.arm_fp = 111;
282 thread_context.arm_ip = 112;
283 thread_context.arm_lr = 113;
284 thread_context.arm_pc = 114;
285
286 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
287 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
288
289 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
290 // Pop r12, r8, r4.
291 const uint8_t instruction[] = {0b10000001, 0b00010001};
292 const uint8_t* current_instruction = instruction;
293
294 bool pc_was_updated = false;
295 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
296 &thread_context),
297 UnwindInstructionResult::kInstructionPending);
298 EXPECT_FALSE(pc_was_updated);
299 ASSERT_EQ(current_instruction, instruction + 2);
300 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3), thread_context.arm_sp);
301
302 EXPECT_EQ(100ul, thread_context.arm_r0);
303 EXPECT_EQ(101ul, thread_context.arm_r1);
304 EXPECT_EQ(102ul, thread_context.arm_r2);
305 EXPECT_EQ(103ul, thread_context.arm_r3);
306 EXPECT_EQ(1ul, thread_context.arm_r4);
307 EXPECT_EQ(105ul, thread_context.arm_r5);
308 EXPECT_EQ(106ul, thread_context.arm_r6);
309 EXPECT_EQ(107ul, thread_context.arm_r7);
310 EXPECT_EQ(2ul, thread_context.arm_r8);
311 EXPECT_EQ(109ul, thread_context.arm_r9);
312 EXPECT_EQ(110ul, thread_context.arm_r10);
313 EXPECT_EQ(111ul, thread_context.arm_fp);
314 EXPECT_EQ(3ul, thread_context.arm_ip);
315 EXPECT_EQ(113ul, thread_context.arm_lr);
316 EXPECT_EQ(114ul, thread_context.arm_pc);
317 }
318
TEST(ChromeAndroid32UnwindInstructionTest,TestPopDiscontinuousRegistersOverflow)319 TEST(ChromeAndroid32UnwindInstructionTest,
320 TestPopDiscontinuousRegistersOverflow) {
321 RegisterContext thread_context = {};
322
323 thread_context.arm_r0 = 100;
324 thread_context.arm_r1 = 101;
325 thread_context.arm_r2 = 102;
326 thread_context.arm_r3 = 103;
327 thread_context.arm_r4 = 104;
328 thread_context.arm_r5 = 105;
329 thread_context.arm_r6 = 106;
330 thread_context.arm_r7 = 107;
331 thread_context.arm_r8 = 108;
332 thread_context.arm_r9 = 109;
333 thread_context.arm_r10 = 110;
334 thread_context.arm_fp = 111;
335 thread_context.arm_ip = 112;
336 thread_context.arm_lr = 113;
337 thread_context.arm_pc = 114;
338
339 // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
340 thread_context.arm_sp = 0xffffffff;
341 // Pop r15, r12, r8, r4.
342 const uint8_t instruction[] = {0b10001001, 0b00010001};
343 const uint8_t* current_instruction = instruction;
344
345 bool pc_was_updated = false;
346 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
347 &thread_context),
348 UnwindInstructionResult::kAborted);
349 EXPECT_FALSE(pc_was_updated);
350 ASSERT_EQ(current_instruction, instruction + 2);
351 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
352
353 EXPECT_EQ(100ul, thread_context.arm_r0);
354 EXPECT_EQ(101ul, thread_context.arm_r1);
355 EXPECT_EQ(102ul, thread_context.arm_r2);
356 EXPECT_EQ(103ul, thread_context.arm_r3);
357 EXPECT_EQ(104ul, thread_context.arm_r4);
358 EXPECT_EQ(105ul, thread_context.arm_r5);
359 EXPECT_EQ(106ul, thread_context.arm_r6);
360 EXPECT_EQ(107ul, thread_context.arm_r7);
361 EXPECT_EQ(108ul, thread_context.arm_r8);
362 EXPECT_EQ(109ul, thread_context.arm_r9);
363 EXPECT_EQ(110ul, thread_context.arm_r10);
364 EXPECT_EQ(111ul, thread_context.arm_fp);
365 EXPECT_EQ(112ul, thread_context.arm_ip);
366 EXPECT_EQ(113ul, thread_context.arm_lr);
367 EXPECT_EQ(114ul, thread_context.arm_pc);
368 }
369
TEST(ChromeAndroid32UnwindInstructionTest,TestRefuseToUnwind)370 TEST(ChromeAndroid32UnwindInstructionTest, TestRefuseToUnwind) {
371 RegisterContext thread_context = {};
372
373 const uint8_t instruction[] = {0b10000000, 0b0};
374 const uint8_t* current_instruction = instruction;
375
376 bool pc_was_updated = false;
377 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
378 &thread_context),
379 UnwindInstructionResult::kAborted);
380 EXPECT_FALSE(pc_was_updated);
381 ASSERT_EQ(current_instruction, instruction + 2);
382 }
383
TEST(ChromeAndroid32UnwindInstructionTest,TestPopRegistersIncludingR14MinRegisters)384 TEST(ChromeAndroid32UnwindInstructionTest,
385 TestPopRegistersIncludingR14MinRegisters) {
386 RegisterContext thread_context = {};
387
388 thread_context.arm_r0 = 100;
389 thread_context.arm_r1 = 101;
390 thread_context.arm_r2 = 102;
391 thread_context.arm_r3 = 103;
392 thread_context.arm_r4 = 104;
393 thread_context.arm_r5 = 105;
394 thread_context.arm_r6 = 106;
395 thread_context.arm_r7 = 107;
396 thread_context.arm_r8 = 108;
397 thread_context.arm_r9 = 109;
398 thread_context.arm_r10 = 110;
399 thread_context.arm_fp = 111;
400 thread_context.arm_ip = 112;
401 thread_context.arm_lr = 113;
402
403 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
404 // r14 = lr
405 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
406
407 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
408 const uint8_t instruction = 0b10101000;
409 const uint8_t* current_instruction = &instruction;
410 bool pc_was_updated = false;
411 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
412 &thread_context),
413 UnwindInstructionResult::kInstructionPending);
414 EXPECT_FALSE(pc_was_updated);
415 ASSERT_EQ(current_instruction, &instruction + 1);
416 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2), thread_context.arm_sp);
417
418 EXPECT_EQ(100ul, thread_context.arm_r0);
419 EXPECT_EQ(101ul, thread_context.arm_r1);
420 EXPECT_EQ(102ul, thread_context.arm_r2);
421 EXPECT_EQ(103ul, thread_context.arm_r3);
422 EXPECT_EQ(1ul, thread_context.arm_r4);
423 EXPECT_EQ(105ul, thread_context.arm_r5);
424 EXPECT_EQ(106ul, thread_context.arm_r6);
425 EXPECT_EQ(107ul, thread_context.arm_r7);
426 EXPECT_EQ(108ul, thread_context.arm_r8);
427 EXPECT_EQ(109ul, thread_context.arm_r9);
428 EXPECT_EQ(110ul, thread_context.arm_r10);
429 EXPECT_EQ(111ul, thread_context.arm_fp);
430 EXPECT_EQ(112ul, thread_context.arm_ip);
431 EXPECT_EQ(2ul, thread_context.arm_lr);
432 }
433
TEST(ChromeAndroid32UnwindInstructionTest,TestPopRegistersIncludingR14MidRegisters)434 TEST(ChromeAndroid32UnwindInstructionTest,
435 TestPopRegistersIncludingR14MidRegisters) {
436 RegisterContext thread_context = {};
437
438 thread_context.arm_r0 = 100;
439 thread_context.arm_r1 = 101;
440 thread_context.arm_r2 = 102;
441 thread_context.arm_r3 = 103;
442 thread_context.arm_r4 = 104;
443 thread_context.arm_r5 = 105;
444 thread_context.arm_r6 = 106;
445 thread_context.arm_r7 = 107;
446 thread_context.arm_r8 = 108;
447 thread_context.arm_r9 = 109;
448 thread_context.arm_r10 = 110;
449 thread_context.arm_fp = 111;
450 thread_context.arm_ip = 112;
451 thread_context.arm_lr = 113;
452
453 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
454 // r14 = lr
455 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
456
457 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
458 const uint8_t instruction = 0b10101100; // Pop r4-r8, r14.
459 const uint8_t* current_instruction = &instruction;
460 bool pc_was_updated = false;
461 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
462 &thread_context),
463 UnwindInstructionResult::kInstructionPending);
464 EXPECT_FALSE(pc_was_updated);
465 ASSERT_EQ(current_instruction, &instruction + 1);
466 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6), thread_context.arm_sp);
467
468 EXPECT_EQ(100ul, thread_context.arm_r0);
469 EXPECT_EQ(101ul, thread_context.arm_r1);
470 EXPECT_EQ(102ul, thread_context.arm_r2);
471 EXPECT_EQ(103ul, thread_context.arm_r3);
472 EXPECT_EQ(1ul, thread_context.arm_r4);
473 EXPECT_EQ(2ul, thread_context.arm_r5);
474 EXPECT_EQ(3ul, thread_context.arm_r6);
475 EXPECT_EQ(4ul, thread_context.arm_r7);
476 EXPECT_EQ(5ul, thread_context.arm_r8);
477 EXPECT_EQ(109ul, thread_context.arm_r9);
478 EXPECT_EQ(110ul, thread_context.arm_r10);
479 EXPECT_EQ(111ul, thread_context.arm_fp);
480 EXPECT_EQ(112ul, thread_context.arm_ip);
481 EXPECT_EQ(6ul, thread_context.arm_lr);
482 }
483
TEST(ChromeAndroid32UnwindInstructionTest,TestPopRegistersIncludingR14MaxRegisters)484 TEST(ChromeAndroid32UnwindInstructionTest,
485 TestPopRegistersIncludingR14MaxRegisters) {
486 RegisterContext thread_context = {};
487
488 thread_context.arm_r0 = 100;
489 thread_context.arm_r1 = 101;
490 thread_context.arm_r2 = 102;
491 thread_context.arm_r3 = 103;
492 thread_context.arm_r4 = 104;
493 thread_context.arm_r5 = 105;
494 thread_context.arm_r6 = 106;
495 thread_context.arm_r7 = 107;
496 thread_context.arm_r8 = 108;
497 thread_context.arm_r9 = 109;
498 thread_context.arm_r10 = 110;
499 thread_context.arm_fp = 111;
500 thread_context.arm_ip = 112;
501 thread_context.arm_lr = 113;
502
503 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
504 // r14 = lr
505 const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
506
507 thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
508 const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
509 const uint8_t* current_instruction = &instruction;
510 bool pc_was_updated = false;
511 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
512 &thread_context),
513 UnwindInstructionResult::kInstructionPending);
514 EXPECT_FALSE(pc_was_updated);
515 ASSERT_EQ(current_instruction, &instruction + 1);
516 EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9), thread_context.arm_sp);
517
518 EXPECT_EQ(100ul, thread_context.arm_r0);
519 EXPECT_EQ(101ul, thread_context.arm_r1);
520 EXPECT_EQ(102ul, thread_context.arm_r2);
521 EXPECT_EQ(103ul, thread_context.arm_r3);
522 EXPECT_EQ(1ul, thread_context.arm_r4);
523 EXPECT_EQ(2ul, thread_context.arm_r5);
524 EXPECT_EQ(3ul, thread_context.arm_r6);
525 EXPECT_EQ(4ul, thread_context.arm_r7);
526 EXPECT_EQ(5ul, thread_context.arm_r8);
527 EXPECT_EQ(6ul, thread_context.arm_r9);
528 EXPECT_EQ(7ul, thread_context.arm_r10);
529 EXPECT_EQ(8ul, thread_context.arm_fp);
530 EXPECT_EQ(112ul, thread_context.arm_ip);
531 EXPECT_EQ(9ul, thread_context.arm_lr);
532 }
533
TEST(ChromeAndroid32UnwindInstructionTest,TestPopRegistersIncludingR14Overflow)534 TEST(ChromeAndroid32UnwindInstructionTest,
535 TestPopRegistersIncludingR14Overflow) {
536 RegisterContext thread_context = {};
537
538 thread_context.arm_r0 = 100;
539 thread_context.arm_r1 = 101;
540 thread_context.arm_r2 = 102;
541 thread_context.arm_r3 = 103;
542 thread_context.arm_r4 = 104;
543 thread_context.arm_r5 = 105;
544 thread_context.arm_r6 = 106;
545 thread_context.arm_r7 = 107;
546 thread_context.arm_r8 = 108;
547 thread_context.arm_r9 = 109;
548 thread_context.arm_r10 = 110;
549 thread_context.arm_fp = 111;
550 thread_context.arm_ip = 112;
551 thread_context.arm_lr = 113;
552
553 // Popping r4 - r[4 + nnn], r14, at most 9 registers.
554 // r14 = lr
555 thread_context.arm_sp = 0xffffffff;
556 const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
557 const uint8_t* current_instruction = &instruction;
558 bool pc_was_updated = false;
559 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
560 &thread_context),
561 UnwindInstructionResult::kAborted);
562 EXPECT_FALSE(pc_was_updated);
563 ASSERT_EQ(current_instruction, &instruction + 1);
564 EXPECT_EQ(0xffffffff, thread_context.arm_sp);
565
566 EXPECT_EQ(100ul, thread_context.arm_r0);
567 EXPECT_EQ(101ul, thread_context.arm_r1);
568 EXPECT_EQ(102ul, thread_context.arm_r2);
569 EXPECT_EQ(103ul, thread_context.arm_r3);
570 EXPECT_EQ(104ul, thread_context.arm_r4);
571 EXPECT_EQ(105ul, thread_context.arm_r5);
572 EXPECT_EQ(106ul, thread_context.arm_r6);
573 EXPECT_EQ(107ul, thread_context.arm_r7);
574 EXPECT_EQ(108ul, thread_context.arm_r8);
575 EXPECT_EQ(109ul, thread_context.arm_r9);
576 EXPECT_EQ(110ul, thread_context.arm_r10);
577 EXPECT_EQ(111ul, thread_context.arm_fp);
578 EXPECT_EQ(112ul, thread_context.arm_ip);
579 EXPECT_EQ(113ul, thread_context.arm_lr);
580 }
581
TEST(ChromeAndroid32UnwindInstructionTest,TestBigStackPointerIncrementMinValue)582 TEST(ChromeAndroid32UnwindInstructionTest,
583 TestBigStackPointerIncrementMinValue) {
584 RegisterContext thread_context = {};
585 thread_context.arm_sp = 0x10000000;
586
587 const uint8_t increment_0[] = {
588 0b10110010,
589 0b00000000,
590 };
591 const uint8_t* current_instruction = &increment_0[0];
592 bool pc_was_updated = false;
593 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
594 &thread_context),
595 UnwindInstructionResult::kInstructionPending);
596 EXPECT_FALSE(pc_was_updated);
597 ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0));
598 // vsp + 0x204 + (0 << 2)
599 // = vsp + 0x204
600 EXPECT_EQ(0x10000204ul, thread_context.arm_sp);
601 }
602
TEST(ChromeAndroid32UnwindInstructionTest,TestBigStackPointerIncrementMidValue)603 TEST(ChromeAndroid32UnwindInstructionTest,
604 TestBigStackPointerIncrementMidValue) {
605 RegisterContext thread_context = {};
606 thread_context.arm_sp = 0x10000000;
607
608 const uint8_t increment_4[] = {
609 0b10110010,
610 0b00000100,
611 };
612 const uint8_t* current_instruction = &increment_4[0];
613
614 // vsp + 0x204 + (4 << 2)
615 // = vsp + 0x204 + 0x10
616 // = vsp + 0x214
617 bool pc_was_updated = false;
618 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
619 &thread_context),
620 UnwindInstructionResult::kInstructionPending);
621 EXPECT_FALSE(pc_was_updated);
622 ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4));
623 EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
624 }
625
TEST(ChromeAndroid32UnwindInstructionTest,TestBigStackPointerIncrementLargeValue)626 TEST(ChromeAndroid32UnwindInstructionTest,
627 TestBigStackPointerIncrementLargeValue) {
628 RegisterContext thread_context = {};
629 thread_context.arm_sp = 0x10000000;
630
631 const uint8_t increment_128[] = {
632 0b10110010,
633 0b10000000,
634 0b00000001,
635 };
636 const uint8_t* current_instruction = &increment_128[0];
637 // vsp + 0x204 + (128 << 2)
638 // = vsp + 0x204 + 512
639 // = vsp + 0x204 + 0x200
640 // = vsp + 0x404
641 bool pc_was_updated = false;
642 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
643 &thread_context),
644 UnwindInstructionResult::kInstructionPending);
645 EXPECT_FALSE(pc_was_updated);
646 ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128));
647 EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
648 }
649
TEST(ChromeAndroid32UnwindInstructionTest,TestBigStackPointerIncrementOverflow)650 TEST(ChromeAndroid32UnwindInstructionTest,
651 TestBigStackPointerIncrementOverflow) {
652 RegisterContext thread_context = {};
653 thread_context.arm_sp = 0xffffffff;
654
655 const uint8_t increment_overflow[] = {
656 0b10110010,
657 0b10000000,
658 0b00000001,
659 }; // ULEB128 = 128
660 const uint8_t* current_instruction = &increment_overflow[0];
661 bool pc_was_updated = false;
662 ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
663 &thread_context),
664 UnwindInstructionResult::kAborted);
665 EXPECT_FALSE(pc_was_updated);
666 ASSERT_EQ(current_instruction,
667 increment_overflow + sizeof(increment_overflow));
668 EXPECT_EQ(0xfffffffful, thread_context.arm_sp);
669 }
670
TEST(ChromeUnwinderAndroid32Test,TestFunctionOffsetTableLookupExactMatchingOffset)671 TEST(ChromeUnwinderAndroid32Test,
672 TestFunctionOffsetTableLookupExactMatchingOffset) {
673 const uint8_t function_offset_table[] = {
674 // Function 1: [(130, 2), (128, 3), (0, 4)]
675 // offset = 130
676 0b10000010,
677 0b00000001,
678 // unwind index = 2
679 0b00000010,
680 // offset = 128
681 0b10000000,
682 0b00000001,
683 // unwind index = 3
684 0b00000011,
685 // offset = 0
686 0b00000000,
687 // unwind index = 4
688 0b00000100,
689 };
690
691 EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
692 &function_offset_table[0],
693 /* instruction_offset_from_function_start */ 128));
694 }
695
TEST(ChromeUnwinderAndroid32Test,TestFunctionOffsetTableLookupNonExactMatchingOffset)696 TEST(ChromeUnwinderAndroid32Test,
697 TestFunctionOffsetTableLookupNonExactMatchingOffset) {
698 const uint8_t function_offset_table[] = {
699 // Function 1: [(130, 2), (128, 3), (0, 4)]
700 // offset = 130
701 0b10000010,
702 0b00000001,
703 // unwind index = 2
704 0b00000010,
705 // offset = 128
706 0b10000000,
707 0b00000001,
708 // unwind index = 3
709 0b00000011,
710 // offset = 0
711 0b00000000,
712 // unwind index = 4
713 0b00000100,
714 };
715
716 EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
717 &function_offset_table[0],
718 /* instruction_offset_from_function_start */ 129));
719 }
720
TEST(ChromeUnwinderAndroid32Test,TestFunctionOffsetTableLookupZeroOffset)721 TEST(ChromeUnwinderAndroid32Test, TestFunctionOffsetTableLookupZeroOffset) {
722 const uint8_t function_offset_table[] = {
723 // Function 1: [(130, 2), (128, 3), (0, 4)]
724 // offset = 130
725 0b10000010,
726 0b00000001,
727 // unwind index = 2
728 0b00000010,
729 // offset = 128
730 0b10000000,
731 0b00000001,
732 // unwind index = 3
733 0b00000011,
734 // offset = 0
735 0b00000000,
736 // unwind index = 4
737 0b00000100,
738 };
739
740 EXPECT_EQ(4ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
741 &function_offset_table[0],
742 /* instruction_offset_from_function_start */ 0));
743 }
744
TEST(ChromeUnwinderAndroid32Test,TestAddressTableLookupEntryInPage)745 TEST(ChromeUnwinderAndroid32Test, TestAddressTableLookupEntryInPage) {
746 const uint32_t page_start_instructions[] = {0, 2};
747 const FunctionTableEntry function_offset_table_indices[] = {
748 // Page 0
749 {
750 /* function_start_address_page_instruction_offset */ 0,
751 /* function_offset_table_byte_index */ 20,
752 },
753 {
754 /* function_start_address_page_instruction_offset */ 4,
755 /* function_offset_table_byte_index */ 40,
756 },
757 // Page 1
758 {
759 /* function_start_address_page_instruction_offset */ 6,
760 /* function_offset_table_byte_index */ 70,
761 },
762 };
763
764 {
765 const uint32_t page_number = 0;
766 const uint32_t page_instruction_offset = 4;
767 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
768 page_start_instructions, function_offset_table_indices,
769 /* instruction_offset */ (page_instruction_offset << 1) +
770 (page_number << 17));
771 ASSERT_NE(std::nullopt, entry_found);
772 EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
773 EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
774 }
775
776 {
777 const uint32_t page_number = 0;
778 const uint32_t page_instruction_offset = 50;
779 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
780 page_start_instructions, function_offset_table_indices,
781 /* instruction_offset */ (page_instruction_offset << 1) +
782 (page_number << 17));
783 ASSERT_NE(std::nullopt, entry_found);
784 EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
785 EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
786 }
787
788 // Lookup last instruction in last function.
789 {
790 const uint32_t page_number = 1;
791 const uint32_t page_instruction_offset = 0xffff;
792 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
793 page_start_instructions, function_offset_table_indices,
794 /* instruction_offset */ (page_instruction_offset << 1) +
795 (page_number << 17));
796 ASSERT_NE(std::nullopt, entry_found);
797 // 0xffff - 6 = 0xfff9.
798 EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
799 EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
800 }
801 }
802
TEST(ChromeUnwinderAndroid32Test,TestAddressTableLookupEmptyPage)803 TEST(ChromeUnwinderAndroid32Test, TestAddressTableLookupEmptyPage) {
804 const uint32_t page_start_instructions[] = {0, 1, 1};
805 const FunctionTableEntry function_offset_table_indices[] = {
806 // Page 0
807 {
808 /* function_start_address_page_instruction_offset */ 0,
809 /* function_offset_table_byte_index */ 20,
810 },
811 // Page 1 is empty
812 // Page 2
813 {
814 /* function_start_address_page_instruction_offset */ 6,
815 /* function_offset_table_byte_index */ 70,
816 },
817 };
818
819 const uint32_t page_number = 1;
820 const uint32_t page_instruction_offset = 4;
821 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
822 page_start_instructions, function_offset_table_indices,
823 /* instruction_offset */ (page_instruction_offset << 1) +
824 (page_number << 17));
825 ASSERT_NE(std::nullopt, entry_found);
826 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
827 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
828 }
829
TEST(ChromeUnwinderAndroid32Test,TestAddressTableLookupInvalidIntructionOffset)830 TEST(ChromeUnwinderAndroid32Test,
831 TestAddressTableLookupInvalidIntructionOffset) {
832 const uint32_t page_start_instructions[] = {0, 1};
833 const FunctionTableEntry function_offset_table_indices[] = {
834 // Page 0
835 // This function spans from page 0 offset 0 to page 1 offset 5.
836 {
837 /* function_start_address_page_instruction_offset */ 0,
838 /* function_offset_table_byte_index */ 20,
839 },
840 // Page 1
841 {
842 /* function_start_address_page_instruction_offset */ 6,
843 /* function_offset_table_byte_index */ 70,
844 },
845 };
846
847 // Instruction offset lies after last page on page table.
848 {
849 const uint32_t page_number = 50;
850 const uint32_t page_instruction_offset = 6;
851 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
852 page_start_instructions, function_offset_table_indices,
853 /* instruction_offset */ (page_instruction_offset << 1) +
854 (page_number << 17));
855 ASSERT_EQ(std::nullopt, entry_found);
856 }
857 {
858 const uint32_t page_number = 2;
859 const uint32_t page_instruction_offset = 0;
860 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
861 page_start_instructions, function_offset_table_indices,
862 /* instruction_offset */ (page_instruction_offset << 1) +
863 (page_number << 17));
864 ASSERT_EQ(std::nullopt, entry_found);
865 }
866 }
867
TEST(ChromeUnwinderAndroid32Test,TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary)868 TEST(ChromeUnwinderAndroid32Test,
869 TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
870 const uint32_t page_start_instructions[] = {0, 1, 2};
871 const FunctionTableEntry function_offset_table_indices[] = {
872 // Page 0
873 {
874 /* function_start_address_page_instruction_offset */ 0,
875 /* function_offset_table_byte_index */ 20,
876 },
877 // Page 1
878 {
879 /* function_start_address_page_instruction_offset */ 6,
880 /* function_offset_table_byte_index */ 70,
881 },
882 // Page 2
883 {
884 /* function_start_address_page_instruction_offset */ 10,
885 /* function_offset_table_byte_index */ 80,
886 }};
887
888 const uint32_t page_number = 1;
889 const uint32_t page_instruction_offset = 4;
890 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
891 page_start_instructions, function_offset_table_indices,
892 /* instruction_offset */ (page_instruction_offset << 1) +
893 (page_number << 17));
894 ASSERT_NE(std::nullopt, entry_found);
895 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
896 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
897 }
898
TEST(ChromeUnwinderAndroid32Test,TestAddressTableLookupWithinFunctionSpanningMultiplePages)899 TEST(ChromeUnwinderAndroid32Test,
900 TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
901 const uint32_t page_start_instructions[] = {0, 1, 1, 1};
902 const FunctionTableEntry function_offset_table_indices[] = {
903 // Page 0
904 // This function spans from page 0 offset 0 to page 3 offset 5.
905 {
906 /* function_start_address_page_instruction_offset */ 0,
907 /* function_offset_table_byte_index */ 20,
908 },
909 // Page 1 is empty
910 // Page 2 is empty
911 // Page 3
912 {
913 /* function_start_address_page_instruction_offset */ 6,
914 /* function_offset_table_byte_index */ 70,
915 },
916 };
917
918 {
919 const uint32_t page_number = 0;
920 const uint32_t page_instruction_offset = 4;
921 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
922 page_start_instructions, function_offset_table_indices,
923 /* instruction_offset */ (page_instruction_offset << 1) +
924 (page_number << 17));
925 ASSERT_NE(std::nullopt, entry_found);
926 EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
927 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
928 }
929 {
930 const uint32_t page_number = 1;
931 const uint32_t page_instruction_offset = 4;
932 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
933 page_start_instructions, function_offset_table_indices,
934 /* instruction_offset */ (page_instruction_offset << 1) +
935 (page_number << 17));
936 ASSERT_NE(std::nullopt, entry_found);
937 EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
938 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
939 }
940 {
941 const uint32_t page_number = 2;
942 const uint32_t page_instruction_offset = 4;
943 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
944 page_start_instructions, function_offset_table_indices,
945 /* instruction_offset */ (page_instruction_offset << 1) +
946 (page_number << 17));
947 ASSERT_NE(std::nullopt, entry_found);
948 EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
949 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
950 }
951 {
952 const uint32_t page_number = 3;
953 const uint32_t page_instruction_offset = 4;
954 const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
955 page_start_instructions, function_offset_table_indices,
956 /* instruction_offset */ (page_instruction_offset << 1) +
957 (page_number << 17));
958 ASSERT_NE(std::nullopt, entry_found);
959 EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
960 EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
961 }
962 }
963
964 // Utility function to add a single native module during test setup. Returns
965 // a pointer to the provided module.
AddNativeModule(ModuleCache * cache,std::unique_ptr<const ModuleCache::Module> module)966 const ModuleCache::Module* AddNativeModule(
967 ModuleCache* cache,
968 std::unique_ptr<const ModuleCache::Module> module) {
969 const ModuleCache::Module* module_ptr = module.get();
970 cache->AddCustomNativeModule(std::move(module));
971 return module_ptr;
972 }
973
TEST(ChromeUnwinderAndroid32Test,CanUnwindFrom)974 TEST(ChromeUnwinderAndroid32Test, CanUnwindFrom) {
975 const uint32_t page_table[] = {0};
976 const FunctionTableEntry function_table[] = {{0, 0}};
977 const uint8_t function_offset_table[] = {0};
978 const uint8_t unwind_instruction_table[] = {0};
979 auto dummy_unwind_info = ChromeUnwindInfoAndroid32{
980 unwind_instruction_table,
981 function_offset_table,
982 function_table,
983 page_table,
984 };
985
986 auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
987 auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
988
989 ModuleCache module_cache;
990 ChromeUnwinderAndroid32 unwinder(dummy_unwind_info,
991 chrome_module->GetBaseAddress(),
992 /* text_section_start_address */
993 chrome_module->GetBaseAddress() + 4);
994 unwinder.Initialize(&module_cache);
995
996 EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
997 EXPECT_TRUE(unwinder.CanUnwindFrom({0x1000, chrome_module.get()}));
998 EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
999 EXPECT_FALSE(unwinder.CanUnwindFrom({0x400, nullptr}));
1000 }
1001
1002 namespace {
ExpectFramesEq(const std::vector<Frame> & expected,const std::vector<Frame> & actual)1003 void ExpectFramesEq(const std::vector<Frame>& expected,
1004 const std::vector<Frame>& actual) {
1005 EXPECT_EQ(actual.size(), expected.size());
1006 if (actual.size() != expected.size()) {
1007 return;
1008 }
1009
1010 for (size_t i = 0; i < actual.size(); i++) {
1011 EXPECT_EQ(expected[i].module, actual[i].module);
1012 EXPECT_EQ(expected[i].instruction_pointer, actual[i].instruction_pointer);
1013 }
1014 }
1015
1016 class AlignedStackMemory {
1017 public:
AlignedStackMemory(std::initializer_list<uintptr_t> values)1018 AlignedStackMemory(std::initializer_list<uintptr_t> values)
1019 : size_(values.size()),
1020 stack_memory_(static_cast<uintptr_t*>(
1021 AlignedAlloc(size_ * sizeof(uintptr_t), 2 * sizeof(uintptr_t)))) {
1022 DCHECK_EQ(size_ % 2, 0u);
1023 ranges::copy(values, stack_memory_.get());
1024 }
1025
stack_start_address() const1026 uintptr_t stack_start_address() const {
1027 return reinterpret_cast<uintptr_t>(stack_memory_.get());
1028 }
1029
stack_end_address() const1030 uintptr_t stack_end_address() const {
1031 return reinterpret_cast<uintptr_t>(stack_memory_.get() + size_);
1032 }
1033
1034 private:
1035 const uintptr_t size_;
1036 const std::unique_ptr<uintptr_t, AlignedFreeDeleter> stack_memory_;
1037 };
1038
1039 } // namespace
1040
TEST(ChromeUnwinderAndroid32Test,TryUnwind)1041 TEST(ChromeUnwinderAndroid32Test, TryUnwind) {
1042 const uint32_t page_table[] = {0, 2};
1043 const size_t number_of_pages = std::size(page_table);
1044 const size_t page_size = 1 << 17;
1045
1046 const FunctionTableEntry function_table[] = {
1047 // Page 0.
1048 {0, 0}, // Function 0.
1049 {0x10, 4}, // Function 1. The function to unwind 2 times.
1050 // Page 1.
1051 {0x5, 8}, // Function 2.
1052 {0x20, 12}, // Function 3.
1053 };
1054 const uint8_t function_offset_table[] = {
1055 // Function 0.
1056 0x2,
1057 0,
1058 0x0,
1059 1,
1060 // Function 1.
1061 0x7f,
1062 0,
1063 0x0,
1064 1,
1065 // Function 2.
1066 0x78,
1067 0,
1068 0x0,
1069 1,
1070 // Function 3.
1071 0x2,
1072 0,
1073 0x0,
1074 1,
1075 };
1076 const uint8_t unwind_instruction_table[] = {
1077 // Offset 0: Pop r4, r14 from stack top.
1078 // Need to pop 2 registers to keep SP aligned.
1079 0b10101000,
1080 // Offset 1: COMPLETE.
1081 0b10110000,
1082 };
1083
1084 auto unwind_info = ChromeUnwindInfoAndroid32{
1085 unwind_instruction_table,
1086 function_offset_table,
1087 function_table,
1088 page_table,
1089 };
1090
1091 ModuleCache module_cache;
1092 const ModuleCache::Module* chrome_module = AddNativeModule(
1093 &module_cache, std::make_unique<TestModule>(
1094 0x1000, number_of_pages * page_size, "ChromeModule"));
1095
1096 uintptr_t text_section_start_address = 0x1100;
1097 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1098 text_section_start_address);
1099
1100 unwinder.Initialize(&module_cache);
1101
1102 // Both first_pc and second_pc lie in Function 1's address range.
1103 uintptr_t first_pc = text_section_start_address + 0x20;
1104 uintptr_t second_pc = text_section_start_address + page_size + 0x4;
1105 // third_pc lies outside chrome_module's address range.
1106 uintptr_t third_pc = text_section_start_address + 3 * page_size;
1107
1108 AlignedStackMemory stack_memory = {
1109 0x0,
1110 third_pc,
1111 0xFFFF,
1112 0xFFFF,
1113 };
1114
1115 std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1116 RegisterContext context;
1117 RegisterContextInstructionPointer(&context) = first_pc;
1118 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1119 context.arm_lr = second_pc;
1120
1121 EXPECT_EQ(
1122 UnwindResult::kUnrecognizedFrame,
1123 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1124 stack_memory.stack_end_address(), &unwound_frames));
1125 ExpectFramesEq(std::vector<Frame>({{first_pc, chrome_module},
1126 {second_pc, chrome_module},
1127 {third_pc, nullptr}}),
1128 unwound_frames);
1129 }
1130
TEST(ChromeUnwinderAndroid32Test,TryUnwindInfiniteLoopSingleFrame)1131 TEST(ChromeUnwinderAndroid32Test, TryUnwindInfiniteLoopSingleFrame) {
1132 const uint32_t page_table[] = {0, 2};
1133 const size_t number_of_pages = std::size(page_table);
1134 const size_t page_size = 1 << 17;
1135
1136 const FunctionTableEntry function_table[] = {
1137 // Page 0.
1138 {0x0, 0}, // Refuse to unwind filler function.
1139 {0x10, 2}, // Function 0. The function to unwind.
1140 // Page 1.
1141 {0x5, 0}, // Refuse to unwind filler function.
1142 };
1143 const uint8_t function_offset_table[] = {
1144 // Refuse to unwind filler function.
1145 0x0,
1146 0,
1147 // Function 0.
1148 0x0,
1149 2,
1150 };
1151 const uint8_t unwind_instruction_table[] = {
1152 // Offset 0: REFUSE_TO_UNWIND.
1153 0b10000000,
1154 0b00000000,
1155 // Offset 2: COMPLETE.
1156 0b10110000,
1157 };
1158
1159 auto unwind_info = ChromeUnwindInfoAndroid32{
1160 unwind_instruction_table,
1161 function_offset_table,
1162 function_table,
1163 page_table,
1164 };
1165
1166 ModuleCache module_cache;
1167 const ModuleCache::Module* chrome_module = AddNativeModule(
1168 &module_cache, std::make_unique<TestModule>(
1169 0x1000, number_of_pages * page_size, "ChromeModule"));
1170
1171 uintptr_t text_section_start_address = 0x1100;
1172 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1173 text_section_start_address);
1174
1175 unwinder.Initialize(&module_cache);
1176 uintptr_t pc = text_section_start_address + 0x20;
1177
1178 AlignedStackMemory stack_memory = {
1179 0xFFFF,
1180 0xFFFF,
1181 };
1182
1183 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1184 RegisterContext context;
1185 RegisterContextInstructionPointer(&context) = pc;
1186 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1187
1188 // Set lr = pc so that both sp and pc stays the same after first round of
1189 // unwind.
1190 context.arm_lr = pc;
1191
1192 EXPECT_EQ(
1193 UnwindResult::kAborted,
1194 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1195 stack_memory.stack_end_address(), &unwound_frames));
1196 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1197 }
1198
TEST(ChromeUnwinderAndroid32Test,TryUnwindInfiniteLoopMultipleFrames)1199 TEST(ChromeUnwinderAndroid32Test, TryUnwindInfiniteLoopMultipleFrames) {
1200 // This test aims to produce a scenario, where after the unwind of a number
1201 // of frames, the sp and pc get to their original state before the unwind.
1202
1203 // Function 1 (pc1, sp1):
1204 // - set pc = lr(pc2)
1205 // Function 2 (pc2, sp1):
1206 // - pop r14(pc2), r15(pc1) off stack
1207 // - vsp = r4 (reset vsp to frame initial vsp)
1208
1209 const uint32_t page_table[] = {0, 3};
1210 const size_t number_of_pages = std::size(page_table);
1211 const size_t page_size = 1 << 17;
1212
1213 const FunctionTableEntry function_table[] = {
1214 // Page 0.
1215 {0x0, 0}, // Refuse to unwind filler function.
1216 {0x10, 2}, // Function 1. The function to unwind.
1217 {0x100, 2}, // Function 2. The function to unwind.
1218 // Page 1.
1219 {0x5, 0}, // Refuse to unwind filler function.
1220 };
1221 const uint8_t function_offset_table[] = {
1222 // Refuse to unwind filler function.
1223 0x0,
1224 0,
1225 // Function 0.
1226 0x0,
1227 2,
1228 // Function 1.
1229 0x2,
1230 3,
1231 0x1,
1232 5,
1233 0x0,
1234 6,
1235 };
1236 const uint8_t unwind_instruction_table[] = {
1237 // Offset 0: REFUSE_TO_UNWIND.
1238 0b10000000,
1239 0b00000000,
1240 // Offset 2: COMPLETE.
1241 0b10110000,
1242 // Offset 3: POP r14, r15 off the stack.
1243 0b10001100,
1244 0b00000000,
1245 // Offset 5: vsp = r4.
1246 0b10010100,
1247 // Offset 6: COMPLETE.
1248 0b10110000,
1249 };
1250
1251 auto unwind_info = ChromeUnwindInfoAndroid32{
1252 unwind_instruction_table,
1253 function_offset_table,
1254 function_table,
1255 page_table,
1256 };
1257
1258 ModuleCache module_cache;
1259 const ModuleCache::Module* chrome_module = AddNativeModule(
1260 &module_cache, std::make_unique<TestModule>(
1261 0x1000, number_of_pages * page_size, "ChromeModule"));
1262
1263 uintptr_t text_section_start_address = 0x1100;
1264 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1265 text_section_start_address);
1266
1267 unwinder.Initialize(&module_cache);
1268 uintptr_t first_pc = text_section_start_address + 0x20; // Function 1.
1269 uintptr_t second_pc = text_section_start_address + 0x110; // Function 2.
1270
1271 AlignedStackMemory stack_memory = {
1272 second_pc,
1273 first_pc,
1274 0xFFFF,
1275 0xFFFF,
1276 };
1277
1278 std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1279 RegisterContext context;
1280 RegisterContextInstructionPointer(&context) = first_pc;
1281 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1282
1283 context.arm_lr = second_pc;
1284 context.arm_r4 = stack_memory.stack_start_address();
1285
1286 EXPECT_EQ(
1287 UnwindResult::kAborted,
1288 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1289 stack_memory.stack_end_address(), &unwound_frames));
1290 ExpectFramesEq(std::vector<Frame>(
1291 {{first_pc, chrome_module}, {second_pc, chrome_module}}),
1292 unwound_frames);
1293 }
1294
TEST(ChromeUnwinderAndroid32Test,TryUnwindUnalignedSPFrameUnwind)1295 TEST(ChromeUnwinderAndroid32Test, TryUnwindUnalignedSPFrameUnwind) {
1296 // SP should be 2-uintptr_t aligned before/after each frame unwind.
1297 const uint32_t page_table[] = {0, 2};
1298 const size_t number_of_pages = std::size(page_table);
1299 const size_t page_size = 1 << 17;
1300
1301 const FunctionTableEntry function_table[] = {
1302 // Page 0.
1303 {0x0, 0}, // Refuse to unwind filler function.
1304 {0x10, 2}, // Function 0. The function to unwind.
1305 // Page 1.
1306 {0x5, 0}, // Refuse to unwind filler function.
1307 };
1308 const uint8_t function_offset_table[] = {
1309 // Refuse to unwind filler function.
1310 0x0,
1311 0,
1312 // Function 0.
1313 0x0,
1314 2,
1315 };
1316 const uint8_t unwind_instruction_table[] = {
1317 // Offset 0: REFUSE_TO_UNWIND.
1318 0b10000000,
1319 0b00000000,
1320 // Offset 2: COMPLETE.
1321 0b10110000,
1322 };
1323
1324 auto unwind_info = ChromeUnwindInfoAndroid32{
1325 unwind_instruction_table,
1326 function_offset_table,
1327 function_table,
1328 page_table,
1329 };
1330
1331 ModuleCache module_cache;
1332 const ModuleCache::Module* chrome_module = AddNativeModule(
1333 &module_cache, std::make_unique<TestModule>(
1334 0x1000, number_of_pages * page_size, "ChromeModule"));
1335
1336 uintptr_t text_section_start_address = 0x1100;
1337 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1338 text_section_start_address);
1339
1340 unwinder.Initialize(&module_cache);
1341 uintptr_t pc = text_section_start_address + 0x20;
1342
1343 AlignedStackMemory stack_memory = {
1344 0xFFFF,
1345 0xFFFF,
1346 };
1347
1348 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1349 RegisterContext context;
1350 RegisterContextInstructionPointer(&context) = pc;
1351 // Make stack memory not aligned to 2 * sizeof(uintptr_t);
1352 RegisterContextStackPointer(&context) =
1353 stack_memory.stack_start_address() + sizeof(uintptr_t);
1354
1355 // The address is outside chrome module, which will result the unwind to
1356 // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1357 context.arm_lr =
1358 text_section_start_address + (number_of_pages + 1) * page_size;
1359
1360 EXPECT_EQ(
1361 UnwindResult::kAborted,
1362 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1363 stack_memory.stack_end_address(), &unwound_frames));
1364 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1365 }
1366
TEST(ChromeUnwinderAndroid32Test,TryUnwindUnalignedSPInstructionUnwind)1367 TEST(ChromeUnwinderAndroid32Test, TryUnwindUnalignedSPInstructionUnwind) {
1368 // SP should be uintptr_t aligned before/after each unwind instruction
1369 // execution.
1370
1371 const uint32_t page_table[] = {0, 2};
1372 const size_t number_of_pages = std::size(page_table);
1373 const size_t page_size = 1 << 17;
1374
1375 const FunctionTableEntry function_table[] = {
1376 // Page 0.
1377 {0x0, 0}, // Refuse to unwind filler function.
1378 {0x10, 2}, // Function 0. The function to unwind.
1379 // Page 1.
1380 {0x5, 0}, // Refuse to unwind filler function.
1381 };
1382 const uint8_t function_offset_table[] = {
1383 // Refuse to unwind filler function.
1384 0x0,
1385 0,
1386 // Function 0.
1387 0x0,
1388 2,
1389 };
1390 const uint8_t unwind_instruction_table[] = {
1391 // Offset 0: REFUSE_TO_UNWIND.
1392 0b10000000, 0b00000000,
1393 // Offset 2:
1394 0b10010100, // vsp = r4, where r4 = stack + (sizeof(uintptr_t) / 2)
1395 0b10110000, // COMPLETE.
1396 };
1397
1398 auto unwind_info = ChromeUnwindInfoAndroid32{
1399 unwind_instruction_table,
1400 function_offset_table,
1401 function_table,
1402 page_table,
1403 };
1404
1405 ModuleCache module_cache;
1406 const ModuleCache::Module* chrome_module = AddNativeModule(
1407 &module_cache, std::make_unique<TestModule>(
1408 0x1000, number_of_pages * page_size, "ChromeModule"));
1409
1410 uintptr_t text_section_start_address = 0x1100;
1411 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1412 text_section_start_address);
1413
1414 unwinder.Initialize(&module_cache);
1415 uintptr_t pc = text_section_start_address + 0x20;
1416
1417 AlignedStackMemory stack_memory = {
1418 0xFFFF,
1419 0xFFFF,
1420 };
1421
1422 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1423 RegisterContext context;
1424 RegisterContextInstructionPointer(&context) = pc;
1425 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1426
1427 // The address is outside chrome module, which will result the unwind to
1428 // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1429 context.arm_lr =
1430 text_section_start_address + (number_of_pages + 1) * page_size;
1431
1432 context.arm_r4 = stack_memory.stack_start_address() + sizeof(uintptr_t) / 2;
1433
1434 EXPECT_EQ(
1435 UnwindResult::kAborted,
1436 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1437 stack_memory.stack_end_address(), &unwound_frames));
1438 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1439 }
1440
TEST(ChromeUnwinderAndroid32Test,TryUnwindSPOverflow)1441 TEST(ChromeUnwinderAndroid32Test, TryUnwindSPOverflow) {
1442 const uint32_t page_table[] = {0, 2};
1443 const size_t number_of_pages = std::size(page_table);
1444 const size_t page_size = 1 << 17;
1445
1446 const FunctionTableEntry function_table[] = {
1447 // Page 0.
1448 {0x0, 0}, // Refuse to unwind filler function.
1449 {0x10, 2}, // Function 0. The function to unwind.
1450 // Page 1.
1451 {0x5, 0}, // Refuse to unwind filler function.
1452 };
1453 const uint8_t function_offset_table[] = {
1454 // Refuse to unwind filler function.
1455 0x0,
1456 0,
1457 // Function 0.
1458 0x0,
1459 2,
1460 };
1461 const uint8_t unwind_instruction_table[] = {
1462 // Offset 0: REFUSE_TO_UNWIND.
1463 0b10000000, 0b00000000,
1464 // Offset 2.
1465 0b10010100, // vsp = r4.
1466 0b10101000, // Pop r4, r14.
1467 0b10110000, // COMPLETE.
1468 };
1469
1470 auto unwind_info = ChromeUnwindInfoAndroid32{
1471 unwind_instruction_table,
1472 function_offset_table,
1473 function_table,
1474 page_table,
1475 };
1476
1477 ModuleCache module_cache;
1478 const ModuleCache::Module* chrome_module = AddNativeModule(
1479 &module_cache, std::make_unique<TestModule>(
1480 0x1000, number_of_pages * page_size, "ChromeModule"));
1481
1482 uintptr_t text_section_start_address = 0x1100;
1483 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1484 text_section_start_address);
1485
1486 unwinder.Initialize(&module_cache);
1487 uintptr_t pc = text_section_start_address + 0x20;
1488
1489 AlignedStackMemory stack_memory = {
1490 0xFFFF,
1491 0xFFFF,
1492 };
1493 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1494 RegisterContext context;
1495 RegisterContextInstructionPointer(&context) = pc;
1496 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1497
1498 // Setting vsp = 0xffffffff should cause SP overflow.
1499 context.arm_r4 = 0xffffffff;
1500
1501 // The address is outside chrome module, which will result the unwind to
1502 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1503 // reasons.
1504 context.arm_lr =
1505 text_section_start_address + (number_of_pages + 1) * page_size;
1506
1507 EXPECT_EQ(
1508 UnwindResult::kAborted,
1509 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1510 stack_memory.stack_end_address(), &unwound_frames));
1511 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1512 }
1513
TEST(ChromeUnwinderAndroid32Test,TryUnwindNullSP)1514 TEST(ChromeUnwinderAndroid32Test, TryUnwindNullSP) {
1515 const uint32_t page_table[] = {0, 2};
1516 const size_t number_of_pages = std::size(page_table);
1517 const size_t page_size = 1 << 17;
1518
1519 const FunctionTableEntry function_table[] = {
1520 // Page 0.
1521 {0x0, 0}, // Refuse to unwind filler function.
1522 {0x10, 2}, // Function 0. The function to unwind.
1523 // Page 1.
1524 {0x5, 0}, // Refuse to unwind filler function.
1525 };
1526 const uint8_t function_offset_table[] = {
1527 // Refuse to unwind filler function.
1528 0x0,
1529 0,
1530 // Function 0.
1531 0x0,
1532 2,
1533 };
1534 const uint8_t unwind_instruction_table[] = {
1535 // Offset 0: REFUSE_TO_UNWIND.
1536 0b10000000, 0b00000000,
1537 // Offset 2.
1538 0b10010100, // vsp = r4.
1539 0b10101000, // Pop r4, r14.
1540 0b10110000, // COMPLETE.
1541 };
1542
1543 auto unwind_info = ChromeUnwindInfoAndroid32{
1544 unwind_instruction_table,
1545 function_offset_table,
1546 function_table,
1547 page_table,
1548 };
1549
1550 ModuleCache module_cache;
1551 const ModuleCache::Module* chrome_module = AddNativeModule(
1552 &module_cache, std::make_unique<TestModule>(
1553 0x1000, number_of_pages * page_size, "ChromeModule"));
1554
1555 uintptr_t text_section_start_address = 0x1100;
1556 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1557 text_section_start_address);
1558
1559 unwinder.Initialize(&module_cache);
1560 uintptr_t pc = text_section_start_address + 0x20;
1561
1562 AlignedStackMemory stack_memory = {
1563 0xFFFF,
1564 0xFFFF,
1565 };
1566 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1567 RegisterContext context;
1568 RegisterContextInstructionPointer(&context) = pc;
1569 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1570
1571 // Setting vsp = 0x0 should cause the unwinder to abort.
1572 context.arm_r4 = 0x0;
1573
1574 // The address is outside chrome module, which will result the unwind to
1575 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1576 // reasons.
1577 context.arm_lr =
1578 text_section_start_address + (number_of_pages + 1) * page_size;
1579
1580 EXPECT_EQ(
1581 UnwindResult::kAborted,
1582 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1583 stack_memory.stack_end_address(), &unwound_frames));
1584 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1585 }
1586
TEST(ChromeUnwinderAndroid32Test,TryUnwindInvalidSPOperation)1587 TEST(ChromeUnwinderAndroid32Test, TryUnwindInvalidSPOperation) {
1588 // This test aims to verify that for each unwind instruction executed, it is
1589 // always true that sp > frame initial sp.
1590
1591 const uint32_t page_table[] = {0, 2};
1592 const size_t number_of_pages = std::size(page_table);
1593 const size_t page_size = 1 << 17;
1594
1595 const FunctionTableEntry function_table[] = {
1596 // Page 0.
1597 {0x0, 0}, // Refuse to unwind filler function.
1598 {0x10, 2}, // Function 0. The function to unwind.
1599 // Page 1.
1600 {0x5, 0}, // Refuse to unwind filler function.
1601 };
1602 const uint8_t function_offset_table[] = {
1603 // Refuse to unwind filler function.
1604 0x0,
1605 0,
1606 // Function 0.
1607 0x0,
1608 2,
1609 };
1610 const uint8_t unwind_instruction_table[] = {
1611 // Offset 0: REFUSE_TO_UNWIND.
1612 0b10000000, 0b00000000,
1613 // Offset 2.
1614 0b10010100, // vsp = r4 (r4 < frame initial sp).
1615 0b10010101, // vsp = r5 (r5 > frame initial sp).
1616 0b10110000, // COMPLETE.
1617 };
1618
1619 auto unwind_info = ChromeUnwindInfoAndroid32{
1620 unwind_instruction_table,
1621 function_offset_table,
1622 function_table,
1623 page_table,
1624 };
1625
1626 ModuleCache module_cache;
1627 const ModuleCache::Module* chrome_module = AddNativeModule(
1628 &module_cache, std::make_unique<TestModule>(
1629 0x1000, number_of_pages * page_size, "ChromeModule"));
1630
1631 uintptr_t text_section_start_address = 0x1100;
1632 ChromeUnwinderAndroid32 unwinder(unwind_info, chrome_module->GetBaseAddress(),
1633 text_section_start_address);
1634
1635 unwinder.Initialize(&module_cache);
1636 uintptr_t pc = text_section_start_address + 0x20;
1637
1638 AlignedStackMemory stack_memory = {
1639 0xFFFF,
1640 0xFFFF,
1641 };
1642 std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1643 RegisterContext context;
1644 RegisterContextInstructionPointer(&context) = pc;
1645 RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1646
1647 context.arm_r4 = stack_memory.stack_start_address() - 2 * sizeof(uintptr_t);
1648 context.arm_r5 = stack_memory.stack_start_address() + 2 * sizeof(uintptr_t);
1649
1650 // The address is outside chrome module, which will result the unwind to
1651 // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1652 // reasons.
1653 context.arm_lr =
1654 text_section_start_address + (number_of_pages + 1) * page_size;
1655
1656 EXPECT_EQ(
1657 UnwindResult::kAborted,
1658 unwinder.TryUnwind(/*capture_state=*/nullptr, &context,
1659 stack_memory.stack_end_address(), &unwound_frames));
1660 ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1661 }
1662
1663 } // namespace base
1664