import hashlib
import json
import re
+import binascii
+import struct
import os
-from os.path import realpath, isfile, basename
+from os.path import realpath, isfile
SANCOV_EXEC = 'sancov.py'
SYMBOLIZER_EXEC = 'addr2line'
return serialized
+def calc_file_crc32(path):
+ """
+ Calculate file's crc32
+
+ Keyword arguments:
+ path -- path to elf binary to parse
+ """
+
+ crc = None
+ with open(path, 'rb') as f:
+ crc = binascii.crc32(f.read())
+ return crc
+
+
+def get_debugfile_name(path):
+ """
+ Get debug filename from .gnu_debuglink section
+
+ Keyword arguments:
+ path -- path to elf binary to parse
+
+ Return:
+ Tuple (filename, crc32)
+ """
+
+ cmd = "objcopy " + path +\
+ " /dev/null --dump-section .gnu_debuglink=/dev/stdout"
+ try:
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ except subprocess.CalledProcessError as err:
+ sys.stderr.write(err.output.decode())
+ exit(err.returncode)
+
+ out = proc.communicate()[0]
+ if not out:
+ return (None, None)
+
+ # debug filename is zero-ended
+ name = out.split('\x00')[0]
+ # crc32 is last 4 bytes
+ crc = struct.unpack('i', out[-4:])[0]
+ return (name, crc)
+
+
+def get_debugfile_by_id(path):
+ """
+ Get debug filename according Build-ID
+
+ Keyword arguments:
+ path -- path to elf binary to parse
+ """
+
+ cmd = "readelf -n " + path
+
+ try:
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ except subprocess.CalledProcessError as err:
+ sys.stderr.write(err.output.decode())
+ exit(err.returncode)
+
+ out = proc.communicate()[0]
+ # Search for such string:
+ # Build ID: b2d87caa09c74d1717fd5b217d85f737fd60c7b8
+ name = re.findall(r"\S*Build ID: ([a-z0-9]*)", out)
+ path = "/usr/lib/debug/.build-id/{}/{}.debug".format(name[0][:2],
+ name[0][2:])
+ if len(name) and os.path.exists(path):
+ return path
+ else:
+ return ""
+
+
def parse_args(args):
"""
Parses command line arguments.
else:
debug_dirs = args[1].split(":")
path_index = 2
- debug_info = [
- debug_dir + "/" + f
+ debug_info = dict(
+ (f, debug_dir + "/" + f)
for debug_dir in debug_dirs for f in os.listdir(debug_dir)
if isfile(debug_dir + "/" + f)
- ]
+ )
if (argc - path_index) % 2 != 0:
usage()
for i in range(path_index, argc, 2):
sancov_dumps.append(realpath(args[i]))
binaries.append(realpath(args[i + 1]))
- # TODO: replace basic filenames comparison check with
- # binary debug-link check
- for debug_file in debug_info:
- if basename(binaries[-1]) + ".debug" == basename(debug_file):
- filtered_debug_info.append(debug_file)
- break
- if len(filtered_debug_info) < len(binaries):
+ debug_file = get_debugfile_by_id(binaries[-1])
+ if debug_file:
+ filtered_debug_info.append(debug_file)
+ continue
+
+ debug_file = get_debugfile_name(binaries[-1])
+ if debug_file[0] in debug_info and\
+ calc_file_crc32(debug_info[debug_file[0]]) == debug_file[1]:
+ filtered_debug_info.append(debug_info[debug_file[0]])
+ else:
filtered_debug_info.append(binaries[-1])
print_symcov(zip(sancov_dumps, binaries, filtered_debug_info))