xref: /aosp_15_r20/external/google-breakpad/src/processor/cfi_frame_info_unittest.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 // Original author: Jim Blandy <[email protected]> <[email protected]>
30 
31 // cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo,
32 // CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker.
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>  // Must come first
36 #endif
37 
38 #include <string.h>
39 
40 #include "breakpad_googletest_includes.h"
41 #include "common/using_std_string.h"
42 #include "processor/cfi_frame_info.h"
43 #include "google_breakpad/processor/memory_region.h"
44 
45 using google_breakpad::CFIFrameInfo;
46 using google_breakpad::CFIFrameInfoParseHandler;
47 using google_breakpad::CFIRuleParser;
48 using google_breakpad::MemoryRegion;
49 using google_breakpad::SimpleCFIWalker;
50 using testing::_;
51 using testing::A;
52 using testing::AtMost;
53 using testing::DoAll;
54 using testing::Return;
55 using testing::SetArgumentPointee;
56 using testing::Test;
57 
58 class MockMemoryRegion: public MemoryRegion {
59  public:
60   MOCK_CONST_METHOD0(GetBase, uint64_t());
61   MOCK_CONST_METHOD0(GetSize, uint32_t());
62   MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint8_t*));
63   MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint16_t*));
64   MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint32_t*));
65   MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint64_t*));
66   MOCK_CONST_METHOD0(Print, void());
67 };
68 
69 // Handy definitions for all tests.
70 struct CFIFixture {
71 
72   // Set up the mock memory object to expect no references.
ExpectNoMemoryReferencesCFIFixture73   void ExpectNoMemoryReferences() {
74     EXPECT_CALL(memory, GetBase()).Times(0);
75     EXPECT_CALL(memory, GetSize()).Times(0);
76     EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint8_t*>())).Times(0);
77     EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint16_t*>())).Times(0);
78     EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint32_t*>())).Times(0);
79     EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint64_t*>())).Times(0);
80   }
81 
82   CFIFrameInfo cfi;
83   MockMemoryRegion memory;
84   CFIFrameInfo::RegisterValueMap<uint64_t> registers, caller_registers;
85 };
86 
87 class Simple: public CFIFixture, public Test { };
88 
89 // FindCallerRegs should fail if no .cfa rule is provided.
TEST_F(Simple,NoCFA)90 TEST_F(Simple, NoCFA) {
91   ExpectNoMemoryReferences();
92 
93   cfi.SetRARule("0");
94   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
95                                              &caller_registers));
96   ASSERT_EQ(".ra: 0", cfi.Serialize());
97 }
98 
99 // FindCallerRegs should fail if no .ra rule is provided.
TEST_F(Simple,NoRA)100 TEST_F(Simple, NoRA) {
101   ExpectNoMemoryReferences();
102 
103   cfi.SetCFARule("0");
104   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
105                                              &caller_registers));
106   ASSERT_EQ(".cfa: 0", cfi.Serialize());
107 }
108 
TEST_F(Simple,SetCFAAndRARule)109 TEST_F(Simple, SetCFAAndRARule) {
110   ExpectNoMemoryReferences();
111 
112   cfi.SetCFARule("330903416631436410");
113   cfi.SetRARule("5870666104170902211");
114   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
115                                             &caller_registers));
116   ASSERT_EQ(2U, caller_registers.size());
117   ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]);
118   ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
119 
120   ASSERT_EQ(".cfa: 330903416631436410 .ra: 5870666104170902211",
121             cfi.Serialize());
122 }
123 
TEST_F(Simple,SetManyRules)124 TEST_F(Simple, SetManyRules) {
125   ExpectNoMemoryReferences();
126 
127   cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -");
128   cfi.SetRARule(".cfa 99804755 +");
129   cfi.SetRegisterRule("register1", ".cfa 54370437 *");
130   cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +");
131   cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -");
132   cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /");
133   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
134                                             &caller_registers));
135   ASSERT_EQ(6U, caller_registers.size());
136   ASSERT_EQ(7664691U,           caller_registers[".cfa"]);
137   ASSERT_EQ(107469446U,         caller_registers[".ra"]);
138   ASSERT_EQ(416732599139967ULL, caller_registers["register1"]);
139   ASSERT_EQ(31740999U,          caller_registers["vodkathumbscrewingly"]);
140   ASSERT_EQ(-22136316ULL,       caller_registers["pubvexingfjordschmaltzy"]);
141   ASSERT_EQ(12U,                caller_registers["uncopyrightables"]);
142   ASSERT_EQ(".cfa: $temp1 68737028 = $temp2 61072337 = $temp1 $temp2 - "
143             ".ra: .cfa 99804755 + "
144             "pubvexingfjordschmaltzy: .cfa 29801007 - "
145             "register1: .cfa 54370437 * "
146             "uncopyrightables: 92642917 .cfa / "
147             "vodkathumbscrewingly: 24076308 .cfa +",
148             cfi.Serialize());
149 }
150 
TEST_F(Simple,RulesOverride)151 TEST_F(Simple, RulesOverride) {
152   ExpectNoMemoryReferences();
153 
154   cfi.SetCFARule("330903416631436410");
155   cfi.SetRARule("5870666104170902211");
156   cfi.SetCFARule("2828089117179001");
157   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
158                                             &caller_registers));
159   ASSERT_EQ(2U, caller_registers.size());
160   ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]);
161   ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
162   ASSERT_EQ(".cfa: 2828089117179001 .ra: 5870666104170902211",
163             cfi.Serialize());
164 }
165 
166 class Scope: public CFIFixture, public Test { };
167 
168 // There should be no value for .cfa in scope when evaluating the CFA rule.
TEST_F(Scope,CFALacksCFA)169 TEST_F(Scope, CFALacksCFA) {
170   ExpectNoMemoryReferences();
171 
172   cfi.SetCFARule(".cfa");
173   cfi.SetRARule("0");
174   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
175                                              &caller_registers));
176 }
177 
178 // There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope,CFALacksRA)179 TEST_F(Scope, CFALacksRA) {
180   ExpectNoMemoryReferences();
181 
182   cfi.SetCFARule(".ra");
183   cfi.SetRARule("0");
184   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
185                                              &caller_registers));
186 }
187 
188 // The current frame's registers should be in scope when evaluating
189 // the CFA rule.
TEST_F(Scope,CFASeesCurrentRegs)190 TEST_F(Scope, CFASeesCurrentRegs) {
191   ExpectNoMemoryReferences();
192 
193   registers[".baraminology"] = 0x06a7bc63e4f13893ULL;
194   registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL;
195   cfi.SetCFARule(".baraminology .ornithorhynchus +");
196   cfi.SetRARule("0");
197   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
198                                             &caller_registers));
199   ASSERT_EQ(2U, caller_registers.size());
200   ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL,
201             caller_registers[".cfa"]);
202 }
203 
204 // .cfa should be in scope in the return address expression.
TEST_F(Scope,RASeesCFA)205 TEST_F(Scope, RASeesCFA) {
206   ExpectNoMemoryReferences();
207 
208   cfi.SetCFARule("48364076");
209   cfi.SetRARule(".cfa");
210   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
211                                             &caller_registers));
212   ASSERT_EQ(2U, caller_registers.size());
213   ASSERT_EQ(48364076U, caller_registers[".ra"]);
214 }
215 
216 // There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope,RALacksRA)217 TEST_F(Scope, RALacksRA) {
218   ExpectNoMemoryReferences();
219 
220   cfi.SetCFARule("0");
221   cfi.SetRARule(".ra");
222   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
223                                              &caller_registers));
224 }
225 
226 // The current frame's registers should be in scope in the return
227 // address expression.
TEST_F(Scope,RASeesCurrentRegs)228 TEST_F(Scope, RASeesCurrentRegs) {
229   ExpectNoMemoryReferences();
230 
231   registers["noachian"] = 0x54dc4a5d8e5eb503ULL;
232   cfi.SetCFARule("10359370");
233   cfi.SetRARule("noachian");
234   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
235                                             &caller_registers));
236   ASSERT_EQ(2U, caller_registers.size());
237   ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]);
238 }
239 
240 // .cfa should be in scope for register rules.
TEST_F(Scope,RegistersSeeCFA)241 TEST_F(Scope, RegistersSeeCFA) {
242   ExpectNoMemoryReferences();
243 
244   cfi.SetCFARule("6515179");
245   cfi.SetRARule(".cfa");
246   cfi.SetRegisterRule("rogerian", ".cfa");
247   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
248                                             &caller_registers));
249   ASSERT_EQ(3U, caller_registers.size());
250   ASSERT_EQ(6515179U, caller_registers["rogerian"]);
251 }
252 
253 // The return address should not be in scope for register rules.
TEST_F(Scope,RegsLackRA)254 TEST_F(Scope, RegsLackRA) {
255   ExpectNoMemoryReferences();
256 
257   cfi.SetCFARule("42740329");
258   cfi.SetRARule("27045204");
259   cfi.SetRegisterRule("$r1", ".ra");
260   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
261                                            &caller_registers));
262   ASSERT_EQ(caller_registers.end(), caller_registers.find("$r1"));
263 }
264 
265 // Register rules can see the current frame's register values.
TEST_F(Scope,RegsSeeRegs)266 TEST_F(Scope, RegsSeeRegs) {
267   ExpectNoMemoryReferences();
268 
269   registers["$r1"] = 0x6ed3582c4bedb9adULL;
270   registers["$r2"] = 0xd27d9e742b8df6d0ULL;
271   cfi.SetCFARule("88239303");
272   cfi.SetRARule("30503835");
273   cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2");
274   cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1");
275   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
276                                             &caller_registers));
277   ASSERT_EQ(4U, caller_registers.size());
278   ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]);
279   ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]);
280 }
281 
282 // Each rule's temporaries are separate.
TEST_F(Scope,SeparateTempsRA)283 TEST_F(Scope, SeparateTempsRA) {
284   ExpectNoMemoryReferences();
285 
286   cfi.SetCFARule("$temp1 76569129 = $temp1");
287   cfi.SetRARule("0");
288   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
289                                             &caller_registers));
290 
291   cfi.SetCFARule("$temp1 76569129 = $temp1");
292   cfi.SetRARule("$temp1");
293   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
294                                              &caller_registers));
295 }
296 
297 class MockCFIRuleParserHandler: public CFIRuleParser::Handler {
298  public:
299   MOCK_METHOD1(CFARule, void(const string&));
300   MOCK_METHOD1(RARule,  void(const string&));
301   MOCK_METHOD2(RegisterRule, void(const string&, const string&));
302 };
303 
304 // A fixture class for testing CFIRuleParser.
305 class CFIParserFixture {
306  public:
CFIParserFixture()307   CFIParserFixture() : parser(&mock_handler) {
308     // Expect no parsing results to be reported to mock_handler. Individual
309     // tests can override this.
310     EXPECT_CALL(mock_handler, CFARule(_)).Times(0);
311     EXPECT_CALL(mock_handler, RARule(_)).Times(0);
312     EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0);
313   }
314 
315   MockCFIRuleParserHandler mock_handler;
316   CFIRuleParser parser;
317 };
318 
319 class Parser: public CFIParserFixture, public Test { };
320 
TEST_F(Parser,Empty)321 TEST_F(Parser, Empty) {
322   EXPECT_FALSE(parser.Parse(""));
323 }
324 
TEST_F(Parser,LoneColon)325 TEST_F(Parser, LoneColon) {
326   EXPECT_FALSE(parser.Parse(":"));
327 }
328 
TEST_F(Parser,CFANoExpr)329 TEST_F(Parser, CFANoExpr) {
330   EXPECT_FALSE(parser.Parse(".cfa:"));
331 }
332 
TEST_F(Parser,CFANoColonNoExpr)333 TEST_F(Parser, CFANoColonNoExpr) {
334   EXPECT_FALSE(parser.Parse(".cfa"));
335 }
336 
TEST_F(Parser,RANoExpr)337 TEST_F(Parser, RANoExpr) {
338   EXPECT_FALSE(parser.Parse(".ra:"));
339 }
340 
TEST_F(Parser,RANoColonNoExpr)341 TEST_F(Parser, RANoColonNoExpr) {
342   EXPECT_FALSE(parser.Parse(".ra"));
343 }
344 
TEST_F(Parser,RegNoExpr)345 TEST_F(Parser, RegNoExpr) {
346   EXPECT_FALSE(parser.Parse("reg:"));
347 }
348 
TEST_F(Parser,NoName)349 TEST_F(Parser, NoName) {
350   EXPECT_FALSE(parser.Parse("expr"));
351 }
352 
TEST_F(Parser,NoNameTwo)353 TEST_F(Parser, NoNameTwo) {
354   EXPECT_FALSE(parser.Parse("expr1 expr2"));
355 }
356 
TEST_F(Parser,StartsWithExpr)357 TEST_F(Parser, StartsWithExpr) {
358   EXPECT_FALSE(parser.Parse("expr1 reg: expr2"));
359 }
360 
TEST_F(Parser,CFA)361 TEST_F(Parser, CFA) {
362   EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return());
363   EXPECT_TRUE(parser.Parse(".cfa: spleen"));
364 }
365 
TEST_F(Parser,RA)366 TEST_F(Parser, RA) {
367   EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return());
368   EXPECT_TRUE(parser.Parse(".ra: notoriety"));
369 }
370 
TEST_F(Parser,Reg)371 TEST_F(Parser, Reg) {
372   EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous"))
373       .WillOnce(Return());
374   EXPECT_TRUE(parser.Parse("nemo: mellifluous"));
375 }
376 
TEST_F(Parser,CFARARegs)377 TEST_F(Parser, CFARARegs) {
378   EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return());
379   EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return());
380   EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian"))
381       .WillOnce(Return());
382   EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius"))
383       .WillOnce(Return());
384   EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression "
385                     "galba: praetorian otho: vitellius"));
386 }
387 
TEST_F(Parser,Whitespace)388 TEST_F(Parser, Whitespace) {
389   EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression"))
390       .WillOnce(Return());
391   EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression"))
392       .WillOnce(Return());
393   EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n "
394                            "expression  \n"));
395 }
396 
TEST_F(Parser,WhitespaceLoneColon)397 TEST_F(Parser, WhitespaceLoneColon) {
398   EXPECT_FALSE(parser.Parse("  \n:\t  "));
399 }
400 
TEST_F(Parser,EmptyName)401 TEST_F(Parser, EmptyName) {
402   EXPECT_CALL(mock_handler, RegisterRule("reg", _))
403       .Times(AtMost(1))
404       .WillRepeatedly(Return());
405   EXPECT_FALSE(parser.Parse("reg: expr1 : expr2"));
406 }
407 
TEST_F(Parser,RuleLoneColon)408 TEST_F(Parser, RuleLoneColon) {
409   EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
410       .Times(AtMost(1))
411       .WillRepeatedly(Return());
412   EXPECT_FALSE(parser.Parse(" r1:   expr   :"));
413 }
414 
TEST_F(Parser,RegNoExprRule)415 TEST_F(Parser, RegNoExprRule) {
416   EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
417       .Times(AtMost(1))
418       .WillRepeatedly(Return());
419   EXPECT_FALSE(parser.Parse("r0: r1:   expr"));
420 }
421 
422 class ParseHandlerFixture: public CFIFixture {
423  public:
ParseHandlerFixture()424   ParseHandlerFixture() : CFIFixture(), handler(&cfi) { }
425   CFIFrameInfoParseHandler handler;
426 };
427 
428 class ParseHandler: public ParseHandlerFixture, public Test { };
429 
TEST_F(ParseHandler,CFARARule)430 TEST_F(ParseHandler, CFARARule) {
431   handler.CFARule("reg-for-cfa");
432   handler.RARule("reg-for-ra");
433   registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
434   registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
435   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
436                                             &caller_registers));
437   ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
438   ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
439 }
440 
TEST_F(ParseHandler,RegisterRules)441 TEST_F(ParseHandler, RegisterRules) {
442   handler.CFARule("reg-for-cfa");
443   handler.RARule("reg-for-ra");
444   handler.RegisterRule("reg1", "reg-for-reg1");
445   handler.RegisterRule("reg2", "reg-for-reg2");
446   handler.RegisterRule("reg3", "reg3");
447   registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
448   registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
449   registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
450   registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
451   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
452                                             &caller_registers));
453   ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
454   ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
455   ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]);
456   ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]);
457   ASSERT_EQ(caller_registers.end(), caller_registers.find("reg3"));
458 }
459 
460 struct SimpleCFIWalkerFixture {
461   struct RawContext {
462     uint64_t r0, r1, r2, r3, r4, sp, pc;
463   };
464   enum Validity {
465     R0_VALID = 0x01,
466     R1_VALID = 0x02,
467     R2_VALID = 0x04,
468     R3_VALID = 0x08,
469     R4_VALID = 0x10,
470     SP_VALID = 0x20,
471     PC_VALID = 0x40
472   };
473   typedef SimpleCFIWalker<uint64_t, RawContext> CFIWalker;
474 
SimpleCFIWalkerFixtureSimpleCFIWalkerFixture475   SimpleCFIWalkerFixture()
476       : walker(register_map,
477                sizeof(register_map) / sizeof(register_map[0])) { }
478 
479   static CFIWalker::RegisterSet register_map[7];
480   CFIFrameInfo call_frame_info;
481   CFIWalker walker;
482   MockMemoryRegion memory;
483   RawContext callee_context, caller_context;
484 };
485 
486 SimpleCFIWalkerFixture::CFIWalker::RegisterSet
487 SimpleCFIWalkerFixture::register_map[7] = {
488   { "r0", NULL,   true,  R0_VALID, &RawContext::r0 },
489   { "r1", NULL,   true,  R1_VALID, &RawContext::r1 },
490   { "r2", NULL,   false, R2_VALID, &RawContext::r2 },
491   { "r3", NULL,   false, R3_VALID, &RawContext::r3 },
492   { "r4", NULL,   true,  R4_VALID, &RawContext::r4 },
493   { "sp", ".cfa", true,  SP_VALID, &RawContext::sp },
494   { "pc", ".ra",  true,  PC_VALID, &RawContext::pc },
495 };
496 
497 class SimpleWalker: public SimpleCFIWalkerFixture, public Test { };
498 
TEST_F(SimpleWalker,Walk)499 TEST_F(SimpleWalker, Walk) {
500   // Stack_top is the current stack pointer, pointing to the lowest
501   // address of a frame that looks like this (all 64-bit words):
502   //
503   // sp ->  saved r0
504   //        garbage
505   //        return address
506   // cfa ->
507   //
508   // r0 has been saved on the stack.
509   // r1 has been saved in r2.
510   // r2 and r3 are not recoverable.
511   // r4 is not recoverable, even though it is a callee-saves register.
512   //    Some earlier frame's unwinder must have failed to recover it.
513 
514   uint64_t stack_top = 0x83254944b20d5512ULL;
515 
516   // Saved r0.
517   EXPECT_CALL(memory,
518               GetMemoryAtAddress(stack_top, A<uint64_t*>()))
519       .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL),
520                             Return(true)));
521   // Saved return address.
522   EXPECT_CALL(memory,
523               GetMemoryAtAddress(stack_top + 16, A<uint64_t*>()))
524       .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL),
525                             Return(true)));
526 
527   call_frame_info.SetCFARule("sp 24 +");
528   call_frame_info.SetRARule(".cfa 8 - ^");
529   call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^");
530   call_frame_info.SetRegisterRule("r1", "r2");
531 
532   callee_context.r0 = 0x94e030ca79edd119ULL;
533   callee_context.r1 = 0x937b4d7e95ce52d9ULL;
534   callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1
535   // callee_context.r3 is not valid in callee.
536   // callee_context.r4 is not valid in callee.
537   callee_context.sp = stack_top;
538   callee_context.pc = 0x25b21b224311d280ULL;
539   int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID;
540 
541   memset(&caller_context, 0, sizeof(caller_context));
542 
543   int caller_validity;
544   EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info,
545                                          callee_context, callee_validity,
546                                          &caller_context, &caller_validity));
547   EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity);
548   EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0);
549   EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1);
550   EXPECT_EQ(stack_top + 24,        caller_context.sp);
551   EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc);
552 }
553