mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-20 19:44:23 +00:00
fix(memmap): reintroduce support for GNU map files
The intial patch stack only supported ELF files, which proved particularly problematic when dealing with incomplete builds (i.e. build didn't complete due to linker errors). This adds support for GNU map files. Most analysis performed by the tool should be possible with map files alone. Change-Id: I89f775a98efc5aef6671a17d0e6e973df555a6fa Signed-off-by: Harrison Mutai <harrison.mutai@arm.com>
This commit is contained in:
parent
1b4d99878c
commit
d0e3053c4f
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