mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-27 11:43:34 +08:00
340097d0b5
The need to maintain elf/elf.h and scripts/glibcelf.py in parallel results in a backporting hazard: they need to be kept in sync to avoid elf/tst-glibcelf consistency check failures. glibcelf (unlike tst-glibcelf) does not use the C implementation to extract constants. This applies the additional glibcpp syntax checks to <elf.h>. This changereplaces the types derived from Python enum types with custom types _TypedConstant, _IntConstant, and _FlagConstant. These types have fewer safeguards, but this also allows incremental construction and greater flexibility for grouping constants among the types. Architectures-specific named constants are now added as members into their superclasses (but value-based lookup is still restricted to generic constants only). Consequently, check_duplicates in elf/tst-glibcelf has been adjusted to accept differently-named constants of the same value if their subtypes are distinct. The ordering check for named constants has been dropped because they are no longer strictly ordered. Further test adjustments: Some of the type names are different. The new types do not support iteration (because it is unclear whether iteration should cover the all named values (including architecture-specific constants), or only the generic named values), so elf/tst-glibcelf now uses by_name explicit (to get all constants). PF_HP_SBP and PF_PARISC_SBP are now of distinct types (PfHP and PfPARISC), so they are how both present on the Python side. EM_NUM and PT_NUM are filtered (which was an oversight in the old conversion). The new version of glibcelf should also be compatible with earlier Python versions because it no longer depends on the enum module and its advanced features. Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
327 lines
10 KiB
Python
327 lines
10 KiB
Python
#!/usr/bin/python3
|
|
# Verify scripts/glibcelf.py contents against elf/elf.h.
|
|
# Copyright (C) 2022 Free Software Foundation, Inc.
|
|
# This file is part of the GNU C Library.
|
|
#
|
|
# The GNU C Library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# The GNU C Library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with the GNU C Library; if not, see
|
|
# <https://www.gnu.org/licenses/>.
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
import glibcelf
|
|
import glibcextract
|
|
|
|
errors_encountered = 0
|
|
|
|
def error(message):
|
|
global errors_encountered
|
|
sys.stdout.write('error: {}\n'.format(message))
|
|
errors_encountered += 1
|
|
|
|
# The enum constants in glibcelf are expected to have exactly these
|
|
# prefixes.
|
|
expected_constant_prefixes = tuple(
|
|
'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split())
|
|
|
|
def find_constant_prefix(name):
|
|
"""Returns a matching prefix from expected_constant_prefixes or None."""
|
|
for prefix in expected_constant_prefixes:
|
|
if name.startswith(prefix):
|
|
return prefix
|
|
return None
|
|
|
|
def find_enum_types():
|
|
"""A generator for OpenIntEnum and IntFlag classes in glibcelf."""
|
|
classes = set((glibcelf._TypedConstant, glibcelf._IntConstant,
|
|
glibcelf._FlagConstant))
|
|
for obj in vars(glibcelf).values():
|
|
if isinstance(obj, type) and obj not in classes \
|
|
and obj.__bases__[0] in classes:
|
|
yield obj
|
|
|
|
def check_basic():
|
|
"""Check basic functionality of the constant classes."""
|
|
|
|
if glibcelf.Pt.PT_NULL is not glibcelf.Pt(0):
|
|
error('Pt(0) not interned')
|
|
if glibcelf.Pt(17609) is glibcelf.Pt(17609):
|
|
error('Pt(17609) unexpectedly interned')
|
|
if glibcelf.Pt(17609) == glibcelf.Pt(17609):
|
|
pass
|
|
else:
|
|
error('Pt(17609) equality')
|
|
if glibcelf.Pt(17610) == glibcelf.Pt(17609):
|
|
error('Pt(17610) equality')
|
|
|
|
if str(glibcelf.Pt.PT_NULL) != 'PT_NULL':
|
|
error('str(PT_NULL)')
|
|
if str(glibcelf.Pt(17609)) != '17609':
|
|
error('str(Pt(17609))')
|
|
|
|
if repr(glibcelf.Pt.PT_NULL) != 'PT_NULL':
|
|
error('repr(PT_NULL)')
|
|
if repr(glibcelf.Pt(17609)) != 'Pt(17609)':
|
|
error('repr(Pt(17609))')
|
|
|
|
if glibcelf.Pt('PT_AARCH64_MEMTAG_MTE') \
|
|
is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
|
|
error('PT_AARCH64_MEMTAG_MTE identity')
|
|
if glibcelf.Pt(0x70000002) is glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
|
|
error('Pt(0x70000002) identity')
|
|
if glibcelf.PtAARCH64(0x70000002) is not glibcelf.Pt.PT_AARCH64_MEMTAG_MTE:
|
|
error('PtAARCH64(0x70000002) identity')
|
|
if glibcelf.Pt.PT_AARCH64_MEMTAG_MTE.short_name != 'AARCH64_MEMTAG_MTE':
|
|
error('PT_AARCH64_MEMTAG_MTE short name')
|
|
|
|
# Special cases for int-like Shn.
|
|
if glibcelf.Shn(32) == glibcelf.Shn.SHN_XINDEX:
|
|
error('Shn(32)')
|
|
if glibcelf.Shn(32) + 0 != 32:
|
|
error('Shn(32) + 0')
|
|
if 32 in glibcelf.Shn:
|
|
error('32 in Shn')
|
|
if 0 not in glibcelf.Shn:
|
|
error('0 not in Shn')
|
|
|
|
def check_duplicates():
|
|
"""Verifies that enum types do not have duplicate values.
|
|
|
|
Different types must have different member names, too.
|
|
|
|
"""
|
|
global_seen = {}
|
|
for typ in find_enum_types():
|
|
seen = {}
|
|
for (name, e) in typ.by_name.items():
|
|
if e.value in seen:
|
|
other = seen[e.value]
|
|
# Value conflicts only count if they are between
|
|
# the same base type.
|
|
if e.__class__ is typ and other.__class__ is typ:
|
|
error('{} has {}={} and {}={}'.format(
|
|
typ, other, e.value, name, e.value))
|
|
else:
|
|
seen[e.value] = name
|
|
if name in global_seen:
|
|
error('{} used in {} and {}'.format(
|
|
name, global_seen[name], typ))
|
|
else:
|
|
global_seen[name] = typ
|
|
|
|
def check_constant_prefixes():
|
|
"""Check that the constant prefixes match expected_constant_prefixes."""
|
|
seen = set()
|
|
for typ in find_enum_types():
|
|
typ_prefix = None
|
|
for val in typ.by_name.values():
|
|
prefix = find_constant_prefix(val.name)
|
|
if prefix is None:
|
|
error('constant {!r} for {} has unknown prefix'.format(
|
|
val, typ))
|
|
break
|
|
elif typ_prefix is None:
|
|
typ_prefix = prefix
|
|
seen.add(typ_prefix)
|
|
elif prefix != typ_prefix:
|
|
error('prefix {!r} for constant {!r}, expected {!r}'.format(
|
|
prefix, val, typ_prefix))
|
|
if typ_prefix is None:
|
|
error('empty enum type {}'.format(typ))
|
|
|
|
for prefix in sorted(set(expected_constant_prefixes) - seen):
|
|
error('missing constant prefix {!r}'.format(prefix))
|
|
# Reverse difference is already covered inside the loop.
|
|
|
|
def find_elf_h_constants(cc):
|
|
"""Returns a dictionary of relevant constants from <elf.h>."""
|
|
return glibcextract.compute_macro_consts(
|
|
source_text='#include <elf.h>',
|
|
cc=cc,
|
|
macro_re='|'.join(
|
|
prefix + '.*' for prefix in expected_constant_prefixes))
|
|
|
|
# The first part of the pair is a name of an <elf.h> constant that is
|
|
# dropped from glibcelf. The second part is the constant as it is
|
|
# used in <elf.h>.
|
|
glibcelf_skipped_aliases = (
|
|
('EM_ARC_A5', 'EM_ARC_COMPACT'),
|
|
)
|
|
|
|
# Constants that provide little value and are not included in
|
|
# glibcelf: *LO*/*HI* range constants, *NUM constants counting the
|
|
# number of constants. Also includes the alias names from
|
|
# glibcelf_skipped_aliases.
|
|
glibcelf_skipped_constants = frozenset(
|
|
[e[0] for e in glibcelf_skipped_aliases]) | frozenset("""
|
|
DT_AARCH64_NUM
|
|
DT_ADDRNUM
|
|
DT_ADDRRNGHI
|
|
DT_ADDRRNGLO
|
|
DT_ALPHA_NUM
|
|
DT_ENCODING
|
|
DT_EXTRANUM
|
|
DT_HIOS
|
|
DT_HIPROC
|
|
DT_IA_64_NUM
|
|
DT_LOOS
|
|
DT_LOPROC
|
|
DT_MIPS_NUM
|
|
DT_NUM
|
|
DT_PPC64_NUM
|
|
DT_PPC_NUM
|
|
DT_PROCNUM
|
|
DT_SPARC_NUM
|
|
DT_VALNUM
|
|
DT_VALRNGHI
|
|
DT_VALRNGLO
|
|
DT_VERSIONTAGNUM
|
|
ELFCLASSNUM
|
|
ELFDATANUM
|
|
EM_NUM
|
|
ET_HIOS
|
|
ET_HIPROC
|
|
ET_LOOS
|
|
ET_LOPROC
|
|
ET_NUM
|
|
PF_MASKOS
|
|
PF_MASKPROC
|
|
PT_HIOS
|
|
PT_HIPROC
|
|
PT_HISUNW
|
|
PT_LOOS
|
|
PT_LOPROC
|
|
PT_LOSUNW
|
|
PT_NUM
|
|
SHF_MASKOS
|
|
SHF_MASKPROC
|
|
SHN_HIOS
|
|
SHN_HIPROC
|
|
SHN_HIRESERVE
|
|
SHN_LOOS
|
|
SHN_LOPROC
|
|
SHN_LORESERVE
|
|
SHT_HIOS
|
|
SHT_HIPROC
|
|
SHT_HIPROC
|
|
SHT_HISUNW
|
|
SHT_HIUSER
|
|
SHT_LOOS
|
|
SHT_LOPROC
|
|
SHT_LOSUNW
|
|
SHT_LOUSER
|
|
SHT_NUM
|
|
STB_HIOS
|
|
STB_HIPROC
|
|
STB_LOOS
|
|
STB_LOPROC
|
|
STB_NUM
|
|
STT_HIOS
|
|
STT_HIPROC
|
|
STT_LOOS
|
|
STT_LOPROC
|
|
STT_NUM
|
|
""".strip().split())
|
|
|
|
def check_constant_values(cc):
|
|
"""Checks the values of <elf.h> constants against glibcelf."""
|
|
|
|
glibcelf_constants = {
|
|
e.name: e for typ in find_enum_types() for e in typ.by_name.values()}
|
|
elf_h_constants = find_elf_h_constants(cc=cc)
|
|
|
|
missing_in_glibcelf = (set(elf_h_constants) - set(glibcelf_constants)
|
|
- glibcelf_skipped_constants)
|
|
for name in sorted(missing_in_glibcelf):
|
|
error('constant {} is missing from glibcelf'.format(name))
|
|
|
|
unexpected_in_glibcelf = \
|
|
set(glibcelf_constants) & glibcelf_skipped_constants
|
|
for name in sorted(unexpected_in_glibcelf):
|
|
error('constant {} is supposed to be filtered from glibcelf'.format(
|
|
name))
|
|
|
|
missing_in_elf_h = set(glibcelf_constants) - set(elf_h_constants)
|
|
for name in sorted(missing_in_elf_h):
|
|
error('constant {} is missing from <elf.h>'.format(name))
|
|
|
|
expected_in_elf_h = glibcelf_skipped_constants - set(elf_h_constants)
|
|
for name in expected_in_elf_h:
|
|
error('filtered constant {} is missing from <elf.h>'.format(name))
|
|
|
|
for alias_name, name_in_glibcelf in glibcelf_skipped_aliases:
|
|
if name_in_glibcelf not in glibcelf_constants:
|
|
error('alias value {} for {} not in glibcelf'.format(
|
|
name_in_glibcelf, alias_name))
|
|
elif (int(elf_h_constants[alias_name])
|
|
!= glibcelf_constants[name_in_glibcelf].value):
|
|
error('<elf.h> has {}={}, glibcelf has {}={}'.format(
|
|
alias_name, elf_h_constants[alias_name],
|
|
name_in_glibcelf, glibcelf_constants[name_in_glibcelf]))
|
|
|
|
# Check for value mismatches:
|
|
for name in sorted(set(glibcelf_constants) & set(elf_h_constants)):
|
|
glibcelf_value = glibcelf_constants[name].value
|
|
elf_h_value = int(elf_h_constants[name])
|
|
# On 32-bit architectures <elf.h> has some constants that are
|
|
# parsed as signed, while they are unsigned in glibcelf. So
|
|
# far, this only affects some flag constants, so special-case
|
|
# them here.
|
|
if (glibcelf_value != elf_h_value
|
|
and not (isinstance(glibcelf_constants[name],
|
|
glibcelf._FlagConstant)
|
|
and glibcelf_value == 1 << 31
|
|
and elf_h_value == -(1 << 31))):
|
|
error('{}: glibcelf has {!r}, <elf.h> has {!r}'.format(
|
|
name, glibcelf_value, elf_h_value))
|
|
|
|
def check_hashes():
|
|
for name, expected_elf, expected_gnu in (
|
|
('', 0, 0x1505),
|
|
('PPPPPPPPPPPP', 0, 0x9f105c45),
|
|
('GLIBC_2.0', 0xd696910, 0xf66c3dd5),
|
|
('GLIBC_2.34', 0x69691b4, 0xc3f3f90c),
|
|
('GLIBC_PRIVATE', 0x963cf85, 0x692a260)):
|
|
for convert in (lambda x: x, lambda x: x.encode('UTF-8')):
|
|
name = convert(name)
|
|
actual_elf = glibcelf.elf_hash(name)
|
|
if actual_elf != expected_elf:
|
|
error('elf_hash({!r}): {:x} != 0x{:x}'.format(
|
|
name, actual_elf, expected_elf))
|
|
actual_gnu = glibcelf.gnu_hash(name)
|
|
if actual_gnu != expected_gnu:
|
|
error('gnu_hash({!r}): {:x} != 0x{:x}'.format(
|
|
name, actual_gnu, expected_gnu))
|
|
|
|
def main():
|
|
"""The main entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Check glibcelf.py and elf.h against each other.")
|
|
parser.add_argument('--cc', metavar='CC',
|
|
help='C compiler (including options) to use')
|
|
args = parser.parse_args()
|
|
|
|
check_basic()
|
|
check_duplicates()
|
|
check_constant_prefixes()
|
|
check_constant_values(cc=args.cc)
|
|
check_hashes()
|
|
|
|
if errors_encountered > 0:
|
|
print("note: errors encountered:", errors_encountered)
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|