2 # Verify that certain symbols are covered by RELRO.
3 # Copyright (C) 2022 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
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.
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.
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/>.
20 """Analyze a (shared) object to verify that certain symbols are
21 present and covered by the PT_GNU_RELRO segment.
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'))
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
))
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
):
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
))
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', default
=(),
60 help='required symbol names', nargs
='*')
61 parser
.add_argument('--optional', metavar
='NAME', default
=(),
62 help='required symbol names', nargs
='*')
66 """The main entry point."""
68 opts
= parser
.parse_args(argv
)
69 img
= glibcelf
.Image
.readfile(opts
.object)
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
77 # Tracks the symbols in check_symbols that have been found.
80 # Discover the extent of the RELRO segment.
81 relro_begin
, relro_end
= find_relro(opts
.object, img
)
82 symbol_table_found
= False
85 def error(msg
: str) -> None:
86 """Record an error condition and write a message to standard output."""
89 sys
.stdout
.write('{}: error: {}\n'.format(opts
.object, msg
))
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
)
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
))
108 error('symbol {!r} has size zero'.format(
109 sym
.st_name
.decode('UTF-8')))
112 check_in_relro('symbol', relro_begin
, relro_end
,
113 sym
.st_name
, sym
.st_value
, sym
.st_size
,
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
,
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')))
130 if not symbol_table_found
:
132 '{}: warning: no symbol table found (stripped object)\n'.format(
136 if __name__
== '__main__':