mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-06-02 12:39:37 +08:00

- The script's use of sort proved to not be portable - No need to check the commits as symbols are only added - Regenerated kernel header to reset the sort order Update #4475
506 lines
16 KiB
Python
Executable File
506 lines
16 KiB
Python
Executable File
#! /usr/bin/env python
|
|
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
"""RTEMS LibBBSD Kernel Symbols
|
|
|
|
Generate the symbols for the kernel headers and merge in any new ones
|
|
"""
|
|
|
|
#
|
|
# Copyright (C) 2021 Chris Johns <chrisj@rtems.org>, All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
version = "1.0"
|
|
|
|
kern_objects = [
|
|
( 'freebsd/sys', '.*\.o' ),
|
|
( 'rtemsbsd/rtems', 'rtems-kernel-.*\.o' )
|
|
] # yapf: disable
|
|
|
|
kern_excludes = [
|
|
'^rtems_',
|
|
'^accept$',
|
|
'^arc4random$',
|
|
'^bind$',
|
|
'^blackhole$',
|
|
'^bootverbose$',
|
|
'^bpf_filter$',
|
|
'^bpf_jitter$',
|
|
'^bpf_jitter_enable$',
|
|
'^bpf_validate$',
|
|
'^cache_enter$',
|
|
'^connect$',
|
|
'^drop_redirect$',
|
|
'^drop_synfin$',
|
|
'^free$',
|
|
'^getentropy$',
|
|
'^getpeername$',
|
|
'^getsockname$',
|
|
'^getsockopt$',
|
|
'^global_epoch$',
|
|
'^global_epoch_preempt$',
|
|
'^ifqmaxlen$',
|
|
'^in6addr_any$',
|
|
'^in6addr_linklocal_allnodes$',
|
|
'^in6addr_loopback$',
|
|
'^in6addr_nodelocal_allnodes$',
|
|
'^in_epoch$',
|
|
'^kevent$',
|
|
'^kqueue$',
|
|
'^listen$',
|
|
'^malloc$',
|
|
'^max_datalen$',
|
|
'^max_hdr$',
|
|
'^max_linkhdr$',
|
|
'^max_protohdr$',
|
|
'^maxsockets$',
|
|
'^nd6_debug$',
|
|
'^nd6_delay$',
|
|
'^nd6_gctimer$',
|
|
'^nd6_maxnudhint$',
|
|
'^nd6_mmaxtries$',
|
|
'^nd6_onlink_ns_rfc4861$',
|
|
'^nd6_prune$',
|
|
'^nd6_umaxtries$',
|
|
'^nd6_useloopback$',
|
|
'^net_epoch$',
|
|
'^net_epoch_preempt$',
|
|
'^nmbclusters$',
|
|
'^nmbjumbo16$',
|
|
'^nmbjumbo9$',
|
|
'^nmbjumbop$',
|
|
'^nmbufs$',
|
|
'^nolocaltimewait$',
|
|
'^path_mtu_discovery$',
|
|
'^pause$',
|
|
'^pf_osfp_entry_pl$',
|
|
'^pf_osfp_pl$',
|
|
'^pipe$',
|
|
'^poll$',
|
|
'^pselect$',
|
|
'^random$',
|
|
'^realloc$',
|
|
'^reallocf$',
|
|
'^recvfrom$',
|
|
'^recvmsg$',
|
|
'^rtems',
|
|
'^select$',
|
|
'^sendmsg$',
|
|
'^sendto$',
|
|
'^setfib$',
|
|
'^setsockopt$',
|
|
'^shutdown$',
|
|
'^socket$',
|
|
'^socketpair$',
|
|
'^soreceive_stream$',
|
|
'^srandom$',
|
|
'^strdup$',
|
|
'^sysctlbyname$',
|
|
'^sysctl$',
|
|
'^sysctlnametomib$',
|
|
'sys_init',
|
|
'^taskqueue_',
|
|
'^tcp_offload_listen_start$',
|
|
'^tcp_offload_listen_stop$',
|
|
'^ticks$',
|
|
'^useloopback$',
|
|
'^_Watchdog_Ticks_since_boot$'
|
|
] # yapf: disable
|
|
|
|
kern_header = 'rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h'
|
|
|
|
|
|
class exit(Exception):
|
|
"""Base class for exceptions."""
|
|
def __init__(self, code):
|
|
self.code = code
|
|
|
|
|
|
class error(Exception):
|
|
"""Base class for exceptions."""
|
|
def set_output(self, msg):
|
|
self.msg = msg
|
|
|
|
def __str__(self):
|
|
return self.msg
|
|
|
|
|
|
class general_error(error):
|
|
"""Raise for a general error."""
|
|
def __init__(self, what):
|
|
self.set_output('error: ' + str(what))
|
|
|
|
|
|
class command:
|
|
def __init__(self, cmd, cwd='.'):
|
|
self.exit_code = 0
|
|
self.output = None
|
|
self.cmd = cmd
|
|
self.cwd = cwd
|
|
self.result = None
|
|
|
|
def run(self):
|
|
|
|
import subprocess
|
|
|
|
#
|
|
# Support Python 2.6
|
|
#
|
|
if "check_output" not in dir(subprocess):
|
|
|
|
def f(*popenargs, **kwargs):
|
|
if 'stdout' in kwargs:
|
|
raise ValueError(
|
|
'stdout argument not allowed, it will be overridden.')
|
|
process = subprocess.Popen(stdout=subprocess.PIPE,
|
|
*popenargs,
|
|
**kwargs)
|
|
output, unused_err = process.communicate()
|
|
retcode = process.poll()
|
|
if retcode:
|
|
cmd = kwargs.get("args")
|
|
if cmd is None:
|
|
cmd = popenargs[0]
|
|
raise subprocess.CalledProcessError(retcode, cmd)
|
|
return output
|
|
|
|
subprocess.check_output = f
|
|
|
|
self.exit_code = 0
|
|
try:
|
|
if os.name == 'nt':
|
|
cmd = ['sh', '-c'] + self.cmd
|
|
else:
|
|
cmd = self.cmd
|
|
output = subprocess.check_output(cmd,
|
|
cwd=self.cwd).decode("utf-8")
|
|
self.output = output.split(os.linesep)
|
|
except subprocess.CalledProcessError as cpe:
|
|
self.exit_code = cpe.returncode
|
|
output = cpe.output.decode("utf-8")
|
|
self.output = output.split(os.linesep)
|
|
except OSError as ose:
|
|
cs = ' '.join(cmd)
|
|
if len(cs) > 80:
|
|
cs = cs[:80] + '...'
|
|
raise general_error('bootstrap failed: %s in %s: %s' % \
|
|
(cs, self.cwd, (str(ose))))
|
|
except KeyboardInterrupt:
|
|
pass
|
|
except:
|
|
raise
|
|
|
|
|
|
class kernel_symbols:
|
|
def __init__(self, excludes):
|
|
self.bsd_tag = '_bsd_'
|
|
self.excludes = [re.compile(exc) for exc in excludes]
|
|
self.bsps = {}
|
|
self.header = {'source': [], 'symbols': []}
|
|
self.output = {'source': [], 'symbols': []}
|
|
self.analysis = {'mapped': [], 'unmapped': [], 'new': []}
|
|
|
|
@staticmethod
|
|
def _find(base, spec):
|
|
found = []
|
|
filter = re.compile(spec[1])
|
|
for root, dirs, files in os.walk(os.path.join(base, spec[0]),
|
|
topdown=True):
|
|
for f in files:
|
|
if filter.match(f):
|
|
found += [os.path.join(root, f)]
|
|
return found
|
|
|
|
@staticmethod
|
|
def _find_bsps(build):
|
|
bsps = []
|
|
filter = re.compile('^.*-rtems[0-9].*-.*')
|
|
for name in os.listdir(build):
|
|
if os.path.isdir(os.path.join(build, name)) and \
|
|
filter.match(name) != None:
|
|
bsps += [name]
|
|
return bsps
|
|
|
|
@staticmethod
|
|
def bsp_arch(bsp):
|
|
bs = bsp.split('-')
|
|
return bs[0] + '-' + bs[1]
|
|
|
|
def _clean(self, symbols):
|
|
syms = []
|
|
for sym in symbols:
|
|
add = True
|
|
for exclude in self.excludes:
|
|
if exclude.search(sym) is not None:
|
|
add = False
|
|
break
|
|
if add:
|
|
syms += [sym]
|
|
return sorted(list(set(syms)))
|
|
|
|
def load_header(self, header):
|
|
with open(header, 'r') as h:
|
|
self.header['source'] = h.read().splitlines()
|
|
filter = re.compile('^#define\s')
|
|
for line in self.header['source']:
|
|
if filter.match(line) != None:
|
|
ls = line.split()
|
|
if len(ls) == 3:
|
|
self.header['symbols'] += [ls[1]]
|
|
self.header['symbols'] = self._clean(self.header['symbols'])
|
|
|
|
def load_symbols(self, specs, excludes, build, tools):
|
|
bsps = self._find_bsps(build)
|
|
for bsp in bsps:
|
|
self.bsps[bsp] = {'output': [], 'objects': [], 'symbols': []}
|
|
for spec in specs:
|
|
self.bsps[bsp]['objects'] += self._find(
|
|
os.path.join(build, bsp), spec)
|
|
arch = self.bsp_arch(bsp)
|
|
if tools is not None:
|
|
cmd = os.path.join(tools, 'bin', arch + '-nm')
|
|
else:
|
|
cmd = arch + '-nm'
|
|
nm = command([cmd] + self.bsps[bsp]['objects'])
|
|
nm.run()
|
|
self.bsps[bsp]['output'] = nm.output
|
|
object = '-'
|
|
syms = []
|
|
for line in nm.output:
|
|
if len(line) == 0:
|
|
continue
|
|
if line[-1] == ':':
|
|
object = os.path.basename(line[:-1])
|
|
continue
|
|
ls = line.split()
|
|
if len(ls) == 3:
|
|
ls = ls[1:]
|
|
if ls[0] in ['A', 'B', 'C', 'D', 'R', 'T', 'W']:
|
|
sym = ls[1]
|
|
if sym.startswith(self.bsd_tag):
|
|
sym = sym[len(self.bsd_tag):]
|
|
if sym in syms:
|
|
print('warning: duplicate symbol: %s:%s: %s (%s)' %
|
|
(bsp, object, sym, ls[1]))
|
|
syms += [sym]
|
|
self.bsps[bsp]['symbols'] += syms
|
|
|
|
def generate_header(self):
|
|
self.output['source'] = [
|
|
'/*', '* RTEMS Libbsd, this file is generated. Do not edit.', '*/',
|
|
'#ifndef _RTEMS_BSD_MACHINE_RTEMS_BSD_KERNEL_SPACE_H_',
|
|
'#error "the header file <machine/rtems-bsd-kernel-space.h> must be included first"',
|
|
'#endif', ''
|
|
] + ['#define\t%s %s%s' % (sym, self.bsd_tag, sym) for sym in self.output['symbols']]
|
|
|
|
def write_header(self, header):
|
|
with open(header, 'wb') as o:
|
|
o.write(
|
|
os.linesep.join(self.output['source'] + ['']).encode("utf-8"))
|
|
|
|
def write_sym_data(self):
|
|
for bsp in self.bsps:
|
|
arch = self.bsp_arch(bsp)
|
|
with open('sym-data-' + arch + '.txt', 'w') as o:
|
|
o.writelines(os.linesep.join(self.bsps[bsp]['output']))
|
|
|
|
def merge(self, symbols):
|
|
self.output['symbols'] = \
|
|
self._clean(self.output['symbols'] + symbols)
|
|
|
|
def merge_bsp(self):
|
|
for bsp in self.bsps:
|
|
self.merge(self.bsps[bsp]['symbols'])
|
|
|
|
def analyse(self):
|
|
for bsp in self.bsps:
|
|
for sym in self.bsps[bsp]['symbols']:
|
|
if sym in self.header['symbols']:
|
|
key = 'mapped'
|
|
else:
|
|
key = 'new'
|
|
self.analysis[key] += [sym]
|
|
for key in self.analysis:
|
|
self.analysis[key] = self._clean(self.analysis[key])
|
|
self.analysis['unmapped'] = [sym for sym in self.header['symbols'] if sym not in self.analysis['mapped']]
|
|
|
|
def diff(self):
|
|
import difflib
|
|
return list(
|
|
difflib.unified_diff(self.header['source'], self.output['source']))
|
|
|
|
def report(self):
|
|
out = [
|
|
'Symbols:',
|
|
' header : %d' % (len(self.header['symbols'])),
|
|
' mapped : %d' % (len(self.analysis['mapped'])),
|
|
' unmapped : %d' % (len(self.analysis['unmapped'])),
|
|
' new : %d' % (len(self.analysis['new']))
|
|
]
|
|
max_len = 0
|
|
for bsp in self.bsps:
|
|
if max_len < len(bsp):
|
|
max_len = len(bsp)
|
|
out += ['BSPs: %*s Unmapped Total' % (max_len - 4, ' ')]
|
|
for bsp in self.bsps:
|
|
unmapped = len(self._clean(self.bsps[bsp]['symbols']))
|
|
total = len(self.bsps[bsp]['symbols'])
|
|
out += [' %-*s: %-8d %d' % (max_len, bsp, unmapped, total)]
|
|
out += ['New:'] + [' ' + sym for sym in self.analysis['new']]
|
|
out += ['Unmapped:'] + [' ' + sym for sym in self.analysis['unmapped']]
|
|
return out
|
|
|
|
|
|
def run(args):
|
|
try:
|
|
argsp = argparse.ArgumentParser(
|
|
prog='rtems-kern-symbols',
|
|
description="RTEMS LibBSD Kernel Symbols")
|
|
argsp.add_argument('-t',
|
|
'--rtems-tools',
|
|
help='RTEMS Tools (default: %(default)s).',
|
|
type=str,
|
|
default=None)
|
|
argsp.add_argument(
|
|
'-w',
|
|
'--write',
|
|
action='store_true',
|
|
help=
|
|
'Write the header to the output file name (default: %(default)s).')
|
|
argsp.add_argument(
|
|
'-d',
|
|
'--diff',
|
|
action='store_true',
|
|
help='Show a diff if the header has changed (default: %(default)s).'
|
|
)
|
|
argsp.add_argument(
|
|
'-o',
|
|
'--output',
|
|
type=str,
|
|
default=kern_header,
|
|
help='output path to the write the header (default: %(default)s).')
|
|
argsp.add_argument(
|
|
'-b',
|
|
'--build',
|
|
type=str,
|
|
default='build',
|
|
help='path to the rtems libbsd build output (default: %(default)s).'
|
|
)
|
|
argsp.add_argument(
|
|
'-K',
|
|
'--kern-header',
|
|
type=str,
|
|
default=kern_header,
|
|
help=
|
|
'kernel header to load existing symbols from(default: %(default)s).'
|
|
)
|
|
argsp.add_argument(
|
|
'-S',
|
|
'--sym-data',
|
|
action="store_true",
|
|
help=
|
|
'Write the BSP symbol data that is parsed (default: %(default)s).')
|
|
argsp.add_argument(
|
|
'-r',
|
|
'--regenerate',
|
|
action="store_true",
|
|
help=
|
|
'Regenerate the header file from the symbols in it, write option ignored (default: %(default)s).'
|
|
)
|
|
argsp.add_argument('-R',
|
|
'--report',
|
|
action="store_true",
|
|
help='Generate a report (default: %(default)s).')
|
|
argopts = argsp.parse_args(args[1:])
|
|
|
|
print('RTEMS LibBSD Kernel Symbols, %s' % (version))
|
|
|
|
if not os.path.exists(argopts.build):
|
|
raise general_error('path does not exist: %s' % (argopts.build))
|
|
|
|
ks = kernel_symbols(kern_excludes)
|
|
|
|
ks.load_header(argopts.kern_header)
|
|
|
|
if argopts.regenerate:
|
|
ks.merge(ks.header['symbols'])
|
|
print('Regenerating: symbols: %d: %s' %
|
|
(len(ks.output['symbols']), argopts.output))
|
|
ks.generate_header()
|
|
diff = ks.diff()
|
|
if len(diff) == 0:
|
|
print('info: no changes; header not updated')
|
|
else:
|
|
print('info: writing: %s' % (argopts.output))
|
|
ks.write_header(argopts.output)
|
|
raise exit(0)
|
|
|
|
ks.load_symbols(kern_objects, kern_excludes, argopts.build,
|
|
argopts.rtems_tools)
|
|
|
|
if argopts.sym_data:
|
|
ks.write_sym_data()
|
|
|
|
ks.analyse()
|
|
ks.merge(ks.header['symbols'])
|
|
ks.merge_bsp()
|
|
ks.generate_header()
|
|
|
|
diff = ks.diff()
|
|
if argopts.write:
|
|
if len(diff) == 0:
|
|
print('info: no changes; header not updated')
|
|
else:
|
|
print('info: writing: %s' % (argopts.output))
|
|
ks.write_header(argopts.output)
|
|
|
|
if argopts.report:
|
|
print(os.linesep.join(ks.report()))
|
|
|
|
if argopts.diff:
|
|
print('Diff: %d' % (len(diff)))
|
|
print(os.linesep.join(diff))
|
|
|
|
except general_error as gerr:
|
|
print(gerr)
|
|
print('RTEMS Kernel Symbols FAILED', file=sys.stderr)
|
|
sys.exit(1)
|
|
except KeyboardInterrupt:
|
|
log.notice('abort: user terminated')
|
|
sys.exit(1)
|
|
except exit as ec:
|
|
sys.exit(ec.code)
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run(sys.argv)
|