def testAnalyzeFunction(self):
     analyzer = sa.ArmAnalyzer()
     symbol = sa.Symbol(0x10, 'F', 0x100, 'foo')
     instructions = [
         (0x10, 'push', '{r4, r5, r6, r7, lr}'),
         (0x12, 'subw', 'sp, sp, #16	; 0x10'),
         (0x16, 'movs', 'lr, r1'),
         (0x18, 'beq.n', '26 <foo+0x26>'),
         (0x1a, 'bl', '30 <foo+0x30>'),
         (0x1e, 'bl', 'deadbeef <bar>'),
         (0x22, 'blx', '0 <woo>'),
         (0x26, 'push', '{r1}'),
         (0x28, 'stmdb', 'sp!, {r4, r5, r6, r7, r8, r9, lr}'),
         (0x2c, 'stmdb', 'sp!, {r4}'),
         (0x30, 'stmdb', 'sp, {r4}'),
         (0x34, 'bx.n', '10 <foo>'),
         (0x36, 'bx.n', 'r3'),
         (0x38, 'ldr', 'pc, [r10]'),
     ]
     (size, callsites) = analyzer.AnalyzeFunction(symbol, instructions)
     self.assertEqual(size, 72)
     expect_callsites = [
         sa.Callsite(0x1e, 0xdeadbeef, False),
         sa.Callsite(0x22, 0x0, False),
         sa.Callsite(0x34, 0x10, True),
         sa.Callsite(0x36, None, True),
         sa.Callsite(0x38, None, True)
     ]
     self.assertEqual(callsites, expect_callsites)
 def testAnalyzeDisassembly(self):
   disasm_text = (
       '\n'
       'Disassembly of section .text:\n'
       '\n'
       '00000900 <wook_task>:\n'
       '	...\n'
       '00001000 <hook_task>:\n'
       '   1000:	dead beef\tfake\n'
       '   1004:	4770\t\tbx	lr\n'
       '   1006:	b113\tcbz	r3, 100929de <flash_command_write>\n'
       '   1008:	00015cfc\t.word	0x00015cfc\n'
       '00002000 <console_task>:\n'
       '   2000:	b508\t\tpush	{r3, lr} ; malformed comments,; r0, r1 \n'
       '   2002:	f00e fcc5\tbl	1000 <hook_task>\n'
       '   2006:	f00e bd3b\tb.w	53968 <get_program_memory_addr>\n'
       '   200a:	dead beef\tfake\n'
       '00004000 <touchpad_calc>:\n'
       '   4000:	4770\t\tbx	lr\n'
       '00010000 <look_task>:'
   )
   function_map = self.analyzer.AnalyzeDisassembly(disasm_text)
   func_hook_task = sa.Function(0x1000, 'hook_task', 0, [
       sa.Callsite(0x1006, 0x100929de, True, None)])
   expect_funcmap = {
       0x1000: func_hook_task,
       0x2000: sa.Function(0x2000, 'console_task', 8,
                           [sa.Callsite(0x2002, 0x1000, False, func_hook_task),
                            sa.Callsite(0x2006, 0x53968, True, None)]),
       0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
   }
   self.assertEqual(function_map, expect_funcmap)
 def testAndesAnalyzeDisassembly(self):
   disasm_text = (
       '\n'
       'build/{BOARD}/RW/ec.RW.elf:     file format elf32-nds32le'
       '\n'
       'Disassembly of section .text:\n'
       '\n'
       '00000900 <wook_task>:\n'
       '   ...\n'
       '00001000 <hook_task>:\n'
       '   1000:   fc 42\tpush25 $r10, #16    ! {$r6~$r10, $fp, $gp, $lp}\n'
       '   1004:   47 70\t\tmovi55 $r0, #1\n'
       '   1006:   b1 13\tbnezs8 100929de <flash_command_write>\n'
       '   1008:   00 01 5c fc\tbne    $r6, $r0, 2af6a\n'
       '00002000 <console_task>:\n'
       '   2000:   fc 00\t\tpush25 $r6, #0    ! {$r6, $fp, $gp, $lp} \n'
       '   2002:   f0 0e fc c5\tjal   1000 <hook_task>\n'
       '   2006:   f0 0e bd 3b\tj  53968 <get_program_memory_addr>\n'
       '   200a:   de ad be ef\tswi.gp $r0, [ + #-11036]\n'
       '00004000 <touchpad_calc>:\n'
       '   4000:   47 70\t\tmovi55 $r0, #1\n'
       '00010000 <look_task>:'
   )
   function_map = self.analyzer.AnalyzeDisassembly(disasm_text)
   func_hook_task = sa.Function(0x1000, 'hook_task', 48, [
       sa.Callsite(0x1006, 0x100929de, True, None)])
   expect_funcmap = {
       0x1000: func_hook_task,
       0x2000: sa.Function(0x2000, 'console_task', 16,
                           [sa.Callsite(0x2002, 0x1000, False, func_hook_task),
                            sa.Callsite(0x2006, 0x53968, True, None)]),
       0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
   }
   self.assertEqual(function_map, expect_funcmap)
    def testPreprocessAnnotation(self):
        funcs = {
            0x1000: sa.Function(0x1000, 'hook_task', 0, []),
            0x2000: sa.Function(0x2000, 'console_task', 0, []),
            0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
        }
        funcs[0x1000].callsites = [
            sa.Callsite(0x1002, 0x1000, False, funcs[0x1000])
        ]
        funcs[0x2000].callsites = [
            sa.Callsite(0x2002, 0x1000, False, funcs[0x1000]),
            sa.Callsite(0x2006, None, True, None),
        ]
        add_set = {
            (funcs[0x2000], funcs[0x2000]),
            (funcs[0x2000], funcs[0x4000]),
            (funcs[0x4000], funcs[0x1000]),
            (funcs[0x4000], funcs[0x2000]),
        }
        remove_list = [
            [funcs[0x1000]],
            [funcs[0x2000], funcs[0x2000]],
            [funcs[0x4000], funcs[0x1000]],
            [funcs[0x2000], funcs[0x4000], funcs[0x2000]],
            [funcs[0x4000], funcs[0x1000], funcs[0x4000]],
        ]
        eliminated_addrs = {0x2006}

        remaining_remove_list = self.analyzer.PreprocessAnnotation(
            funcs, add_set, remove_list, eliminated_addrs)

        expect_funcs = {
            0x1000: sa.Function(0x1000, 'hook_task', 0, []),
            0x2000: sa.Function(0x2000, 'console_task', 0, []),
            0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
        }
        expect_funcs[0x2000].callsites = [
            sa.Callsite(None, 0x4000, False, expect_funcs[0x4000])
        ]
        expect_funcs[0x4000].callsites = [
            sa.Callsite(None, 0x2000, False, expect_funcs[0x2000])
        ]
        self.assertEqual(funcs, expect_funcs)
        self.assertEqual(remaining_remove_list, [
            [funcs[0x2000], funcs[0x4000], funcs[0x2000]],
        ])
    def testAnalyzeCallGraph(self):
        funcs = {
            0x1000: sa.Function(0x1000, 'hook_task', 0, []),
            0x2000: sa.Function(0x2000, 'console_task', 8, []),
            0x3000: sa.Function(0x3000, 'task_a', 12, []),
            0x4000: sa.Function(0x4000, 'task_b', 96, []),
            0x5000: sa.Function(0x5000, 'task_c', 32, []),
            0x6000: sa.Function(0x6000, 'task_d', 100, []),
            0x7000: sa.Function(0x7000, 'task_e', 24, []),
            0x8000: sa.Function(0x8000, 'task_f', 20, []),
            0x9000: sa.Function(0x9000, 'task_g', 20, []),
            0x10000: sa.Function(0x10000, 'task_x', 16, []),
        }
        funcs[0x1000].callsites = [
            sa.Callsite(0x1002, 0x3000, False, funcs[0x3000]),
            sa.Callsite(0x1006, 0x4000, False, funcs[0x4000])
        ]
        funcs[0x2000].callsites = [
            sa.Callsite(0x2002, 0x5000, False, funcs[0x5000]),
            sa.Callsite(0x2006, 0x2000, False, funcs[0x2000]),
            sa.Callsite(0x200a, 0x10000, False, funcs[0x10000])
        ]
        funcs[0x3000].callsites = [
            sa.Callsite(0x3002, 0x4000, False, funcs[0x4000]),
            sa.Callsite(0x3006, 0x1000, False, funcs[0x1000])
        ]
        funcs[0x4000].callsites = [
            sa.Callsite(0x4002, 0x6000, True, funcs[0x6000]),
            sa.Callsite(0x4006, 0x7000, False, funcs[0x7000]),
            sa.Callsite(0x400a, 0x8000, False, funcs[0x8000])
        ]
        funcs[0x5000].callsites = [
            sa.Callsite(0x5002, 0x4000, False, funcs[0x4000])
        ]
        funcs[0x7000].callsites = [
            sa.Callsite(0x7002, 0x7000, False, funcs[0x7000])
        ]
        funcs[0x8000].callsites = [
            sa.Callsite(0x8002, 0x9000, False, funcs[0x9000])
        ]
        funcs[0x9000].callsites = [
            sa.Callsite(0x9002, 0x4000, False, funcs[0x4000])
        ]
        funcs[0x10000].callsites = [
            sa.Callsite(0x10002, 0x2000, False, funcs[0x2000])
        ]

        cycles = self.analyzer.AnalyzeCallGraph(
            funcs, [[funcs[0x2000]] * 2, [funcs[0x10000], funcs[0x2000]] * 3,
                    [funcs[0x1000], funcs[0x3000], funcs[0x1000]]])

        expect_func_stack = {
            0x1000: (268, [
                funcs[0x1000], funcs[0x3000], funcs[0x4000], funcs[0x8000],
                funcs[0x9000], funcs[0x4000], funcs[0x7000]
            ]),
            0x2000: (208, [
                funcs[0x2000], funcs[0x10000], funcs[0x2000], funcs[0x10000],
                funcs[0x2000], funcs[0x5000], funcs[0x4000], funcs[0x7000]
            ]),
            0x3000: (280, [
                funcs[0x3000], funcs[0x1000], funcs[0x3000], funcs[0x4000],
                funcs[0x8000], funcs[0x9000], funcs[0x4000], funcs[0x7000]
            ]),
            0x4000: (120, [funcs[0x4000], funcs[0x7000]]),
            0x5000: (152, [funcs[0x5000], funcs[0x4000], funcs[0x7000]]),
            0x6000: (100, [funcs[0x6000]]),
            0x7000: (24, [funcs[0x7000]]),
            0x8000:
            (160, [funcs[0x8000], funcs[0x9000], funcs[0x4000],
                   funcs[0x7000]]),
            0x9000: (140, [funcs[0x9000], funcs[0x4000], funcs[0x7000]]),
            0x10000: (200, [
                funcs[0x10000], funcs[0x2000], funcs[0x10000], funcs[0x2000],
                funcs[0x5000], funcs[0x4000], funcs[0x7000]
            ]),
        }
        expect_cycles = [
            {funcs[0x4000], funcs[0x8000], funcs[0x9000]},
            {funcs[0x7000]},
        ]
        for func in funcs.values():
            (stack_max_usage, stack_max_path) = expect_func_stack[func.address]
            self.assertEqual(func.stack_max_usage, stack_max_usage)
            self.assertEqual(func.stack_max_path, stack_max_path)

        self.assertEqual(len(cycles), len(expect_cycles))
        for cycle in cycles:
            self.assertTrue(cycle in expect_cycles)
 def testCallsite(self):
     callsite_a = sa.Callsite(0x1002, 0x3000, False)
     callsite_b = sa.Callsite(0x1002, 0x3000, True)
     self.assertEqual(callsite_a, callsite_a)
     self.assertNotEqual(callsite_a, callsite_b)
     self.assertNotEqual(callsite_a, None)
    def testResolveAnnotation(self):
        self.analyzer.annotation = {}
        (add_rules, remove_rules,
         invalid_sigtxts) = self.analyzer.LoadAnnotation()
        self.assertEqual(add_rules, {})
        self.assertEqual(remove_rules, [])
        self.assertEqual(invalid_sigtxts, set())

        self.analyzer.annotation = {'add': None, 'remove': None}
        (add_rules, remove_rules,
         invalid_sigtxts) = self.analyzer.LoadAnnotation()
        self.assertEqual(add_rules, {})
        self.assertEqual(remove_rules, [])
        self.assertEqual(invalid_sigtxts, set())

        self.analyzer.annotation = {
            'add':
            None,
            'remove': [
                [['a', 'b'], ['0', '[', '2'], 'x'],
                [['a', 'b[x:3]'], ['0', '1', '2'], 'x'],
            ],
        }
        (add_rules, remove_rules,
         invalid_sigtxts) = self.analyzer.LoadAnnotation()
        self.assertEqual(add_rules, {})
        self.assertEqual(
            list.sort(remove_rules),
            list.sort([
                [('a', None, None), ('1', None, None), ('x', None, None)],
                [('a', None, None), ('0', None, None), ('x', None, None)],
                [('a', None, None), ('2', None, None), ('x', None, None)],
                [('b', os.path.abspath('x'), 3), ('1', None, None),
                 ('x', None, None)],
                [('b', os.path.abspath('x'), 3), ('0', None, None),
                 ('x', None, None)],
                [('b', os.path.abspath('x'), 3), ('2', None, None),
                 ('x', None, None)],
            ]))
        self.assertEqual(invalid_sigtxts, {'['})

        self.analyzer.annotation = {
            'add': {
                'touchpad_calc': [dict(name='__array', stride=8, offset=4)],
            }
        }
        (add_rules, remove_rules,
         invalid_sigtxts) = self.analyzer.LoadAnnotation()
        self.assertEqual(
            add_rules, {
                ('touchpad_calc', None, None):
                set([('console_task', None, None), ('hook_task', None, None)])
            })

        funcs = {
            0x1000: sa.Function(0x1000, 'hook_task', 0, []),
            0x2000: sa.Function(0x2000, 'console_task', 0, []),
            0x4000: sa.Function(0x4000, 'touchpad_calc', 0, []),
            0x5000: sa.Function(0x5000, 'touchpad_calc.constprop.42', 0, []),
            0x13000: sa.Function(0x13000, 'inlined_mul', 0, []),
            0x13100: sa.Function(0x13100, 'inlined_mul', 0, []),
        }
        funcs[0x1000].callsites = [sa.Callsite(0x1002, None, False, None)]
        # Set address_to_line_cache to fake the results of addr2line.
        self.analyzer.address_to_line_cache = {
            (0x1000, False): [('hook_task', os.path.abspath('a.c'), 10)],
            (0x1002, False): [('toot_calc', os.path.abspath('t.c'), 1234)],
            (0x2000, False): [('console_task', os.path.abspath('b.c'), 20)],
            (0x4000, False): [('toudhpad_calc', os.path.abspath('a.c'), 20)],
            (0x5000, False):
            [('touchpad_calc.constprop.42', os.path.abspath('b.c'), 40)],
            (0x12000, False): [('trackpad_range', os.path.abspath('t.c'), 10)],
            (0x13000, False): [('inlined_mul', os.path.abspath('x.c'), 12)],
            (0x13100, False): [('inlined_mul', os.path.abspath('x.c'), 12)],
        }
        self.analyzer.annotation = {
            'add': {
                'hook_task.lto.573': ['touchpad_calc.lto.2501[a.c]'],
                'console_task': ['touchpad_calc[b.c]', 'inlined_mul_alias'],
                'hook_task[q.c]': ['hook_task'],
                'inlined_mul[x.c]': ['inlined_mul'],
                'toot_calc[t.c:1234]': ['hook_task'],
            },
            'remove': [
                ['touchpad?calc['],
                'touchpad_calc',
                ['touchpad_calc[a.c]'],
                ['task_unk[a.c]'],
                ['touchpad_calc[x/a.c]'],
                ['trackpad_range'],
                ['inlined_mul'],
                ['inlined_mul', 'console_task', 'touchpad_calc[a.c]'],
                ['inlined_mul', 'inlined_mul_alias', 'console_task'],
                ['inlined_mul', 'inlined_mul_alias', 'console_task'],
            ],
        }
        (add_rules, remove_rules,
         invalid_sigtxts) = self.analyzer.LoadAnnotation()
        self.assertEqual(invalid_sigtxts, {'touchpad?calc['})

        signature_set = set()
        for src_sig, dst_sigs in add_rules.items():
            signature_set.add(src_sig)
            signature_set.update(dst_sigs)

        for remove_sigs in remove_rules:
            signature_set.update(remove_sigs)

        (signature_map,
         failed_sigs) = self.analyzer.MapAnnotation(funcs, signature_set)
        result = self.analyzer.ResolveAnnotation(funcs)
        (add_set, remove_list, eliminated_addrs, failed_sigs) = result

        expect_signature_map = {
            ('hook_task', None, None): {funcs[0x1000]},
            ('touchpad_calc', os.path.abspath('a.c'), None): {funcs[0x4000]},
            ('touchpad_calc', os.path.abspath('b.c'), None): {funcs[0x5000]},
            ('console_task', None, None): {funcs[0x2000]},
            ('inlined_mul_alias', None, None): {funcs[0x13100]},
            ('inlined_mul', os.path.abspath('x.c'), None):
            {funcs[0x13000], funcs[0x13100]},
            ('inlined_mul', None, None): {funcs[0x13000], funcs[0x13100]},
        }
        self.assertEqual(len(signature_map), len(expect_signature_map))
        for sig, funclist in signature_map.items():
            self.assertEqual(set(funclist), expect_signature_map[sig])

        self.assertEqual(
            add_set, {
                (funcs[0x1000], funcs[0x4000]),
                (funcs[0x1000], funcs[0x1000]),
                (funcs[0x2000], funcs[0x5000]),
                (funcs[0x2000], funcs[0x13100]),
                (funcs[0x13000], funcs[0x13000]),
                (funcs[0x13000], funcs[0x13100]),
                (funcs[0x13100], funcs[0x13000]),
                (funcs[0x13100], funcs[0x13100]),
            })
        expect_remove_list = [
            [funcs[0x4000]],
            [funcs[0x13000]],
            [funcs[0x13100]],
            [funcs[0x13000], funcs[0x2000], funcs[0x4000]],
            [funcs[0x13100], funcs[0x2000], funcs[0x4000]],
            [funcs[0x13000], funcs[0x13100], funcs[0x2000]],
            [funcs[0x13100], funcs[0x13100], funcs[0x2000]],
        ]
        self.assertEqual(len(remove_list), len(expect_remove_list))
        for remove_path in remove_list:
            self.assertTrue(remove_path in expect_remove_list)

        self.assertEqual(eliminated_addrs, {0x1002})
        self.assertEqual(
            failed_sigs, {
                ('touchpad?calc[', sa.StackAnalyzer.ANNOTATION_ERROR_INVALID),
                ('touchpad_calc', sa.StackAnalyzer.ANNOTATION_ERROR_AMBIGUOUS),
                ('hook_task[q.c]', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
                ('task_unk[a.c]', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
                ('touchpad_calc[x/a.c]',
                 sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
                ('trackpad_range', sa.StackAnalyzer.ANNOTATION_ERROR_NOTFOUND),
            })