link_exe.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import sys
  2. import subprocess
  3. import optparse
  4. import process_command_files as pcf
  5. from process_whole_archive_option import ProcessWholeArchiveOption
  6. def get_leaks_suppressions(cmd):
  7. supp, newcmd = [], []
  8. for arg in cmd:
  9. if arg.endswith(".supp"):
  10. supp.append(arg)
  11. else:
  12. newcmd.append(arg)
  13. return supp, newcmd
  14. MUSL_LIBS = '-lc', '-lcrypt', '-ldl', '-lm', '-lpthread', '-lrt', '-lutil'
  15. CUDA_LIBRARIES = {
  16. '-lcublas_static': '-lcublas',
  17. '-lcublasLt_static': '-lcublasLt',
  18. '-lcudart_static': '-lcudart',
  19. '-lcudnn_static': '-lcudnn',
  20. '-lcufft_static_nocallback': '-lcufft',
  21. '-lcurand_static': '-lcurand',
  22. '-lcusolver_static': '-lcusolver',
  23. '-lcusparse_static': '-lcusparse',
  24. '-lmyelin_compiler_static': '-lmyelin',
  25. '-lmyelin_executor_static': '-lnvcaffe_parser',
  26. '-lmyelin_pattern_library_static': '',
  27. '-lmyelin_pattern_runtime_static': '',
  28. '-lnvinfer_static': '-lnvinfer',
  29. '-lnvinfer_plugin_static': '-lnvinfer_plugin',
  30. '-lnvonnxparser_static': '-lnvonnxparser',
  31. '-lnvparsers_static': '-lnvparsers',
  32. }
  33. def remove_excessive_flags(cmd):
  34. flags = []
  35. for flag in cmd:
  36. if not flag.endswith('.ios.interface') and not flag.endswith('.pkg.fake'):
  37. flags.append(flag)
  38. return flags
  39. def fix_sanitize_flag(cmd, opts):
  40. """
  41. Remove -fsanitize=address flag if sanitazers are linked explicitly for linux target.
  42. """
  43. for flag in cmd:
  44. if flag.startswith('--target') and 'linux' not in flag.lower():
  45. # use toolchained sanitize libraries
  46. return cmd
  47. assert opts.clang_ver
  48. CLANG_RT = 'contrib/libs/clang' + opts.clang_ver + '-rt/lib/'
  49. sanitize_flags = {
  50. '-fsanitize=address': CLANG_RT + 'asan',
  51. '-fsanitize=memory': CLANG_RT + 'msan',
  52. '-fsanitize=leak': CLANG_RT + 'lsan',
  53. '-fsanitize=undefined': CLANG_RT + 'ubsan',
  54. '-fsanitize=thread': CLANG_RT + 'tsan',
  55. }
  56. used_sanitize_libs = []
  57. aux = []
  58. for flag in cmd:
  59. if flag.startswith('-fsanitize-coverage='):
  60. # do not link sanitizer libraries from clang
  61. aux.append('-fno-sanitize-link-runtime')
  62. if flag in sanitize_flags and any(s.startswith(sanitize_flags[flag]) for s in cmd):
  63. # exclude '-fsanitize=' if appropriate library is linked explicitly
  64. continue
  65. if any(flag.startswith(lib) for lib in sanitize_flags.values()):
  66. used_sanitize_libs.append(flag)
  67. continue
  68. aux.append(flag)
  69. # move sanitize libraries out of the repeatedly searched group of archives
  70. flags = []
  71. for flag in aux:
  72. if flag == '-Wl,--start-group':
  73. flags += ['-Wl,--whole-archive'] + used_sanitize_libs + ['-Wl,--no-whole-archive']
  74. flags.append(flag)
  75. return flags
  76. def fix_cmd_for_musl(cmd):
  77. flags = []
  78. for flag in cmd:
  79. if flag not in MUSL_LIBS:
  80. flags.append(flag)
  81. return flags
  82. def fix_cmd_for_dynamic_cuda(cmd):
  83. flags = []
  84. for flag in cmd:
  85. if flag in CUDA_LIBRARIES:
  86. flags.append(CUDA_LIBRARIES[flag])
  87. else:
  88. flags.append(flag)
  89. return flags
  90. def gen_default_suppressions(inputs, output, source_root):
  91. import collections
  92. import os
  93. supp_map = collections.defaultdict(set)
  94. for filename in inputs:
  95. sanitizer = os.path.basename(filename).split('.', 1)[0]
  96. with open(os.path.join(source_root, filename)) as src:
  97. for line in src:
  98. line = line.strip()
  99. if not line or line.startswith('#'):
  100. continue
  101. supp_map[sanitizer].add(line)
  102. with open(output, "wb") as dst:
  103. for supp_type, supps in supp_map.items():
  104. dst.write('extern "C" const char *__%s_default_suppressions() {\n' % supp_type)
  105. dst.write(' return "{}";\n'.format('\\n'.join(sorted(supps))))
  106. dst.write('}\n')
  107. def fix_blas_resolving(cmd):
  108. # Intel mkl comes as a precompiled static library and thus can not be recompiled with sanitizer runtime instrumentation.
  109. # That's why we prefer to use cblas instead of Intel mkl as a drop-in replacement under sanitizers.
  110. # But if the library has dependencies on mkl and cblas simultaneously, it will get a linking error.
  111. # Hence we assume that it's probably compiling without sanitizers and we can easily remove cblas to prevent multiple definitions of the same symbol at link time.
  112. for arg in cmd:
  113. if arg.startswith('contrib/libs') and arg.endswith('mkl-lp64.a'):
  114. return [arg for arg in cmd if not arg.endswith('libcontrib-libs-cblas.a')]
  115. return cmd
  116. def parse_args():
  117. parser = optparse.OptionParser()
  118. parser.disable_interspersed_args()
  119. parser.add_option('--musl', action='store_true')
  120. parser.add_option('--custom-step')
  121. parser.add_option('--python')
  122. parser.add_option('--source-root')
  123. parser.add_option('--clang-ver')
  124. parser.add_option('--dynamic-cuda', action='store_true')
  125. parser.add_option('--arch')
  126. parser.add_option('--linker-output')
  127. parser.add_option('--whole-archive-peers', action='append')
  128. parser.add_option('--whole-archive-libs', action='append')
  129. return parser.parse_args()
  130. if __name__ == '__main__':
  131. opts, args = parse_args()
  132. args = pcf.skip_markers(args)
  133. cmd = fix_blas_resolving(args)
  134. cmd = remove_excessive_flags(cmd)
  135. if opts.musl:
  136. cmd = fix_cmd_for_musl(cmd)
  137. cmd = fix_sanitize_flag(cmd, opts)
  138. if 'ld.lld' in str(cmd):
  139. if '-fPIE' in str(cmd) or '-fPIC' in str(cmd):
  140. # support explicit PIE
  141. pass
  142. else:
  143. cmd.append('-Wl,-no-pie')
  144. if opts.dynamic_cuda:
  145. cmd = fix_cmd_for_dynamic_cuda(cmd)
  146. cmd = ProcessWholeArchiveOption(opts.arch, opts.whole_archive_peers, opts.whole_archive_libs).construct_cmd(cmd)
  147. if opts.custom_step:
  148. assert opts.python
  149. subprocess.check_call([opts.python] + [opts.custom_step] + args)
  150. supp, cmd = get_leaks_suppressions(cmd)
  151. if supp:
  152. src_file = "default_suppressions.cpp"
  153. gen_default_suppressions(supp, src_file, opts.source_root)
  154. cmd += [src_file]
  155. if opts.linker_output:
  156. stdout = open(opts.linker_output, 'w')
  157. else:
  158. stdout = sys.stdout
  159. rc = subprocess.call(cmd, shell=False, stderr=sys.stderr, stdout=stdout)
  160. sys.exit(rc)