elf: Extract glibcelf constants from <elf.h>

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>
This commit is contained in:
Florian Weimer 2022-09-22 12:10:41 +02:00
parent e6e6184bed
commit 340097d0b5
2 changed files with 433 additions and 652 deletions

View File

@ -18,7 +18,6 @@
# <https://www.gnu.org/licenses/>.
import argparse
import enum
import sys
import glibcelf
@ -45,11 +44,57 @@ def find_constant_prefix(name):
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.__bases__[0] in (
glibcelf._OpenIntEnum, enum.Enum, enum.IntFlag):
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.
@ -59,17 +104,16 @@ def check_duplicates():
global_seen = {}
for typ in find_enum_types():
seen = {}
last = None
for (name, e) in typ.__members__.items():
for (name, e) in typ.by_name.items():
if e.value in seen:
error('{} has {}={} and {}={}'.format(
typ, seen[e.value], e.value, name, e.value))
last = e
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 last is not None and last.value > e.value:
error('{} has {}={} after {}={}'.format(
typ, name, e.value, last.name, last.value))
if name in global_seen:
error('{} used in {} and {}'.format(
name, global_seen[name], typ))
@ -81,7 +125,7 @@ def check_constant_prefixes():
seen = set()
for typ in find_enum_types():
typ_prefix = None
for val in typ:
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(
@ -113,7 +157,6 @@ def find_elf_h_constants(cc):
# used in <elf.h>.
glibcelf_skipped_aliases = (
('EM_ARC_A5', 'EM_ARC_COMPACT'),
('PF_PARISC_SBP', 'PF_HP_SBP')
)
# Constants that provide little value and are not included in
@ -146,6 +189,7 @@ DT_VALRNGLO
DT_VERSIONTAGNUM
ELFCLASSNUM
ELFDATANUM
EM_NUM
ET_HIOS
ET_HIPROC
ET_LOOS
@ -159,6 +203,7 @@ PT_HISUNW
PT_LOOS
PT_LOPROC
PT_LOSUNW
PT_NUM
SHF_MASKOS
SHF_MASKPROC
SHN_HIOS
@ -193,7 +238,7 @@ 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}
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)
@ -229,12 +274,13 @@ def check_constant_values(cc):
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> as some constants that are
# 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], enum.IntFlag)
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(
@ -266,6 +312,7 @@ def main():
help='C compiler (including options) to use')
args = parser.parse_args()
check_basic()
check_duplicates()
check_constant_prefixes()
check_constant_values(cc=args.cc)

File diff suppressed because it is too large Load Diff