protoherobora.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #!/usr/bin/env python3
  2. import subprocess
  3. import os
  4. import sys
  5. import json
  6. # TODO: dedup
  7. def is_cmdfile_arg(arg):
  8. # type: (str) -> bool
  9. return arg.startswith('@')
  10. def cmdfile_path(arg):
  11. # type: (str) -> str
  12. return arg[1:]
  13. def read_from_command_file(arg):
  14. # type: (str) -> list[str]
  15. with open(arg) as afile:
  16. return afile.read().splitlines()
  17. def skip_markers(args):
  18. # type: (list[str]) -> list[str]
  19. res = []
  20. for arg in args:
  21. if arg == '--ya-start-command-file' or arg == '--ya-end-command-file':
  22. continue
  23. res.append(arg)
  24. return res
  25. def iter_args(
  26. args, # type: list[str]
  27. ):
  28. for arg in args:
  29. if not is_cmdfile_arg(arg):
  30. if arg == '--ya-start-command-file' or arg == '--ya-end-command-file':
  31. continue
  32. yield arg
  33. else:
  34. for cmdfile_arg in read_from_command_file(cmdfile_path(arg)):
  35. yield cmdfile_arg
  36. def get_args(args):
  37. # type: (list[str]) -> list[str]
  38. return list(iter_args(args))
  39. # end TODO
  40. def run(*args):
  41. return subprocess.check_output(list(args), shell=False).strip()
  42. def gen_renames_1(d):
  43. for l in d.split('\n'):
  44. l = l.strip()
  45. if ' ' in l:
  46. yield l.split(' ')[-1]
  47. def gen_renames_2(p, d):
  48. for s in gen_renames_1(d):
  49. """
  50. Since clang-17, the option -fsanitize-address-globals-dead-stripping
  51. has been enabled by default. Due to this, we have broken optimization
  52. that merges calls to the `asan.module_ctor` function, as we are renaming
  53. symbols with a prefix of 'py2_'. When this flag is enabled, and
  54. the functions are not merged, false-positive ODR (One Definition Rule)
  55. violations occur on objects such as `typeinfo std::exception`, because
  56. the runtime is trying to handle global objects that have already been handled.
  57. """
  58. if 'asan_globals' in s:
  59. continue
  60. yield s + ' ' + p + s
  61. def gen_renames(p, d):
  62. return '\n'.join(gen_renames_2(p, d)).strip() + '\n'
  63. def rename_syms(where, ret, libs):
  64. p = 'py2_'
  65. # join libs
  66. run(where + 'llvm-ar', 'qcL', ret, *libs)
  67. # find symbols to rename
  68. syms = run(where + 'llvm-nm', '--extern-only', '--defined-only', '-A', ret)
  69. # prepare rename plan
  70. renames = gen_renames(p, syms)
  71. with open('syms', 'w') as f:
  72. f.write(renames)
  73. # rename symbols
  74. run(where + 'llvm-objcopy', '--redefine-syms=syms', ret)
  75. # back-rename some symbols
  76. args = [
  77. where + 'llvm-objcopy',
  78. '--redefine-sym',
  79. p + 'init_api_implementation=init6google8protobuf8internal19_api_implementation',
  80. '--redefine-sym',
  81. p + 'init_message=init6google8protobuf5pyext8_message',
  82. '--redefine-sym',
  83. p + 'init6google8protobuf8internal19_api_implementation=init6google8protobuf8internal19_api_implementation',
  84. '--redefine-sym',
  85. p + 'init6google8protobuf5pyext8_message=init6google8protobuf5pyext8_message',
  86. '--redefine-sym',
  87. p + '_init6google8protobuf8internal19_api_implementation=_init6google8protobuf8internal19_api_implementation',
  88. '--redefine-sym',
  89. p + '_init6google8protobuf5pyext8_message=_init6google8protobuf5pyext8_message',
  90. ret,
  91. ]
  92. run(*args)
  93. return ret
  94. def find_lld(args):
  95. for x in args:
  96. if 'lld-link' in x:
  97. return x
  98. raise IndexError()
  99. def fix_py2(cmd, have_comand_files=False, prefix='lib', suffix='a'):
  100. args = cmd
  101. if have_comand_files:
  102. args = get_args(cmd)
  103. if 'protobuf_old' not in str(args):
  104. return cmd
  105. py2_libs = [prefix + 'contrib-libs-protobuf_old.' + suffix, prefix + 'pypython-protobuf-py2.' + suffix]
  106. def need_rename(x):
  107. for v in py2_libs:
  108. if v in x:
  109. return True
  110. return False
  111. old = []
  112. lib = []
  113. try:
  114. where = os.path.dirname(cmd[cmd.index('--objcopy-exe') + 1]) + '/'
  115. except ValueError:
  116. where = os.path.dirname(find_lld(cmd)) + '/'
  117. for x in args:
  118. if need_rename(x):
  119. lib.append(x)
  120. else:
  121. old.append(x)
  122. name = rename_syms(where, 'libprotoherobora.' + suffix, lib)
  123. if not have_comand_files:
  124. return old + [name]
  125. for file in cmd:
  126. if is_cmdfile_arg(file):
  127. cmd_file_path = cmdfile_path(file)
  128. args = read_from_command_file(cmd_file_path)
  129. if not 'protobuf_old' in str(args):
  130. continue
  131. with open(cmd_file_path, 'w') as afile:
  132. for arg in args:
  133. if not need_rename(arg):
  134. afile.write(arg + '\n')
  135. afile.write(name)
  136. return cmd
  137. if __name__ == '__main__':
  138. args = sys.argv[1:]
  139. if 'lld-link' in str(args):
  140. cmd = fix_py2(args, have_comand_files=True, prefix='', suffix='lib')
  141. else:
  142. cmd = fix_py2(args)
  143. sys.stdout.write(json.dumps(cmd))