check-wx-segment.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #!/usr/bin/python3
  2. # Check ELF program headers for WX segments.
  3. # Copyright (C) 2020-2026 Free Software Foundation, Inc.
  4. # This file is part of the GNU C Library.
  5. #
  6. # The GNU C Library is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU Lesser General Public
  8. # License as published by the Free Software Foundation; either
  9. # version 2.1 of the License, or (at your option) any later version.
  10. #
  11. # The GNU C Library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. # Lesser General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public
  17. # License along with the GNU C Library; if not, see
  18. # <https://www.gnu.org/licenses/>.
  19. """Check that the program headers do not contain write-exec segments."""
  20. import argparse
  21. import os.path
  22. import re
  23. import sys
  24. # Regular expression to extract the RWE flags field. The
  25. # address/offset columns have varying width.
  26. RE_LOAD = re.compile(
  27. r'^ LOAD +(?:0x[0-9a-fA-F]+ +){5}([R ][W ][ E]) +0x[0-9a-fA-F]+\n\Z')
  28. def process_file(path, inp, xfail):
  29. """Analyze one input file."""
  30. errors = 0
  31. for line in inp:
  32. error = None
  33. if line.startswith(' LOAD '):
  34. match = RE_LOAD.match(line)
  35. if match is None:
  36. error = 'Invalid LOAD line'
  37. else:
  38. flags, = match.groups()
  39. if 'W' in flags and 'E' in flags:
  40. if xfail:
  41. print('{}: warning: WX segment (as expected)'.format(
  42. path))
  43. else:
  44. error = 'WX segment'
  45. if error is not None:
  46. print('{}: error: {}: {!r}'.format(path, error, line.strip()))
  47. errors += 1
  48. if xfail and errors == 0:
  49. print('{}: warning: missing expected WX segment'.format(path))
  50. return errors
  51. def main():
  52. """The main entry point."""
  53. parser = argparse.ArgumentParser(description=__doc__)
  54. parser.add_argument('--xfail',
  55. help='Mark input files as XFAILed ("*" for all)',
  56. type=str, default='')
  57. parser.add_argument('phdrs',
  58. help='Files containing readelf -Wl output',
  59. nargs='*')
  60. opts = parser.parse_args(sys.argv)
  61. xfails = set(opts.xfail.split(' '))
  62. xfails_all = opts.xfail.strip() == '*'
  63. errors = 0
  64. for path in opts.phdrs:
  65. xfail = ((os.path.basename(path) + '.phdrs') in xfails
  66. or xfails_all)
  67. with open(path) as inp:
  68. errors += process_file(path, inp, xfail)
  69. if errors > 0:
  70. sys.exit(1)
  71. if __name__ == '__main__':
  72. main()