[go: nahoru, domu]

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