executables.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. from __future__ import absolute_import
  5. import os
  6. import struct
  7. import subprocess
  8. from mozpack.errors import errors
  9. MACHO_SIGNATURES = [
  10. 0xfeedface, # mach-o 32-bits big endian
  11. 0xcefaedfe, # mach-o 32-bits little endian
  12. 0xfeedfacf, # mach-o 64-bits big endian
  13. 0xcffaedfe, # mach-o 64-bits little endian
  14. ]
  15. FAT_SIGNATURE = 0xcafebabe # mach-o FAT binary
  16. ELF_SIGNATURE = 0x7f454c46 # Elf binary
  17. UNKNOWN = 0
  18. MACHO = 1
  19. ELF = 2
  20. def get_type(path):
  21. '''
  22. Check the signature of the give file and returns what kind of executable
  23. matches.
  24. '''
  25. with open(path, 'rb') as f:
  26. signature = f.read(4)
  27. if len(signature) < 4:
  28. return UNKNOWN
  29. signature = struct.unpack('>L', signature)[0]
  30. if signature == ELF_SIGNATURE:
  31. return ELF
  32. if signature in MACHO_SIGNATURES:
  33. return MACHO
  34. if signature != FAT_SIGNATURE:
  35. return UNKNOWN
  36. # We have to sanity check the second four bytes, because Java class
  37. # files use the same magic number as Mach-O fat binaries.
  38. # This logic is adapted from file(1), which says that Mach-O uses
  39. # these bytes to count the number of architectures within, while
  40. # Java uses it for a version number. Conveniently, there are only
  41. # 18 labelled Mach-O architectures, and Java's first released
  42. # class format used the version 43.0.
  43. num = f.read(4)
  44. if len(num) < 4:
  45. return UNKNOWN
  46. num = struct.unpack('>L', num)[0]
  47. if num < 20:
  48. return MACHO
  49. return UNKNOWN
  50. def is_executable(path):
  51. '''
  52. Return whether a given file path points to an executable or a library,
  53. where an executable or library is identified by:
  54. - the file extension on OS/2 and WINNT
  55. - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD,
  56. Solaris)
  57. As this function is intended for use to choose between the ExecutableFile
  58. and File classes in FileFinder, and choosing ExecutableFile only matters
  59. on OS/2, OS/X, ELF and WINNT (in GCC build) systems, we don't bother
  60. detecting other kind of executables.
  61. '''
  62. from buildconfig import substs
  63. if not os.path.exists(path):
  64. return False
  65. if substs['OS_ARCH'] == 'WINNT':
  66. return path.lower().endswith((substs['DLL_SUFFIX'],
  67. substs['BIN_SUFFIX']))
  68. return get_type(path) != UNKNOWN
  69. def may_strip(path):
  70. '''
  71. Return whether strip() should be called
  72. '''
  73. from buildconfig import substs
  74. return not substs['PKG_SKIP_STRIP']
  75. def strip(path):
  76. '''
  77. Execute the STRIP command with STRIP_FLAGS on the given path.
  78. '''
  79. from buildconfig import substs
  80. strip = substs['STRIP']
  81. flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else []
  82. cmd = [strip] + flags + [path]
  83. if subprocess.call(cmd) != 0:
  84. errors.fatal('Error executing ' + ' '.join(cmd))
  85. def may_elfhack(path):
  86. '''
  87. Return whether elfhack() should be called
  88. '''
  89. # elfhack only supports libraries. We should check the ELF header for
  90. # the right flag, but checking the file extension works too.
  91. from buildconfig import substs
  92. return ('USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and
  93. path.endswith(substs['DLL_SUFFIX']) and
  94. 'COMPILE_ENVIRONMENT' in substs and substs['COMPILE_ENVIRONMENT'])
  95. def elfhack(path):
  96. '''
  97. Execute the elfhack command on the given path.
  98. '''
  99. from buildconfig import topobjdir
  100. cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path]
  101. if 'ELF_HACK_FLAGS' in os.environ:
  102. cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split()
  103. if subprocess.call(cmd) != 0:
  104. errors.fatal('Error executing ' + ' '.join(cmd))