mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-19 02:54:24 +00:00
Merge "fix(memmap): reintroduce support for GNU map files" into integration
This commit is contained in:
commit
f3c25f9c93
5 changed files with 107 additions and 12 deletions
|
@ -8,14 +8,16 @@ import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from memory.elfparser import TfaElfParser
|
from memory.elfparser import TfaElfParser
|
||||||
|
from memory.mapparser import TfaMapParser
|
||||||
|
|
||||||
|
|
||||||
class TfaBuildParser:
|
class TfaBuildParser:
|
||||||
"""A class for performing analysis on the memory layout of a TF-A build."""
|
"""A class for performing analysis on the memory layout of a TF-A build."""
|
||||||
|
|
||||||
def __init__(self, path: Path):
|
def __init__(self, path: Path, map_backend=False):
|
||||||
self._modules = dict()
|
self._modules = dict()
|
||||||
self._path = path
|
self._path = path
|
||||||
|
self.map_backend = map_backend
|
||||||
self._parse_modules()
|
self._parse_modules()
|
||||||
|
|
||||||
def __getitem__(self, module: str):
|
def __getitem__(self, module: str):
|
||||||
|
@ -23,15 +25,24 @@ class TfaBuildParser:
|
||||||
return self._modules[module]
|
return self._modules[module]
|
||||||
|
|
||||||
def _parse_modules(self):
|
def _parse_modules(self):
|
||||||
"""Parse ELF files in the build path."""
|
"""Parse the build files using the selected backend."""
|
||||||
for elf_file in self._path.glob("**/*.elf"):
|
backend = TfaElfParser
|
||||||
module_name = elf_file.name.split("/")[-1].split(".")[0]
|
files = list(self._path.glob("**/*.elf"))
|
||||||
with open(elf_file, "rb") as file:
|
io_perms = "rb"
|
||||||
self._modules[module_name] = TfaElfParser(file)
|
|
||||||
|
if self.map_backend or len(files) == 0:
|
||||||
|
backend = TfaMapParser
|
||||||
|
files = self._path.glob("**/*.map")
|
||||||
|
io_perms = "r"
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
module_name = file.name.split("/")[-1].split(".")[0]
|
||||||
|
with open(file, io_perms) as f:
|
||||||
|
self._modules[module_name] = backend(f)
|
||||||
|
|
||||||
if not len(self._modules):
|
if not len(self._modules):
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
f"failed to find ELF files in path {self._path}!"
|
f"failed to find files to analyse in path {self._path}!"
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -54,7 +65,7 @@ class TfaBuildParser:
|
||||||
"""Returns map of memory usage per memory type for each module."""
|
"""Returns map of memory usage per memory type for each module."""
|
||||||
mem_map = {}
|
mem_map = {}
|
||||||
for k, v in self._modules.items():
|
for k, v in self._modules.items():
|
||||||
mod_mem_map = v.get_elf_memory_layout()
|
mod_mem_map = v.get_memory_layout()
|
||||||
if len(mod_mem_map):
|
if len(mod_mem_map):
|
||||||
mem_map[k] = mod_mem_map
|
mem_map[k] = mod_mem_map
|
||||||
return mem_map
|
return mem_map
|
||||||
|
|
|
@ -131,7 +131,7 @@ class TfaElfParser:
|
||||||
"""Get a dictionary of segments and their section mappings."""
|
"""Get a dictionary of segments and their section mappings."""
|
||||||
return [asdict(v) for k, v in self._segments.items()]
|
return [asdict(v) for k, v in self._segments.items()]
|
||||||
|
|
||||||
def get_elf_memory_layout(self):
|
def get_memory_layout(self):
|
||||||
"""Get the total memory consumed by this module from the memory
|
"""Get the total memory consumed by this module from the memory
|
||||||
configuration.
|
configuration.
|
||||||
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
|
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
|
||||||
|
|
75
tools/memory/memory/mapparser.py
Normal file
75
tools/memory/memory/mapparser.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023, Arm Limited. All rights reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
from re import match, search
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
|
||||||
|
class TfaMapParser:
|
||||||
|
"""A class representing a map file built for TF-A.
|
||||||
|
|
||||||
|
Provides a basic interface for reading the symbol table. The constructor
|
||||||
|
accepts a file-like object with the contents a Map file. Only GNU map files
|
||||||
|
are supported at this stage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, map_file: TextIO):
|
||||||
|
self._symbols = self.read_symbols(map_file)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def symbols(self):
|
||||||
|
return self._symbols.items()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_symbols(file: TextIO, pattern: str = None) -> dict:
|
||||||
|
pattern = r"\b(0x\w*)\s*(\w*)\s=" if not pattern else pattern
|
||||||
|
symbols = {}
|
||||||
|
|
||||||
|
for line in file.readlines():
|
||||||
|
match = search(pattern, line)
|
||||||
|
|
||||||
|
if match is not None:
|
||||||
|
value, name = match.groups()
|
||||||
|
symbols[name] = int(value, 16)
|
||||||
|
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def get_memory_layout(self) -> dict:
|
||||||
|
"""Get the total memory consumed by this module from the memory
|
||||||
|
configuration.
|
||||||
|
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
|
||||||
|
"""
|
||||||
|
assert len(self._symbols), "Symbol table is empty!"
|
||||||
|
expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
|
||||||
|
memory_layout = {}
|
||||||
|
|
||||||
|
region_symbols = filter(lambda s: match(expr, s), self._symbols)
|
||||||
|
|
||||||
|
for symbol in region_symbols:
|
||||||
|
region, _, attr = tuple(symbol.lower().strip("__").split("_"))
|
||||||
|
if region not in memory_layout:
|
||||||
|
memory_layout[region] = {}
|
||||||
|
|
||||||
|
memory_layout[region][attr] = self._symbols[symbol]
|
||||||
|
|
||||||
|
if "start" and "length" and "end" in memory_layout[region]:
|
||||||
|
memory_layout[region]["limit"] = (
|
||||||
|
memory_layout[region]["end"]
|
||||||
|
+ memory_layout[region]["length"]
|
||||||
|
)
|
||||||
|
memory_layout[region]["free"] = (
|
||||||
|
memory_layout[region]["limit"]
|
||||||
|
- memory_layout[region]["end"]
|
||||||
|
)
|
||||||
|
memory_layout[region]["total"] = memory_layout[region][
|
||||||
|
"length"
|
||||||
|
]
|
||||||
|
memory_layout[region]["size"] = (
|
||||||
|
memory_layout[region]["end"]
|
||||||
|
- memory_layout[region]["start"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return memory_layout
|
|
@ -66,6 +66,11 @@ from memory.printer import TfaPrettyPrinter
|
||||||
default=False,
|
default=False,
|
||||||
help="Display numbers in decimal base.",
|
help="Display numbers in decimal base.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--no-elf-images",
|
||||||
|
is_flag=True,
|
||||||
|
help="Analyse the build's map files instead of ELF images.",
|
||||||
|
)
|
||||||
def main(
|
def main(
|
||||||
root: Path,
|
root: Path,
|
||||||
platform: str,
|
platform: str,
|
||||||
|
@ -76,11 +81,12 @@ def main(
|
||||||
depth: int,
|
depth: int,
|
||||||
width: int,
|
width: int,
|
||||||
d: bool,
|
d: bool,
|
||||||
|
no_elf_images: bool,
|
||||||
):
|
):
|
||||||
build_path = root if root else Path("build/", platform, build_type)
|
build_path = root if root else Path("build/", platform, build_type)
|
||||||
click.echo(f"build-path: {build_path.resolve()}")
|
click.echo(f"build-path: {build_path.resolve()}")
|
||||||
|
|
||||||
parser = TfaBuildParser(build_path)
|
parser = TfaBuildParser(build_path, map_backend=no_elf_images)
|
||||||
printer = TfaPrettyPrinter(columns=width, as_decimal=d)
|
printer = TfaPrettyPrinter(columns=width, as_decimal=d)
|
||||||
|
|
||||||
if footprint or not (tree or symbols):
|
if footprint or not (tree or symbols):
|
||||||
|
|
|
@ -95,13 +95,16 @@ class TfaPrettyPrinter:
|
||||||
self,
|
self,
|
||||||
symbols: list,
|
symbols: list,
|
||||||
modules: list,
|
modules: list,
|
||||||
start: int = 11,
|
start: int = 12,
|
||||||
):
|
):
|
||||||
assert len(symbols), "Empty symbol list!"
|
assert len(symbols), "Empty symbol list!"
|
||||||
modules = sorted(modules)
|
modules = sorted(modules)
|
||||||
col_width = int((self.term_size - start) / len(modules))
|
col_width = int((self.term_size - start) / len(modules))
|
||||||
|
address_fixed_width = 11
|
||||||
|
|
||||||
num_fmt = "0=#010x" if not self.as_decimal else ">10"
|
num_fmt = (
|
||||||
|
f"0=#0{address_fixed_width}x" if not self.as_decimal else ">10"
|
||||||
|
)
|
||||||
|
|
||||||
_symbol_map = [
|
_symbol_map = [
|
||||||
" " * start
|
" " * start
|
||||||
|
|
Loading…
Add table
Reference in a new issue