From af5b49e992de76b9777d1bfa392fb4242c0ff823 Mon Sep 17 00:00:00 2001 From: Harrison Mutai Date: Thu, 23 Feb 2023 10:33:58 +0000 Subject: [PATCH 1/5] 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 --- .versionrc.js | 1 - Makefile | 15 ++- docs/index.rst | 1 + docs/tools/index.rst | 12 +++ docs/tools/memory-layout-tool.rst | 120 ++++++++++++++++++++++++ poetry.lock | 144 ++++++++++++++++++++++------- pyproject.toml | 12 +++ tools/memory/__init__.py | 7 ++ tools/memory/memory/__init__.py | 7 ++ tools/memory/memory/buildparser.py | 56 +++++++++++ tools/memory/memory/elfparser.py | 33 +++++++ tools/memory/memory/memmap.py | 78 ++++++++++++++++ tools/memory/memory/printer.py | 93 +++++++++++++++++++ tools/memory/print_memory_map.py | 102 -------------------- 14 files changed, 535 insertions(+), 146 deletions(-) create mode 100644 docs/tools/index.rst create mode 100644 docs/tools/memory-layout-tool.rst create mode 100644 tools/memory/__init__.py create mode 100644 tools/memory/memory/__init__.py create mode 100755 tools/memory/memory/buildparser.py create mode 100644 tools/memory/memory/elfparser.py create mode 100755 tools/memory/memory/memmap.py create mode 100755 tools/memory/memory/printer.py delete mode 100755 tools/memory/print_memory_map.py diff --git a/.versionrc.js b/.versionrc.js index 9e54c7be7..4e9c71fb5 100644 --- a/.versionrc.js +++ b/.versionrc.js @@ -94,7 +94,6 @@ module.exports = { return contents.replace(/^(version\s=\s")((\d).?)*$/m, _ver) } - }, }, { diff --git a/Makefile b/Makefile index 1a802447a..a92530009 100644 --- a/Makefile +++ b/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" diff --git a/docs/index.rst b/docs/index.rst index d5ab8fcf7..bce9bb74e 100644 --- a/docs/index.rst +++ b/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 diff --git a/docs/tools/index.rst b/docs/tools/index.rst new file mode 100644 index 000000000..2dee2c079 --- /dev/null +++ b/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.* diff --git a/docs/tools/memory-layout-tool.rst b/docs/tools/memory-layout-tool.rst new file mode 100644 index 000000000..ce14dab64 --- /dev/null +++ b/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/ diff --git a/poetry.lock b/poetry.lock index 58522c9b1..92b38da1a 100644 --- a/poetry.lock +++ b/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" diff --git a/pyproject.toml b/pyproject.toml index b99f77727..46353b4ba 100644 --- a/pyproject.toml +++ b/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" diff --git a/tools/memory/__init__.py b/tools/memory/__init__.py new file mode 100644 index 000000000..0b4c8d355 --- /dev/null +++ b/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 +# diff --git a/tools/memory/memory/__init__.py b/tools/memory/memory/__init__.py new file mode 100644 index 000000000..0b4c8d355 --- /dev/null +++ b/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 +# diff --git a/tools/memory/memory/buildparser.py b/tools/memory/memory/buildparser.py new file mode 100755 index 000000000..6f467cd6f --- /dev/null +++ b/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()) diff --git a/tools/memory/memory/elfparser.py b/tools/memory/memory/elfparser.py new file mode 100644 index 000000000..3964e6cb2 --- /dev/null +++ b/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() diff --git a/tools/memory/memory/memmap.py b/tools/memory/memory/memmap.py new file mode 100755 index 000000000..705722870 --- /dev/null +++ b/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() diff --git a/tools/memory/memory/printer.py b/tools/memory/memory/printer.py new file mode 100755 index 000000000..11fd7f021 --- /dev/null +++ b/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)) diff --git a/tools/memory/print_memory_map.py b/tools/memory/print_memory_map.py deleted file mode 100755 index ef53f7ed0..000000000 --- a/tools/memory/print_memory_map.py +++ /dev/null @@ -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')) From 5e7e8bfa4f6ea38d7f001fd1a4a1f266049241c8 Mon Sep 17 00:00:00 2001 From: Harrison Mutai Date: Wed, 19 Apr 2023 09:30:15 +0100 Subject: [PATCH 2/5] build(bl1): add symbols for memory layout Add symbols for mapping the physical memory layout of BL1. There are symbols that partially satisfy this requirement, however, the naming of these is inconsistent. Change-Id: I615a7eb28d17e4c2983636ec021124de304573df Signed-off-by: Harrison Mutai --- bl1/bl1.ld.S | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bl1/bl1.ld.S b/bl1/bl1.ld.S index bec234b1e..a2527e63a 100644 --- a/bl1/bl1.ld.S +++ b/bl1/bl1.ld.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -24,6 +24,11 @@ MEMORY { } SECTIONS { + ROM_REGION_START = ORIGIN(ROM); + ROM_REGION_LENGTH = LENGTH(ROM); + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); + . = BL1_RO_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -97,6 +102,7 @@ SECTIONS { ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__, "cpu_ops not defined for this platform.") + ROM_REGION_END = .; . = BL1_RW_BASE; ASSERT(BL1_RW_BASE == ALIGN(PAGE_SIZE), @@ -157,4 +163,5 @@ SECTIONS { #endif /* USE_COHERENT_MEM */ ASSERT(. <= BL1_RW_LIMIT, "BL1's RW section has exceeded its limit.") + RAM_REGION_END = .; } From f6088168f0608604bc1cd57d8ab52d848fdb835b Mon Sep 17 00:00:00 2001 From: Harrison Mutai Date: Wed, 19 Apr 2023 10:08:56 +0100 Subject: [PATCH 3/5] build(bl2): add symbols for memory layout Add symbols for mapping the physical memory layout of BL2. There are symbols that partially satisfy this requirement, however, the naming of these is inconsistent. Signed-off-by: Harrison Mutai Change-Id: I83ce5e3f5c45b71022279649f823ed0cb33a145d --- bl2/bl2.ld.S | 3 +++ bl2/bl2_el3.ld.S | 13 +++++++++++++ bl2u/bl2u.ld.S | 3 +++ 3 files changed, 19 insertions(+) diff --git a/bl2/bl2.ld.S b/bl2/bl2.ld.S index 458a12b92..5f689d55c 100644 --- a/bl2/bl2.ld.S +++ b/bl2/bl2.ld.S @@ -16,6 +16,8 @@ MEMORY { } SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); . = BL2_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -116,6 +118,7 @@ SECTIONS { __RW_END__ = .; __BL2_END__ = .; + RAM_REGION_END = .; __BSS_SIZE__ = SIZEOF(.bss); diff --git a/bl2/bl2_el3.ld.S b/bl2/bl2_el3.ld.S index aa457faf4..5da631c07 100644 --- a/bl2/bl2_el3.ld.S +++ b/bl2/bl2_el3.ld.S @@ -31,7 +31,12 @@ MEMORY { #endif /* !BL2_IN_XIP_MEM */ SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); #if BL2_IN_XIP_MEM + ROM_REGION_START = ORIGIN(ROM); + ROM_REGION_LENGTH = LENGTH(ROM); + . = BL2_RO_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -43,6 +48,11 @@ SECTIONS { "BL2_BASE address is not aligned on a page boundary.") #endif /* BL2_IN_XIP_MEM */ +#if SEPARATE_BL2_NOLOAD_REGION + RAM_NOLOAD_REGION_START = ORIGIN(RAM_NOLOAD); + RAM_NOLOAD_REGION_LENGTH = LENGTH(RAM_NOLOAD); +#endif + #if SEPARATE_CODE_AND_RODATA .text . : { __TEXT_START__ = .; @@ -109,6 +119,7 @@ SECTIONS { "cpu_ops not defined for this platform.") #if BL2_IN_XIP_MEM + ROM_REGION_END = .; . = BL2_RW_BASE; ASSERT(BL2_RW_BASE == ALIGN(PAGE_SIZE), @@ -138,6 +149,7 @@ SECTIONS { #if SEPARATE_BL2_NOLOAD_REGION __BL2_NOLOAD_END__ = .; + RAM_NOLOAD_REGION_END = .; . = SAVED_ADDR; #endif /* SEPARATE_BL2_NOLOAD_REGION */ @@ -198,6 +210,7 @@ SECTIONS { __COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__; #endif /* USE_COHERENT_MEM */ + RAM_REGION_END = .; #if BL2_IN_XIP_MEM ASSERT(. <= BL2_RW_LIMIT, "BL2's RW content has exceeded its limit.") #else /* BL2_IN_XIP_MEM */ diff --git a/bl2u/bl2u.ld.S b/bl2u/bl2u.ld.S index 52a925bd4..21c91b493 100644 --- a/bl2u/bl2u.ld.S +++ b/bl2u/bl2u.ld.S @@ -18,6 +18,8 @@ MEMORY { } SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); . = BL2U_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -115,4 +117,5 @@ SECTIONS { __BSS_SIZE__ = SIZEOF(.bss); ASSERT(. <= BL2U_LIMIT, "BL2U image has exceeded its limit.") + RAM_REGION_END = .; } From 9b5498a721f68d1e5cad397fc0d84303dbac1557 Mon Sep 17 00:00:00 2001 From: Harrison Mutai Date: Wed, 19 Apr 2023 09:30:15 +0100 Subject: [PATCH 4/5] build(bl31): add symbols for memory layout Add symbols for mapping the physical memory layout of BL31. There are symbols that partially satisfy this requirement, however, the naming of these is inconsistent. Signed-off-by: Harrison Mutai Change-Id: I413cc4e9d7471582eed61d631bed6214bd17a564 --- bl31/bl31.ld.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bl31/bl31.ld.S b/bl31/bl31.ld.S index 5ac83fa59..abcae0cd6 100644 --- a/bl31/bl31.ld.S +++ b/bl31/bl31.ld.S @@ -26,6 +26,8 @@ MEMORY { #endif /* PLAT_EXTRA_LD_SCRIPT */ SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); . = BL31_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -198,6 +200,7 @@ SECTIONS { ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.") #endif /* SEPARATE_NOBITS_REGION */ + RAM_REGION_END = .; /DISCARD/ : { *(.dynsym .dynstr .hash .gnu.hash) From fcb72e16ce901b0a379cc26abc6396be7a18279e Mon Sep 17 00:00:00 2001 From: Harrison Mutai Date: Wed, 19 Apr 2023 09:30:15 +0100 Subject: [PATCH 5/5] build(bl32): add symbols for memory layout Add symbols for mapping the physical memory layout of BL32. There are symbols that partially satisfy this requirement, however, the naming of these is inconsistent. Signed-off-by: Harrison Mutai Change-Id: I106187f93b227d604bda650892f9e919047b3fc7 --- bl32/sp_min/sp_min.ld.S | 3 +++ bl32/tsp/tsp.ld.S | 3 +++ 2 files changed, 6 insertions(+) diff --git a/bl32/sp_min/sp_min.ld.S b/bl32/sp_min/sp_min.ld.S index 1695e1e0a..0a2bad01e 100644 --- a/bl32/sp_min/sp_min.ld.S +++ b/bl32/sp_min/sp_min.ld.S @@ -20,6 +20,8 @@ MEMORY { #endif /* PLAT_SP_MIN_EXTRA_LD_SCRIPT */ SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); . = BL32_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -149,4 +151,5 @@ SECTIONS { } ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.") + RAM_REGION_END = .; } diff --git a/bl32/tsp/tsp.ld.S b/bl32/tsp/tsp.ld.S index a6658dd7c..b735f45e8 100644 --- a/bl32/tsp/tsp.ld.S +++ b/bl32/tsp/tsp.ld.S @@ -16,6 +16,8 @@ MEMORY { } SECTIONS { + RAM_REGION_START = ORIGIN(RAM); + RAM_REGION_LENGTH = LENGTH(RAM); . = BL32_BASE; ASSERT(. == ALIGN(PAGE_SIZE), @@ -121,4 +123,5 @@ SECTIONS { #endif /* USE_COHERENT_MEM */ ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.") + RAM_REGION_END = .; }