You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
4.7 KiB
164 lines
4.7 KiB
#!/usr/bin/env python3 |
|
# |
|
# Copyright (c) 2017 Intel Corporation |
|
# |
|
# SPDX-License-Identifier: Apache-2.0 |
|
|
|
""" |
|
gperf C file post-processor |
|
|
|
We use gperf to build up a perfect hashtable of pointer values. The way gperf |
|
does this is to create a table 'wordlist' indexed by a string representation |
|
of a pointer address, and then doing memcmp() on a string passed in for |
|
comparison |
|
|
|
We are exclusively working with 4-byte pointer values. This script adjusts |
|
the generated code so that we work with pointers directly and not strings. |
|
This saves a considerable amount of space. |
|
""" |
|
|
|
import sys |
|
import argparse |
|
import os |
|
import re |
|
from packaging import version |
|
|
|
# --- debug stuff --- |
|
|
|
def debug(text): |
|
if not args.verbose: |
|
return |
|
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
|
|
|
|
|
def error(text): |
|
sys.exit(os.path.basename(sys.argv[0]) + " ERROR: " + text) |
|
|
|
|
|
def warn(text): |
|
sys.stdout.write( |
|
os.path.basename( |
|
sys.argv[0]) + |
|
" WARNING: " + |
|
text + |
|
"\n") |
|
|
|
|
|
def reformat_str(match_obj): |
|
addr_str = match_obj.group(0) |
|
|
|
# Nip quotes |
|
addr_str = addr_str[1:-1] |
|
addr_vals = [0, 0, 0, 0, 0, 0, 0 , 0] |
|
ctr = 7 |
|
i = 0 |
|
|
|
while True: |
|
if i >= len(addr_str): |
|
break |
|
|
|
if addr_str[i] == "\\": |
|
if addr_str[i + 1].isdigit(): |
|
# Octal escape sequence |
|
val_str = addr_str[i + 1:i + 4] |
|
addr_vals[ctr] = int(val_str, 8) |
|
i += 4 |
|
else: |
|
# Char value that had to be escaped by C string rules |
|
addr_vals[ctr] = ord(addr_str[i + 1]) |
|
i += 2 |
|
|
|
else: |
|
addr_vals[ctr] = ord(addr_str[i]) |
|
i += 1 |
|
|
|
ctr -= 1 |
|
|
|
return "(char *)0x%02x%02x%02x%02x%02x%02x%02x%02x" % tuple(addr_vals) |
|
|
|
|
|
def process_line(line, fp): |
|
if line.startswith("#"): |
|
fp.write(line) |
|
return |
|
|
|
# Set the lookup function to static inline so it gets rolled into |
|
# k_object_find(), nothing else will use it |
|
if re.search(args.pattern + " [*]$", line): |
|
fp.write("static inline " + line) |
|
return |
|
|
|
m = re.search("gperf version (.*) [*][/]$", line) |
|
if m: |
|
v = version.parse(m.groups()[0]) |
|
v_lo = version.parse("3.0") |
|
v_hi = version.parse("3.1") |
|
if (v < v_lo or v > v_hi): |
|
warn("gperf %s is not tested, versions %s through %s supported" % |
|
(v, v_lo, v_hi)) |
|
|
|
# Replace length lookups with constant len since we're always |
|
# looking at pointers |
|
line = re.sub(r'lengthtable\[key\]', r'sizeof(void *)', line) |
|
|
|
# Empty wordlist entries to have NULLs instead of "" |
|
line = re.sub(r'[{]["]["][}]', r'{}', line) |
|
|
|
# Suppress a compiler warning since this table is no longer necessary |
|
line = re.sub(r'static unsigned char lengthtable', |
|
r'static unsigned char __unused lengthtable', line) |
|
|
|
# drop all use of register keyword, let compiler figure that out, |
|
# we have to do this since we change stuff to take the address of some |
|
# parameters |
|
line = re.sub(r'register', r'', line) |
|
|
|
# Hashing the address of the string |
|
line = re.sub(r"hash [(]str, len[)]", |
|
r"hash((const char *)&str, len)", line) |
|
|
|
# Just compare pointers directly instead of using memcmp |
|
if re.search("if [(][*]str", line): |
|
fp.write(" if (str == s)\n") |
|
return |
|
|
|
# Take the strings with the binary information for the pointer values, |
|
# and just turn them into pointers |
|
line = re.sub(r'["].*["]', reformat_str, line) |
|
|
|
# Use a bigger data type for the asso_values table to provide some margin |
|
line = re.sub(r'char asso_values', r'short asso_values', line) |
|
|
|
fp.write(line) |
|
|
|
|
|
def parse_args(): |
|
global args |
|
|
|
parser = argparse.ArgumentParser( |
|
description=__doc__, |
|
formatter_class=argparse.RawDescriptionHelpFormatter, |
|
allow_abbrev=False) |
|
|
|
parser.add_argument("-i", "--input", required=True, |
|
help="Input C file from gperf") |
|
parser.add_argument("-o", "--output", required=True, |
|
help="Output C file with processing done") |
|
parser.add_argument("-p", "--pattern", required=True, |
|
help="Search pattern for objects") |
|
parser.add_argument("-v", "--verbose", action="store_true", |
|
help="Print extra debugging information") |
|
args = parser.parse_args() |
|
if "VERBOSE" in os.environ: |
|
args.verbose = 1 |
|
|
|
def main(): |
|
parse_args() |
|
|
|
with open(args.input, "r") as in_fp, open(args.output, "w") as out_fp: |
|
for line in in_fp.readlines(): |
|
process_line(line, out_fp) |
|
|
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|