1#!/usr/bin/env python
2# Copyright 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Tests for extract_unwind_tables.py
7
8This test suite contains various tests for extracting CFI tables from breakpad
9symbol files.
10"""
11
12import optparse
13import os
14import struct
15import sys
16import tempfile
17import unittest
18
19import extract_unwind_tables
20
21sys.path.append(os.path.join(os.path.dirname(__file__), "gyp"))
22from util import build_utils
23
24
25class TestExtractUnwindTables(unittest.TestCase):
26  def testExtractCfi(self):
27    with tempfile.NamedTemporaryFile() as input_file, \
28        tempfile.NamedTemporaryFile() as output_file:
29      input_file.write("""
30MODULE Linux arm CDE12FE1DF2B37A9C6560B4CBEE056420 lib_chrome.so
31INFO CODE_ID E12FE1CD2BDFA937C6560B4CBEE05642
32FILE 0 ../../base/allocator/allocator_check.cc
33FILE 1 ../../base/allocator/allocator_extension.cc
34FILE 2 ../../base/allocator/allocator_shim.cc
35FUNC 1adcb60 54 0 i2d_name_canon
361adcb60 1a 509 17054
373b94c70 2 69 40
38PUBLIC e17001 0 assist_ranker::(anonymous namespace)::FakePredict::Initialize()
39PUBLIC e17005 0 (anonymous namespace)::FileDeleter(base::File)
40STACK CFI INIT e17000 4 .cfa: sp 0 + .ra: lr
41STACK CFI INIT 0 4 .cfa: sp 0 + .ra: lr
42STACK CFI 2 .cfa: sp 4 +
43STACK CFI 4 .cfa: sp 12 + .ra: .cfa -8 + ^ r7: .cfa -12 + ^
44STACK CFI 6 .cfa: sp 16 +
45STACK CFI INIT e1a96e 20 .cfa: sp 0 + .ra: lr
46STACK CFI e1a970 .cfa: sp 4 +
47STACK CFI e1a972 .cfa: sp 12 + .ra: .cfa -8 + ^ r7: .cfa -12 + ^
48STACK CFI e1a974 .cfa: sp 16 +
49STACK CFI INIT e1a1e4 b0 .cfa: sp 0 + .ra: lr
50STACK CFI e1a1e6 .cfa: sp 16 + .ra: .cfa -4 + ^ r4: .cfa -16 + ^ r5: .cfa -12 +
51STACK CFI e1a1e8 .cfa: sp 80 +
52STACK CFI INIT 0 4 .cfa: sp 0 + .ra: lr
53STACK CFI INIT 3b92e24 3c .cfa: sp 0 + .ra: lr
54STACK CFI 3b92e4c .cfa: sp 16 + .ra: .cfa -12 + ^
55STACK CFI INIT e17004 0 .cfa: sp 0 + .ra: lr
56STACK CFI e17004 2 .cfa: sp 0 + .ra: lr
57STACK CFI INIT 3b92e70 38 .cfa: sp 0 + .ra: lr
58STACK CFI 3b92e74 .cfa: sp 8 + .ra: .cfa -4 + ^ r4: .cfa -8 + ^
59STACK CFI 3b92e90 .cfa: sp 0 + .ra: .ra r4: r4
60STACK CFI INIT 3b93114 6c .cfa: sp 0 + .ra: lr
61STACK CFI 3b93118 .cfa: r7 16 + .ra: .cfa -4 + ^
62STACK CFI INIT 3b92114 6c .cfa: sp 0 + .ra: lr
63STACK CFI 3b92118 .cfa: r7 16 + .ra: .cfa -20 + ^
64STACK CFI INIT 3b93214 fffff .cfa: sp 0 + .ra: lr
65STACK CFI 3b93218 .cfa: r7 16 + .ra: .cfa -4 + ^
66""")
67      input_file.flush()
68      extract_unwind_tables._ParseCfiData(input_file.name, output_file.name)
69
70      expected_cfi_data = {
71        0xe1a1e4 : [0x2, 0x11, 0x4, 0x50],
72        0xe1a296 : [],
73        0xe1a96e : [0x2, 0x4, 0x4, 0xe, 0x6, 0x10],
74        0xe1a990 : [],
75        0x3b92e24: [0x28, 0x13],
76        0x3b92e62: [],
77      }
78      expected_function_count = len(expected_cfi_data)
79
80      actual_output = []
81      with open(output_file.name, 'rb') as f:
82        while True:
83          read = f.read(2)
84          if not read:
85            break
86          actual_output.append(struct.unpack('H', read)[0])
87
88      # First value is size of unw_index table.
89      unw_index_size = actual_output[1] << 16 | actual_output[0]
90      # |unw_index_size| should match entry count.
91      self.assertEqual(expected_function_count, unw_index_size)
92      # |actual_output| is in blocks of 2 bytes. Skip first 4 bytes representing
93      # size.
94      unw_index_start = 2
95      unw_index_addr_end = unw_index_start + expected_function_count * 2
96      unw_index_end = unw_index_addr_end + expected_function_count
97      unw_index_addr_col = actual_output[unw_index_start : unw_index_addr_end]
98      unw_index_index_col = actual_output[unw_index_addr_end : unw_index_end]
99
100      unw_data_start = unw_index_end
101      unw_data = actual_output[unw_data_start:]
102
103      for func_iter in range(0, expected_function_count):
104        func_addr = (unw_index_addr_col[func_iter * 2 + 1] << 16 |
105                     unw_index_addr_col[func_iter * 2])
106        index = unw_index_index_col[func_iter]
107        # If index is CANT_UNWIND then invalid function.
108        if index == 0xFFFF:
109          self.assertEqual(expected_cfi_data[func_addr], [])
110          continue
111
112        func_start = index + 1
113        func_end = func_start + unw_data[index] * 2
114        self.assertEquals(
115            len(expected_cfi_data[func_addr]), func_end - func_start)
116        func_cfi = unw_data[func_start : func_end]
117        self.assertEqual(expected_cfi_data[func_addr], func_cfi)
118
119
120if __name__ == '__main__':
121  unittest.main()
122