mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-01 10:19:01 +00:00
esp32c6: fix .rodata flash mapping being off by a few bytes
The flash MMU maps 64kB pages, so a flash-mapped section is only read correctly when its virtual address and its offset within the firmware image agree modulo 64kB. targets/esp32c6.ld reproduces the image offset in .rodata_dummy by accumulating the preceding segment sizes and headers, which works only if the linker inserts no alignment padding between the dummy and .rodata -- padding moves the virtual address without moving the image offset. lld gives .rodata 8-byte alignment, but the accumulated offset is only 4-byte aligned, so whenever the preceding segments happened to leave the running offset at a 4-mod-8 boundary the whole of .rodata was mapped 4 bytes off. Every read of constant data then returned neighbouring bytes. This is silent and looks like arbitrary memory corruption rather than a mapping bug. It was found while bringing up WiFi: the blob rejected its init config because the first field of a const struct read back as a code pointer, and whether a given build was affected depended on unrelated code size changes. Pad .data, .iram and .text to a multiple of 8 so the running image offset stays 8-aligned and matches, and add ASSERTs for both flash-mapped sections so this cannot regress silently. Signed-off-by: Ron Evans <ron@hybridgroup.com>
This commit is contained in:
+36
-1
@@ -65,7 +65,9 @@ SECTIONS
|
|||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_sbss = ABSOLUTE(.);
|
_sbss = ABSOLUTE(.);
|
||||||
*(.sbss)
|
*(.sbss)
|
||||||
|
*(.sbss.*)
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_ebss = ABSOLUTE(.);
|
_ebss = ABSOLUTE(.);
|
||||||
} >SRAM
|
} >SRAM
|
||||||
@@ -78,10 +80,13 @@ SECTIONS
|
|||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_sdata = ABSOLUTE(.);
|
_sdata = ABSOLUTE(.);
|
||||||
*(.sdata)
|
*(.sdata)
|
||||||
|
*(.sdata.*)
|
||||||
*(.data .data.*)
|
*(.data .data.*)
|
||||||
*(.dram*)
|
*(.dram*)
|
||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_edata = ABSOLUTE(.);
|
_edata = ABSOLUTE(.);
|
||||||
|
/* Keep the image segment a multiple of 8; see the MMU note below. */
|
||||||
|
. = ALIGN (8);
|
||||||
} >SRAM
|
} >SRAM
|
||||||
|
|
||||||
/* Code that must run from IRAM (not flash-mapped), for example interrupt
|
/* Code that must run from IRAM (not flash-mapped), for example interrupt
|
||||||
@@ -113,6 +118,8 @@ SECTIONS
|
|||||||
. = ALIGN(256);
|
. = ALIGN(256);
|
||||||
*(.text.exception_vectors)
|
*(.text.exception_vectors)
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
|
/* Keep the image segment a multiple of 8; see the MMU note below. */
|
||||||
|
. = ALIGN(8);
|
||||||
} > SRAM
|
} > SRAM
|
||||||
|
|
||||||
/* Heap starts after IRAM.
|
/* Heap starts after IRAM.
|
||||||
@@ -149,6 +156,8 @@ SECTIONS
|
|||||||
.text : ALIGN(4)
|
.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
*(.text .text.*)
|
*(.text .text.*)
|
||||||
|
/* Keep the image segment a multiple of 8; see the MMU note below. */
|
||||||
|
. = ALIGN(8);
|
||||||
} > IROM
|
} > IROM
|
||||||
|
|
||||||
/* === DROM sections (flash-mapped read-only data) === */
|
/* === DROM sections (flash-mapped read-only data) === */
|
||||||
@@ -168,13 +177,39 @@ SECTIONS
|
|||||||
. += 0x8; /* rodata segment header */
|
. += 0x8; /* rodata segment header */
|
||||||
} > DROM
|
} > DROM
|
||||||
|
|
||||||
/* Constant global variables, stored in DROM. */
|
/* Constant global variables, stored in DROM.
|
||||||
|
*
|
||||||
|
* The flash MMU maps 64kB pages, so a flash-mapped section is only read
|
||||||
|
* correctly when its virtual address and its offset within the firmware
|
||||||
|
* image agree modulo 64kB. The dummy section above reproduces the image
|
||||||
|
* offset by accumulating the preceding segment sizes and headers, but that
|
||||||
|
* only works if the linker does not then insert alignment padding between
|
||||||
|
* the dummy and this section: the padding moves the virtual address without
|
||||||
|
* moving the image offset. lld gives .rodata 8-byte alignment, so every
|
||||||
|
* preceding segment is padded to a multiple of 8 above to keep the two in
|
||||||
|
* step. Getting this wrong shifts the whole of .rodata by a few bytes, which
|
||||||
|
* is silent and looks like arbitrary data corruption rather than a mapping
|
||||||
|
* bug -- it was found via a WiFi blob rejecting a struct whose first field
|
||||||
|
* read back as a code pointer. The ASSERT after this section catches it.
|
||||||
|
*/
|
||||||
.rodata : ALIGN(4)
|
.rodata : ALIGN(4)
|
||||||
{
|
{
|
||||||
*(.rodata*)
|
*(.rodata*)
|
||||||
|
*(.srodata*)
|
||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
} >DROM
|
} >DROM
|
||||||
|
|
||||||
|
/* The flash MMU constraint described above, enforced at link time for both
|
||||||
|
* flash-mapped sections. */
|
||||||
|
ASSERT(((ADDR(.text) - ORIGIN(IROM)) & 0xffff) ==
|
||||||
|
((0x18 + ((SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0)
|
||||||
|
+ SIZEOF(.iram) + 0x8 + 0x8) & 0xffff),
|
||||||
|
".text virtual address and flash offset disagree modulo 64kB")
|
||||||
|
ASSERT(((ADDR(.rodata) - ORIGIN(DROM)) & 0xffff) ==
|
||||||
|
((0x18 + ((SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0)
|
||||||
|
+ SIZEOF(.iram) + 0x8 + SIZEOF(.text) + 0x8 + 0x8) & 0xffff),
|
||||||
|
".rodata virtual address and flash offset disagree modulo 64kB")
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
*(.eh_frame) /* we don't do exception handling in C */
|
*(.eh_frame) /* we don't do exception handling in C */
|
||||||
|
|||||||
Reference in New Issue
Block a user