1#
2# Copyright (C) 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16"""Parses the contents of a sancov file generated by LLVM Sanitizer Coverage.
17
18
19    Typical usage example:
20
21    ParseSancovFile(file_name)
22"""
23
24import struct
25import parser
26
27
28MAGIC32 = 0xC0BFFFFFFFFFFF32
29MAGIC64 = 0xC0BFFFFFFFFFFF64
30
31
32class SancovParser(object):
33    """Sancov parser object to represent *.sancov files.
34
35    Attributes:
36        _sancov_file: the stream-like file handle to a sancov file
37        _bitness: the bitness of the offsets (32/64)
38        _entry_type: the type of each offset (32bit->Integer, 64bit->Long)
39    """
40
41    def __init__(self, sancov_file):
42        """Inits the parser with the input stream and default values.
43
44        Args:
45            sancov_file: An input binary file stream to a .sancov file
46        """
47        self._sancov_file = sancov_file
48        self._bitness = -1
49        self._entry_type = None
50        self._size = 0
51
52    def Parse(self):
53        """Runs the parser to generate the unpacked binary offsets in the file.
54
55        Returns:
56            A tuple of offsets into the original binary.
57
58        Raises:
59            parser.FileFormatError: invalid file format or invalid counts.
60        """
61        self.GetBitness()
62        return struct.unpack_from(
63            self._entry_type * (self._size * 8 / self._bitness),
64            self._sancov_file.read(self._size))
65
66    def GetBitness(self):
67        """Parses the magic header to determine the bitness.
68
69        Returns:
70            The sancov file bitness.
71
72        Raises:
73            parser.FileFormatError: invalid file format or invalid counts.
74        """
75        if self._bitness > 0:
76            return self._bitness
77        self._sancov_file.seek(0, 2)
78        self._size = self._sancov_file.tell() - 8
79        self._sancov_file.seek(0, 0)
80        if self._size < 0:
81            raise parser.FileFormatError('Empty file.')
82        magic = struct.unpack('L', self._sancov_file.read(8))[0];
83        if magic == MAGIC64:
84            self._entry_type = 'L'
85            self._bitness = 64
86        elif magic == MAGIC32:
87            self._entry_type = 'I'
88            self._bitness = 32
89        else:
90            raise parser.FileFormatError('Invalid magic.')
91        return self._bitness
92
93def ParseSancovFile(file_name):
94    """Parses the .sancov file specified by the input.
95
96    Args:
97        file_name: A string file path to a .sancov file
98
99    Returns:
100        A tuple of bitness, and the unpacked offsets into the original binary.
101    """
102    with open(file_name, 'rb') as stream:
103        p = SancovParser(stream)
104        offsets = p.Parse()
105        return (p._bitness, offsets)
106