]>
Commit | Line | Data |
---|---|---|
30035d67 FW |
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 | ||
20 | import argparse | |
21 | import enum | |
22 | import sys | |
23 | ||
24 | import glibcelf | |
25 | import glibcextract | |
26 | ||
27 | errors_encountered = 0 | |
28 | ||
29 | def 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. | |
36 | expected_constant_prefixes = tuple( | |
37 | 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split()) | |
38 | ||
39 | def 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 | ||
46 | def 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 | ||
53 | def 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 | ||
79 | def 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 | ||
103 | def 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>. | |
114 | glibcelf_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. | |
123 | glibcelf_skipped_constants = frozenset( | |
124 | [e[0] for e in glibcelf_skipped_aliases]) | frozenset(""" | |
125 | DT_AARCH64_NUM | |
126 | DT_ADDRNUM | |
127 | DT_ADDRRNGHI | |
128 | DT_ADDRRNGLO | |
129 | DT_ALPHA_NUM | |
130 | DT_ENCODING | |
131 | DT_EXTRANUM | |
132 | DT_HIOS | |
133 | DT_HIPROC | |
134 | DT_IA_64_NUM | |
135 | DT_LOOS | |
136 | DT_LOPROC | |
137 | DT_MIPS_NUM | |
138 | DT_NUM | |
139 | DT_PPC64_NUM | |
140 | DT_PPC_NUM | |
141 | DT_PROCNUM | |
142 | DT_SPARC_NUM | |
143 | DT_VALNUM | |
144 | DT_VALRNGHI | |
145 | DT_VALRNGLO | |
146 | DT_VERSIONTAGNUM | |
147 | ELFCLASSNUM | |
148 | ELFDATANUM | |
149 | ET_HIOS | |
150 | ET_HIPROC | |
151 | ET_LOOS | |
152 | ET_LOPROC | |
153 | ET_NUM | |
154 | PF_MASKOS | |
155 | PF_MASKPROC | |
156 | PT_HIOS | |
157 | PT_HIPROC | |
158 | PT_HISUNW | |
159 | PT_LOOS | |
160 | PT_LOPROC | |
161 | PT_LOSUNW | |
162 | SHF_MASKOS | |
163 | SHF_MASKPROC | |
164 | SHN_HIOS | |
165 | SHN_HIPROC | |
166 | SHN_HIRESERVE | |
167 | SHN_LOOS | |
168 | SHN_LOPROC | |
169 | SHN_LORESERVE | |
170 | SHT_HIOS | |
171 | SHT_HIPROC | |
172 | SHT_HIPROC | |
173 | SHT_HISUNW | |
174 | SHT_HIUSER | |
175 | SHT_LOOS | |
176 | SHT_LOPROC | |
177 | SHT_LOSUNW | |
178 | SHT_LOUSER | |
179 | SHT_NUM | |
180 | STB_HIOS | |
181 | STB_HIPROC | |
182 | STB_LOOS | |
183 | STB_LOPROC | |
184 | STB_NUM | |
185 | STT_HIOS | |
186 | STT_HIPROC | |
187 | STT_LOOS | |
188 | STT_LOPROC | |
189 | STT_NUM | |
190 | """.strip().split()) | |
191 | ||
192 | def 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 | ||
243 | def 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 | ||
259 | if __name__ == '__main__': | |
260 | main() |