linker: sort app shared mem partition by alignment
If CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT is enabled,
the app shared memory partition may cause waste of memory
due to the need for padding.
For example, tests/subsys/jwt and board mps2_an385:
z_test_mem_partition: addr 0x20000000, size 52
z_libc_partition : addr 0x20000040, size 4
k_mbedtls_partition : addr 0x20008000, size 32736
ending at 0x2000ffff, taking up 65536 bytes
With power-of-two size and alignment requirement,
k_mbedtls_partition takes up 32KB memory and needs to be
aligned on 32KB boundary. If the above partitions are
ordered as shown, there needs to be a lot of padding
after z_libc_partition before k_mbedtls_partition can
start. In order to minimize padding, these partitions
need to be sort by size in descending order.
After the changes here, the partitions are:
k_mbedtls_partition : addr 0x20000000, size 32736
z_test_mem_partition: addr 0x20008000, size 52
z_libc_partition : addr 0x20008040, size 4
ending at 0x2000805f, taking up 32864 bytes
With the above example, sorting results in a saving
of 32672 bytes of saving.
Fixes #14121
Signed-off-by: Daniel Leung <daniel.leung@intel.com>
diff --git a/scripts/gen_app_partitions.py b/scripts/gen_app_partitions.py
index 469e25d..dcdab84 100644
--- a/scripts/gen_app_partitions.py
+++ b/scripts/gen_app_partitions.py
@@ -37,9 +37,16 @@
import os
import re
import string
+import subprocess
+from collections import OrderedDict
from elf_helper import ElfHelper
from elftools.elf.elffile import ELFFile
+from elftools.elf.sections import SymbolTableSection
+from operator import itemgetter
+SZ = 'size'
+SRC = 'sources'
+LIB = 'libraries'
# This script will create sections and linker variables to place the
# application shared memory partitions.
@@ -91,7 +98,9 @@
section_regex = re.compile(r'data_smem_([A-Za-z0-9_]*)_(data|bss)')
-def find_partitions(filename, partitions, sources):
+elf_part_size_regex = re.compile(r'z_data_smem_(.*)_part_size')
+
+def find_obj_file_partitions(filename, partitions):
with open(filename, 'rb') as f:
full_lib = ELFFile( f)
if (not full_lib):
@@ -106,23 +115,67 @@
partition_name = m.groups()[0]
if partition_name not in partitions:
- partitions[partition_name] = []
- if args.verbose:
- sources.update({partition_name: filename})
+ partitions[partition_name] = {SZ: section.header.sh_size}
- return (partitions, sources)
+ if args.verbose:
+ partitions[partition_name][SRC] = filename
+
+ else:
+ partitions[partition_name][SZ] += section.header.sh_size
+
+
+ return partitions
+
+
+def parse_obj_files(partitions):
+ # Iterate over all object files to find partitions
+ for dirpath, dirs, files in os.walk(args.directory):
+ for filename in files:
+ if re.match(".*\.obj$",filename):
+ fullname = os.path.join(dirpath, filename)
+ find_obj_file_partitions(fullname, partitions)
+
+
+def parse_elf_file(partitions):
+ with open(args.elf, 'rb') as f:
+ elffile = ELFFile(f)
+
+ symbol_tbls = [s for s in elffile.iter_sections()
+ if isinstance(s, SymbolTableSection)]
+
+ for section in symbol_tbls:
+ for nsym, symbol in enumerate(section.iter_symbols()):
+ if symbol['st_shndx'] != "SHN_ABS":
+ continue
+
+ x = elf_part_size_regex.match(symbol.name)
+ if not x:
+ continue
+
+ partition_name = x.groups()[0]
+ size = symbol['st_value']
+ if partition_name not in partitions:
+ partitions[partition_name] = {SZ: size}
+
+ if args.verbose:
+ partitions[partition_name][SRC] = args.elf
+
+ else:
+ partitions[partition_name][SZ] += size
def generate_final_linker(linker_file, partitions):
string = linker_start_seq
size_string = ''
- for partition, libs in partitions.items():
+ for partition, item in partitions.items():
string += data_template.format(partition)
- for lib in libs:
- string += library_data_template.format(lib)
+ if LIB in item:
+ for lib in item[LIB]:
+ string += library_data_template.format(lib)
string += bss_template.format(partition)
- for lib in libs:
- string += library_bss_template.format(lib)
+ if LIB in item:
+ for lib in item[LIB]:
+ string += library_bss_template.format(lib)
string += footer_template.format(partition)
size_string += size_cal_string.format(partition)
@@ -137,8 +190,10 @@
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument("-d", "--directory", required=True,
+ parser.add_argument("-d", "--directory", required=False, default=None,
help="Root build directory")
+ parser.add_argument("-e", "--elf", required=False, default=None,
+ help="ELF file")
parser.add_argument("-o", "--output", required=False,
help="Output ld file")
parser.add_argument("-v", "--verbose", action="count", default =0,
@@ -154,26 +209,34 @@
parse_args()
linker_file = args.output
partitions = {}
- sources = {}
- for dirpath, dirs, files in os.walk(args.directory):
- for filename in files:
- if re.match(".*\.obj$",filename):
- fullname = os.path.join(dirpath, filename)
- find_partitions(fullname, partitions,
- sources)
+ if args.directory is not None:
+ parse_obj_files(partitions)
+ elif args.elf is not None:
+ parse_elf_file(partitions)
+ else:
+ return
for lib, ptn in args.library:
if ptn not in partitions:
- partitions[ptn] = [lib]
- else:
- partitions[ptn].append(lib)
+ partitions[ptn] = {}
- generate_final_linker(args.output, partitions)
+ if LIB not in partitions[ptn]:
+ partitions[ptn][LIB] = [lib]
+ else:
+ partitions[ptn][LIB].append(lib)
+
+ partsorted = OrderedDict(sorted(partitions.items(),
+ key=lambda x: x[1][SZ], reverse=True))
+
+ generate_final_linker(args.output, partsorted)
if args.verbose:
print("Partitions retrieved:")
- for key in partitions:
- print(" %s: %s\n", key, sources[key])
+ for key in partsorted:
+ print(" {0}: size {1}: {2}".format(key,
+ partsorted[key][SZ],
+ partsorted[key][SRC]))
+
if __name__ == '__main__':
main()