Skip to content
Permalink
Browse files

DEVICE_DEFINE(): properly align struct device instances

The DEVICE_DEFINE() macro creates instances of struct device that are
gathered into a contiguous list by the linker. However, some assemblers
pad sections to the next 16-byte boundary or so by default, screwing up
the list walk in z_sys_device_do_config_level(). This is especially
true for 64-bit compilation where sizeof(struct device) isn't a
multiple of 16.

Enforcing an alignment at the linker level would solve this issue when
instances of struct device are gathered from different object files.
However it doesn't solve it when multiple instances are created within
the same object file where the first instance still has a gap with the
next instance, as the assembler does add padding upon section switch
even though the object file ends up with a single section with both
instances. In that case the linker would get rid of the trailing padding
only, leaving the inner gaps between instances in place.

The actual fix is to provide an explicit alignment attribute to the
section for every instances, using __alignof(struct device) which is
the alignment expected by the compiler for that structure.

This also means that the x86_64 workaround in the struct device
definition may go as the "edge case" it refers to is now properly
handled.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
  • Loading branch information...
Nicolas Pitre authored and andrewboie committed May 17, 2019
1 parent 6ecbe71 commit 7daa5451cf0c02594671b8626d968f5b63ec62e4
Showing with 4 additions and 10 deletions.
  1. +4 −10 include/device.h
@@ -108,7 +108,8 @@ extern "C" {
.config_info = (cfg_info) \
}; \
static struct device _CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
__attribute__((__section__(".init_" #level STRINGIFY(prio)), \
aligned(__alignof(struct device)))) = { \
.config = &_CONCAT(__config_, dev_name), \
.driver_api = api, \
.driver_data = data \
@@ -165,7 +166,8 @@ extern "C" {
.config_info = (cfg_info) \
}; \
static struct device _CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
__attribute__((__section__(".init_" #level STRINGIFY(prio)), \
aligned(__alignof(struct device)))) = { \
.config = &_CONCAT(__config_, dev_name), \
.driver_api = api, \
.driver_data = data, \
@@ -276,14 +278,6 @@ struct device {
struct device_config *config;
const void *driver_api;
void *driver_data;
#if defined(__x86_64) && __SIZEOF_POINTER__ == 4
/* The x32 ABI hits an edge case. This is a 12 byte struct,
* but the x86_64 linker will pack them only in units of 8
* bytes, leading to alignment problems when iterating over
* the link-time array.
*/
void *padding;
#endif
};

void z_sys_device_do_config_level(s32_t level);

0 comments on commit 7daa545

Please sign in to comment.
You can’t perform that action at this time.