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