]>
Commit | Line | Data |
---|---|---|
30035d67 FW |
1 | #!/usr/bin/python3 |
2 | # Verify scripts/glibcelf.py contents against elf/elf.h. | |
6d7e8eda | 3 | # Copyright (C) 2022-2023 Free Software Foundation, Inc. |
30035d67 FW |
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 | |
30035d67 FW |
21 | import sys |
22 | ||
23 | import glibcelf | |
24 | import glibcextract | |
25 | ||
26 | errors_encountered = 0 | |
27 | ||
28 | def error(message): | |
29 | global errors_encountered | |
30 | sys.stdout.write('error: {}\n'.format(message)) | |
31 | errors_encountered += 1 | |
32 | ||
33 | # The enum constants in glibcelf are expected to have exactly these | |
34 | # prefixes. | |
35 | expected_constant_prefixes = tuple( | |
36 | 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split()) | |
37 | ||
38 | def find_constant_prefix(name): | |
39 | """Returns a matching prefix from expected_constant_prefixes or None.""" | |
40 | for prefix in expected_constant_prefixes: | |
41 | if name.startswith(prefix): | |
42 | return prefix | |
43 | return None | |
44 | ||
45 | def find_enum_types(): | |
46 | """A generator for OpenIntEnum and IntFlag classes in glibcelf.""" | |
340097d0 FW |
47 | classes = set((glibcelf._TypedConstant, glibcelf._IntConstant, |
48 | glibcelf._FlagConstant)) | |
30035d67 | 49 | for obj in vars(glibcelf).values(): |
340097d0 FW |
50 | if isinstance(obj, type) and obj not in classes \ |
51 | and obj.__bases__[0] in classes: | |
30035d67 FW |
52 | yield obj |
53 | ||
340097d0 FW |
54 | def check_basic(): |
55 | """Check basic functionality of the constant classes.""" | |
56 | ||
57 | if glibcelf.Pt.PT_NULL is not glibcelf.Pt(0): | |
58 | error('Pt(0) not interned') | |
59 | if glibcelf.Pt(17609) is glibcelf.Pt(17609): | |
60 | error('Pt(17609) unexpectedly interned') | |
61 | if glibcelf.Pt(17609) == glibcelf.Pt(17609): | |
62 | pass | |
63 | else: | |
64 | error('Pt(17609) equality') | |
65 | if glibcelf.Pt(17610) == glibcelf.Pt(17609): | |
66 | error('Pt(17610) equality') | |
67 | ||
68 | if str(glibcelf.Pt.PT_NULL) != 'PT_NULL': | |
69 | error('str(PT_NULL)') | |
70 | if str(glibcelf.Pt(17609)) != '17609': | |
71 | error('str(Pt(17609))') | |
72 | ||
73 | if repr(glibcelf.Pt.PT_NULL) != 'PT_NULL': | |
74 | error('repr(PT_NULL)') | |
75 | if repr(glibcelf.Pt(17609)) != 'Pt(17609)': | |
76 | error('repr(Pt(17609))') | |
77 | ||
78 | if glibcelf.Pt('PT_AARCH64_MEMTAG_MTE') \ | |
79 | is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE: | |
80 | error('PT_AARCH64_MEMTAG_MTE identity') | |
81 | if glibcelf.Pt(0x70000002) is glibcelf.Pt.PT_AARCH64_MEMTAG_MTE: | |
82 | error('Pt(0x70000002) identity') | |
83 | if glibcelf.PtAARCH64(0x70000002) is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE: | |
84 | error('PtAARCH64(0x70000002) identity') | |
85 | if glibcelf.Pt.PT_AARCH64_MEMTAG_MTE.short_name != 'AARCH64_MEMTAG_MTE': | |
86 | error('PT_AARCH64_MEMTAG_MTE short name') | |
87 | ||
88 | # Special cases for int-like Shn. | |
89 | if glibcelf.Shn(32) == glibcelf.Shn.SHN_XINDEX: | |
90 | error('Shn(32)') | |
91 | if glibcelf.Shn(32) + 0 != 32: | |
92 | error('Shn(32) + 0') | |
93 | if 32 in glibcelf.Shn: | |
94 | error('32 in Shn') | |
95 | if 0 not in glibcelf.Shn: | |
96 | error('0 not in Shn') | |
97 | ||
30035d67 FW |
98 | def check_duplicates(): |
99 | """Verifies that enum types do not have duplicate values. | |
100 | ||
101 | Different types must have different member names, too. | |
102 | ||
103 | """ | |
104 | global_seen = {} | |
105 | for typ in find_enum_types(): | |
106 | seen = {} | |
340097d0 | 107 | for (name, e) in typ.by_name.items(): |
30035d67 | 108 | if e.value in seen: |
340097d0 FW |
109 | other = seen[e.value] |
110 | # Value conflicts only count if they are between | |
111 | # the same base type. | |
112 | if e.__class__ is typ and other.__class__ is typ: | |
113 | error('{} has {}={} and {}={}'.format( | |
114 | typ, other, e.value, name, e.value)) | |
30035d67 FW |
115 | else: |
116 | seen[e.value] = name | |
30035d67 FW |
117 | if name in global_seen: |
118 | error('{} used in {} and {}'.format( | |
119 | name, global_seen[name], typ)) | |
120 | else: | |
121 | global_seen[name] = typ | |
122 | ||
123 | def check_constant_prefixes(): | |
124 | """Check that the constant prefixes match expected_constant_prefixes.""" | |
125 | seen = set() | |
126 | for typ in find_enum_types(): | |
127 | typ_prefix = None | |
340097d0 | 128 | for val in typ.by_name.values(): |
30035d67 FW |
129 | prefix = find_constant_prefix(val.name) |
130 | if prefix is None: | |
131 | error('constant {!r} for {} has unknown prefix'.format( | |
132 | val, typ)) | |
133 | break | |
134 | elif typ_prefix is None: | |
135 | typ_prefix = prefix | |
136 | seen.add(typ_prefix) | |
137 | elif prefix != typ_prefix: | |
138 | error('prefix {!r} for constant {!r}, expected {!r}'.format( | |
139 | prefix, val, typ_prefix)) | |
140 | if typ_prefix is None: | |
141 | error('empty enum type {}'.format(typ)) | |
142 | ||
143 | for prefix in sorted(set(expected_constant_prefixes) - seen): | |
144 | error('missing constant prefix {!r}'.format(prefix)) | |
145 | # Reverse difference is already covered inside the loop. | |
146 | ||
147 | def find_elf_h_constants(cc): | |
148 | """Returns a dictionary of relevant constants from <elf.h>.""" | |
149 | return glibcextract.compute_macro_consts( | |
150 | source_text='#include <elf.h>', | |
151 | cc=cc, | |
152 | macro_re='|'.join( | |
153 | prefix + '.*' for prefix in expected_constant_prefixes)) | |
154 | ||
155 | # The first part of the pair is a name of an <elf.h> constant that is | |
156 | # dropped from glibcelf. The second part is the constant as it is | |
157 | # used in <elf.h>. | |
158 | glibcelf_skipped_aliases = ( | |
159 | ('EM_ARC_A5', 'EM_ARC_COMPACT'), | |
30035d67 FW |
160 | ) |
161 | ||
162 | # Constants that provide little value and are not included in | |
163 | # glibcelf: *LO*/*HI* range constants, *NUM constants counting the | |
164 | # number of constants. Also includes the alias names from | |
165 | # glibcelf_skipped_aliases. | |
166 | glibcelf_skipped_constants = frozenset( | |
167 | [e[0] for e in glibcelf_skipped_aliases]) | frozenset(""" | |
168 | DT_AARCH64_NUM | |
169 | DT_ADDRNUM | |
170 | DT_ADDRRNGHI | |
171 | DT_ADDRRNGLO | |
172 | DT_ALPHA_NUM | |
173 | DT_ENCODING | |
174 | DT_EXTRANUM | |
175 | DT_HIOS | |
176 | DT_HIPROC | |
177 | DT_IA_64_NUM | |
178 | DT_LOOS | |
179 | DT_LOPROC | |
180 | DT_MIPS_NUM | |
181 | DT_NUM | |
182 | DT_PPC64_NUM | |
183 | DT_PPC_NUM | |
184 | DT_PROCNUM | |
185 | DT_SPARC_NUM | |
186 | DT_VALNUM | |
187 | DT_VALRNGHI | |
188 | DT_VALRNGLO | |
189 | DT_VERSIONTAGNUM | |
190 | ELFCLASSNUM | |
191 | ELFDATANUM | |
340097d0 | 192 | EM_NUM |
30035d67 FW |
193 | ET_HIOS |
194 | ET_HIPROC | |
195 | ET_LOOS | |
196 | ET_LOPROC | |
197 | ET_NUM | |
198 | PF_MASKOS | |
199 | PF_MASKPROC | |
200 | PT_HIOS | |
201 | PT_HIPROC | |
202 | PT_HISUNW | |
203 | PT_LOOS | |
204 | PT_LOPROC | |
205 | PT_LOSUNW | |
340097d0 | 206 | PT_NUM |
30035d67 FW |
207 | SHF_MASKOS |
208 | SHF_MASKPROC | |
209 | SHN_HIOS | |
210 | SHN_HIPROC | |
211 | SHN_HIRESERVE | |
212 | SHN_LOOS | |
213 | SHN_LOPROC | |
214 | SHN_LORESERVE | |
215 | SHT_HIOS | |
216 | SHT_HIPROC | |
217 | SHT_HIPROC | |
218 | SHT_HISUNW | |
219 | SHT_HIUSER | |
220 | SHT_LOOS | |
221 | SHT_LOPROC | |
222 | SHT_LOSUNW | |
223 | SHT_LOUSER | |
224 | SHT_NUM | |
225 | STB_HIOS | |
226 | STB_HIPROC | |
227 | STB_LOOS | |
228 | STB_LOPROC | |
229 | STB_NUM | |
230 | STT_HIOS | |
231 | STT_HIPROC | |
232 | STT_LOOS | |
233 | STT_LOPROC | |
234 | STT_NUM | |
235 | """.strip().split()) | |
236 | ||
237 | def check_constant_values(cc): | |
238 | """Checks the values of <elf.h> constants against glibcelf.""" | |
239 | ||
240 | glibcelf_constants = { | |
340097d0 | 241 | e.name: e for typ in find_enum_types() for e in typ.by_name.values()} |
30035d67 FW |
242 | elf_h_constants = find_elf_h_constants(cc=cc) |
243 | ||
244 | missing_in_glibcelf = (set(elf_h_constants) - set(glibcelf_constants) | |
245 | - glibcelf_skipped_constants) | |
246 | for name in sorted(missing_in_glibcelf): | |
247 | error('constant {} is missing from glibcelf'.format(name)) | |
248 | ||
249 | unexpected_in_glibcelf = \ | |
250 | set(glibcelf_constants) & glibcelf_skipped_constants | |
251 | for name in sorted(unexpected_in_glibcelf): | |
252 | error('constant {} is supposed to be filtered from glibcelf'.format( | |
253 | name)) | |
254 | ||
255 | missing_in_elf_h = set(glibcelf_constants) - set(elf_h_constants) | |
256 | for name in sorted(missing_in_elf_h): | |
257 | error('constant {} is missing from <elf.h>'.format(name)) | |
258 | ||
259 | expected_in_elf_h = glibcelf_skipped_constants - set(elf_h_constants) | |
260 | for name in expected_in_elf_h: | |
261 | error('filtered constant {} is missing from <elf.h>'.format(name)) | |
262 | ||
263 | for alias_name, name_in_glibcelf in glibcelf_skipped_aliases: | |
264 | if name_in_glibcelf not in glibcelf_constants: | |
265 | error('alias value {} for {} not in glibcelf'.format( | |
266 | name_in_glibcelf, alias_name)) | |
267 | elif (int(elf_h_constants[alias_name]) | |
268 | != glibcelf_constants[name_in_glibcelf].value): | |
269 | error('<elf.h> has {}={}, glibcelf has {}={}'.format( | |
270 | alias_name, elf_h_constants[alias_name], | |
271 | name_in_glibcelf, glibcelf_constants[name_in_glibcelf])) | |
272 | ||
273 | # Check for value mismatches: | |
274 | for name in sorted(set(glibcelf_constants) & set(elf_h_constants)): | |
275 | glibcelf_value = glibcelf_constants[name].value | |
276 | elf_h_value = int(elf_h_constants[name]) | |
340097d0 | 277 | # On 32-bit architectures <elf.h> has some constants that are |
30035d67 FW |
278 | # parsed as signed, while they are unsigned in glibcelf. So |
279 | # far, this only affects some flag constants, so special-case | |
280 | # them here. | |
281 | if (glibcelf_value != elf_h_value | |
340097d0 FW |
282 | and not (isinstance(glibcelf_constants[name], |
283 | glibcelf._FlagConstant) | |
30035d67 FW |
284 | and glibcelf_value == 1 << 31 |
285 | and elf_h_value == -(1 << 31))): | |
286 | error('{}: glibcelf has {!r}, <elf.h> has {!r}'.format( | |
287 | name, glibcelf_value, elf_h_value)) | |
288 | ||
bd13cb19 FW |
289 | def check_hashes(): |
290 | for name, expected_elf, expected_gnu in ( | |
291 | ('', 0, 0x1505), | |
292 | ('PPPPPPPPPPPP', 0, 0x9f105c45), | |
293 | ('GLIBC_2.0', 0xd696910, 0xf66c3dd5), | |
294 | ('GLIBC_2.34', 0x69691b4, 0xc3f3f90c), | |
295 | ('GLIBC_PRIVATE', 0x963cf85, 0x692a260)): | |
296 | for convert in (lambda x: x, lambda x: x.encode('UTF-8')): | |
297 | name = convert(name) | |
298 | actual_elf = glibcelf.elf_hash(name) | |
299 | if actual_elf != expected_elf: | |
300 | error('elf_hash({!r}): {:x} != 0x{:x}'.format( | |
301 | name, actual_elf, expected_elf)) | |
302 | actual_gnu = glibcelf.gnu_hash(name) | |
303 | if actual_gnu != expected_gnu: | |
304 | error('gnu_hash({!r}): {:x} != 0x{:x}'.format( | |
305 | name, actual_gnu, expected_gnu)) | |
306 | ||
30035d67 FW |
307 | def main(): |
308 | """The main entry point.""" | |
309 | parser = argparse.ArgumentParser( | |
310 | description="Check glibcelf.py and elf.h against each other.") | |
311 | parser.add_argument('--cc', metavar='CC', | |
312 | help='C compiler (including options) to use') | |
313 | args = parser.parse_args() | |
314 | ||
340097d0 | 315 | check_basic() |
30035d67 FW |
316 | check_duplicates() |
317 | check_constant_prefixes() | |
318 | check_constant_values(cc=args.cc) | |
bd13cb19 | 319 | check_hashes() |
30035d67 FW |
320 | |
321 | if errors_encountered > 0: | |
322 | print("note: errors encountered:", errors_encountered) | |
323 | sys.exit(1) | |
324 | ||
325 | if __name__ == '__main__': | |
326 | main() |