pebble/tools/analyze_static_memory_layout.py
2025-01-27 11:38:16 -08:00

136 lines
4.3 KiB
Python

#!/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('<KERNEL HEAP>', 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 <APP CODE + HEAP>" % (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)