1#!/usr/bin/python3 2# Verify scripts/glibcelf.py contents against elf/elf.h. 3# Copyright (C) 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 20import argparse 21import enum 22import sys 23 24import glibcelf 25import glibcextract 26 27errors_encountered = 0 28 29def error(message): 30 global errors_encountered 31 sys.stdout.write('error: {}\n'.format(message)) 32 errors_encountered += 1 33 34# The enum constants in glibcelf are expected to have exactly these 35# prefixes. 36expected_constant_prefixes = tuple( 37 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split()) 38 39def find_constant_prefix(name): 40 """Returns a matching prefix from expected_constant_prefixes or None.""" 41 for prefix in expected_constant_prefixes: 42 if name.startswith(prefix): 43 return prefix 44 return None 45 46def find_enum_types(): 47 """A generator for OpenIntEnum and IntFlag classes in glibcelf.""" 48 for obj in vars(glibcelf).values(): 49 if isinstance(obj, type) and obj.__bases__[0] in ( 50 glibcelf._OpenIntEnum, enum.Enum, enum.IntFlag): 51 yield obj 52 53def check_duplicates(): 54 """Verifies that enum types do not have duplicate values. 55 56 Different types must have different member names, too. 57 58 """ 59 global_seen = {} 60 for typ in find_enum_types(): 61 seen = {} 62 last = None 63 for (name, e) in typ.__members__.items(): 64 if e.value in seen: 65 error('{} has {}={} and {}={}'.format( 66 typ, seen[e.value], e.value, name, e.value)) 67 last = e 68 else: 69 seen[e.value] = name 70 if last is not None and last.value > e.value: 71 error('{} has {}={} after {}={}'.format( 72 typ, name, e.value, last.name, last.value)) 73 if name in global_seen: 74 error('{} used in {} and {}'.format( 75 name, global_seen[name], typ)) 76 else: 77 global_seen[name] = typ 78 79def check_constant_prefixes(): 80 """Check that the constant prefixes match expected_constant_prefixes.""" 81 seen = set() 82 for typ in find_enum_types(): 83 typ_prefix = None 84 for val in typ: 85 prefix = find_constant_prefix(val.name) 86 if prefix is None: 87 error('constant {!r} for {} has unknown prefix'.format( 88 val, typ)) 89 break 90 elif typ_prefix is None: 91 typ_prefix = prefix 92 seen.add(typ_prefix) 93 elif prefix != typ_prefix: 94 error('prefix {!r} for constant {!r}, expected {!r}'.format( 95 prefix, val, typ_prefix)) 96 if typ_prefix is None: 97 error('empty enum type {}'.format(typ)) 98 99 for prefix in sorted(set(expected_constant_prefixes) - seen): 100 error('missing constant prefix {!r}'.format(prefix)) 101 # Reverse difference is already covered inside the loop. 102 103def find_elf_h_constants(cc): 104 """Returns a dictionary of relevant constants from <elf.h>.""" 105 return glibcextract.compute_macro_consts( 106 source_text='#include <elf.h>', 107 cc=cc, 108 macro_re='|'.join( 109 prefix + '.*' for prefix in expected_constant_prefixes)) 110 111# The first part of the pair is a name of an <elf.h> constant that is 112# dropped from glibcelf. The second part is the constant as it is 113# used in <elf.h>. 114glibcelf_skipped_aliases = ( 115 ('EM_ARC_A5', 'EM_ARC_COMPACT'), 116 ('PF_PARISC_SBP', 'PF_HP_SBP') 117) 118 119# Constants that provide little value and are not included in 120# glibcelf: *LO*/*HI* range constants, *NUM constants counting the 121# number of constants. Also includes the alias names from 122# glibcelf_skipped_aliases. 123glibcelf_skipped_constants = frozenset( 124 [e[0] for e in glibcelf_skipped_aliases]) | frozenset(""" 125DT_AARCH64_NUM 126DT_ADDRNUM 127DT_ADDRRNGHI 128DT_ADDRRNGLO 129DT_ALPHA_NUM 130DT_ENCODING 131DT_EXTRANUM 132DT_HIOS 133DT_HIPROC 134DT_IA_64_NUM 135DT_LOOS 136DT_LOPROC 137DT_MIPS_NUM 138DT_NUM 139DT_PPC64_NUM 140DT_PPC_NUM 141DT_PROCNUM 142DT_SPARC_NUM 143DT_VALNUM 144DT_VALRNGHI 145DT_VALRNGLO 146DT_VERSIONTAGNUM 147ELFCLASSNUM 148ELFDATANUM 149ET_HIOS 150ET_HIPROC 151ET_LOOS 152ET_LOPROC 153ET_NUM 154PF_MASKOS 155PF_MASKPROC 156PT_HIOS 157PT_HIPROC 158PT_HISUNW 159PT_LOOS 160PT_LOPROC 161PT_LOSUNW 162SHF_MASKOS 163SHF_MASKPROC 164SHN_HIOS 165SHN_HIPROC 166SHN_HIRESERVE 167SHN_LOOS 168SHN_LOPROC 169SHN_LORESERVE 170SHT_HIOS 171SHT_HIPROC 172SHT_HIPROC 173SHT_HISUNW 174SHT_HIUSER 175SHT_LOOS 176SHT_LOPROC 177SHT_LOSUNW 178SHT_LOUSER 179SHT_NUM 180STB_HIOS 181STB_HIPROC 182STB_LOOS 183STB_LOPROC 184STB_NUM 185STT_HIOS 186STT_HIPROC 187STT_LOOS 188STT_LOPROC 189STT_NUM 190""".strip().split()) 191 192def check_constant_values(cc): 193 """Checks the values of <elf.h> constants against glibcelf.""" 194 195 glibcelf_constants = { 196 e.name: e for typ in find_enum_types() for e in typ} 197 elf_h_constants = find_elf_h_constants(cc=cc) 198 199 missing_in_glibcelf = (set(elf_h_constants) - set(glibcelf_constants) 200 - glibcelf_skipped_constants) 201 for name in sorted(missing_in_glibcelf): 202 error('constant {} is missing from glibcelf'.format(name)) 203 204 unexpected_in_glibcelf = \ 205 set(glibcelf_constants) & glibcelf_skipped_constants 206 for name in sorted(unexpected_in_glibcelf): 207 error('constant {} is supposed to be filtered from glibcelf'.format( 208 name)) 209 210 missing_in_elf_h = set(glibcelf_constants) - set(elf_h_constants) 211 for name in sorted(missing_in_elf_h): 212 error('constant {} is missing from <elf.h>'.format(name)) 213 214 expected_in_elf_h = glibcelf_skipped_constants - set(elf_h_constants) 215 for name in expected_in_elf_h: 216 error('filtered constant {} is missing from <elf.h>'.format(name)) 217 218 for alias_name, name_in_glibcelf in glibcelf_skipped_aliases: 219 if name_in_glibcelf not in glibcelf_constants: 220 error('alias value {} for {} not in glibcelf'.format( 221 name_in_glibcelf, alias_name)) 222 elif (int(elf_h_constants[alias_name]) 223 != glibcelf_constants[name_in_glibcelf].value): 224 error('<elf.h> has {}={}, glibcelf has {}={}'.format( 225 alias_name, elf_h_constants[alias_name], 226 name_in_glibcelf, glibcelf_constants[name_in_glibcelf])) 227 228 # Check for value mismatches: 229 for name in sorted(set(glibcelf_constants) & set(elf_h_constants)): 230 glibcelf_value = glibcelf_constants[name].value 231 elf_h_value = int(elf_h_constants[name]) 232 # On 32-bit architectures <elf.h> as some constants that are 233 # parsed as signed, while they are unsigned in glibcelf. So 234 # far, this only affects some flag constants, so special-case 235 # them here. 236 if (glibcelf_value != elf_h_value 237 and not (isinstance(glibcelf_constants[name], enum.IntFlag) 238 and glibcelf_value == 1 << 31 239 and elf_h_value == -(1 << 31))): 240 error('{}: glibcelf has {!r}, <elf.h> has {!r}'.format( 241 name, glibcelf_value, elf_h_value)) 242 243def main(): 244 """The main entry point.""" 245 parser = argparse.ArgumentParser( 246 description="Check glibcelf.py and elf.h against each other.") 247 parser.add_argument('--cc', metavar='CC', 248 help='C compiler (including options) to use') 249 args = parser.parse_args() 250 251 check_duplicates() 252 check_constant_prefixes() 253 check_constant_values(cc=args.cc) 254 255 if errors_encountered > 0: 256 print("note: errors encountered:", errors_encountered) 257 sys.exit(1) 258 259if __name__ == '__main__': 260 main() 261