link_dyn_lib.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. from __future__ import print_function
  2. import sys
  3. import os
  4. import json
  5. import subprocess
  6. import tempfile
  7. import collections
  8. import optparse
  9. import pipes
  10. # Explicitly enable local imports
  11. # Don't forget to add imported scripts to inputs of the calling command!
  12. sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  13. import thinlto_cache
  14. import link_exe
  15. from process_whole_archive_option import ProcessWholeArchiveOption
  16. def shlex_join(cmd):
  17. # equivalent to shlex.join() in python 3
  18. return ' '.join(pipes.quote(part) for part in cmd)
  19. def parse_export_file(p):
  20. with open(p, 'r') as f:
  21. for l in f:
  22. l = l.strip()
  23. if l and '#' not in l:
  24. words = l.split()
  25. if len(words) == 2 and words[0] == 'linux_version':
  26. yield {'linux_version': words[1]}
  27. elif len(words) == 2:
  28. yield {'lang': words[0], 'sym': words[1]}
  29. elif len(words) == 1:
  30. yield {'lang': 'C', 'sym': words[0]}
  31. else:
  32. raise Exception('unsupported exports line: ' + l)
  33. def to_c(sym):
  34. symbols = collections.deque(sym.split('::'))
  35. c_prefixes = [ # demangle prefixes for c++ symbols
  36. '_ZN', # namespace
  37. '_ZTIN', # typeinfo for
  38. '_ZTSN', # typeinfo name for
  39. '_ZTTN', # VTT for
  40. '_ZTVN', # vtable for
  41. '_ZNK', # const methods
  42. ]
  43. c_sym = ''
  44. while symbols:
  45. s = symbols.popleft()
  46. if s == '*':
  47. c_sym += '*'
  48. break
  49. if '*' in s and len(s) > 1:
  50. raise Exception('Unsupported format, cannot guess length of symbol: ' + s)
  51. c_sym += str(len(s)) + s
  52. if symbols:
  53. raise Exception('Unsupported format: ' + sym)
  54. if c_sym[-1] != '*':
  55. c_sym += 'E*'
  56. return ['{prefix}{sym}'.format(prefix=prefix, sym=c_sym) for prefix in c_prefixes]
  57. def fix_darwin_param(ex):
  58. for item in ex:
  59. if item.get('linux_version'):
  60. continue
  61. if item['lang'] == 'C':
  62. yield '-Wl,-exported_symbol,_' + item['sym']
  63. elif item['lang'] == 'C++':
  64. for sym in to_c(item['sym']):
  65. yield '-Wl,-exported_symbol,_' + sym
  66. else:
  67. raise Exception('unsupported lang: ' + item['lang'])
  68. def fix_gnu_param(arch, ex):
  69. d = collections.defaultdict(list)
  70. version = None
  71. for item in ex:
  72. if item.get('linux_version'):
  73. if not version:
  74. version = item.get('linux_version')
  75. else:
  76. raise Exception('More than one linux_version defined')
  77. elif item['lang'] == 'C++':
  78. d['C'].extend(to_c(item['sym']))
  79. else:
  80. d[item['lang']].append(item['sym'])
  81. with tempfile.NamedTemporaryFile(mode='wt', delete=False) as f:
  82. if version:
  83. f.write('{} {{\nglobal:\n'.format(version))
  84. else:
  85. f.write('{\nglobal:\n')
  86. for k, v in d.items():
  87. f.write(' extern "' + k + '" {\n')
  88. for x in v:
  89. f.write(' ' + x + ';\n')
  90. f.write(' };\n')
  91. f.write('local: *;\n};\n')
  92. ret = ['-Wl,--version-script=' + f.name]
  93. if arch == 'ANDROID':
  94. ret += ['-Wl,--export-dynamic']
  95. return ret
  96. def fix_windows_param(ex):
  97. with tempfile.NamedTemporaryFile(delete=False) as def_file:
  98. exports = []
  99. for item in ex:
  100. if item.get('lang') == 'C':
  101. exports.append(item.get('sym'))
  102. def_file.write('EXPORTS\n')
  103. for export in exports:
  104. def_file.write(' {}\n'.format(export))
  105. return ['/DEF:{}'.format(def_file.name)]
  106. CUDA_LIBRARIES = {
  107. '-lcublas_static': '-lcublas',
  108. '-lcublasLt_static': '-lcublasLt',
  109. '-lcudart_static': '-lcudart',
  110. '-lcudnn_static': '-lcudnn',
  111. '-lcufft_static_nocallback': '-lcufft',
  112. '-lcurand_static': '-lcurand',
  113. '-lcusolver_static': '-lcusolver',
  114. '-lcusparse_static': '-lcusparse',
  115. '-lmyelin_compiler_static': '-lmyelin',
  116. '-lmyelin_executor_static': '-lnvcaffe_parser',
  117. '-lmyelin_pattern_library_static': '',
  118. '-lmyelin_pattern_runtime_static': '',
  119. '-lnvinfer_static': '-lnvinfer',
  120. '-lnvinfer_plugin_static': '-lnvinfer_plugin',
  121. '-lnvonnxparser_static': '-lnvonnxparser',
  122. '-lnvparsers_static': '-lnvparsers',
  123. }
  124. def fix_cmd(arch, c):
  125. if arch == 'WINDOWS':
  126. prefix = '/DEF:'
  127. f = fix_windows_param
  128. else:
  129. prefix = '-Wl,--version-script='
  130. if arch in ('DARWIN', 'IOS', 'IOSSIM'):
  131. f = fix_darwin_param
  132. else:
  133. f = lambda x: fix_gnu_param(arch, x)
  134. def do_fix(p):
  135. if p.startswith(prefix) and p.endswith('.exports'):
  136. fname = p[len(prefix) :]
  137. return list(f(list(parse_export_file(fname))))
  138. if p.endswith('.pkg.fake'):
  139. return []
  140. return [p]
  141. return sum((do_fix(x) for x in c), [])
  142. def fix_cmd_for_dynamic_cuda(cmd):
  143. flags = []
  144. for flag in cmd:
  145. if flag in CUDA_LIBRARIES:
  146. flags.append(CUDA_LIBRARIES[flag])
  147. else:
  148. flags.append(flag)
  149. return flags
  150. def parse_args(args):
  151. parser = optparse.OptionParser()
  152. parser.disable_interspersed_args()
  153. parser.add_option('--arch')
  154. parser.add_option('--target')
  155. parser.add_option('--soname')
  156. parser.add_option('--source-root')
  157. parser.add_option('--build-root')
  158. parser.add_option('--fix-elf')
  159. parser.add_option('--linker-output')
  160. parser.add_option('--dynamic-cuda', action='store_true')
  161. parser.add_option('--cuda-architectures',
  162. help='List of supported CUDA architectures, separated by ":" (e.g. "sm_52:compute_70:lto_90a"')
  163. parser.add_option('--nvprune-exe')
  164. parser.add_option('--objcopy-exe')
  165. parser.add_option('--whole-archive-peers', action='append')
  166. parser.add_option('--whole-archive-libs', action='append')
  167. parser.add_option('--custom-step')
  168. parser.add_option('--python')
  169. thinlto_cache.add_options(parser)
  170. return parser.parse_args(args)
  171. if __name__ == '__main__':
  172. args = sys.argv[1:]
  173. plugins = []
  174. if '--start-plugins' in args:
  175. ib = args.index('--start-plugins')
  176. ie = args.index('--end-plugins')
  177. plugins = args[ib + 1:ie]
  178. args = args[:ib] + args[ie + 1:]
  179. for p in plugins:
  180. res = subprocess.check_output([sys.executable, p] + args).decode().strip()
  181. if res:
  182. args = json.loads(res)
  183. opts, args = parse_args(args)
  184. assert opts.arch
  185. assert opts.target
  186. cmd = args
  187. cmd = fix_cmd(opts.arch, cmd)
  188. if opts.dynamic_cuda:
  189. cmd = fix_cmd_for_dynamic_cuda(cmd)
  190. else:
  191. cuda_manager = link_exe.CUDAManager(opts.cuda_architectures, opts.nvprune_exe)
  192. cmd = link_exe.process_cuda_libraries_by_nvprune(cmd, cuda_manager, opts.build_root)
  193. cmd = link_exe.process_cuda_libraries_by_objcopy(cmd, opts.build_root, opts.objcopy_exe)
  194. cmd = ProcessWholeArchiveOption(opts.arch, opts.whole_archive_peers, opts.whole_archive_libs).construct_cmd(cmd)
  195. thinlto_cache.preprocess(opts, cmd)
  196. if opts.custom_step:
  197. assert opts.python
  198. subprocess.check_call([opts.python] + [opts.custom_step] + cmd)
  199. if opts.linker_output:
  200. stdout = open(opts.linker_output, 'w')
  201. else:
  202. stdout = sys.stdout
  203. proc = subprocess.Popen(cmd, shell=False, stderr=sys.stderr, stdout=stdout)
  204. proc.communicate()
  205. thinlto_cache.postprocess(opts)
  206. if proc.returncode:
  207. print('linker has failed with retcode:', proc.returncode, file=sys.stderr)
  208. print('linker command:', shlex_join(cmd), file=sys.stderr)
  209. sys.exit(proc.returncode)
  210. if opts.fix_elf:
  211. cmd = [opts.fix_elf, opts.target]
  212. proc = subprocess.Popen(cmd, shell=False, stderr=sys.stderr, stdout=sys.stdout)
  213. proc.communicate()
  214. if proc.returncode:
  215. print('fix_elf has failed with retcode:', proc.returncode, file=sys.stderr)
  216. print('fix_elf command:', shlex_join(cmd), file=sys.stderr)
  217. sys.exit(proc.returncode)
  218. if opts.soname and opts.soname != opts.target:
  219. if os.path.exists(opts.soname):
  220. os.unlink(opts.soname)
  221. os.link(opts.target, opts.soname)
  222. # -----------------Test---------------- #
  223. def write_temp_file(content):
  224. import yatest.common as yc
  225. filename = yc.output_path('test.exports')
  226. with open(filename, 'w') as f:
  227. f.write(content)
  228. return filename
  229. def test_fix_cmd_darwin():
  230. export_file_content = """
  231. C++ geobase5::details::lookup_impl::*
  232. C++ geobase5::hardcoded_service
  233. """
  234. filename = write_temp_file(export_file_content)
  235. args = ['-Wl,--version-script={}'.format(filename)]
  236. assert fix_cmd('DARWIN', args) == [
  237. '-Wl,-exported_symbol,__ZN8geobase57details11lookup_impl*',
  238. '-Wl,-exported_symbol,__ZTIN8geobase57details11lookup_impl*',
  239. '-Wl,-exported_symbol,__ZTSN8geobase57details11lookup_impl*',
  240. '-Wl,-exported_symbol,__ZTTN8geobase57details11lookup_impl*',
  241. '-Wl,-exported_symbol,__ZTVN8geobase57details11lookup_impl*',
  242. '-Wl,-exported_symbol,__ZNK8geobase57details11lookup_impl*',
  243. '-Wl,-exported_symbol,__ZN8geobase517hardcoded_serviceE*',
  244. '-Wl,-exported_symbol,__ZTIN8geobase517hardcoded_serviceE*',
  245. '-Wl,-exported_symbol,__ZTSN8geobase517hardcoded_serviceE*',
  246. '-Wl,-exported_symbol,__ZTTN8geobase517hardcoded_serviceE*',
  247. '-Wl,-exported_symbol,__ZTVN8geobase517hardcoded_serviceE*',
  248. '-Wl,-exported_symbol,__ZNK8geobase517hardcoded_serviceE*',
  249. ]
  250. def run_fix_gnu_param(export_file_content):
  251. filename = write_temp_file(export_file_content)
  252. result = fix_gnu_param('LINUX', list(parse_export_file(filename)))[0]
  253. version_script_path = result[len('-Wl,--version-script=') :]
  254. with open(version_script_path) as f:
  255. content = f.read()
  256. return content
  257. def test_fix_gnu_param():
  258. export_file_content = """
  259. C++ geobase5::details::lookup_impl::*
  260. C getFactoryMap
  261. """
  262. assert (
  263. run_fix_gnu_param(export_file_content)
  264. == """{
  265. global:
  266. extern "C" {
  267. _ZN8geobase57details11lookup_impl*;
  268. _ZTIN8geobase57details11lookup_impl*;
  269. _ZTSN8geobase57details11lookup_impl*;
  270. _ZTTN8geobase57details11lookup_impl*;
  271. _ZTVN8geobase57details11lookup_impl*;
  272. _ZNK8geobase57details11lookup_impl*;
  273. getFactoryMap;
  274. };
  275. local: *;
  276. };
  277. """
  278. )
  279. def test_fix_gnu_param_with_linux_version():
  280. export_file_content = """
  281. C++ geobase5::details::lookup_impl::*
  282. linux_version ver1.0
  283. C getFactoryMap
  284. """
  285. assert (
  286. run_fix_gnu_param(export_file_content)
  287. == """ver1.0 {
  288. global:
  289. extern "C" {
  290. _ZN8geobase57details11lookup_impl*;
  291. _ZTIN8geobase57details11lookup_impl*;
  292. _ZTSN8geobase57details11lookup_impl*;
  293. _ZTTN8geobase57details11lookup_impl*;
  294. _ZTVN8geobase57details11lookup_impl*;
  295. _ZNK8geobase57details11lookup_impl*;
  296. getFactoryMap;
  297. };
  298. local: *;
  299. };
  300. """
  301. )