Skip to content

Commit 2884b27

Browse files
committed
rp2_common/pico_standard_link: implement customizable linker script using C preprocessor
Background: For cases where modifications to the linker script are required, pico-sdk only provides the option to provide an entire linker script using pico_set_linker_script. This patch adds a function pico_customize_linker_script to create a linker script in the build process, based on a template provided by pico-sdk and local settings provided by the user. Use cases for linker script modification include: - using the linker script as a rudimentary file system for binary blobs - sharing WiFi firmware blobs between bootloader and application (picowota) - reducing the number of code duplication in linker scripts (e.g. memmap_blocked_ram.ld which differs in only one line from the default) In such cases, deriving the linker script from a template may/should lead to code that is easier to maintain. Implementation: The template is memmap.ld.in, the user input is provided by specifying a customization file to be included, and the linker script is generated by the C preprocessor. The template exposes a few settings by #defining them before including the customization file, and provides a few hooks to add elements to the template. Examples and hints for use, based on a working example where cyw43 firmware lives in a separate region in flash: in CMakeLists.txt: pico_customize_linker_script(my_target tweaks.h) tweaks.h: #undef MAIN_FLASH_LENGTH #define MAIN_FLASH_LENGTH 256k #undef ADDITIONAL_FLASH_REGIONS #define ADDITIONAL_FLASH_REGIONS \ FLASH_CYWFW(rx): ORIGIN = 0x10040000, LENGTH = 256k #undef ADDITIONAL_SECTIONS #define ADDITIONAL_SECTIONS \ .fw_cyw43 : { \\ . = ALIGN(4k); \\ KEEP(*(.fw_cyw43.meta)) \\ . = ALIGN(512); \\ *(.fw_cyw43.blob) \\ } > FLASH_CYWFW Details: - The linker script will be a build product named ${TARGET}.ld - When using \\ at the end of lines, newlines are inserted in the resulting linker script in a postprocessing step. This is done to improve readability of the resulting linker script. - Lines starting with # need to be removed from the output; they are now turned into /*comments*/ - Values that are to be overridden need to be #undeffed before redefining them, which is a bit awkward; another option is to use #ifdef for each and every variable in the template; yet another option (for integers) is to use linker variables (i.e. simply setting MAIN_FLASH_LENGTH=256k).
1 parent 6a7db34 commit 2884b27

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed

src/rp2_common/pico_standard_link/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ if (NOT TARGET pico_standard_link)
3939
pico_add_link_depend(${TARGET} ${LDSCRIPT})
4040
endfunction()
4141

