summaryrefslogtreecommitdiff
path: root/doc/api/linker_lists.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/api/linker_lists.rst')
-rw-r--r--doc/api/linker_lists.rst59
1 files changed, 59 insertions, 0 deletions
diff --git a/doc/api/linker_lists.rst b/doc/api/linker_lists.rst
index 72f514e0ac..7063fdc831 100644
--- a/doc/api/linker_lists.rst
+++ b/doc/api/linker_lists.rst
@@ -96,5 +96,64 @@ defined for the whole list and each sub-list:
%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: