mirror of https://gitee.com/Nocallback/glibc.git
Browse Source
This is necessary to place the libio vtables into the RELRO segment. New tests elf/tst-relro-ldso and elf/tst-relro-libc are added to verify that this is what actually happens. The new tests fail on ia64 due to lack of (default) RELRO support inbutils, so they are XFAILed there.nsz/bug23293
7 changed files with 190 additions and 118 deletions
@ -0,0 +1,137 @@ |
|||
#!/usr/bin/python3 |
|||
# Verify that certain symbols are covered by RELRO. |
|||
# Copyright (C) 2022 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/>. |
|||
|
|||
"""Analyze a (shared) object to verify that certain symbols are |
|||
present and covered by the PT_GNU_RELRO segment. |
|||
|
|||
""" |
|||
|
|||
import argparse |
|||
import os.path |
|||
import sys |
|||
|
|||
# Make available glibc Python modules. |
|||
sys.path.append(os.path.join( |
|||
os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts')) |
|||
|
|||
import glibcelf |
|||
|
|||
def find_relro(path: str, img: glibcelf.Image) -> (int, int): |
|||
"""Discover the address range of the PT_GNU_RELRO segment.""" |
|||
for phdr in img.phdrs(): |
|||
if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO: |
|||
# The computation is not entirely accurate because |
|||
# _dl_protect_relro in elf/dl-reloc.c rounds both the |
|||
# start end and downwards using the run-time page size. |
|||
return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz |
|||
sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path)) |
|||
sys.exit(1) |
|||
|
|||
def check_in_relro(kind, relro_begin, relro_end, name, start, size, error): |
|||
"""Check if a section or symbol falls within in the RELRO segment.""" |
|||
end = start + size - 1 |
|||
if not (relro_begin <= start < end < relro_end): |
|||
error( |
|||
'{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format( |
|||
kind, name.decode('UTF-8'), start, size, |
|||
relro_begin, relro_end)) |
|||
|
|||
def get_parser(): |
|||
"""Return an argument parser for this script.""" |
|||
parser = argparse.ArgumentParser(description=__doc__) |
|||
parser.add_argument('object', help='path to object file to check') |
|||
parser.add_argument('--required', metavar='NAME', default=(), |
|||
help='required symbol names', nargs='*') |
|||
parser.add_argument('--optional', metavar='NAME', default=(), |
|||
help='required symbol names', nargs='*') |
|||
return parser |
|||
|
|||
def main(argv): |
|||
"""The main entry point.""" |
|||
parser = get_parser() |
|||
opts = parser.parse_args(argv) |
|||
img = glibcelf.Image.readfile(opts.object) |
|||
|
|||
required_symbols = frozenset([sym.encode('UTF-8') |
|||
for sym in opts.required]) |
|||
optional_symbols = frozenset([sym.encode('UTF-8') |
|||
for sym in opts.optional]) |
|||
check_symbols = required_symbols | optional_symbols |
|||
|
|||
# Tracks the symbols in check_symbols that have been found. |
|||
symbols_found = set() |
|||
|
|||
# Discover the extent of the RELRO segment. |
|||
relro_begin, relro_end = find_relro(opts.object, img) |
|||
symbol_table_found = False |
|||
|
|||
errors = False |
|||
def error(msg: str) -> None: |
|||
"""Record an error condition and write a message to standard output.""" |
|||
nonlocal errors |
|||
errors = True |
|||
sys.stdout.write('{}: error: {}\n'.format(opts.object, msg)) |
|||
|
|||
# Iterate over section headers to find the symbol table. |
|||
for shdr in img.shdrs(): |
|||
if shdr.sh_type == glibcelf.Sht.SHT_SYMTAB: |
|||
symbol_table_found = True |
|||
for sym in img.syms(shdr): |
|||
if sym.st_name in check_symbols: |
|||
symbols_found.add(sym.st_name) |
|||
|
|||
# Validate symbol type, section, and size. |
|||
if sym.st_info.type != glibcelf.Stt.STT_OBJECT: |
|||
error('symbol {!r} has wrong type {}'.format( |
|||
sym.st_name.decode('UTF-8'), sym.st_info.type)) |
|||
if sym.st_shndx in glibcelf.Shn: |
|||
error('symbol {!r} has reserved section {}'.format( |
|||
sym.st_name.decode('UTF-8'), sym.st_shndx)) |
|||
continue |
|||
if sym.st_size == 0: |
|||
error('symbol {!r} has size zero'.format( |
|||
sym.st_name.decode('UTF-8'))) |
|||
continue |
|||
|
|||
check_in_relro('symbol', relro_begin, relro_end, |
|||
sym.st_name, sym.st_value, sym.st_size, |
|||
error) |
|||
continue # SHT_SYMTAB |
|||
if shdr.sh_name == b'.data.rel.ro' \ |
|||
or shdr.sh_name.startswith(b'.data.rel.ro.'): |
|||
check_in_relro('section', relro_begin, relro_end, |
|||
shdr.sh_name, shdr.sh_addr, shdr.sh_size, |
|||
error) |
|||
continue |
|||
|
|||
if required_symbols - symbols_found: |
|||
for sym in sorted(required_symbols - symbols_found): |
|||
error('symbol {!r} not found'.format(sym.decode('UTF-8'))) |
|||
|
|||
if errors: |
|||
sys.exit(1) |
|||
|
|||
if not symbol_table_found: |
|||
sys.stdout.write( |
|||
'{}: warning: no symbol table found (stripped object)\n'.format( |
|||
opts.object)) |
|||
sys.exit(77) |
|||
|
|||
if __name__ == '__main__': |
|||
main(sys.argv[1:]) |
|||
Loading…
Reference in new issue