#!/usr/bin/env python # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import re import sh class SectionInfo(object): def __init__(self, name, begin, end): self.name = name self.begin = begin self.end = end def read_section_info(elf_file): section_headers_output = sh.arm_none_eabi_readelf('-S', elf_file) line_pattern = re.compile(r"""\[\s*\d+\]\s+ # Number (\S+)\s+ # Name \S+\s+ # Type ([0-9a-f]+)\s+ # Virtual Address [0-9a-f]+\s+ # Load Address ([0-9a-f]+)\s+ # Size """, flags=re.VERBOSE) sections = [] for line in section_headers_output: match = line_pattern.search(line) if match is None: continue name = match.group(1) addr = int(match.group(2), 16) size = int(match.group(3), 16) if (addr < 0x20000000 or addr > 0x20030000) and (addr < 0x10000000 or addr > 0x10010000): # We only care about stuff that goes into ram or CCM continue if (name == 'DISCARD'): continue sections.append(SectionInfo(name, addr, addr + size)) return sections def find_symbol(word): return re.compile(r"""\b({0})$""".format(word)).search def read_layout_symbols(elf_file): symbols_output = sh.arm_none_eabi_objdump('-t', elf_file) desired_symbols = [ 'system_stm32f4xx.c', '_heap_start', '_heap_end' ] symbols = {} line_pattern = re.compile(r"""^(\S+)""") for line in symbols_output: for s in desired_symbols: if find_symbol(s)(line): match = line_pattern.search(line) symbols[s] = int(match.group(1), 16) return symbols def analyze_layout(elf_file): sections = read_section_info(elf_file) symbols = read_layout_symbols(elf_file) ram_start_address = 0x20000000 ram_end_address = 0x20020000 if 'system_stm32f4xx.c' in symbols: # We have a snowy! Adjust the RAM size to be larger ram_end_address = 0x20030000 # Add a dummy section that spans where the kernel heap is sections.append(SectionInfo('', symbols['_heap_start'], symbols['_heap_end'])) sections = sorted(sections, key=lambda x: x.begin) last_end = 0 padding_total = 0 for s in sections: if last_end != 0 and last_end != s.begin: # There's a gap between sections! Use the last section's name to identify what it is. padding_end = s.begin # Don't insert a padding after CCM and before RAM if padding_end != ram_start_address: if last_name == '.worker_bss' or last_name == '.workerlib_paddin': name = 'WORKER CODE + HEAP' else: name = 'PADDING' padding_total += padding_end - last_end print "0x%x - 0x%x %6u bytes <%s>" % \ (last_end, padding_end, padding_end - last_end, name) print "0x%x - 0x%x %6u bytes %s" % (s.begin, s.end, s.end - s.begin, s.name) last_end = s.end last_name = s.name # The app code + heap region doesn't have a section for it, it just takes up everything at the # end of the address space. print "0x%x - 0x%x %6u bytes " % (last_end, ram_end_address, ram_end_address - last_end) print 'Total padding: %u bytes' % padding_total if (__name__ == '__main__'): parser = argparse.ArgumentParser() parser.add_argument('elf_file') args = parser.parse_args() sections = analyze_layout(args.elf_file)