scripts: Add glibcelf.py module
[platform/upstream/glibc.git] / elf / tst-glibcelf.py
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()