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