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/>.
27 errors_encountered
= 0
30 global errors_encountered
31 sys
.stdout
.write('error: {}\n'.format(message
))
32 errors_encountered
+= 1
34 # The enum constants in glibcelf are expected to have exactly these
36 expected_constant_prefixes
= tuple(
37 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split())
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
):
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
):
53 def check_duplicates():
54 """Verifies that enum types do not have duplicate values.
56 Different types must have different member names, too.
60 for typ
in find_enum_types():
63 for (name
, e
) in typ
.__members
__.items():
65 error('{} has {}={} and {}={}'.format(
66 typ
, seen
[e
.value
], e
.value
, name
, e
.value
))
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
))
77 global_seen
[name
] = typ
79 def check_constant_prefixes():
80 """Check that the constant prefixes match expected_constant_prefixes."""
82 for typ
in find_enum_types():
85 prefix
= find_constant_prefix(val
.name
)
87 error('constant {!r} for {} has unknown prefix'.format(
90 elif typ_prefix
is None:
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
))
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.
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>',
109 prefix
+ '.*' for prefix
in expected_constant_prefixes
))
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
114 glibcelf_skipped_aliases
= (
115 ('EM_ARC_A5', 'EM_ARC_COMPACT'),
116 ('PF_PARISC_SBP', 'PF_HP_SBP')
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("""
192 def check_constant_values(cc
):
193 """Checks the values of <elf.h> constants against glibcelf."""
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
)
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
))
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(
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
))
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
))
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
]))
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
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
))
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()
252 check_constant_prefixes()
253 check_constant_values(cc
=args
.cc
)
255 if errors_encountered
> 0:
256 print("note: errors encountered:", errors_encountered
)
259 if __name__
== '__main__':