(root)/
glibc-2.38/
elf/
tst-relro-symbols.py
       1  #!/usr/bin/python3
       2  # Verify that certain symbols are covered by RELRO.
       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  """Analyze a (shared) object to verify that certain symbols are
      21  present and covered by the PT_GNU_RELRO segment.
      22  
      23  """
      24  
      25  import argparse
      26  import os.path
      27  import sys
      28  
      29  # Make available glibc Python modules.
      30  sys.path.append(os.path.join(
      31      os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts'))
      32  
      33  import glibcelf
      34  
      35  def find_relro(path: str, img: glibcelf.Image) -> (int, int):
      36      """Discover the address range of the PT_GNU_RELRO segment."""
      37      for phdr in img.phdrs():
      38          if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO:
      39              # The computation is not entirely accurate because
      40              # _dl_protect_relro in elf/dl-reloc.c rounds both the
      41              # start end and downwards using the run-time page size.
      42              return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz
      43      sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path))
      44      sys.exit(1)
      45  
      46  def check_in_relro(kind, relro_begin, relro_end, name, start, size, error):
      47      """Check if a section or symbol falls within in the RELRO segment."""
      48      end = start + size - 1
      49      if not (relro_begin <= start < end < relro_end):
      50          error(
      51              '{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format(
      52                  kind, name.decode('UTF-8'), start, size,
      53                  relro_begin, relro_end))
      54  
      55  def get_parser():
      56      """Return an argument parser for this script."""
      57      parser = argparse.ArgumentParser(description=__doc__)
      58      parser.add_argument('object', help='path to object file to check')
      59      parser.add_argument('--required', metavar='NAME', action='append',
      60                          default=[], help='required symbol names')
      61      parser.add_argument('--optional', metavar='NAME', action='append',
      62                          default=[], help='required symbol names')
      63      return parser
      64  
      65  def main(argv):
      66      """The main entry point."""
      67      parser = get_parser()
      68      opts = parser.parse_args(argv)
      69      img = glibcelf.Image.readfile(opts.object)
      70  
      71      required_symbols = frozenset([sym.encode('UTF-8')
      72                                    for sym in opts.required])
      73      optional_symbols = frozenset([sym.encode('UTF-8')
      74                                    for sym in opts.optional])
      75      check_symbols = required_symbols | optional_symbols
      76  
      77      # Tracks the symbols in check_symbols that have been found.
      78      symbols_found = set()
      79  
      80      # Discover the extent of the RELRO segment.
      81      relro_begin, relro_end = find_relro(opts.object, img)
      82      symbol_table_found = False
      83  
      84      errors = False
      85      def error(msg: str) -> None:
      86          """Record an error condition and write a message to standard output."""
      87          nonlocal errors
      88          errors = True
      89          sys.stdout.write('{}: error: {}\n'.format(opts.object, msg))
      90  
      91      # Iterate over section headers to find the symbol table.
      92      for shdr in img.shdrs():
      93          if shdr.sh_type == glibcelf.Sht.SHT_SYMTAB:
      94              symbol_table_found = True
      95              for sym in img.syms(shdr):
      96                  if sym.st_name in check_symbols:
      97                      symbols_found.add(sym.st_name)
      98  
      99                      # Validate symbol type, section, and size.
     100                      if sym.st_info.type != glibcelf.Stt.STT_OBJECT:
     101                          error('symbol {!r} has wrong type {}'.format(
     102                              sym.st_name.decode('UTF-8'), sym.st_info.type))
     103                      if sym.st_shndx in glibcelf.Shn:
     104                          error('symbol {!r} has reserved section {}'.format(
     105                              sym.st_name.decode('UTF-8'), sym.st_shndx))
     106                          continue
     107                      if sym.st_size == 0:
     108                          error('symbol {!r} has size zero'.format(
     109                              sym.st_name.decode('UTF-8')))
     110                          continue
     111  
     112                      check_in_relro('symbol', relro_begin, relro_end,
     113                                     sym.st_name, sym.st_value, sym.st_size,
     114                                     error)
     115              continue # SHT_SYMTAB
     116          if shdr.sh_name == b'.data.rel.ro' \
     117             or shdr.sh_name.startswith(b'.data.rel.ro.'):
     118              check_in_relro('section', relro_begin, relro_end,
     119                             shdr.sh_name, shdr.sh_addr, shdr.sh_size,
     120                             error)
     121              continue
     122  
     123      if required_symbols - symbols_found:
     124          for sym in sorted(required_symbols - symbols_found):
     125              error('symbol {!r} not found'.format(sym.decode('UTF-8')))
     126  
     127      if errors:
     128          sys.exit(1)
     129  
     130      if not symbol_table_found:
     131          sys.stdout.write(
     132              '{}: warning: no symbol table found (stripped object)\n'.format(
     133                  opts.object))
     134          sys.exit(77)
     135  
     136  if __name__ == '__main__':
     137      main(sys.argv[1:])