From 51d2cc2245e850785b078a604e0316f8aa67213a Mon Sep 17 00:00:00 2001 From: Guillaume Souchere Date: Thu, 12 Mar 2026 13:26:31 +0100 Subject: [PATCH] fix(esp_common): Remove alignement in ojects placced in specific sections Let the compiler use variabel natural alignment --- components/esp_common/include/esp_attr.h | 8 ++- .../section_macros/ld/test_section.ld | 10 +++- .../section_macros/main/test_entries_a.c | 12 +++++ .../section_macros/main/test_entries_b.c | 8 +++ .../section_macros/main/test_ptr_entry.h | 26 +++++++++ .../section_macros/main/test_section_macros.c | 53 +++++++++++++++++++ .../section_macros/pytest_section_macros.py | 2 + 7 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 components/esp_common/test_apps/section_macros/main/test_ptr_entry.h diff --git a/components/esp_common/include/esp_attr.h b/components/esp_common/include/esp_attr.h index 101746a93a..a831e61cb8 100644 --- a/components/esp_common/include/esp_attr.h +++ b/components/esp_common/include/esp_attr.h @@ -252,7 +252,7 @@ FORCE_INLINE_ATTR TYPE& operator<<=(TYPE& a, int b) { a = a << b; return a; } #include #define _SECTION_ATTR_IMPL_GENERIC(SECTION, COUNTER) \ - __attribute__((used, aligned(4), section("__DATA," SECTION))) + __attribute__((used, section("__DATA," SECTION))) #define PLACE_IN_SECTION(SECTION) _SECTION_ATTR_IMPL_GENERIC(SECTION, __COUNTER__) @@ -278,8 +278,12 @@ FORCE_INLINE_ATTR TYPE& operator<<=(TYPE& a, int b) { a = a << b; return a; } #else /* ELF targets (Linux and embedded) */ +/* Use the variable's own natural alignment so that pointer arithmetic over + * the section (end - start) gives the correct entry count with no padding gaps. + * On 32-bit embedded targets uint32_t aligns to 4; on 64-bit hosts a struct + * with a pointer member aligns to 8 — both correct without an explicit override. */ #define _SECTION_ATTR_IMPL_GENERIC(SECTION, COUNTER) \ - __attribute__((used, aligned(4), section("." SECTION "." _COUNTER_STRINGIFY(COUNTER)))) + __attribute__((used, section("." SECTION "." _COUNTER_STRINGIFY(COUNTER)))) #define PLACE_IN_SECTION(SECTION) _SECTION_ATTR_IMPL_GENERIC(SECTION, __COUNTER__) diff --git a/components/esp_common/test_apps/section_macros/ld/test_section.ld b/components/esp_common/test_apps/section_macros/ld/test_section.ld index 926d9557c5..e5db75e90b 100644 --- a/components/esp_common/test_apps/section_macros/ld/test_section.ld +++ b/components/esp_common/test_apps/section_macros/ld/test_section.ld @@ -4,7 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 * * Linker script for the section macros test on Linux host builds. - * Collects all uint32_t entries placed in the .test_data_table section. + * Collects all uint32_t entries placed in the .test_data_table section, + * and all test_ptr_entry_t entries placed in the .test_ptr_table section. */ SECTIONS @@ -15,5 +16,12 @@ SECTIONS KEEP(*(SORT(.test_data_table*))) PROVIDE(_test_data_table_end = .); } + + .test_ptr_table : + { + PROVIDE(_test_ptr_table_start = .); + KEEP(*(SORT(.test_ptr_table*))) + PROVIDE(_test_ptr_table_end = .); + } } INSERT AFTER .data; diff --git a/components/esp_common/test_apps/section_macros/main/test_entries_a.c b/components/esp_common/test_apps/section_macros/main/test_entries_a.c index 4a3a16f07a..bcfe16a637 100644 --- a/components/esp_common/test_apps/section_macros/main/test_entries_a.c +++ b/components/esp_common/test_apps/section_macros/main/test_entries_a.c @@ -5,6 +5,7 @@ */ #include "esp_attr.h" +#include "test_ptr_entry.h" #include /* @@ -14,3 +15,14 @@ static const uint32_t entry_a1 PLACE_IN_SECTION("test_data_table") = 0xDEADBEEF; static const uint32_t entry_a2 PLACE_IN_SECTION("test_data_table") = 0xCAFEBABE; static const uint32_t entry_a3 PLACE_IN_SECTION("test_data_table") = 0x12345678; + +/* + * Two pointer-containing struct entries from translation unit A. + * Fields cover every data type size: pointer, u64, u32, u16, u8. + */ +static const test_ptr_entry_t ptr_a1 PLACE_IN_SECTION("test_ptr_table") = { + .name = "ptr_a1", .u32 = 0xA1A2A3A4, .u16 = 0xA5A6, .u8 = 0xA7 +}; +static const test_ptr_entry_t ptr_a2 PLACE_IN_SECTION("test_ptr_table") = { + .name = "ptr_a2", .u32 = 0xB1B2B3B4, .u16 = 0xB5B6, .u8 = 0xB7 +}; diff --git a/components/esp_common/test_apps/section_macros/main/test_entries_b.c b/components/esp_common/test_apps/section_macros/main/test_entries_b.c index bad3e70abc..aac42fdc21 100644 --- a/components/esp_common/test_apps/section_macros/main/test_entries_b.c +++ b/components/esp_common/test_apps/section_macros/main/test_entries_b.c @@ -5,6 +5,7 @@ */ #include "esp_attr.h" +#include "test_ptr_entry.h" #include /* @@ -14,3 +15,10 @@ */ static const uint32_t entry_b1 PLACE_IN_SECTION("test_data_table") = 0xAAAAAAAA; static const uint32_t entry_b2 PLACE_IN_SECTION("test_data_table") = 0x55555555; + +/* + * One pointer-containing struct entry from translation unit B. + */ +static const test_ptr_entry_t ptr_b1 PLACE_IN_SECTION("test_ptr_table") = { + .name = "ptr_b1", .u32 = 0xC1C2C3C4, .u16 = 0xC5C6, .u8 = 0xC7 +}; diff --git a/components/esp_common/test_apps/section_macros/main/test_ptr_entry.h b/components/esp_common/test_apps/section_macros/main/test_ptr_entry.h new file mode 100644 index 0000000000..ee191ea692 --- /dev/null +++ b/components/esp_common/test_apps/section_macros/main/test_ptr_entry.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +/** + * A struct covering every fundamental data type size, used to verify that + * PLACE_IN_SECTION() correctly places and aligns section entries on both + * 32-bit embedded targets and 64-bit host builds (Linux/macOS). + * + * Layout (no padding on any platform when members are in descending size order): + * ptr : pointer — 8 bytes on 64-bit, 4 bytes on 32-bit + * u32 : uint32_t — 4 bytes + * u16 : uint16_t — 2 bytes + * u8 : uint8_t — 1 byte + */ +typedef struct { + const char *name; /* pointer — drives struct alignment on 64-bit */ + uint32_t u32; + uint16_t u16; + uint8_t u8; +} test_ptr_entry_t; diff --git a/components/esp_common/test_apps/section_macros/main/test_section_macros.c b/components/esp_common/test_apps/section_macros/main/test_section_macros.c index c55f810e91..ff2b8c774b 100644 --- a/components/esp_common/test_apps/section_macros/main/test_section_macros.c +++ b/components/esp_common/test_apps/section_macros/main/test_section_macros.c @@ -7,7 +7,9 @@ #include #include #include +#include #include "esp_attr.h" +#include "test_ptr_entry.h" /* * Test for the portable link-time section macros defined in esp_attr.h. @@ -21,6 +23,7 @@ */ _SECTION_ATTR_SYMBOL_DECL_GENERIC(uint32_t, test_data_table) +_SECTION_ATTR_SYMBOL_DECL_GENERIC(test_ptr_entry_t, test_ptr_table) /* Expected values — order is linker-determined, so we check membership */ static const uint32_t expected_values[] = { @@ -78,4 +81,54 @@ void app_main(void) } printf("SUCCESS: All %zu section entries verified.\n", count); + + /* ------------------------------------------------------------------ */ + /* Part 2: pointer-containing struct section */ + /* ------------------------------------------------------------------ */ + + /* expected entries — all fields checked to ensure no data corruption */ + static const test_ptr_entry_t expected_ptr[] = { + { .name = "ptr_a1", .u32 = 0xA1A2A3A4, .u16 = 0xA5A6, .u8 = 0xA7 }, + { .name = "ptr_a2", .u32 = 0xB1B2B3B4, .u16 = 0xB5B6, .u8 = 0xB7 }, + { .name = "ptr_b1", .u32 = 0xC1C2C3C4, .u16 = 0xC5C6, .u8 = 0xC7 }, + }; +#define EXPECTED_PTR_COUNT (sizeof(expected_ptr) / sizeof(expected_ptr[0])) + + const test_ptr_entry_t *pstart = _SECTION_START(test_ptr_table); + const test_ptr_entry_t *pend = _SECTION_END(test_ptr_table); + + size_t pcount = (size_t)(pend - pstart); + printf("Pointer section entry count: %zu (expected %zu)\n", + pcount, (size_t)EXPECTED_PTR_COUNT); + + if (pcount != EXPECTED_PTR_COUNT) { + printf("FAIL: pointer section entry count mismatch\n"); + exit(1); + } + + /* Check each expected entry is present and pointer is valid */ + for (size_t i = 0; i < EXPECTED_PTR_COUNT; i++) { + int found = 0; + for (size_t j = 0; j < pcount; j++) { + /* Verify the pointer is non-NULL and dereferenceable */ + if (pstart[j].name == NULL) { + printf("FAIL: entry %zu has NULL name pointer (alignment issue?)\n", j); + exit(1); + } + if (strcmp(pstart[j].name, expected_ptr[i].name) == 0 && + pstart[j].u32 == expected_ptr[i].u32 && + pstart[j].u16 == expected_ptr[i].u16 && + pstart[j].u8 == expected_ptr[i].u8) { + found = 1; + break; + } + } + if (!found) { + printf("FAIL: expected pointer entry {%s} not found or fields corrupted\n", + expected_ptr[i].name); + exit(1); + } + } + + printf("SUCCESS: All %zu pointer section entries verified.\n", pcount); } diff --git a/components/esp_common/test_apps/section_macros/pytest_section_macros.py b/components/esp_common/test_apps/section_macros/pytest_section_macros.py index 25d26dca70..d1e0a3c350 100644 --- a/components/esp_common/test_apps/section_macros/pytest_section_macros.py +++ b/components/esp_common/test_apps/section_macros/pytest_section_macros.py @@ -10,6 +10,7 @@ from pytest_embedded_idf.utils import idf_parametrize @idf_parametrize('target', ['linux'], indirect=['target']) def test_section_macros(dut: Dut) -> None: dut.expect('SUCCESS: All 5 section entries verified.') + dut.expect('SUCCESS: All 3 pointer section entries verified.') @pytest.mark.host_test @@ -17,3 +18,4 @@ def test_section_macros(dut: Dut) -> None: @idf_parametrize('target', ['linux'], indirect=['target']) def test_section_macros_macos(dut: Dut) -> None: dut.expect('SUCCESS: All 5 section entries verified.') + dut.expect('SUCCESS: All 3 pointer section entries verified.')