1117169980
Due to the use of gperf to generate hash table for kobjects, the addresses of these kobjects cannot change during the last few phases of linking (especially between zephyr_prebuilt.elf and zephyr.elf). Because of this, the gperf generated data needs to be placed at the end of memory to avoid pushing symbols around in memory. This prevents moving these generated blocks to earlier sections, for example, pinned data section needed for demand paging. So create placeholders for use in intermediate linking to reserve space for these generated blocks. Due to uncertainty on the size of these blocks, more space is being reserved which could result in wasted space. Though, this retains the use of hash table for faster lookup. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
140 lines
4.2 KiB
Python
Executable file
140 lines
4.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2021 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""
|
|
Process ELF file to generate placeholders for kobject
|
|
hash table and lookup functions produced by gperf,
|
|
since their sizes depend on how many kobjects have
|
|
been declared. The output header files will be used
|
|
during linking for intermediate output binaries so
|
|
that the addresses of these kobjects would remain
|
|
the same during later stages of linking.
|
|
"""
|
|
|
|
import sys
|
|
import argparse
|
|
import os
|
|
from distutils.version import LooseVersion
|
|
|
|
import elftools
|
|
from elftools.elf.elffile import ELFFile
|
|
|
|
|
|
if LooseVersion(elftools.__version__) < LooseVersion('0.24'):
|
|
sys.exit("pyelftools is out of date, need version 0.24 or later")
|
|
|
|
|
|
def write_define(out_fp, prefix, name, value):
|
|
"""Write the #define to output file"""
|
|
define_name = f"KOBJECT_{prefix}_{name}"
|
|
out_fp.write(f"#ifndef {define_name}\n")
|
|
out_fp.write(f"#define {define_name} {value}\n")
|
|
out_fp.write("#endif\n\n")
|
|
|
|
|
|
def output_simple_header(one_sect):
|
|
"""Write the header for kobject section"""
|
|
|
|
out_fn = os.path.join(args.outdir,
|
|
f"linker-kobject-prebuilt-{one_sect['name']}.h")
|
|
out_fp = open(out_fn, "w")
|
|
|
|
if one_sect['exists']:
|
|
align = one_sect['align']
|
|
size = one_sect['size']
|
|
prefix = one_sect['define_prefix']
|
|
|
|
write_define(out_fp, prefix, 'ALIGN', align)
|
|
write_define(out_fp, prefix, 'SZ', size)
|
|
|
|
out_fp.close()
|
|
|
|
|
|
def generate_linker_headers(obj):
|
|
"""Generate linker header files to be included by the linker script"""
|
|
|
|
# Sections we are interested in
|
|
sections = {
|
|
".data": {
|
|
"name": "data",
|
|
"define_prefix": "DATA",
|
|
"exists": False,
|
|
"multiplier": int(args.datapct) + 100,
|
|
},
|
|
".rodata": {
|
|
"name": "rodata",
|
|
"define_prefix": "RODATA",
|
|
"exists": False,
|
|
"extra_bytes": args.rodata,
|
|
},
|
|
".priv_stacks.noinit": {
|
|
"name": "priv-stacks",
|
|
"define_prefix": "PRIV_STACKS",
|
|
"exists": False,
|
|
},
|
|
}
|
|
|
|
for one_sect in obj.iter_sections():
|
|
# REALLY NEED to match exact type as all other sections
|
|
# (symbol, debug, etc.) are descendants where
|
|
# isinstance() would match.
|
|
if type(one_sect) is not elftools.elf.sections.Section: # pylint: disable=unidiomatic-typecheck
|
|
continue
|
|
|
|
name = one_sect.name
|
|
if name in sections.keys():
|
|
# Need section alignment and size
|
|
sections[name]['align'] = one_sect['sh_addralign']
|
|
sections[name]['size'] = one_sect['sh_size']
|
|
sections[name]['exists'] = True
|
|
|
|
if "multiplier" in sections[name]:
|
|
sections[name]['size'] *= sections[name]['multiplier'] / 100
|
|
sections[name]['size'] = int(sections[name]['size'])
|
|
|
|
if "extra_bytes" in sections[name]:
|
|
sections[name]['size'] += int(sections[name]['extra_bytes'])
|
|
|
|
for one_sect in sections:
|
|
output_simple_header(sections[one_sect])
|
|
|
|
|
|
def parse_args():
|
|
"""Parse command line arguments"""
|
|
global args
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
parser.add_argument("--object", required=True,
|
|
help="Points to kobject_prebuilt_hash.c.obj")
|
|
parser.add_argument("--outdir", required=True,
|
|
help="Output directory (<build_dir>/include/generated)")
|
|
parser.add_argument("--datapct", required=True,
|
|
help="Multipler to the size of reserved space for DATA region")
|
|
parser.add_argument("--rodata", required=True,
|
|
help="Extra bytes to reserve for RODATA region")
|
|
parser.add_argument("-v", "--verbose", action="store_true",
|
|
help="Verbose messages")
|
|
args = parser.parse_args()
|
|
if "VERBOSE" in os.environ:
|
|
args.verbose = 1
|
|
|
|
|
|
def main():
|
|
"""Main program"""
|
|
parse_args()
|
|
|
|
with open(args.object, "rb") as obj_fp:
|
|
obj = ELFFile(obj_fp)
|
|
|
|
generate_linker_headers(obj)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|