elf: Add ELF_DYNAMIC_AFTER_RELOC to rewrite PLT
[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-2024 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 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."""
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:
52             yield obj
53
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
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 = {}
107         for (name, e) in typ.by_name.items():
108             if e.value in seen:
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))
115             else:
116                 seen[e.value] = name
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
128         for val in typ.by_name.values():
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'),
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 DT_X86_64_NUM
191 ELFCLASSNUM
192 ELFDATANUM
193 EM_NUM
194 ET_HIOS
195 ET_HIPROC
196 ET_LOOS
197 ET_LOPROC
198 ET_NUM
199 PF_MASKOS
200 PF_MASKPROC
201 PT_HIOS
202 PT_HIPROC
203 PT_HISUNW
204 PT_LOOS
205 PT_LOPROC
206 PT_LOSUNW
207 PT_NUM
208 SHF_MASKOS
209 SHF_MASKPROC
210 SHN_HIOS
211 SHN_HIPROC
212 SHN_HIRESERVE
213 SHN_LOOS
214 SHN_LOPROC
215 SHN_LORESERVE
216 SHT_HIOS
217 SHT_HIPROC
218 SHT_HIPROC
219 SHT_HISUNW
220 SHT_HIUSER
221 SHT_LOOS
222 SHT_LOPROC
223 SHT_LOSUNW
224 SHT_LOUSER
225 SHT_NUM
226 STB_HIOS
227 STB_HIPROC
228 STB_LOOS
229 STB_LOPROC
230 STB_NUM
231 STT_HIOS
232 STT_HIPROC
233 STT_LOOS
234 STT_LOPROC
235 STT_NUM
236 """.strip().split())
237
238 def check_constant_values(cc):
239     """Checks the values of <elf.h> constants against glibcelf."""
240
241     glibcelf_constants = {
242         e.name: e for typ in find_enum_types() for e in typ.by_name.values()}
243     elf_h_constants = find_elf_h_constants(cc=cc)
244
245     missing_in_glibcelf = (set(elf_h_constants) - set(glibcelf_constants)
246                            - glibcelf_skipped_constants)
247     for name in sorted(missing_in_glibcelf):
248         error('constant {} is missing from glibcelf'.format(name))
249
250     unexpected_in_glibcelf = \
251         set(glibcelf_constants) & glibcelf_skipped_constants
252     for name in sorted(unexpected_in_glibcelf):
253         error('constant {} is supposed to be filtered from glibcelf'.format(
254             name))
255
256     missing_in_elf_h = set(glibcelf_constants) - set(elf_h_constants)
257     for name in sorted(missing_in_elf_h):
258         error('constant {} is missing from <elf.h>'.format(name))
259
260     expected_in_elf_h = glibcelf_skipped_constants - set(elf_h_constants)
261     for name in expected_in_elf_h:
262         error('filtered constant {} is missing from <elf.h>'.format(name))
263
264     for alias_name, name_in_glibcelf in glibcelf_skipped_aliases:
265         if name_in_glibcelf not in glibcelf_constants:
266             error('alias value {} for {} not in glibcelf'.format(
267                 name_in_glibcelf, alias_name))
268         elif (int(elf_h_constants[alias_name])
269               != glibcelf_constants[name_in_glibcelf].value):
270             error('<elf.h> has {}={}, glibcelf has {}={}'.format(
271                 alias_name, elf_h_constants[alias_name],
272                 name_in_glibcelf, glibcelf_constants[name_in_glibcelf]))
273
274     # Check for value mismatches:
275     for name in sorted(set(glibcelf_constants) & set(elf_h_constants)):
276         glibcelf_value = glibcelf_constants[name].value
277         elf_h_value = int(elf_h_constants[name])
278         # On 32-bit architectures <elf.h> has some constants that are
279         # parsed as signed, while they are unsigned in glibcelf.  So
280         # far, this only affects some flag constants, so special-case
281         # them here.
282         if (glibcelf_value != elf_h_value
283             and not (isinstance(glibcelf_constants[name],
284                                 glibcelf._FlagConstant)
285                      and glibcelf_value == 1 << 31
286                      and elf_h_value == -(1 << 31))):
287             error('{}: glibcelf has {!r}, <elf.h> has {!r}'.format(
288                 name, glibcelf_value, elf_h_value))
289
290 def check_hashes():
291     for name, expected_elf, expected_gnu in (
292             ('', 0, 0x1505),
293             ('PPPPPPPPPPPP', 0, 0x9f105c45),
294             ('GLIBC_2.0', 0xd696910, 0xf66c3dd5),
295             ('GLIBC_2.34', 0x69691b4, 0xc3f3f90c),
296             ('GLIBC_PRIVATE', 0x963cf85, 0x692a260)):
297         for convert in (lambda x: x, lambda x: x.encode('UTF-8')):
298             name = convert(name)
299             actual_elf = glibcelf.elf_hash(name)
300             if actual_elf != expected_elf:
301                 error('elf_hash({!r}): {:x} != 0x{:x}'.format(
302                     name, actual_elf, expected_elf))
303             actual_gnu = glibcelf.gnu_hash(name)
304             if actual_gnu != expected_gnu:
305                 error('gnu_hash({!r}): {:x} != 0x{:x}'.format(
306                     name, actual_gnu, expected_gnu))
307
308 def main():
309     """The main entry point."""
310     parser = argparse.ArgumentParser(
311         description="Check glibcelf.py and elf.h against each other.")
312     parser.add_argument('--cc', metavar='CC',
313                         help='C compiler (including options) to use')
314     args = parser.parse_args()
315
316     check_basic()
317     check_duplicates()
318     check_constant_prefixes()
319     check_constant_values(cc=args.cc)
320     check_hashes()
321
322     if errors_encountered > 0:
323         print("note: errors encountered:", errors_encountered)
324         sys.exit(1)
325
326 if __name__ == '__main__':
327     main()