42+
function(pico_customize_linker_script TARGET LDSCRIPT_INCLUDE)
43+
set(LDSCRIPT_INPUT ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/memmap.ld.in)
44+
set(LDSCRIPT_GENERATED ${TARGET}.ld)
45+
message("generating linker script at ${LDSCRIPT_GENERATED} from ${LDSCRIPT_INPUT} and ${LDSCRIPT_INCLUDE}")
46+
47+
add_custom_command(TARGET ${TARGET} DEPENDS ${LDSCRIPT_INCLUDE} PRE_BUILD
48+
COMMAND ${CMAKE_C_COMPILER} -E -x c -CC ${CFLAGS} -DLD_INCLUDE=\"${LDSCRIPT_INCLUDE}\" ${LDSCRIPT_INPUT} | sed -e "s@^#.*@/*&*/@" | tr "\\\\" "\\n" > ${LDSCRIPT_GENERATED}
49+
VERBATIM
50+
)
51+
set_target_properties(${TARGET} PROPERTIES PICO_TARGET_LINKER_SCRIPT ${LDSCRIPT_GENERATED})
52+
endfunction()
53+
4254
function(pico_set_binary_type TARGET TYPE)
4355
set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE})
4456
endfunction()
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
/* Based on GCC ARM embedded samples.
2+
Defines the following symbols for use by code:
3+
__exidx_start
4+
__exidx_end
5+
__etext
6+
__data_start__
7+
__preinit_array_start
8+
__preinit_array_end
9+
__init_array_start
10+
__init_array_end
11+
__fini_array_start
12+
__fini_array_end
13+
__data_end__
14+
__bss_start__
15+
__bss_end__
16+
__end__
17+
end
18+
__HeapLimit
19+
__StackLimit
20+
__StackTop
21+
__stack (== StackTop)
22+
*/
23+
24+
/* defaults that can be redefined */
25+
#define MAIN_FLASH_LENGTH 2048k
26+
#define MAIN_RAM_LENGTH 256k
27+
#define MAIN_RAM_ORIGIN 0x20000000
28+
#define MAIN_ROM_REGION FLASH
29+
30+
#define ADDITIONAL_FLASH_REGIONS
31+
#define ADDITIONAL_RAM_REGIONS
32+
#define ADDITIONAL_SECTIONS
33+
34+
#ifdef LD_INCLUDE
35+
#include LD_INCLUDE
36+
#endif
37+
38+
MEMORY
39+
{
40+
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = MAIN_FLASH_LENGTH
41+
ADDITIONAL_FLASH_REGIONS
42+
RAM(rwx) : ORIGIN = MAIN_RAM_ORIGIN, LENGTH = MAIN_RAM_LENGTH
43+
ADDITIONAL_RAM_REGIONS
44+
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
45+
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
46+
}
47+
48+
ENTRY(_entry_point)
49+
50+
SECTIONS
51+
{
52+
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
53+
and checksummed. It is usually built by the boot_stage2 target
54+
in the Raspberry Pi Pico SDK
55+
*/
56+
57+
.flash_begin : {
58+
__flash_binary_start = .;
59+
} > FLASH
60+
61+
.boot2 : {
62+
__boot2_start__ = .;
63+
KEEP (*(.boot2))
64+
__boot2_end__ = .;
65+
} > FLASH
66+
67+
ASSERT(__boot2_end__ - __boot2_start__ == 256,
68+
"ERROR: Pico second stage bootloader must be 256 bytes in size")
69+
70+
/* The second stage will always enter the image at the start of .text.
71+
The debugger will use the ELF entry point, which is the _entry_point
72+
symbol if present, otherwise defaults to start of .text.
73+
This can be used to transfer control back to the bootrom on debugger
74+
launches only, to perform proper flash setup.
75+
*/
76+
77+
.text : {
78+
__logical_binary_start = .;
79+
KEEP (*(.vectors))
80+
KEEP (*(.binary_info_header))
81+
__binary_info_header_end = .;
82+
KEEP (*(.reset))
83+
/* TODO revisit this now memset/memcpy/float in ROM */
84+
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
85+
* FLASH ... we will include any thing excluded here in .data below by default */
86+
*(.init)
87+
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
88+
*(.fini)
89+
/* Pull all c'tors into .text */
90+
*crtbegin.o(.ctors)
91+
*crtbegin?.o(.ctors)
92+
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
93+
*(SORT(.ctors.*))
94+
*(.ctors)
95+
/* Followed by destructors */
96+
*crtbegin.o(.dtors)
97+
*crtbegin?.o(.dtors)
98+
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
99+
*(SORT(.dtors.*))
100+
*(.dtors)
101+
102+
*(.eh_frame*)
103+
. = ALIGN(4);
104+
} > MAIN_ROM_REGION
105+
106+
ADDITIONAL_SECTIONS
107+
108+
.rodata : {
109+
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
110+
. = ALIGN(4);
111+
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
112+
. = ALIGN(4);
113+
} > MAIN_ROM_REGION
114+
115+
.ARM.extab :
116+
{
117+
*(.ARM.extab* .gnu.linkonce.armextab.*)
118+
} > MAIN_ROM_REGION
119+
120+
__exidx_start = .;
121+
.ARM.exidx :
122+
{
123+
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
124+
} > MAIN_ROM_REGION
125+
__exidx_end = .;
126+
127+
/* Machine inspectable binary information */
128+
. = ALIGN(4);
129+
__binary_info_start = .;
130+
.binary_info :
131+
{
132+
KEEP(*(.binary_info.keep.*))
133+
*(.binary_info.*)
134+
} > MAIN_ROM_REGION
135+
__binary_info_end = .;
136+
. = ALIGN(4);
137+
138+
.ram_vector_table (NOLOAD): {
139+
*(.ram_vector_table)
140+
} > RAM
141+
142+
.uninitialized_data (NOLOAD): {
143+
. = ALIGN(4);
144+
*(.uninitialized_data*)
145+
} > RAM
146+
147+
.data : {
148+
__data_start__ = .;
149+
*(vtable)
150+
151+
*(.time_critical*)
152+
153+
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
154+
*(.text*)
155+
. = ALIGN(4);
156+
*(.rodata*)
157+
. = ALIGN(4);
158+
159+
*(.data*)
160+
161+
. = ALIGN(4);
162+
*(.after_data.*)
163+
. = ALIGN(4);
164+
/* preinit data */
165+
PROVIDE_HIDDEN (__mutex_array_start = .);
166+
KEEP(*(SORT(.mutex_array.*)))
167+
KEEP(*(.mutex_array))
168+
PROVIDE_HIDDEN (__mutex_array_end = .);
169+
170+
. = ALIGN(4);
171+
/* preinit data */
172+
PROVIDE_HIDDEN (__preinit_array_start = .);
173+
KEEP(*(SORT(.preinit_array.*)))
174+
KEEP(*(.preinit_array))
175+
PROVIDE_HIDDEN (__preinit_array_end = .);
176+
177+
. = ALIGN(4);
178+
/* init data */
179+
PROVIDE_HIDDEN (__init_array_start = .);
180+
KEEP(*(SORT(.init_array.*)))
181+
KEEP(*(.init_array))
182+
PROVIDE_HIDDEN (__init_array_end = .);
183+
184+
. = ALIGN(4);
185+
/* finit data */
186+
PROVIDE_HIDDEN (__fini_array_start = .);
187+
*(SORT(.fini_array.*))
188+
*(.fini_array)
189+
PROVIDE_HIDDEN (__fini_array_end = .);
190+
191+
*(.jcr)
192+
. = ALIGN(4);
193+
/* All data end */
194+
__data_end__ = .;
195+
} > RAM AT> MAIN_ROM_REGION
196+
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
197+
__etext = LOADADDR(.data);
198+
199+
/* Start and end symbols must be word-aligned */
200+
.scratch_x : {
201+
__scratch_x_start__ = .;
202+
*(.scratch_x.*)
203+
. = ALIGN(4);
204+
__scratch_x_end__ = .;
205+
} > SCRATCH_X AT > MAIN_ROM_REGION
206+
__scratch_x_source__ = LOADADDR(.scratch_x);
207+
208+
.scratch_y : {
209+
__scratch_y_start__ = .;
210+
*(.scratch_y.*)
211+
. = ALIGN(4);
212+
__scratch_y_end__ = .;
213+
} > SCRATCH_Y AT > MAIN_ROM_REGION
214+
__scratch_y_source__ = LOADADDR(.scratch_y);
215+
216+
.bss : {
217+
. = ALIGN(4);
218+
__bss_start__ = .;
219+
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
220+
*(COMMON)
221+
. = ALIGN(4);
222+
__bss_end__ = .;
223+
} > RAM
224+
225+
.heap (NOLOAD):
226+
{
227+
__end__ = .;
228+
end = __end__;
229+
KEEP(*(.heap*))
230+
__HeapLimit = .;
231+
} > RAM
232+
233+
/* .stack*_dummy section doesn't contains any symbols. It is only
234+
* used for linker to calculate size of stack sections, and assign
235+
* values to stack symbols later
236+
*
237+
* stack1 section may be empty/missing if platform_launch_core1 is not used */
238+
239+
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
240+
* stack is not used then all of SCRATCH_X is free.
241+
*/
242+
.stack1_dummy (NOLOAD):
243+
{
244+
*(.stack1*)
245+
} > SCRATCH_X
246+
.stack_dummy (NOLOAD):
247+
{
248+
KEEP(*(.stack*))
249+
} > SCRATCH_Y
250+
251+
.flash_end : {
252+
PROVIDE(__flash_binary_end = .);
253+
} > MAIN_ROM_REGION
254+
255+
/* stack limit is poorly named, but historically is maximum heap ptr */
256+
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
257+
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
258+
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
259+
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
260+
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
261+
PROVIDE(__stack = __StackTop);
262+
263+
/* Check if data + heap + stack exceeds RAM limit */
264+
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
265+
266+
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
267+
/* todo assert on extra code */
268+
}
269+

0 commit comments

Comments
 (0)