123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/profiler/chrome_unwinder_android_v2.h"
- #include "base/memory/aligned_memory.h"
- #include "base/profiler/chrome_unwind_info_android.h"
- #include "base/profiler/stack_sampling_profiler_test_util.h"
- #include "base/test/gtest_util.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace base {
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerIncrementMinValue) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b00000000;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerIncrementMidValue) {
- // xxxxxx = 4; vsp = vsp + (4 << 2) + 4 = vsp + 16 + 4 = vsp + 0x14.
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b00000100;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerIncrementMaxValue) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b00111111;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerIncrementOverflow) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b00111111;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0xffffffff;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0xffffffff, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerDecrementMinValue) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b01000000;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerDecrementMidValue) {
- // xxxxxx = 4; vsp = vsp - (4 << 2) - 4 = vsp - 16 - 4 = vsp - 0x14.
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b01000100;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerDecrementMaxValue) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b01111111;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x10000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestSmallStackPointerDecrementUnderflow) {
- RegisterContext thread_context = {};
- const uint8_t instruction = 0b01111111;
- const uint8_t* current_instruction = &instruction;
- thread_context.arm_sp = 0x00000000;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0x0ul, thread_context.arm_sp);
- }
- using ChromeAndroidUnwindSetStackPointerFromRegisterValueTest =
- ::testing::TestWithParam<uint8_t>;
- INSTANTIATE_TEST_SUITE_P(
- All,
- ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
- // The function should set all registers except
- // - callee saved registers (r0, r1, r2, r3)
- // - sp (r13)
- // - pc (r15)
- ::testing::Values(4, 5, 6, 7, 8, 9, 10, 11, 12, 14));
- TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
- TestSetStackPointerFromRegisterValue) {
- const uint8_t register_index = GetParam();
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111; // r11
- thread_context.arm_ip = 112; // r12
- thread_context.arm_lr = 114; // r14
- const uint8_t instruction = 0b10010000 + register_index;
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
- RegisterContext thread_context = {};
- thread_context.arm_lr = 114; // r14
- thread_context.arm_pc = 115; // r15
- const uint8_t instruction = 0b10110000;
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kCompleted);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(114ul, thread_context.arm_pc);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
- RegisterContext thread_context = {};
- thread_context.arm_lr = 114; // r14
- thread_context.arm_pc = 115; // r15
- const uint8_t instruction = 0b10110000;
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = true;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kCompleted);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(115ul, thread_context.arm_pc);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestPopDiscontinuousRegistersIncludingPC) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- thread_context.arm_pc = 114;
- // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
- const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
- thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
- // Pop r15, r12, r8, r4.
- const uint8_t instruction[] = {0b10001001, 0b00010001};
- const uint8_t* current_instruction = instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_TRUE(pc_was_updated);
- ASSERT_EQ(current_instruction, instruction + 2);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4), thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(1ul, thread_context.arm_r4);
- EXPECT_EQ(105ul, thread_context.arm_r5);
- EXPECT_EQ(106ul, thread_context.arm_r6);
- EXPECT_EQ(107ul, thread_context.arm_r7);
- EXPECT_EQ(2ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(3ul, thread_context.arm_ip);
- EXPECT_EQ(113ul, thread_context.arm_lr);
- EXPECT_EQ(4ul, thread_context.arm_pc);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestPopDiscontinuousRegisters) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- thread_context.arm_pc = 114;
- // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
- const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
- thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
- // Pop r12, r8, r4.
- const uint8_t instruction[] = {0b10000001, 0b00010001};
- const uint8_t* current_instruction = instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, instruction + 2);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3), thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(1ul, thread_context.arm_r4);
- EXPECT_EQ(105ul, thread_context.arm_r5);
- EXPECT_EQ(106ul, thread_context.arm_r6);
- EXPECT_EQ(107ul, thread_context.arm_r7);
- EXPECT_EQ(2ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(3ul, thread_context.arm_ip);
- EXPECT_EQ(113ul, thread_context.arm_lr);
- EXPECT_EQ(114ul, thread_context.arm_pc);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestPopDiscontinuousRegistersOverflow) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- thread_context.arm_pc = 114;
- // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
- thread_context.arm_sp = 0xffffffff;
- // Pop r15, r12, r8, r4.
- const uint8_t instruction[] = {0b10001001, 0b00010001};
- const uint8_t* current_instruction = instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, instruction + 2);
- EXPECT_EQ(0xffffffff, thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(104ul, thread_context.arm_r4);
- EXPECT_EQ(105ul, thread_context.arm_r5);
- EXPECT_EQ(106ul, thread_context.arm_r6);
- EXPECT_EQ(107ul, thread_context.arm_r7);
- EXPECT_EQ(108ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(112ul, thread_context.arm_ip);
- EXPECT_EQ(113ul, thread_context.arm_lr);
- EXPECT_EQ(114ul, thread_context.arm_pc);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestRefuseToUnwind) {
- RegisterContext thread_context = {};
- const uint8_t instruction[] = {0b10000000, 0b0};
- const uint8_t* current_instruction = instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, instruction + 2);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestPopRegistersIncludingR14MinRegisters) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- // Popping r4 - r[4 + nnn], r14, at most 9 registers.
- // r14 = lr
- const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
- thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
- const uint8_t instruction = 0b10101000;
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2), thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(1ul, thread_context.arm_r4);
- EXPECT_EQ(105ul, thread_context.arm_r5);
- EXPECT_EQ(106ul, thread_context.arm_r6);
- EXPECT_EQ(107ul, thread_context.arm_r7);
- EXPECT_EQ(108ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(112ul, thread_context.arm_ip);
- EXPECT_EQ(2ul, thread_context.arm_lr);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestPopRegistersIncludingR14MidRegisters) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- // Popping r4 - r[4 + nnn], r14, at most 9 registers.
- // r14 = lr
- const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
- thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
- const uint8_t instruction = 0b10101100; // Pop r4-r8, r14.
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6), thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(1ul, thread_context.arm_r4);
- EXPECT_EQ(2ul, thread_context.arm_r5);
- EXPECT_EQ(3ul, thread_context.arm_r6);
- EXPECT_EQ(4ul, thread_context.arm_r7);
- EXPECT_EQ(5ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(112ul, thread_context.arm_ip);
- EXPECT_EQ(6ul, thread_context.arm_lr);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestPopRegistersIncludingR14MaxRegisters) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- // Popping r4 - r[4 + nnn], r14, at most 9 registers.
- // r14 = lr
- const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
- thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
- const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9), thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(1ul, thread_context.arm_r4);
- EXPECT_EQ(2ul, thread_context.arm_r5);
- EXPECT_EQ(3ul, thread_context.arm_r6);
- EXPECT_EQ(4ul, thread_context.arm_r7);
- EXPECT_EQ(5ul, thread_context.arm_r8);
- EXPECT_EQ(6ul, thread_context.arm_r9);
- EXPECT_EQ(7ul, thread_context.arm_r10);
- EXPECT_EQ(8ul, thread_context.arm_fp);
- EXPECT_EQ(112ul, thread_context.arm_ip);
- EXPECT_EQ(9ul, thread_context.arm_lr);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestPopRegistersIncludingR14Overflow) {
- RegisterContext thread_context = {};
- thread_context.arm_r0 = 100;
- thread_context.arm_r1 = 101;
- thread_context.arm_r2 = 102;
- thread_context.arm_r3 = 103;
- thread_context.arm_r4 = 104;
- thread_context.arm_r5 = 105;
- thread_context.arm_r6 = 106;
- thread_context.arm_r7 = 107;
- thread_context.arm_r8 = 108;
- thread_context.arm_r9 = 109;
- thread_context.arm_r10 = 110;
- thread_context.arm_fp = 111;
- thread_context.arm_ip = 112;
- thread_context.arm_lr = 113;
- // Popping r4 - r[4 + nnn], r14, at most 9 registers.
- // r14 = lr
- thread_context.arm_sp = 0xffffffff;
- const uint8_t instruction = 0b10101111; // Pop r4 - r11, r14.
- const uint8_t* current_instruction = &instruction;
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, &instruction + 1);
- EXPECT_EQ(0xffffffff, thread_context.arm_sp);
- EXPECT_EQ(100ul, thread_context.arm_r0);
- EXPECT_EQ(101ul, thread_context.arm_r1);
- EXPECT_EQ(102ul, thread_context.arm_r2);
- EXPECT_EQ(103ul, thread_context.arm_r3);
- EXPECT_EQ(104ul, thread_context.arm_r4);
- EXPECT_EQ(105ul, thread_context.arm_r5);
- EXPECT_EQ(106ul, thread_context.arm_r6);
- EXPECT_EQ(107ul, thread_context.arm_r7);
- EXPECT_EQ(108ul, thread_context.arm_r8);
- EXPECT_EQ(109ul, thread_context.arm_r9);
- EXPECT_EQ(110ul, thread_context.arm_r10);
- EXPECT_EQ(111ul, thread_context.arm_fp);
- EXPECT_EQ(112ul, thread_context.arm_ip);
- EXPECT_EQ(113ul, thread_context.arm_lr);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMinValue) {
- RegisterContext thread_context = {};
- thread_context.arm_sp = 0x10000000;
- const uint8_t increment_0[] = {
- 0b10110010,
- 0b00000000,
- };
- const uint8_t* current_instruction = &increment_0[0];
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0));
- // vsp + 0x204 + (0 << 2)
- // = vsp + 0x204
- EXPECT_EQ(0x10000204ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMidValue) {
- RegisterContext thread_context = {};
- thread_context.arm_sp = 0x10000000;
- const uint8_t increment_4[] = {
- 0b10110010,
- 0b00000100,
- };
- const uint8_t* current_instruction = &increment_4[0];
- // vsp + 0x204 + (4 << 2)
- // = vsp + 0x204 + 0x10
- // = vsp + 0x214
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4));
- EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest,
- TestBigStackPointerIncrementLargeValue) {
- RegisterContext thread_context = {};
- thread_context.arm_sp = 0x10000000;
- const uint8_t increment_128[] = {
- 0b10110010,
- 0b10000000,
- 0b00000001,
- };
- const uint8_t* current_instruction = &increment_128[0];
- // vsp + 0x204 + (128 << 2)
- // = vsp + 0x204 + 512
- // = vsp + 0x204 + 0x200
- // = vsp + 0x404
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kInstructionPending);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128));
- EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
- }
- TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementOverflow) {
- RegisterContext thread_context = {};
- thread_context.arm_sp = 0xffffffff;
- const uint8_t increment_overflow[] = {
- 0b10110010,
- 0b10000000,
- 0b00000001,
- }; // ULEB128 = 128
- const uint8_t* current_instruction = &increment_overflow[0];
- bool pc_was_updated = false;
- ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
- &thread_context),
- UnwindInstructionResult::kAborted);
- EXPECT_FALSE(pc_was_updated);
- ASSERT_EQ(current_instruction,
- increment_overflow + sizeof(increment_overflow));
- EXPECT_EQ(0xfffffffful, thread_context.arm_sp);
- }
- TEST(ChromeUnwinderAndroidV2Test,
- TestFunctionOffsetTableLookupExactMatchingOffset) {
- const uint8_t function_offset_table[] = {
- // Function 1: [(130, 2), (128, 3), (0, 4)]
- // offset = 130
- 0b10000010,
- 0b00000001,
- // unwind index = 2
- 0b00000010,
- // offset = 128
- 0b10000000,
- 0b00000001,
- // unwind index = 3
- 0b00000011,
- // offset = 0
- 0b00000000,
- // unwind index = 4
- 0b00000100,
- };
- EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
- &function_offset_table[0],
- /* instruction_offset_from_function_start */ 128));
- }
- TEST(ChromeUnwinderAndroidV2Test,
- TestFunctionOffsetTableLookupNonExactMatchingOffset) {
- const uint8_t function_offset_table[] = {
- // Function 1: [(130, 2), (128, 3), (0, 4)]
- // offset = 130
- 0b10000010,
- 0b00000001,
- // unwind index = 2
- 0b00000010,
- // offset = 128
- 0b10000000,
- 0b00000001,
- // unwind index = 3
- 0b00000011,
- // offset = 0
- 0b00000000,
- // unwind index = 4
- 0b00000100,
- };
- EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
- &function_offset_table[0],
- /* instruction_offset_from_function_start */ 129));
- }
- TEST(ChromeUnwinderAndroidV2Test, TestFunctionOffsetTableLookupZeroOffset) {
- const uint8_t function_offset_table[] = {
- // Function 1: [(130, 2), (128, 3), (0, 4)]
- // offset = 130
- 0b10000010,
- 0b00000001,
- // unwind index = 2
- 0b00000010,
- // offset = 128
- 0b10000000,
- 0b00000001,
- // unwind index = 3
- 0b00000011,
- // offset = 0
- 0b00000000,
- // unwind index = 4
- 0b00000100,
- };
- EXPECT_EQ(4ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
- &function_offset_table[0],
- /* instruction_offset_from_function_start */ 0));
- }
- TEST(ChromeUnwinderAndroidV2Test, TestAddressTableLookupEntryInPage) {
- const uint32_t page_start_instructions[] = {0, 2};
- const FunctionTableEntry function_offset_table_indices[] = {
- // Page 0
- {
- /* function_start_address_page_instruction_offset */ 0,
- /* function_offset_table_byte_index */ 20,
- },
- {
- /* function_start_address_page_instruction_offset */ 4,
- /* function_offset_table_byte_index */ 40,
- },
- // Page 1
- {
- /* function_start_address_page_instruction_offset */ 6,
- /* function_offset_table_byte_index */ 70,
- },
- };
- {
- const uint32_t page_number = 0;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
- }
- {
- const uint32_t page_number = 0;
- const uint32_t page_instruction_offset = 50;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
- }
- // Lookup last instruction in last function.
- {
- const uint32_t page_number = 1;
- const uint32_t page_instruction_offset = 0xffff;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- // 0xffff - 6 = 0xfff9.
- EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
- }
- }
- TEST(ChromeUnwinderAndroidV2Test, TestAddressTableLookupEmptyPage) {
- const uint32_t page_start_instructions[] = {0, 1, 1};
- const FunctionTableEntry function_offset_table_indices[] = {
- // Page 0
- {
- /* function_start_address_page_instruction_offset */ 0,
- /* function_offset_table_byte_index */ 20,
- },
- // Page 1 is empty
- // Page 2
- {
- /* function_start_address_page_instruction_offset */ 6,
- /* function_offset_table_byte_index */ 70,
- },
- };
- const uint32_t page_number = 1;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- TEST(ChromeUnwinderAndroidV2Test,
- TestAddressTableLookupInvalidIntructionOffset) {
- const uint32_t page_start_instructions[] = {0, 1};
- const FunctionTableEntry function_offset_table_indices[] = {
- // Page 0
- // This function spans from page 0 offset 0 to page 1 offset 5.
- {
- /* function_start_address_page_instruction_offset */ 0,
- /* function_offset_table_byte_index */ 20,
- },
- // Page 1
- {
- /* function_start_address_page_instruction_offset */ 6,
- /* function_offset_table_byte_index */ 70,
- },
- };
- // Instruction offset lies after last page on page table.
- {
- const uint32_t page_number = 50;
- const uint32_t page_instruction_offset = 6;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_EQ(absl::nullopt, entry_found);
- }
- {
- const uint32_t page_number = 2;
- const uint32_t page_instruction_offset = 0;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_EQ(absl::nullopt, entry_found);
- }
- }
- TEST(ChromeUnwinderAndroidV2Test,
- TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
- const uint32_t page_start_instructions[] = {0, 1, 2};
- const FunctionTableEntry function_offset_table_indices[] = {
- // Page 0
- {
- /* function_start_address_page_instruction_offset */ 0,
- /* function_offset_table_byte_index */ 20,
- },
- // Page 1
- {
- /* function_start_address_page_instruction_offset */ 6,
- /* function_offset_table_byte_index */ 70,
- },
- // Page 2
- {
- /* function_start_address_page_instruction_offset */ 10,
- /* function_offset_table_byte_index */ 80,
- }};
- const uint32_t page_number = 1;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- TEST(ChromeUnwinderAndroidV2Test,
- TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
- const uint32_t page_start_instructions[] = {0, 1, 1, 1};
- const FunctionTableEntry function_offset_table_indices[] = {
- // Page 0
- // This function spans from page 0 offset 0 to page 3 offset 5.
- {
- /* function_start_address_page_instruction_offset */ 0,
- /* function_offset_table_byte_index */ 20,
- },
- // Page 1 is empty
- // Page 2 is empty
- // Page 3
- {
- /* function_start_address_page_instruction_offset */ 6,
- /* function_offset_table_byte_index */ 70,
- },
- };
- {
- const uint32_t page_number = 0;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- {
- const uint32_t page_number = 1;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- {
- const uint32_t page_number = 2;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- {
- const uint32_t page_number = 3;
- const uint32_t page_instruction_offset = 4;
- const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
- page_start_instructions, function_offset_table_indices,
- /* instruction_offset */ (page_instruction_offset << 1) +
- (page_number << 17));
- ASSERT_NE(absl::nullopt, entry_found);
- EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
- EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
- }
- }
- // Utility function to add a single native module during test setup. Returns
- // a pointer to the provided module.
- const ModuleCache::Module* AddNativeModule(
- ModuleCache* cache,
- std::unique_ptr<const ModuleCache::Module> module) {
- const ModuleCache::Module* module_ptr = module.get();
- cache->AddCustomNativeModule(std::move(module));
- return module_ptr;
- }
- TEST(ChromeUnwinderAndroidV2Test, CanUnwindFrom) {
- const uint32_t page_table[] = {0};
- const FunctionTableEntry function_table[] = {{0, 0}};
- const uint8_t function_offset_table[] = {0};
- const uint8_t unwind_instruction_table[] = {0};
- auto dummy_unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, 1ul),
- make_span(function_offset_table, 1ul),
- make_span(function_table, 1ul),
- make_span(page_table, 1ul),
- };
- auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
- auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
- ModuleCache module_cache;
- ChromeUnwinderAndroidV2 unwinder(dummy_unwind_info,
- chrome_module->GetBaseAddress(),
- /* text_section_start_address */
- chrome_module->GetBaseAddress() + 4);
- unwinder.Initialize(&module_cache);
- EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
- EXPECT_TRUE(unwinder.CanUnwindFrom({0x1000, chrome_module.get()}));
- EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
- EXPECT_FALSE(unwinder.CanUnwindFrom({0x400, nullptr}));
- }
- namespace {
- void ExpectFramesEq(const std::vector<Frame>& expected,
- const std::vector<Frame>& actual) {
- EXPECT_EQ(actual.size(), expected.size());
- if (actual.size() != expected.size())
- return;
- for (size_t i = 0; i < actual.size(); i++) {
- EXPECT_EQ(expected[i].module, actual[i].module);
- EXPECT_EQ(expected[i].instruction_pointer, actual[i].instruction_pointer);
- }
- }
- class AlignedStackMemory {
- public:
- AlignedStackMemory(std::initializer_list<uintptr_t> values)
- : size_(values.size()),
- stack_memory_(static_cast<uintptr_t*>(
- AlignedAlloc(size_ * sizeof(uintptr_t), 2 * sizeof(uintptr_t)))) {
- DCHECK_EQ(size_ % 2, 0u);
- std::copy(values.begin(), values.end(), stack_memory_.get());
- }
- uintptr_t stack_start_address() const {
- return reinterpret_cast<uintptr_t>(stack_memory_.get());
- }
- uintptr_t stack_end_address() const {
- return reinterpret_cast<uintptr_t>(stack_memory_.get() + size_);
- }
- private:
- const uintptr_t size_;
- const std::unique_ptr<uintptr_t, AlignedFreeDeleter> stack_memory_;
- };
- } // namespace
- TEST(ChromeUnwinderAndroidV2Test, TryUnwind) {
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0, 0}, // Function 0.
- {0x10, 4}, // Function 1. The function to unwind 2 times.
- // Page 1.
- {0x5, 8}, // Function 2.
- {0x20, 12}, // Function 3.
- };
- const uint8_t function_offset_table[] = {
- // Function 0.
- 0x2,
- 0,
- 0x0,
- 1,
- // Function 1.
- 0x7f,
- 0,
- 0x0,
- 1,
- // Function 2.
- 0x78,
- 0,
- 0x0,
- 1,
- // Function 3.
- 0x2,
- 0,
- 0x0,
- 1,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: Pop r4, r14 from stack top.
- // Need to pop 2 registers to keep SP aligned.
- 0b10101000,
- // Offset 1: COMPLETE.
- 0b10110000,
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- // Both first_pc and second_pc lie in Function 1's address range.
- uintptr_t first_pc = text_section_start_address + 0x20;
- uintptr_t second_pc = text_section_start_address + page_size + 0x4;
- // third_pc lies outside chrome_module's address range.
- uintptr_t third_pc = text_section_start_address + 3 * page_size;
- AlignedStackMemory stack_memory = {
- 0x0,
- third_pc,
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = first_pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- context.arm_lr = second_pc;
- EXPECT_EQ(UnwindResult::kUnrecognizedFrame,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{first_pc, chrome_module},
- {second_pc, chrome_module},
- {third_pc, nullptr}}),
- unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindInfiniteLoopSingleFrame) {
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000,
- 0b00000000,
- // Offset 2: COMPLETE.
- 0b10110000,
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- // Set lr = pc so that both sp and pc stays the same after first round of
- // unwind.
- context.arm_lr = pc;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindInfiniteLoopMultipleFrames) {
- // This test aims to produce a scenario, where after the unwind of a number
- // of frames, the sp and pc get to their original state before the unwind.
- // Function 1 (pc1, sp1):
- // - set pc = lr(pc2)
- // Function 2 (pc2, sp1):
- // - pop r14(pc2), r15(pc1) off stack
- // - vsp = r4 (reset vsp to frame initial vsp)
- const uint32_t page_table[] = {0, 3};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 1. The function to unwind.
- {0x100, 2}, // Function 2. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- // Function 1.
- 0x2,
- 3,
- 0x1,
- 5,
- 0x0,
- 6,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000,
- 0b00000000,
- // Offset 2: COMPLETE.
- 0b10110000,
- // Offset 3: POP r14, r15 off the stack.
- 0b10001100,
- 0b00000000,
- // Offset 5: vsp = r4.
- 0b10010100,
- // Offset 6: COMPLETE.
- 0b10110000,
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t first_pc = text_section_start_address + 0x20; // Function 1.
- uintptr_t second_pc = text_section_start_address + 0x110; // Function 2.
- AlignedStackMemory stack_memory = {
- second_pc,
- first_pc,
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = first_pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- context.arm_lr = second_pc;
- context.arm_r4 = stack_memory.stack_start_address();
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>(
- {{first_pc, chrome_module}, {second_pc, chrome_module}}),
- unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindUnalignedSPFrameUnwind) {
- // SP should be 2-uintptr_t aligned before/after each frame unwind.
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000,
- 0b00000000,
- // Offset 2: COMPLETE.
- 0b10110000,
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- // Make stack memory not aligned to 2 * sizeof(uintptr_t);
- RegisterContextStackPointer(&context) =
- stack_memory.stack_start_address() + sizeof(uintptr_t);
- // The address is outside chrome module, which will result the unwind to
- // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
- context.arm_lr =
- text_section_start_address + (number_of_pages + 1) * page_size;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindUnalignedSPInstructionUnwind) {
- // SP should be uintptr_t aligned before/after each unwind instruction
- // execution.
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000, 0b00000000,
- // Offset 2:
- 0b10010100, // vsp = r4, where r4 = stack + (sizeof(uintptr_t) / 2)
- 0b10110000, // COMPLETE.
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- // The address is outside chrome module, which will result the unwind to
- // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
- context.arm_lr =
- text_section_start_address + (number_of_pages + 1) * page_size;
- context.arm_r4 = stack_memory.stack_start_address() + sizeof(uintptr_t) / 2;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindSPOverflow) {
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000, 0b00000000,
- // Offset 2.
- 0b10010100, // vsp = r4.
- 0b10101000, // Pop r4, r14.
- 0b10110000, // COMPLETE.
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- // Setting vsp = 0xffffffff should cause SP overflow.
- context.arm_r4 = 0xffffffff;
- // The address is outside chrome module, which will result the unwind to
- // stop with result kUnrecognizedFrame if the unwinder did not abort for other
- // reasons.
- context.arm_lr =
- text_section_start_address + (number_of_pages + 1) * page_size;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindNullSP) {
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000, 0b00000000,
- // Offset 2.
- 0b10010100, // vsp = r4.
- 0b10101000, // Pop r4, r14.
- 0b10110000, // COMPLETE.
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- // Setting vsp = 0x0 should cause the unwinder to abort.
- context.arm_r4 = 0x0;
- // The address is outside chrome module, which will result the unwind to
- // stop with result kUnrecognizedFrame if the unwinder did not abort for other
- // reasons.
- context.arm_lr =
- text_section_start_address + (number_of_pages + 1) * page_size;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- TEST(ChromeUnwinderAndroidV2Test, TryUnwindInvalidSPOperation) {
- // This test aims to verify that for each unwind instruction executed, it is
- // always true that sp > frame initial sp.
- const uint32_t page_table[] = {0, 2};
- const size_t number_of_pages = std::size(page_table);
- const size_t page_size = 1 << 17;
- const FunctionTableEntry function_table[] = {
- // Page 0.
- {0x0, 0}, // Refuse to unwind filler function.
- {0x10, 2}, // Function 0. The function to unwind.
- // Page 1.
- {0x5, 0}, // Refuse to unwind filler function.
- };
- const uint8_t function_offset_table[] = {
- // Refuse to unwind filler function.
- 0x0,
- 0,
- // Function 0.
- 0x0,
- 2,
- };
- const uint8_t unwind_instruction_table[] = {
- // Offset 0: REFUSE_TO_UNWIND.
- 0b10000000, 0b00000000,
- // Offset 2.
- 0b10010100, // vsp = r4 (r4 < frame initial sp).
- 0b10010101, // vsp = r5 (r5 > frame initial sp).
- 0b10110000, // COMPLETE.
- };
- auto unwind_info = ChromeUnwindInfoAndroid{
- make_span(unwind_instruction_table, std::size(unwind_instruction_table)),
- make_span(function_offset_table, std::size(function_offset_table)),
- make_span(function_table, std::size(function_table)),
- make_span(page_table, std::size(page_table)),
- };
- ModuleCache module_cache;
- const ModuleCache::Module* chrome_module = AddNativeModule(
- &module_cache, std::make_unique<TestModule>(
- 0x1000, number_of_pages * page_size, "ChromeModule"));
- uintptr_t text_section_start_address = 0x1100;
- ChromeUnwinderAndroidV2 unwinder(unwind_info, chrome_module->GetBaseAddress(),
- text_section_start_address);
- unwinder.Initialize(&module_cache);
- uintptr_t pc = text_section_start_address + 0x20;
- AlignedStackMemory stack_memory = {
- 0xFFFF,
- 0xFFFF,
- };
- std::vector<Frame> unwound_frames = {{pc, chrome_module}};
- RegisterContext context;
- RegisterContextInstructionPointer(&context) = pc;
- RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
- context.arm_r4 = stack_memory.stack_start_address() - 2 * sizeof(uintptr_t);
- context.arm_r5 = stack_memory.stack_start_address() + 2 * sizeof(uintptr_t);
- // The address is outside chrome module, which will result the unwind to
- // stop with result kUnrecognizedFrame if the unwinder did not abort for other
- // reasons.
- context.arm_lr =
- text_section_start_address + (number_of_pages + 1) * page_size;
- EXPECT_EQ(UnwindResult::kAborted,
- unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
- &unwound_frames));
- ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
- }
- } // namespace base
|