Hi Rasmus,
On 2026-05-22T21:27:48, Rasmus Villemoes <[email protected]> wrote:
> tools: add linker-lists.py parser/checker script
>
> Add a python script which will make use of the special symbols emitted
> by the linker list macros, and perform various sanity checks. By
> default, it ends with printing a list of all the defined linker lists,
> including their start/end addresses, the size and aligment of
> individual items and the number of items.
>
> With --check, it only does the sanity checking and its exit code
> reflects whether any problems were found. That is eventually intended
> to be done as part of the build.
>
> With --dump, it not only prints the lists and their overall
> properties, but also the names/addresses of each item belong to the
> list.
>
> Signed-off-by: Rasmus Villemoes <[email protected]>
>
> tools/linker-lists.py | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 234 insertions(+)
Perhaps check-linker-lists.py ? BTW I did write a checker script which
works just with the existing linker lists, but I don't think it is as
powerful as the mechanism you have here. I'll send it as an RFC just
for comparison. That is how I found the alignment problems.
Please use single quotes by default for strings (unless you need a
single quote in the string, iwc the outer would should be double
quotes)
> diff --git a/tools/linker-lists.py b/tools/linker-lists.py
> new file mode 100755
> index 00000000000..419879ee31a
> --- /dev/null
> +++ b/tools/linker-lists.py
> @@ -0,0 +1,234 @@
> +#!/usr/bin/python3
> +# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +
> +import sys
> +from argparse import ArgumentParser
> +import subprocess
Please switch to '#!/usr/bin/env python3' as the absolute path is not
portable. Also please add a short module docstring
> diff --git a/tools/linker-lists.py b/tools/linker-lists.py
> @@ -0,0 +1,234 @@
> +class unique_dict(dict):
> + def __init__(self, name, format_spec, *arg, **kw):
> + super(unique_dict, self).__init__(*arg, **kw)
Can this just be super().__init__(...) ?
> + self.name = name
> + self.format_spec = format_spec
> +
> + def __setitem__(self, key, value):
> + if key in self:
> + old = self[key]
> + if value != old:
> + warn(key,
> + f"Inconsistent {self.name} for list '{key}': Old value
> {self.format_spec}, new value {self.format_spec}" %
> + (old, value))
> + return
> + super(unique_dict, self).__setitem__(key, value)
Mixing an f-string with trailing %-formatting is hard to follow - how
about something like::
warn(key, f"Inconsistent {self.name} for list '{key}': "
f"Old value {old:{self.format_spec}}, new value
{value:{self.format_spec}}")
with format_spec stored as 'd' / '#010x' rather than C-style
> diff --git a/tools/linker-lists.py b/tools/linker-lists.py
> @@ -0,0 +1,234 @@
> +def handle_symbol(symbol, address, size):
> + if not symbol.startswith('_u_boot_list_'):
> + return
> +
> + symbol = symbol[13:]
> + if symbol.endswith('_1_start'):
> + assert(size == 0)
> + start_address[symbol[0:-8]] = address
> + return
> +
> + if symbol.endswith('_3_end'):
> + assert(size == 0)
> + end_address[symbol[0:-6]] = address
> + return
> +
> + if symbol.endswith('_0_item_align'):
> + item_alignment[symbol[0:-13]] = size
> + return
> +
> + if symbol.endswith('_0_item_size'):
> + item_size[symbol[0:-12]] = size
> + return
The magic slice offsets ([13:] etc.) are fiddly to verify by eye and
easy to break if any suffix changes. Please use
str.removeprefix()/str.removesuffix() - they are self-documenting and
the offsets disappear.
Also, the two assert(size == 0) calls are the wrong tool - if the ELF
ever contains a malformed start/end marker the script crashes rather
than reports. Since the whole point is sanity checking, better to call
warn() (and carry on) instead?
> diff --git a/tools/linker-lists.py b/tools/linker-lists.py
> @@ -0,0 +1,234 @@
> +subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
> +
> +for line in subp.stdout:
> + if '_u_boot_list_' not in line:
> + continue
> + parser(line)
There is no subp.wait() and no return-code check. If the ELF is
missing, or nm/readelf is not installed, the tool writes its complaint
to stderr and silently reports zero lists - and with --check still
exits 0, which is exactly the failure mode this is meant to catch in
CI. Please check subp.returncode after the loop and fail with a
message. Using 'with subprocess.Popen(...) as subp:' also ensures the
pipe is cleaned up.
> diff --git a/tools/linker-lists.py b/tools/linker-lists.py
> @@ -0,0 +1,234 @@
> +ap = ArgumentParser(description='Linker lists sanity checker')
> +
> +ap.add_argument('--parser', '-p', default='nm', choices=['nm', 'readelf'],
> + help='Program to use to parse the ELF file (nm or readelf)')
The whole body of the script runs at module scope. Please wrap it in a
main() function with the standard
if __name__ == '__main__':
sys.exit(main())
guard. That matches the other tools/ scripts and lets the parsing
helpers be imported by a future test without side effects.
> Add a python script which will make use of the special symbols emitted
> by the linker list macros, and perform various sanity checks. By
> default, it ends with printing a list of all the defined linker lists,
> including their start/end addresses, the size and aligment of
> individual items and the number of items.
Typos: 'alignment', and later 'each item belong to the list' should
read 'belonging to the list'.
Regards,
Simon