# # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Parses the contents of a sancov file generated by LLVM Sanitizer Coverage. Typical usage example: ParseSancovFile(file_name) """ import struct import parser MAGIC32 = 0xC0BFFFFFFFFFFF32 MAGIC64 = 0xC0BFFFFFFFFFFF64 class SancovParser(object): """Sancov parser object to represent *.sancov files. Attributes: _sancov_file: the stream-like file handle to a sancov file _bitness: the bitness of the offsets (32/64) _entry_type: the type of each offset (32bit->Integer, 64bit->Long) """ def __init__(self, sancov_file): """Inits the parser with the input stream and default values. Args: sancov_file: An input binary file stream to a .sancov file """ self._sancov_file = sancov_file self._bitness = -1 self._entry_type = None self._size = 0 def Parse(self): """Runs the parser to generate the unpacked binary offsets in the file. Returns: A tuple of offsets into the original binary. Raises: parser.FileFormatError: invalid file format or invalid counts. """ self.GetBitness() return struct.unpack_from( self._entry_type * (self._size * 8 / self._bitness), self._sancov_file.read(self._size)) def GetBitness(self): """Parses the magic header to determine the bitness. Returns: The sancov file bitness. Raises: parser.FileFormatError: invalid file format or invalid counts. """ if self._bitness > 0: return self._bitness self._sancov_file.seek(0, 2) self._size = self._sancov_file.tell() - 8 self._sancov_file.seek(0, 0) if self._size < 0: raise parser.FileFormatError('Empty file.') magic = struct.unpack('L', self._sancov_file.read(8))[0]; if magic == MAGIC64: self._entry_type = 'L' self._bitness = 64 elif magic == MAGIC32: self._entry_type = 'I' self._bitness = 32 else: raise parser.FileFormatError('Invalid magic.') return self._bitness def ParseSancovFile(file_name): """Parses the .sancov file specified by the input. Args: file_name: A string file path to a .sancov file Returns: A tuple of bitness, and the unpacked offsets into the original binary. """ with open(file_name, 'rb') as stream: p = SancovParser(stream) offsets = p.Parse() return (p._bitness, offsets)