Clone of mesa.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

symbols-check.py 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env python
  2. import argparse
  3. import os
  4. import platform
  5. import subprocess
  6. # This list contains symbols that _might_ be exported for some platforms
  7. PLATFORM_SYMBOLS = [
  8. '__bss_end__',
  9. '__bss_start__',
  10. '__bss_start',
  11. '__end__',
  12. '_bss_end__',
  13. '_edata',
  14. '_end',
  15. '_fini',
  16. '_init',
  17. ]
  18. def get_symbols(nm, lib):
  19. '''
  20. List all the (non platform-specific) symbols exported by the library
  21. '''
  22. symbols = []
  23. platform_name = platform.system()
  24. output = subprocess.check_output([nm, '-gP', lib],
  25. stderr=open(os.devnull, 'w')).decode("ascii")
  26. for line in output.splitlines():
  27. fields = line.split()
  28. if len(fields) == 2 or fields[1] == 'U':
  29. continue
  30. symbol_name = fields[0]
  31. if platform_name == 'Linux':
  32. if symbol_name in PLATFORM_SYMBOLS:
  33. continue
  34. elif platform_name == 'Darwin':
  35. assert symbol_name[0] == '_'
  36. symbol_name = symbol_name[1:]
  37. symbols.append(symbol_name)
  38. return symbols
  39. def main():
  40. parser = argparse.ArgumentParser()
  41. parser.add_argument('--symbols-file',
  42. action='store',
  43. required=True,
  44. help='path to file containing symbols')
  45. parser.add_argument('--lib',
  46. action='store',
  47. required=True,
  48. help='path to library')
  49. parser.add_argument('--nm',
  50. action='store',
  51. required=True,
  52. help='path to binary (or name in $PATH)')
  53. args = parser.parse_args()
  54. try:
  55. lib_symbols = get_symbols(args.nm, args.lib)
  56. except:
  57. # We can't run this test, but we haven't technically failed it either
  58. # Return the GNU "skip" error code
  59. exit(77)
  60. mandatory_symbols = []
  61. optional_symbols = []
  62. with open(args.symbols_file) as symbols_file:
  63. qualifier_optional = '(optional)'
  64. for line in symbols_file.readlines():
  65. # Strip comments
  66. line = line.split('#')[0]
  67. line = line.strip()
  68. if not line:
  69. continue
  70. # Line format:
  71. # [qualifier] symbol
  72. qualifier = None
  73. symbol = None
  74. fields = line.split()
  75. if len(fields) == 1:
  76. symbol = fields[0]
  77. elif len(fields) == 2:
  78. qualifier = fields[0]
  79. symbol = fields[1]
  80. else:
  81. print(args.symbols_file + ': invalid format: ' + line)
  82. exit(1)
  83. # The only supported qualifier is 'optional', which means the
  84. # symbol doesn't have to be exported by the library
  85. if qualifier and not qualifier == qualifier_optional:
  86. print(args.symbols_file + ': invalid qualifier: ' + qualifier)
  87. exit(1)
  88. if qualifier == qualifier_optional:
  89. optional_symbols.append(symbol)
  90. else:
  91. mandatory_symbols.append(symbol)
  92. unknown_symbols = []
  93. for symbol in lib_symbols:
  94. if symbol in mandatory_symbols:
  95. continue
  96. if symbol in optional_symbols:
  97. continue
  98. if symbol[:2] == '_Z':
  99. # Ignore random C++ symbols
  100. #TODO: figure out if there's any way to avoid exporting them in the first place
  101. continue
  102. unknown_symbols.append(symbol)
  103. missing_symbols = [
  104. sym for sym in mandatory_symbols if sym not in lib_symbols
  105. ]
  106. for symbol in unknown_symbols:
  107. print(args.lib + ': unknown symbol exported: ' + symbol)
  108. for symbol in missing_symbols:
  109. print(args.lib + ': missing symbol: ' + symbol)
  110. if unknown_symbols or missing_symbols:
  111. exit(1)
  112. exit(0)
  113. if __name__ == '__main__':
  114. main()