#!/usr/bin/env python3 # Finds potential ODR violations due to weak symbols. # For example, if you have two different structs with the same name in different files, # their inline constructors may collide. # This works only on Linux. It is designed to be run from the cmake build directory. # clang seems more willing to emit non-inlined ctors. Of course perform a Debug build. import re import subprocess output = subprocess.check_output( "nm --radix=d -g --demangle -l --print-size CMakeFiles/fishlib.dir/src/*.o", shell=True, universal_newlines=True, ) files_by_name = {} # Symbol to set of paths sizes_by_name = {} # Symbol to set of int sizes for line in output.split("\n"): # Keep only weak symbols with default values (e.g. emitted inline functions). # Example line: "0000000000000000 0000000000000107 W symbol_name" # First number is offset, second is size. # Note this is decimal because of radix=d. m = re.match(r"\d+ (\d+) W (.*)\t(.*)", line) if not m: continue size, name, filename = m.groups() files_by_name.setdefault(name, set()).add(filename) sizes_by_name.setdefault(name, set()).add(int(size)) odr_violations = 0 for name, sizes in sizes_by_name.items(): if len(sizes) == 1: continue files = files_by_name[name] # Ignore symbols that only appear in one file. # These are typically headers - unclear why they get different sizes but it appears benign. if len(files) == 1: continue # Multiple sizes for this symbol name. odr_violations += 1 print("Multiple sizes for symbol: " + name) print("\t%s" % ", ".join([str(x) for x in sizes])) print("\tFound in files:") for filename in files: print("\t\t%s" % filename) if odr_violations == 0: print("No ODR violations found, hooray\n") # Show potential weak symbols. suspicious_odrs = 0 for (name, files) in files_by_name.items(): if len(files) != 1: continue (filename,) = files if ".cpp" in filename: if suspicious_odrs == 0: print("Some suspicious singles:") suspicious_odrs += 1 print("\t%s" % name) print("\t\tIn file %s" % filename)