[PATCH] linker_list: Fix ll_entry_get alignment

Simon Glass sjg at google.com
Mon Oct 2 03:16:41 CEST 2023


Hi Sean,

On Sat, 30 Sept 2023 at 09:23, Sean Anderson <seanga2 at gmail.com> wrote:
>
> On 9/30/23 10:36, Sean Anderson wrote:
> > When ll_entry_get is used on a list entry ll_entry_declare'd in the same
> > file, the lack of alignment on the access will override the
> > ll_entry_declare alignment. This causes GCC to use the default section
> > alignment of 32 bytes. As list entries are not necessarily 32-byte aligned,
> > this will cause a gap in the linker list, corrupting further entries.
> >
> > As a specific example, get_fs_loader uses DM_DRIVER_GET(fs_loader) in the
> > same file where U_BOOT_DRIVER(fs_loader) is present. This causes a crash
> > when walking the driver list.
> >
> > Fix this by adding appropriate alignment to all accesses.
> >
> > Fixes: 42ebaae3a33 ("common: Implement support for linker-generated arrays")
> > Signed-off-by: Sean Anderson <seanga2 at gmail.com>
> > ---
> >
> >   include/linker_lists.h | 5 +++--
> >   1 file changed, 3 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/linker_lists.h b/include/linker_lists.h
> > index f9a2ee0c762..e0c8a01b9ba 100644
> > --- a/include/linker_lists.h
> > +++ b/include/linker_lists.h
> > @@ -209,7 +209,8 @@
> >    */
> >   #define ll_entry_get(_type, _name, _list)                           \
> >       ({                                                              \
> > -             extern _type _u_boot_list_2_##_list##_2_##_name;        \
> > +             extern _type __aligned(4)                               \
> > +                     _u_boot_list_2_##_list##_2_##_name;             \
> >               _type *_ll_result =                                     \
> >                       &_u_boot_list_2_##_list##_2_##_name;            \
> >               _ll_result;                                             \
> > @@ -229,7 +230,7 @@
> >    * @_list: name of the list
> >    */
> >   #define ll_entry_ref(_type, _name, _list)                           \
> > -     ((_type *)&_u_boot_list_2_##_list##_2_##_name)
> > +     ((_type __aligned(4) *)&_u_boot_list_2_##_list##_2_##_name)
>
> OK, so this causes an error in clang. And it isn't really necessary
> because the entry is already declared at this point.
>
> So I guess the right fix is to replace DM_DRIVER_GET with DM_DRIVER_REF in
> get_fs_loader. But this seems like a really big footgun. You can use the
> wrong one and there are no errors except at runtime. I wonder if we can add
> a warning of some kind?

I can imagine having a runtime check, something like:

ll_check(sizeof(struct something))

which checks that the linker list (end - start) is a multiple of the
struct size. Do you think that would find the problem?

If so, then it could be perhaps be turned into a link-time check. This
produces a list of the linker lists along with their individual
members:

or ll in $(nm /tmp/b/coreboot/u-boot |grep u_boot_list_2 |sed
's/.*_u_boot_list_2_\(.*\)_2_.*/\1/' |uniq); do echo; echo "linker
list: %ll"; nm /tmp/b/coreboot/u-boot |grep $ll; done

...
linker list: ut_str_test
011a9a20 D _u_boot_list_2_ut_str_test_2_str_dectoul
011a9a30 D _u_boot_list_2_ut_str_test_2_str_hextoul
011a9a40 D _u_boot_list_2_ut_str_test_2_str_itoa
011a9a50 D _u_boot_list_2_ut_str_test_2_str_simple_strtoul
011a9a60 D _u_boot_list_2_ut_str_test_2_str_simple_strtoull
011a9a70 D _u_boot_list_2_ut_str_test_2_str_trailing
011a9a80 D _u_boot_list_2_ut_str_test_2_str_upper
011a9a90 D _u_boot_list_2_ut_str_test_2_str_xtoa
011a9aa0 D _u_boot_list_2_ut_str_test_2_test_str_to_list
...

Then you can check that the address of each one increments by the same amount.

Maybe.

Regards,
Simon


More information about the U-Boot mailing list