1#!/usr/bin/python3 2# Check ELF program headers for WX segments. 3# Copyright (C) 2020-2022 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 20"""Check that the program headers do not contain write-exec segments.""" 21 22import argparse 23import os.path 24import re 25import sys 26 27# Regular expression to extract the RWE flags field. The 28# address/offset columns have varying width. 29RE_LOAD = re.compile( 30 r'^ LOAD +(?:0x[0-9a-fA-F]+ +){5}([R ][W ][ E]) +0x[0-9a-fA-F]+\n\Z') 31 32def process_file(path, inp, xfail): 33 """Analyze one input file.""" 34 35 errors = 0 36 for line in inp: 37 error = None 38 if line.startswith(' LOAD '): 39 match = RE_LOAD.match(line) 40 if match is None: 41 error = 'Invalid LOAD line' 42 else: 43 flags, = match.groups() 44 if 'W' in flags and 'E' in flags: 45 if xfail: 46 print('{}: warning: WX segment (as expected)'.format( 47 path)) 48 else: 49 error = 'WX segment' 50 51 if error is not None: 52 print('{}: error: {}: {!r}'.format(path, error, line.strip())) 53 errors += 1 54 55 if xfail and errors == 0: 56 print('{}: warning: missing expected WX segment'.format(path)) 57 return errors 58 59 60def main(): 61 """The main entry point.""" 62 parser = argparse.ArgumentParser(description=__doc__) 63 parser.add_argument('--xfail', 64 help='Mark input files as XFAILed ("*" for all)', 65 type=str, default='') 66 parser.add_argument('phdrs', 67 help='Files containing readelf -Wl output', 68 nargs='*') 69 opts = parser.parse_args(sys.argv) 70 71 xfails = set(opts.xfail.split(' ')) 72 xfails_all = opts.xfail.strip() == '*' 73 74 errors = 0 75 for path in opts.phdrs: 76 xfail = ((os.path.basename(path) + '.phdrs') in xfails 77 or xfails_all) 78 with open(path) as inp: 79 errors += process_file(path, inp, xfail) 80 if errors > 0: 81 sys.exit(1) 82 83 84if __name__ == '__main__': 85 main() 86