• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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