summaryrefslogtreecommitdiffstats
path: root/rtems-kern-symbols
diff options
context:
space:
mode:
Diffstat (limited to 'rtems-kern-symbols')
-rwxr-xr-xrtems-kern-symbols505
1 files changed, 505 insertions, 0 deletions
diff --git a/rtems-kern-symbols b/rtems-kern-symbols
new file mode 100755
index 00000000..52494a85
--- /dev/null
+++ b/rtems-kern-symbols
@@ -0,0 +1,505 @@
+#! /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)