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.
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.
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.
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/>.
26 errors_encountered
= 0
29 global errors_encountered
30 sys
.stdout
.write('error: {}\n'.format(message
))
31 errors_encountered
+= 1
33 # The enum constants in glibcelf are expected to have exactly these
35 expected_constant_prefixes
= tuple(
36 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split())
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
):
45 def find_enum_types():
46 """A generator for OpenIntEnum and IntFlag classes in glibcelf."""
47 classes
= set((glibcelf
._TypedConstant
, glibcelf
._IntConstant
,
48 glibcelf
._FlagConstant
))
49 for obj
in vars(glibcelf
).values():
50 if isinstance(obj
, type) and obj
not in classes \
51 and obj
.__bases
__[0] in classes
:
55 """Check basic functionality of the constant classes."""
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):
64 error('Pt(17609) equality')
65 if glibcelf
.Pt(17610) == glibcelf
.Pt(17609):
66 error('Pt(17610) equality')
68 if str(glibcelf
.Pt
.PT_NULL
) != 'PT_NULL':
70 if str(glibcelf
.Pt(17609)) != '17609':
71 error('str(Pt(17609))')
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))')
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')
88 # Special cases for int-like Shn.
89 if glibcelf
.Shn(32) == glibcelf
.Shn
.SHN_XINDEX
:
91 if glibcelf
.Shn(32) + 0 != 32:
93 if 32 in glibcelf
.Shn
:
95 if 0 not in glibcelf
.Shn
:
98 def check_duplicates():
99 """Verifies that enum types do not have duplicate values.
101 Different types must have different member names, too.
105 for typ
in find_enum_types():
107 for (name
, e
) in typ
.by_name
.items():
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
))
117 if name
in global_seen
:
118 error('{} used in {} and {}'.format(
119 name
, global_seen
[name
], typ
))
121 global_seen
[name
] = typ
123 def check_constant_prefixes():
124 """Check that the constant prefixes match expected_constant_prefixes."""
126 for typ
in find_enum_types():
128 for val
in typ
.by_name
.values():
129 prefix
= find_constant_prefix(val
.name
)
131 error('constant {!r} for {} has unknown prefix'.format(
134 elif typ_prefix
is None:
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
))
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.
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>',
153 prefix
+ '.*' for prefix
in expected_constant_prefixes
))
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
158 glibcelf_skipped_aliases
= (
159 ('EM_ARC_A5', 'EM_ARC_COMPACT'),
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("""
237 def check_constant_values(cc
):
238 """Checks the values of <elf.h> constants against glibcelf."""
240 glibcelf_constants
= {
241 e
.name
: e
for typ
in find_enum_types() for e
in typ
.by_name
.values()}
242 elf_h_constants
= find_elf_h_constants(cc
=cc
)
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
))
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(
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
))
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
))
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
]))
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
])
277 # On 32-bit architectures <elf.h> has some constants that are
278 # parsed as signed, while they are unsigned in glibcelf. So
279 # far, this only affects some flag constants, so special-case
281 if (glibcelf_value
!= elf_h_value
282 and not (isinstance(glibcelf_constants
[name
],
283 glibcelf
._FlagConstant
)
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
))
290 for name
, expected_elf
, expected_gnu
in (
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')):
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
))
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()
317 check_constant_prefixes()
318 check_constant_values(cc
=args
.cc
)
321 if errors_encountered
> 0:
322 print("note: errors encountered:", errors_encountered
)
325 if __name__
== '__main__':