Browse Source

refactor: improve readability of symbol table

Make the symbol table produced by the memory mapping script more
readable. Add a generic interface for interacting with ELF binaries.
This interface enables us to get symbols that provide some insights into
TF-A's memory usage.

Change-Id: I6646f817a1d38d6184b837b78039b7465a533c5c
Signed-off-by: Harrison Mutai <harrison.mutai@arm.com>
pull/1982/merge
Harrison Mutai 2 years ago
parent
commit
af5b49e992
  1. 1
      .versionrc.js
  2. 15
      Makefile
  3. 1
      docs/index.rst
  4. 12
      docs/tools/index.rst
  5. 120
      docs/tools/memory-layout-tool.rst
  6. 144
      poetry.lock
  7. 12
      pyproject.toml
  8. 7
      tools/memory/__init__.py
  9. 7
      tools/memory/memory/__init__.py
  10. 56
      tools/memory/memory/buildparser.py
  11. 33
      tools/memory/memory/elfparser.py
  12. 78
      tools/memory/memory/memmap.py
  13. 93
      tools/memory/memory/printer.py
  14. 102
      tools/memory/print_memory_map.py

1
.versionrc.js

@ -94,7 +94,6 @@ module.exports = {
return contents.replace(/^(version\s=\s")((\d).?)*$/m, _ver)
}
},
},
{

15
Makefile

@ -1074,11 +1074,6 @@ ROMLIBPATH ?= lib/romlib
# Variable for use with Python
PYTHON ?= python3
# Variables for use with PRINT_MEMORY_MAP
PRINT_MEMORY_MAP_PATH ?= tools/memory
PRINT_MEMORY_MAP ?= ${PRINT_MEMORY_MAP_PATH}/print_memory_map.py
INVERTED_MEMMAP ?= 0
# Variables for use with documentation build using Sphinx tool
DOCS_PATH ?= docs
@ -1139,7 +1134,6 @@ $(eval $(call assert_booleans,\
GICV2_G0_FOR_EL3 \
HANDLE_EA_EL3_FIRST_NS \
HW_ASSISTED_COHERENCY \
INVERTED_MEMMAP \
MEASURED_BOOT \
DRTM_SUPPORT \
NS_TIMER_SWITCH \
@ -1653,9 +1647,14 @@ endif
romlib.bin: libraries FORCE
${Q}${MAKE} PLAT_DIR=${PLAT_DIR} BUILD_PLAT=${BUILD_PLAT} ENABLE_BTI=${ENABLE_BTI} ARM_ARCH_MINOR=${ARM_ARCH_MINOR} INCLUDES='${INCLUDES}' DEFINES='${DEFINES}' --no-print-directory -C ${ROMLIBPATH} all
# Call print_memory_map tool
memmap: all
${Q}${PYTHON} ${PRINT_MEMORY_MAP} ${BUILD_PLAT} ${INVERTED_MEMMAP}
ifdef UNIX_MK
${Q}PYTHONPATH=${CURDIR}/tools/memory \
${PYTHON} -m memory.memmap -sr ${BUILD_PLAT}
else
${Q}set PYTHONPATH=${CURDIR}/tools/memory && \
${PYTHON} -m memory.memmap -sr ${BUILD_PLAT}
endif
doc:
@echo " BUILD DOCUMENTATION"

1
docs/index.rst

@ -17,6 +17,7 @@ Trusted Firmware-A Documentation
security_advisories/index
design_documents/index
threat_model/index
tools/index
change-log
glossary
license

12
docs/tools/index.rst

@ -0,0 +1,12 @@
Tools
=====
.. toctree::
:maxdepth: 1
:caption: Contents
memory-layout-tool
--------------
*Copyright (c) 2023, Arm Limited. All rights reserved.*

120
docs/tools/memory-layout-tool.rst

@ -0,0 +1,120 @@
TF-A Memory Layout Tool
=======================
TF-A's memory layout tool is a Python script for analyzing the virtual
memory layout of TF-A builds.
Prerequisites
~~~~~~~~~~~~~
#. Python (3.8 or later)
#. `Poetry`_ Python package manager
Getting Started
~~~~~~~~~~~~~~~
#. Install Poetry
.. code:: shell
curl -sSL https://install.python-poetry.org | python3 -
#. Install the required packages
.. code:: shell
poetry install --with memory
#. Verify that the tool runs in the installed virtual environment
.. code:: shell
poetry run memory --help
Symbol Virtual Map
~~~~~~~~~~~~~~~~~~
The tool can be used to generate a visualisation of the symbol table. By
default, it prints the symbols representing the start and end address of the
main memory regions in an ELF file (i.e. text, bss, rodata) but can be modified
to print any set of symbols.
.. code:: shell
$ poetry run memory -s
build-path: build/fvp/release
Virtual Address Map:
+------------__BL1_RAM_END__------------+---------------------------------------+
+---------__COHERENT_RAM_END__----------+ |
+--------__COHERENT_RAM_START__---------+ |
0x0403b000 +----------__XLAT_TABLE_END__-----------+ |
0x04036000 +---------__XLAT_TABLE_START__----------+ |
+--------__BASE_XLAT_TABLE_END__--------+ |
0x04035600 +--------------__BSS_END__--------------+ |
+-------__BASE_XLAT_TABLE_START__-------+ |
+-----__PMF_PERCPU_TIMESTAMP_END__------+ |
+---------__PMF_TIMESTAMP_END__---------+ |
0x04035400 +--------__PMF_TIMESTAMP_START__--------+ |
+-------------__BSS_START__-------------+ |
0x04034a00 +------------__STACKS_END__-------------+ |
0x04034500 +-----------__STACKS_START__------------+ |
0x040344c5 +-----------__DATA_RAM_END__------------+ |
+-----------__BL1_RAM_START__-----------+ |
0x04034000 +----------__DATA_RAM_START__-----------+ |
| +---------__COHERENT_RAM_END__----------+
| +--------__COHERENT_RAM_START__---------+
0x0402e000 | +----------__XLAT_TABLE_END__-----------+
0x04029000 | +---------__XLAT_TABLE_START__----------+
| +--------__BASE_XLAT_TABLE_END__--------+
0x04028800 | +--------------__BSS_END__--------------+
| +-------__BASE_XLAT_TABLE_START__-------+
| +-----__PMF_PERCPU_TIMESTAMP_END__------+
| +---------__PMF_TIMESTAMP_END__---------+
0x04028580 | +--------__PMF_TIMESTAMP_START__--------+
0x04028000 | +-------------__BSS_START__-------------+
0x04027e40 | +------------__STACKS_END__-------------+
0x04027840 | +-----------__STACKS_START__------------+
0x04027000 | +------------__RODATA_END__-------------+
| +------------__CPU_OPS_END__------------+
| +-----------__CPU_OPS_START__-----------+
| +--------__FCONF_POPULATOR_END__--------+
| +--------------__GOT_END__--------------+
| +-------------__GOT_START__-------------+
| +---------__PMF_SVC_DESCS_END__---------+
0x04026c10 | +--------__PMF_SVC_DESCS_START__--------+
0x04026bf8 | +-------__FCONF_POPULATOR_START__-------+
| +-----------__RODATA_START__------------+
0x04026000 | +-------------__TEXT_END__--------------+
0x04021000 | +------------__TEXT_START__-------------+
0x000062b5 +------------__BL1_ROM_END__------------+ |
0x00005df0 +----------__DATA_ROM_START__-----------+ |
+------------__CPU_OPS_END__------------+ |
+--------------__GOT_END__--------------+ |
+-------------__GOT_START__-------------+ |
0x00005de8 +------------__RODATA_END__-------------+ |
+-----------__CPU_OPS_START__-----------+ |
+--------__FCONF_POPULATOR_END__--------+ |
+---------__PMF_SVC_DESCS_END__---------+ |
0x00005c98 +--------__PMF_SVC_DESCS_START__--------+ |
0x00005c80 +-------__FCONF_POPULATOR_START__-------+ |
+-----------__RODATA_START__------------+ |
0x00005000 +-------------__TEXT_END__--------------+ |
0x00000000 +------------__TEXT_START__-------------+---------------------------------------+
Addresses are displayed in hexadecimal by default but can be printed in decimal
instead with the ``-d`` option.
Because of the length of many of the symbols, the tool defaults to a text width
of 120 chars. This can be increased if needed with the ``-w`` option.
For more detailed help instructions, run:
.. code:: shell
poetry run memory --help
--------------
*Copyright (c) 2023, Arm Limited. All rights reserved.*
.. _Poetry: https://python-poetry.org/docs/

144
poetry.lock

@ -12,6 +12,25 @@ files = [
{file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
]
[[package]]
name = "anytree"
version = "2.8.0"
description = "Powerful and Lightweight Python Tree Data Structure.."
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "anytree-2.8.0-py2.py3-none-any.whl", hash = "sha256:14c55ac77492b11532395049a03b773d14c7e30b22aa012e337b1e983de31521"},
{file = "anytree-2.8.0.tar.gz", hash = "sha256:3f0f93f355a91bc3e6245319bf4c1d50e3416cc7a35cc1133c1ff38306bbccab"},
]
[package.dependencies]
six = ">=1.9.0"
[package.extras]
dev = ["check-manifest"]
test = ["coverage"]
[[package]]
name = "babel"
version = "2.12.1"
@ -213,14 +232,14 @@ files = [
[[package]]
name = "importlib-metadata"
version = "6.0.0"
version = "6.6.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
{file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
{file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"},
{file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"},
]
[package.dependencies]
@ -395,38 +414,38 @@ testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov",
[[package]]
name = "packaging"
version = "23.0"
version = "23.1"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
{file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
]
[[package]]
name = "pip"
version = "23.0.1"
version = "23.1.2"
description = "The PyPA recommended tool for installing Python packages."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pip-23.0.1-py3-none-any.whl", hash = "sha256:236bcb61156d76c4b8a05821b988c7b8c35bf0da28a4b614e8d6ab5212c25c6f"},
{file = "pip-23.0.1.tar.gz", hash = "sha256:cd015ea1bfb0fcef59d8a286c1f8bebcb983f6317719d415dc5351efb7cd7024"},
{file = "pip-23.1.2-py3-none-any.whl", hash = "sha256:3ef6ac33239e4027d9a5598a381b9d30880a1477e50039db2eac6e8a8f6d1b18"},
{file = "pip-23.1.2.tar.gz", hash = "sha256:0e7c86f486935893c708287b30bd050a36ac827ec7fe5e43fe7cb198dd835fba"},
]
[[package]]
name = "pip-tools"
version = "6.12.3"
version = "6.13.0"
description = "pip-tools keeps your pinned dependencies fresh."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pip-tools-6.12.3.tar.gz", hash = "sha256:480d44fae6e09fad3f9bd3d0a7e8423088715d10477e8ef0663440db25e3114f"},
{file = "pip_tools-6.12.3-py3-none-any.whl", hash = "sha256:8510420f46572b2e26c357541390593d9365eb6edd2d1e7505267910ecaec080"},
{file = "pip-tools-6.13.0.tar.gz", hash = "sha256:61d46bd2eb8016ed4a924e196e6e5b0a268cd3babd79e593048720db23522bb1"},
{file = "pip_tools-6.13.0-py3-none-any.whl", hash = "sha256:50943f151d87e752abddec8158622c34ad7f292e193836e90e30d87da60b19d9"},
]
[package.dependencies]
@ -440,16 +459,46 @@ wheel = "*"
coverage = ["pytest-cov"]
testing = ["flit-core (>=2,<4)", "poetry-core (>=1.0.0)", "pytest (>=7.2.0)", "pytest-rerunfailures", "pytest-xdist"]
[[package]]
name = "prettytable"
version = "3.7.0"
description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "prettytable-3.7.0-py3-none-any.whl", hash = "sha256:f4aaf2ed6e6062a82fd2e6e5289bbbe705ec2788fe401a3a1f62a1cea55526d2"},
{file = "prettytable-3.7.0.tar.gz", hash = "sha256:ef8334ee40b7ec721651fc4d37ecc7bb2ef55fde5098d994438f0dfdaa385c0c"},
]
[package.dependencies]
wcwidth = "*"
[package.extras]
tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"]
[[package]]
name = "pyelftools"
version = "0.29"
description = "Library for analyzing ELF files and DWARF debugging information"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "pyelftools-0.29-py2.py3-none-any.whl", hash = "sha256:519f38cf412f073b2d7393aa4682b0190fa901f7c3fa0bff2b82d537690c7fc1"},
{file = "pyelftools-0.29.tar.gz", hash = "sha256:ec761596aafa16e282a31de188737e5485552469ac63b60cfcccf22263fd24ff"},
]
[[package]]
name = "pygments"
version = "2.14.0"
version = "2.15.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
{file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
{file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
{file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
]
[package.extras]
@ -472,14 +521,14 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "pytz"
version = "2022.7.1"
version = "2023.3"
description = "World timezone definitions, modern and historical"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"},
{file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"},
{file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
{file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
]
[[package]]
@ -534,21 +583,21 @@ files = [
[[package]]
name = "requests"
version = "2.28.2"
version = "2.30.0"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=3.7, <4"
python-versions = ">=3.7"
files = [
{file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"},
{file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"},
{file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"},
{file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
@ -556,14 +605,14 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "setuptools"
version = "67.6.0"
version = "67.7.2"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"},
{file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"},
{file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"},
{file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"},
]
[package.extras]
@ -571,6 +620,18 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-g
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
@ -792,20 +853,33 @@ files = [
[[package]]
name = "urllib3"
version = "1.26.15"
version = "2.0.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
python-versions = ">=3.7"
files = [
{file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"},
{file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"},
{file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"},
{file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "wcwidth"
version = "0.2.6"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
{file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
]
[[package]]
name = "wheel"
@ -841,4 +915,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "07432d506e3dc69114203b554d82c1489372ce0087d4a430d0380e437afa5714"
content-hash = "9c25ef33612d10c7caafa551a3cf6a12753167c6400f49cc261fddd18c7eaf6e"

12
pyproject.toml

@ -5,6 +5,12 @@ description = "Trusted Firmware-A (TF-A) Python dependencies."
authors = ["Arm Ltd."]
license = "BSD-3-Clause"
readme = "readme.rst"
packages = [
{ include = "memory", from = "tools/memory"}
]
[tool.poetry.scripts]
memory = "memory.memmap:main"
[tool.poetry.dependencies]
python = "^3.8"
@ -18,3 +24,9 @@ pip-tools = "^6.4.0"
[tool.poetry.group.ci.dependencies]
click = "^8.1.3"
[tool.poetry.group.memory.dependencies]
pyelftools = "^0.29"
anytree = "^2.8.0"
click = "^8.1.3"
prettytable = "^3.5.0"

7
tools/memory/__init__.py

@ -0,0 +1,7 @@
#!/usr/bin/env python3
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#

7
tools/memory/memory/__init__.py

@ -0,0 +1,7 @@
#!/usr/bin/env python3
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#

56
tools/memory/memory/buildparser.py

@ -0,0 +1,56 @@
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import re
from pathlib import Path
from memory.elfparser import TfaElfParser
class TfaBuildParser:
"""A class for performing analysis on the memory layout of a TF-A build."""
def __init__(self, path: Path):
self._modules = dict()
self._path = path
self._parse_modules()
def __getitem__(self, module: str):
"""Returns an TfaElfParser instance indexed by module."""
return self._modules[module]
def _parse_modules(self):
"""Parse ELF files in the build path."""
for elf_file in self._path.glob("**/*.elf"):
module_name = elf_file.name.split("/")[-1].split(".")[0]
with open(elf_file, "rb") as file:
self._modules[module_name] = TfaElfParser(file)
if not len(self._modules):
raise FileNotFoundError(
f"failed to find ELF files in path {self._path}!"
)
@property
def symbols(self) -> list:
return [
(*sym, k) for k, v in self._modules.items() for sym in v.symbols
]
@staticmethod
def filter_symbols(symbols: list, regex: str = None) -> list:
"""Returns a map of symbols to modules."""
regex = r".*" if not regex else regex
return sorted(
filter(lambda s: re.match(regex, s[0]), symbols),
key=lambda s: (-s[1], s[0]),
reverse=True,
)
@property
def module_names(self):
"""Returns sorted list of module names."""
return sorted(self._modules.keys())

33
tools/memory/memory/elfparser.py

@ -0,0 +1,33 @@
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
from typing import BinaryIO
from elftools.elf.elffile import ELFFile
class TfaElfParser:
"""A class representing an ELF file built for TF-A.
Provides a basic interface for reading the symbol table and other
attributes of an ELF file. The constructor accepts a file-like object with
the contents an ELF file.
"""
def __init__(self, elf_file: BinaryIO):
self._segments = {}
self._memory_layout = {}
elf = ELFFile(elf_file)
self._symbols = {
sym.name: sym.entry["st_value"]
for sym in elf.get_section_by_name(".symtab").iter_symbols()
}
@property
def symbols(self):
return self._symbols.items()

78
tools/memory/memory/memmap.py

@ -0,0 +1,78 @@
#!/usr/bin/env python3
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
from pathlib import Path
import click
from memory.buildparser import TfaBuildParser
from memory.printer import TfaPrettyPrinter
@click.command()
@click.option(
"-r",
"--root",
type=Path,
default=None,
help="Root containing build output.",
)
@click.option(
"-p",
"--platform",
show_default=True,
default="fvp",
help="The platform targeted for analysis.",
)
@click.option(
"-b",
"--build-type",
default="release",
help="The target build type.",
type=click.Choice(["debug", "release"], case_sensitive=False),
)
@click.option(
"-s",
"--symbols",
is_flag=True,
show_default=True,
default=True,
help="Generate a map of important TF symbols.",
)
@click.option("-w", "--width", type=int, envvar="COLUMNS")
@click.option(
"-d",
is_flag=True,
default=False,
help="Display numbers in decimal base.",
)
def main(
root: Path,
platform: str,
build_type: str,
symbols: bool,
width: int,
d: bool,
):
build_path = root if root else Path("build/", platform, build_type)
click.echo(f"build-path: {build_path.resolve()}")
parser = TfaBuildParser(build_path)
printer = TfaPrettyPrinter(columns=width, as_decimal=d)
if symbols:
expr = (
r"(.*)(TEXT|BSS|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF"
r"|R.M)(.*)(START|END)__$"
)
printer.print_symbol_table(
parser.filter_symbols(parser.symbols, expr), parser.module_names
)
if __name__ == "__main__":
main()

93
tools/memory/memory/printer.py

@ -0,0 +1,93 @@
#
# Copyright (c) 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
class TfaPrettyPrinter:
"""A class for printing the memory layout of ELF files.
This class provides interfaces for printing various memory layout views of
ELF files in a TF-A build. It can be used to understand how the memory is
structured and consumed.
"""
def __init__(self, columns: int = None, as_decimal: bool = False):
self.term_size = columns if columns and columns > 120 else 120
self._symbol_map = None
self.as_decimal = as_decimal
def format_args(self, *args, width=10, fmt=None):
if not fmt and type(args[0]) is int:
fmt = f">{width}x" if not self.as_decimal else f">{width}"
return [f"{arg:{fmt}}" if fmt else arg for arg in args]
@staticmethod
def map_elf_symbol(
leading: str,
section_name: str,
rel_pos: int,
columns: int,
width: int = None,
is_edge: bool = False,
):
empty_col = "{:{}{}}"
# Some symbols are longer than the column width, truncate them until
# we find a more elegant way to display them!
len_over = len(section_name) - width
if len_over > 0:
section_name = section_name[len_over:-len_over]
sec_row = f"+{section_name:-^{width-1}}+"
sep, fill = ("+", "-") if is_edge else ("|", "")
sec_row_l = empty_col.format(sep, fill + "<", width) * rel_pos
sec_row_r = empty_col.format(sep, fill + ">", width) * (
columns - rel_pos - 1
)
return leading + sec_row_l + sec_row + sec_row_r
def print_symbol_table(
self,
symbols: list,
modules: list,
start: int = 11,
):
assert len(symbols), "Empty symbol list!"
modules = sorted(modules)
col_width = int((self.term_size - start) / len(modules))
num_fmt = "0=#010x" if not self.as_decimal else ">10"
_symbol_map = [
" " * start
+ "".join(self.format_args(*modules, fmt=f"^{col_width}"))
]
last_addr = None
for i, (name, addr, mod) in enumerate(symbols):
# Do not print out an address twice if two symbols overlap,
# for example, at the end of one region and start of another.
leading = (
f"{addr:{num_fmt}}" + " " if addr != last_addr else " " * start
)
_symbol_map.append(
self.map_elf_symbol(
leading,
name,
modules.index(mod),
len(modules),
width=col_width,
is_edge=(not i or i == len(symbols) - 1),
)
)
last_addr = addr
self._symbol_map = ["Memory Layout:"]
self._symbol_map += list(reversed(_symbol_map))
print("\n".join(self._symbol_map))

102
tools/memory/print_memory_map.py

@ -1,102 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2019-2022, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import re
import os
import sys
import operator
# List of folder/map to parse
bl_images = ['bl1', 'bl2', 'bl31']
# List of symbols to search for
blx_symbols = ['__BL1_RAM_START__', '__BL1_RAM_END__',
'__BL2_END__',
'__BL31_END__',
'__RO_START__', '__RO_END_UNALIGNED__', '__RO_END__',
'__TEXT_START__', '__TEXT_END__',
'__TEXT_RESIDENT_START__', '__TEXT_RESIDENT_END__',
'__RODATA_START__', '__RODATA_END__',
'__DATA_START__', '__DATA_END__',
'__STACKS_START__', '__STACKS_END__',
'__BSS_START__', '__BSS_END__',
'__COHERENT_RAM_START__', '__COHERENT_RAM_END__',
'__CPU_OPS_START__', '__CPU_OPS_END__',
'__FCONF_POPULATOR_START__', '__FCONF_POPULATOR_END__',
'__GOT_START__', '__GOT_END__',
'__PARSER_LIB_DESCS_START__', '__PARSER_LIB_DESCS_END__',
'__PMF_TIMESTAMP_START__', '__PMF_TIMESTAMP_END__',
'__PMF_SVC_DESCS_START__', '__PMF_SVC_DESCS_END__',
'__RELA_START__', '__RELA_END__',
'__RT_SVC_DESCS_START__', '__RT_SVC_DESCS_END__',
'__BASE_XLAT_TABLE_START__', '__BASE_XLAT_TABLE_END__',
'__XLAT_TABLE_START__', '__XLAT_TABLE_END__',
]
# Regex to extract address from map file
address_pattern = re.compile(r"\b0x\w*")
# List of found element: [address, symbol, file]
address_list = []
# Get the directory from command line or use a default one
inverted_print = True
if len(sys.argv) >= 2:
build_dir = sys.argv[1]
if len(sys.argv) >= 3:
inverted_print = sys.argv[2] == '0'
else:
build_dir = 'build/fvp/debug'
max_len = max(len(word) for word in blx_symbols) + 2
if (max_len % 2) != 0:
max_len += 1
# Extract all the required symbols from the map files
for image in bl_images:
file_path = os.path.join(build_dir, image, '{}.map'.format(image))
if os.path.isfile(file_path):
with open (file_path, 'rt') as mapfile:
for line in mapfile:
for symbol in blx_symbols:
skip_symbol = 0
# Regex to find symbol definition
line_pattern = re.compile(r"\b0x\w*\s*" + symbol + "\s= .")
match = line_pattern.search(line)
if match:
# Extract address from line
match = address_pattern.search(line)
if match:
if '_END__' in symbol:
sym_start = symbol.replace('_END__', '_START__')
if [match.group(0), sym_start, image] in address_list:
address_list.remove([match.group(0), sym_start, image])
skip_symbol = 1
if skip_symbol == 0:
address_list.append([match.group(0), symbol, image])
# Sort by address
address_list.sort(key=operator.itemgetter(0))
# Invert list for lower address at bottom
if inverted_print:
address_list = reversed(address_list)
# Generate memory view
print(('{:-^%d}' % (max_len * 3 + 20 + 7)).format('Memory Map from: ' + build_dir))
for address in address_list:
if "bl1" in address[2]:
print(address[0], ('+{:-^%d}+ |{:^%d}| |{:^%d}|' % (max_len, max_len, max_len)).format(address[1], '', ''))
elif "bl2" in address[2]:
print(address[0], ('|{:^%d}| +{:-^%d}+ |{:^%d}|' % (max_len, max_len, max_len)).format('', address[1], ''))
elif "bl31" in address[2]:
print(address[0], ('|{:^%d}| |{:^%d}| +{:-^%d}+' % (max_len, max_len, max_len)).format('', '', address[1]))
else:
print(address[0], ('|{:^%d}| |{:^%d}| +{:-^%d}+' % (max_len, max_len, max_len)).format('', '', address[1]))
print(('{:^20}{:_^%d} {:_^%d} {:_^%d}' % (max_len, max_len, max_len)).format('', '', '', ''))
print(('{:^20}{:^%d} {:^%d} {:^%d}' % (max_len, max_len, max_len)).format('address', 'bl1', 'bl2', 'bl31'))
Loading…
Cancel
Save