rtld: properly handle root directory in load path (bug 30435)
[platform/upstream/glibc.git] / 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:])