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 3ec41518a..3dbf28e51 100644 --- a/Makefile +++ b/Makefile @@ -1081,11 +1081,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 @@ -1146,7 +1141,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 \ @@ -1660,9 +1654,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/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 = .; } 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 = .; } 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) 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 = .; } 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 437290a61..44e78d36a 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'))