~funderscore blog cgit wiki get in touch
aboutsummaryrefslogtreecommitdiff
blob: 3cd447f187dfeb21f9a657d222d5aeb95ccdda9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
.. SPDX-License-Identifier: GPL-2.0+

Linker-Generated Arrays
=======================

A linker list is constructed by grouping together linker input
sections, each containing one entry of the list. Each input section
contains a constant initialized variable which holds the entry's
content. Linker list input sections are constructed from the list
and entry names, plus a prefix which allows grouping all lists
together. Assuming _list and _entry are the list and entry names,
then the corresponding input section name is

::

  __u_boot_list_ + 2_ + @_list + _2_ + @_entry

and the C variable name is

::

  _u_boot_list + _2_ + @_list + _2_ + @_entry

This ensures uniqueness for both input section and C variable name.

Note that the names differ only in the characters, "__" for the
section and "_" for the variable, so that the linker cannot confuse
section and symbol names. From now on, both names will be referred
to as

::

  %u_boot_list_ + 2_ + @_list + _2_ + @_entry

Entry variables need never be referred to directly.

The naming scheme for input sections allows grouping all linker lists
into a single linker output section and grouping all entries for a
single list.

Note the two '_2_' constant components in the names: their presence
allows putting a start and end symbols around a list, by mapping
these symbols to sections names with components "1" (before) and
"3" (after) instead of "2" (within).
Start and end symbols for a list can generally be defined as

::

  %u_boot_list_2_ + @_list + _1_...
  %u_boot_list_2_ + @_list + _3_...

Start and end symbols for the whole of the linker lists area can be
defined as

::

  %u_boot_list_1_...
  %u_boot_list_3_...

Here is an example of the sorted sections which result from a list
"array" made up of three entries : "first", "second" and "third",
iterated at least once.

::

  __u_boot_list_2_array_1
  __u_boot_list_2_array_2_first
  __u_boot_list_2_array_2_second
  __u_boot_list_2_array_2_third
  __u_boot_list_2_array_3

If lists must be divided into sublists (e.g. for iterating only on
part of a list), one can simply give the list a name of the form
'outer_2_inner', where 'outer' is the global list name and 'inner'
is the sub-list name. Iterators for the whole list should use the
global list name ("outer"); iterators for only a sub-list should use
the full sub-list name ("outer_2_inner").

Here is an example of the sections generated from a global list
named "drivers", two sub-lists named "i2c" and "pci", and iterators
defined for the whole list and each sub-list:

::

  %u_boot_list_2_drivers_1
  %u_boot_list_2_drivers_2_i2c_1
  %u_boot_list_2_drivers_2_i2c_2_first
  %u_boot_list_2_drivers_2_i2c_2_first
  %u_boot_list_2_drivers_2_i2c_2_second
  %u_boot_list_2_drivers_2_i2c_2_third
  %u_boot_list_2_drivers_2_i2c_3
  %u_boot_list_2_drivers_2_pci_1
  %u_boot_list_2_drivers_2_pci_2_first
  %u_boot_list_2_drivers_2_pci_2_second
  %u_boot_list_2_drivers_2_pci_2_third
  %u_boot_list_2_drivers_2_pci_3
  %u_boot_list_2_drivers_3

Alignment issues
----------------

The linker script uses alphabetic sorting to group the different linker
lists together. Each group has its own struct and potentially its own
alignment. But when the linker packs the structs together it cannot ensure
that a linker list starts on the expected alignment boundary.

For example, if the first list has a struct size of 8 and we place 3 of
them in the image, that means that the next struct will start at offset
0x18 from the start of the linker_list section. If the next struct has
a size of 16 then it will start at an 8-byte aligned offset, but not a
16-byte aligned offset.

With sandbox on x86_64, a reference to a linker list item using
ll_entry_get() can force alignment of that particular linker_list item,
if it is in the same file as the linker_list item is declared.

Consider this example, where struct driver is 0x80 bytes::

    ll_entry_declare(struct driver, fred, driver)

    ...

    void *p = ll_entry_get(struct driver, fred, driver)

If these two lines of code are in the same file, then the entry is forced
to be aligned at the 'struct driver' alignment, which is 16 bytes. If the
second line of code is in a different file, then no action is taken, since
the compiler cannot update the alignment of the linker_list item.

In the first case, an 8-byte 'fill' region is added::

   __u_boot_list_2_driver_2_testbus_drv
               0x0000000000270018       0x80 test/built-in.o
               0x0000000000270018                _u_boot_list_2_driver_2_testbus_drv
   __u_boot_list_2_driver_2_testfdt1_drv
               0x0000000000270098       0x80 test/built-in.o
               0x0000000000270098                _u_boot_list_2_driver_2_testfdt1_drv
   *fill*         0x0000000000270118        0x8
   __u_boot_list_2_driver_2_testfdt_drv
               0x0000000000270120       0x80 test/built-in.o
               0x0000000000270120                _u_boot_list_2_driver_2_testfdt_drv
   __u_boot_list_2_driver_2_testprobe_drv
               0x00000000002701a0       0x80 test/built-in.o
               0x00000000002701a0                _u_boot_list_2_driver_2_testprobe_drv

With this, the linker_list no-longer works since items after testfdt1_drv
are not at the expected address.

Ideally we would have a way to tell gcc not to align structs in this way.
It is not clear how we could do this, and in any case it would require us
to adjust every struct used by the linker_list feature.

The simplest fix seems to be to force each separate linker_list to start
on the largest possible boundary that can be required by the compiler. This
is the purpose of CONFIG_LINKER_LIST_ALIGN


.. kernel-doc:: include/linker_lists.h
   :internal: