sanitizer.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import collections
  2. import os
  3. import sys
  4. import json
  5. def get_leaks_suppressions(cmd):
  6. supp, newcmd = [], []
  7. for arg in cmd:
  8. if arg.endswith(".supp"):
  9. supp.append(arg)
  10. else:
  11. newcmd.append(arg)
  12. return supp, newcmd
  13. def fix_sanitize_flag(cmd, clang_ver):
  14. """
  15. Remove -fsanitize=address flag if sanitazers are linked explicitly for linux target.
  16. """
  17. for flag in cmd:
  18. if flag.startswith('--target') and 'linux' not in flag.lower():
  19. # use toolchained sanitize libraries
  20. return cmd
  21. CLANG_RT = 'contrib/libs/clang' + clang_ver + '-rt/lib/'
  22. sanitize_flags = {
  23. '-fsanitize=address': CLANG_RT + 'asan',
  24. '-fsanitize=memory': CLANG_RT + 'msan',
  25. '-fsanitize=leak': CLANG_RT + 'lsan',
  26. '-fsanitize=undefined': CLANG_RT + 'ubsan',
  27. '-fsanitize=thread': CLANG_RT + 'tsan',
  28. }
  29. used_sanitize_libs = []
  30. aux = []
  31. for flag in cmd:
  32. if flag.startswith('-fsanitize-coverage='):
  33. # do not link sanitizer libraries from clang
  34. aux.append('-fno-sanitize-link-runtime')
  35. if flag in sanitize_flags and any(s.startswith(sanitize_flags[flag]) for s in cmd):
  36. # exclude '-fsanitize=' if appropriate library is linked explicitly
  37. continue
  38. if any(flag.startswith(lib) for lib in sanitize_flags.values()):
  39. used_sanitize_libs.append(flag)
  40. continue
  41. aux.append(flag)
  42. # move sanitize libraries out of the repeatedly searched group of archives
  43. flags = []
  44. for flag in aux:
  45. if flag == '-Wl,--start-group':
  46. flags += ['-Wl,--whole-archive'] + used_sanitize_libs + ['-Wl,--no-whole-archive']
  47. flags.append(flag)
  48. return flags
  49. def gen_default_suppressions(inputs, output, source_root):
  50. supp_map = collections.defaultdict(set)
  51. for filename in inputs:
  52. sanitizer = os.path.basename(filename).split('.', 1)[0]
  53. with open(os.path.join(source_root, filename)) as src:
  54. for line in src:
  55. line = line.strip()
  56. if not line or line.startswith('#'):
  57. continue
  58. supp_map[sanitizer].add(line)
  59. with open(output, "wb") as dst:
  60. for supp_type, supps in supp_map.items():
  61. dst.write('extern "C" const char *__%s_default_suppressions() {\n' % supp_type)
  62. dst.write(' return "{}";\n'.format('\\n'.join(sorted(supps))))
  63. dst.write('}\n')
  64. def sanitize(cmd):
  65. clang_ver = cmd[cmd.index('--clang-ver') + 1]
  66. source_root = cmd[cmd.index('--source-root') + 1]
  67. cmd = fix_sanitize_flag(cmd, clang_ver)
  68. supp, cmd = get_leaks_suppressions(cmd)
  69. if supp:
  70. src_file = "default_suppressions.cpp"
  71. gen_default_suppressions(supp, src_file, source_root)
  72. cmd += [src_file]
  73. return cmd
  74. if __name__ == '__main__':
  75. cmd = sys.argv[1:]
  76. if 'link_exe.py' in str(cmd):
  77. cmd = sanitize(cmd)
  78. else:
  79. cmd = [s for s in cmd if not s.endswith('.supp')]
  80. sys.stdout.write(json.dumps(cmd))