1#!/usr/bin/env python3 2# 3# Copyright (c) 2021 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8Process ELF file to generate placeholders for kobject 9hash table and lookup functions produced by gperf, 10since their sizes depend on how many kobjects have 11been declared. The output header files will be used 12during linking for intermediate output binaries so 13that the addresses of these kobjects would remain 14the same during later stages of linking. 15""" 16 17import sys 18import argparse 19import os 20from packaging import version 21 22import elftools 23from elftools.elf.elffile import ELFFile 24 25 26if version.parse(elftools.__version__) < version.parse('0.24'): 27 sys.exit("pyelftools is out of date, need version 0.24 or later") 28 29 30def write_define(out_fp, prefix, name, value): 31 """Write the #define to output file""" 32 define_name = f"KOBJECT_{prefix}_{name}" 33 out_fp.write(f"#ifndef {define_name}\n") 34 out_fp.write(f"#define {define_name} {value}\n") 35 out_fp.write("#endif\n\n") 36 37 38def output_simple_header(one_sect): 39 """Write the header for kobject section""" 40 41 out_fn = os.path.join(args.outdir, 42 f"linker-kobject-prebuilt-{one_sect['name']}.h") 43 out_fp = open(out_fn, "w") 44 45 if one_sect['exists']: 46 align = one_sect['align'] 47 size = one_sect['size'] 48 prefix = one_sect['define_prefix'] 49 50 write_define(out_fp, prefix, 'ALIGN', align) 51 write_define(out_fp, prefix, 'SZ', size) 52 53 out_fp.close() 54 55 56def generate_linker_headers(obj): 57 """Generate linker header files to be included by the linker script""" 58 59 # Sections we are interested in 60 sections = { 61 ".data": { 62 "name": "data", 63 "define_prefix": "DATA", 64 "exists": False, 65 "multiplier": int(args.datapct) + 100, 66 }, 67 ".rodata": { 68 "name": "rodata", 69 "define_prefix": "RODATA", 70 "exists": False, 71 "extra_bytes": args.rodata, 72 }, 73 ".priv_stacks.noinit": { 74 "name": "priv-stacks", 75 "define_prefix": "PRIV_STACKS", 76 "exists": False, 77 }, 78 } 79 80 for one_sect in obj.iter_sections(): 81 # REALLY NEED to match exact type as all other sections 82 # (symbol, debug, etc.) are descendants where 83 # isinstance() would match. 84 if type(one_sect) is not elftools.elf.sections.Section: # pylint: disable=unidiomatic-typecheck 85 continue 86 87 name = one_sect.name 88 if name in sections: 89 # Need section alignment and size 90 sections[name]['align'] = one_sect['sh_addralign'] 91 sections[name]['size'] = one_sect['sh_size'] 92 sections[name]['exists'] = True 93 94 if "multiplier" in sections[name]: 95 sections[name]['size'] *= sections[name]['multiplier'] / 100 96 sections[name]['size'] = int(sections[name]['size']) 97 98 if "extra_bytes" in sections[name]: 99 sections[name]['size'] += int(sections[name]['extra_bytes']) 100 101 for one_sect in sections: 102 output_simple_header(sections[one_sect]) 103 104 105def parse_args(): 106 """Parse command line arguments""" 107 global args 108 109 parser = argparse.ArgumentParser( 110 description=__doc__, 111 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) 112 113 parser.add_argument("--object", required=True, 114 help="Points to kobject_prebuilt_hash.c.obj") 115 parser.add_argument("--outdir", required=True, 116 help="Output directory (<build_dir>/include/generated)") 117 parser.add_argument("--datapct", required=True, 118 help="Multiplier to the size of reserved space for DATA region") 119 parser.add_argument("--rodata", required=True, 120 help="Extra bytes to reserve for RODATA region") 121 parser.add_argument("-v", "--verbose", action="store_true", 122 help="Verbose messages") 123 args = parser.parse_args() 124 if "VERBOSE" in os.environ: 125 args.verbose = 1 126 127 128def main(): 129 """Main program""" 130 parse_args() 131 132 with open(args.object, "rb") as obj_fp: 133 obj = ELFFile(obj_fp) 134 135 generate_linker_headers(obj) 136 137 138if __name__ == "__main__": 139 main() 140