From d5c02f6b947f0ec850e1ed5201db7a8a23cef5a9 Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 25 Dec 2022 13:04:52 +0100 Subject: [PATCH] added --- scripts/app-tls-size.py | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 scripts/app-tls-size.py diff --git a/scripts/app-tls-size.py b/scripts/app-tls-size.py new file mode 100644 index 000000000..214e4a433 --- /dev/null +++ b/scripts/app-tls-size.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# app-tls-size - Calculate size of Thread-Local Storage +# Copyright 2022 Simon Arlott + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# PlatformIO usage: +# +# [env:...] +# extra_scripts = post:app-tls-size.py + +import argparse +import collections +import re +import subprocess +import sys + +RE_ELF_SECTION = re.compile(r"^\s*(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+") +Symbol = collections.namedtuple("Symbol", ["value", "size", "line"]) +RE_ELF_SYMBOL = re.compile(r"^(?P\s*(?P\w+):\s+)(?P\w+)(?P\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+)\s+(?P\w+))") + +def print_tls_size(fw_elf): + tls_offset = None + width = 8 + + lines = subprocess.run(["readelf", "-W", "--program-headers", fw_elf], + check=True, universal_newlines=True, stdout=subprocess.PIPE + ).stdout.strip().split("\n") + + for line in lines: + match = RE_ELF_SECTION.match(line) + if match: + if tls_offset is None and match["type"] == "TLS": + tls_offset = int(match["virtaddr"], 16) + + header = True + lines = subprocess.run(["readelf", "-W", "--syms", "--dyn-syms", fw_elf], + check=True, universal_newlines=True, stdout=subprocess.PIPE + ).stdout.strip().split("\n") + syms = set() + + for line in lines: + match = RE_ELF_SYMBOL.match(line) + if match: + header = False + + if match["type"] == "TLS": + syms.add(Symbol(int(match["value"], 16), int(match["size"]), line)) + width = len(match['value']) + elif tls_offset is not None and (match["type"] == "NOTYPE" and match["bind"] == "GLOBAL" + and match["visibility"] == "DEFAULT" + and match["name"] in set(["_thread_local_start", "_thread_local_end"]) + ): + value = int(match["value"], 16) - tls_offset + line = ("{1}{2:0{0}x}{3}").format(len(match['value']), + match["before_value"], value, match["after_value"]) + syms.add(Symbol(value, int(match["size"]), line)) + + elif header: + print(line) + + if syms: + syms = list(syms) + syms.sort() + size = (syms[-1].value + syms[-1].size) - syms[0].value + else: + size = 0 + + value = syms[0].value + for sym in syms: + if sym.value > value: + print("\t{1:0{0}x} {2:5d} TLS UNKNOWN".format(width, value, sym.value - value)) + print(sym.line) + value = sym.value + sym.size + + print() + print(f"Total Thread-Local Storage size: {size} bytes") + +def after_fw_elf(source, target, env): + fw_elf = str(target[0]) + print_tls_size(fw_elf) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Calculate size of Thread-Local Storage") + parser.add_argument("fw_elf", metavar="ELF", type=str, help="Firmware ELF filename") + + args = parser.parse_args() + print_tls_size(**vars(args)) +elif __name__ == "SCons.Script": + Import("env") + + env.AddPostAction("${BUILD_DIR}/${PROGNAME}.elf", after_fw_elf) + \ No newline at end of file