show_mod_init_func.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/env python
  2. # Copyright 2016 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """
  6. Prints the contents of the __DATA,__mod_init_func section of a Mach-O image.
  7. Usage:
  8. tools/mac/show_mod_init_func.py out/gn/Chromium\ Framework.unstripped
  9. This is meant to be used on a Mach-O executable. If a dSYM is present, use
  10. dump-static-initializers.py instead.
  11. """
  12. from __future__ import print_function
  13. import optparse
  14. import os
  15. import subprocess
  16. import sys
  17. def ShowModuleInitializers(binary, xcode_path):
  18. """Gathers the module initializers for |binary| and symbolizes the addresses.
  19. """
  20. initializers = GetModuleInitializers(binary, xcode_path)
  21. if not initializers:
  22. # atos will do work even if there are no addresses, so bail early.
  23. return
  24. symbols = SymbolizeAddresses(binary, initializers, xcode_path)
  25. print(binary)
  26. for initializer in zip(initializers, symbols):
  27. print('%s @ %s' % initializer)
  28. def GetModuleInitializers(binary, xcode_path):
  29. """Parses the __DATA,__mod_init_func segment of |binary| and returns a list
  30. of string hexadecimal addresses of the module initializers.
  31. """
  32. if xcode_path:
  33. otool_path = os.path.join(xcode_path, 'Contents', 'Developer',
  34. 'Toolchains', 'XcodeDefault.xctoolchain', 'usr', 'bin', 'otool')
  35. else:
  36. otool_path = 'otool'
  37. # The -v flag will display the addresses in a usable form (as opposed to
  38. # just its on-disk little-endian byte representation).
  39. otool = [otool_path, '-v', '-s', '__DATA', '__mod_init_func', binary]
  40. lines = subprocess.check_output(otool).strip().split('\n')
  41. # Skip the first two header lines and then get the address of the
  42. # initializer in the second column. The first address is the address
  43. # of the initializer pointer.
  44. # out/gn/Chromium Framework.unstripped:
  45. # Contents of (__DATA,__mod_init_func) section
  46. # 0x0000000008761498 0x000000000385d120
  47. return [line.split(' ')[1] for line in lines[2:]]
  48. def SymbolizeAddresses(binary, addresses, xcode_path):
  49. """Given a |binary| and a list of |addresses|, symbolizes them using atos.
  50. """
  51. if xcode_path:
  52. atos_path = os.path.join(xcode_path, 'Contents', 'Developer', 'usr',
  53. 'bin', 'atos')
  54. else:
  55. atos_path = 'atos'
  56. atos = [atos_path, '-o', binary] + addresses
  57. lines = subprocess.check_output(atos).strip().split('\n')
  58. return lines
  59. def Main():
  60. parser = optparse.OptionParser(usage='%prog filename')
  61. parser.add_option(
  62. '--xcode-path',
  63. default=None,
  64. help='Optional custom path to xcode binaries. By default, commands such '
  65. 'as `otool` will be run as `/usr/bin/otool` which only works '
  66. 'if there is a system-wide install of Xcode.')
  67. opts, args = parser.parse_args()
  68. if len(args) != 1:
  69. parser.error('missing binary filename')
  70. return 1
  71. ShowModuleInitializers(args[0], opts.xcode_path)
  72. return 0
  73. if __name__ == '__main__':
  74. sys.exit(Main())