]> git.ipfire.org Git - thirdparty/glibc.git/blame - elf/tst-glibcelf.py
install.texi: Build was tested with binutils 2.41 (just released)
[thirdparty/glibc.git] / elf / tst-glibcelf.py
CommitLineData
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
20import argparse
30035d67
FW
21import sys
22
23import glibcelf
24import glibcextract
25
26errors_encountered = 0
27
28def 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.
35expected_constant_prefixes = tuple(
36 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split())
37
38def 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
45def 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
54def 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
98def 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
123def 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
147def 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>.
158glibcelf_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.
166glibcelf_skipped_constants = frozenset(
167 [e[0] for e in glibcelf_skipped_aliases]) | frozenset("""
168DT_AARCH64_NUM
169DT_ADDRNUM
170DT_ADDRRNGHI
171DT_ADDRRNGLO
172DT_ALPHA_NUM
173DT_ENCODING
174DT_EXTRANUM
175DT_HIOS
176DT_HIPROC
177DT_IA_64_NUM
178DT_LOOS
179DT_LOPROC
180DT_MIPS_NUM
181DT_NUM
182DT_PPC64_NUM
183DT_PPC_NUM
184DT_PROCNUM
185DT_SPARC_NUM
186DT_VALNUM
187DT_VALRNGHI
188DT_VALRNGLO
189DT_VERSIONTAGNUM
190ELFCLASSNUM
191ELFDATANUM
340097d0 192EM_NUM
30035d67
FW
193ET_HIOS
194ET_HIPROC
195ET_LOOS
196ET_LOPROC
197ET_NUM
198PF_MASKOS
199PF_MASKPROC
200PT_HIOS
201PT_HIPROC
202PT_HISUNW
203PT_LOOS
204PT_LOPROC
205PT_LOSUNW
340097d0 206PT_NUM
30035d67
FW
207SHF_MASKOS
208SHF_MASKPROC
209SHN_HIOS
210SHN_HIPROC
211SHN_HIRESERVE
212SHN_LOOS
213SHN_LOPROC
214SHN_LORESERVE
215SHT_HIOS
216SHT_HIPROC
217SHT_HIPROC
218SHT_HISUNW
219SHT_HIUSER
220SHT_LOOS
221SHT_LOPROC
222SHT_LOSUNW
223SHT_LOUSER
224SHT_NUM
225STB_HIOS
226STB_HIPROC
227STB_LOOS
228STB_LOPROC
229STB_NUM
230STT_HIOS
231STT_HIPROC
232STT_LOOS
233STT_LOPROC
234STT_NUM
235""".strip().split())
236
237def 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
289def 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
307def 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
325if __name__ == '__main__':
326 main()