mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-24 22:10:13 +00:00
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-2023 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()
|