Skip to content

Commit de38bb5

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 de38bb5

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

src/rp2_common/pico_standard_link/CMakeLists.txt

+12
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()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
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+
.vectors : {
78+
__logical_binary_start = .;
79+
KEEP (*(.vectors))
80+
} > MAIN_ROM_REGION
81+
82+
.text : {
83+
KEEP (*(.binary_info_header))
84+
__binary_info_header_end = .;
85+
KEEP (*(.reset))
86+
/* TODO revisit this now memset/memcpy/float in ROM */
87+
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
88+
* FLASH ... we will include any thing excluded here in .data below by default */
89+
*(.init)
90+
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
91+
*(.fini)
92+
/* Pull all c'tors into .text */
93+
*crtbegin.o(.ctors)
94+
*crtbegin?.o(.ctors)
95+
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
96+
*(SORT(.ctors.*))
97+
*(.ctors)
98+
/* Followed by destructors */
99+
*crtbegin.o(.dtors)
100+
*crtbegin?.o(.dtors)
101+
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
102+
*(SORT(.dtors.*))
103+
*(.dtors)
104+
105+
*(.eh_frame*)
106+
. = ALIGN(4);
107+
} > MAIN_ROM_REGION
108+
109+
ADDITIONAL_SECTIONS
110+
111+
.rodata : {
112+
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
113+
. = ALIGN(4);
114+
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
115+
. = ALIGN(4);
116+
} > MAIN_ROM_REGION
117+
118+
.ARM.extab :
119+
{
120+
*(.ARM.extab* .gnu.linkonce.armextab.*)
121+
} > MAIN_ROM_REGION
122+
123+
__exidx_start = .;
124+
.ARM.exidx :
125+
{
126+
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
127+
} > MAIN_ROM_REGION
128+
__exidx_end = .;
129+
130+
/* Machine inspectable binary information */
131+
. = ALIGN(4);
132+
__binary_info_start = .;
133+
.binary_info :
134+
{
135+
KEEP(*(.binary_info.keep.*))
136+
*(.binary_info.*)
137+
} > MAIN_ROM_REGION
138+
__binary_info_end = .;
139+
. = ALIGN(4);
140+
141+
.ram_vector_table (NOLOAD): {
142+
*(.ram_vector_table)
143+
} > RAM
144+
145+
.uninitialized_data (NOLOAD): {
146+
. = ALIGN(4);
147+
*(.uninitialized_data*)
148+
} > RAM
149+
150+
.data : {
151+
__data_start__ = .;
152+
*(vtable)
153+
154+
*(.time_critical*)
155+
156+
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
157+
*(.text*)
158+
. = ALIGN(4);
159+
*(.rodata*)
160+
. = ALIGN(4);
161+
162+
*(.data*)
163+
164+
. = ALIGN(4);
165+
*(.after_data.*)
166+
. = ALIGN(4);
167+
/* preinit data */
168+
PROVIDE_HIDDEN (__mutex_array_start = .);
169+
KEEP(*(SORT(.mutex_array.*)))
170+
KEEP(*(.mutex_array))
171+
PROVIDE_HIDDEN (__mutex_array_end = .);
172+
173+
. = ALIGN(4);
174+
/* preinit data */
175+
PROVIDE_HIDDEN (__preinit_array_start = .);
176+
KEEP(*(SORT(.preinit_array.*)))
177+
KEEP(*(.preinit_array))
178+
PROVIDE_HIDDEN (__preinit_array_end = .);
179+
180+
. = ALIGN(4);
181+
/* init data */
182+
PROVIDE_HIDDEN (__init_array_start = .);
183+
KEEP(*(SORT(.init_array.*)))
184+
KEEP(*(.init_array))
185+
PROVIDE_HIDDEN (__init_array_end = .);
186+
187+
. = ALIGN(4);
188+
/* finit data */
189+
PROVIDE_HIDDEN (__fini_array_start = .);
190+
*(SORT(.fini_array.*))
191+
*(.fini_array)
192+
PROVIDE_HIDDEN (__fini_array_end = .);
193+
194+
*(.jcr)
195+
. = ALIGN(4);
196+
/* All data end */
197+
__data_end__ = .;
198+
} > RAM AT> MAIN_ROM_REGION
199+
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
200+
__etext = LOADADDR(.data);
201+
202+
/* Start and end symbols must be word-aligned */
203+
.scratch_x : {
204+
__scratch_x_start__ = .;
205+
*(.scratch_x.*)
206+
. = ALIGN(4);
207+
__scratch_x_end__ = .;
208+
} > SCRATCH_X AT > MAIN_ROM_REGION
209+
__scratch_x_source__ = LOADADDR(.scratch_x);
210+
211+
.scratch_y : {
212+
__scratch_y_start__ = .;
213+
*(.scratch_y.*)
214+
. = ALIGN(4);
215+
__scratch_y_end__ = .;
216+
} > SCRATCH_Y AT > MAIN_ROM_REGION
217+
__scratch_y_source__ = LOADADDR(.scratch_y);
218+
219+
.bss : {
220+
. = ALIGN(4);
221+
__bss_start__ = .;
222+
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
223+
*(COMMON)
224+
. = ALIGN(4);
225+
__bss_end__ = .;
226+
} > RAM
227+
228+
.heap (NOLOAD):
229+
{
230+
__end__ = .;
231+
end = __end__;
232+
KEEP(*(.heap*))
233+
__HeapLimit = .;
234+
} > RAM
235+
236+
/* .stack*_dummy section doesn't contains any symbols. It is only
237+
* used for linker to calculate size of stack sections, and assign
238+
* values to stack symbols later
239+
*
240+
* stack1 section may be empty/missing if platform_launch_core1 is not used */
241+
242+
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
243+
* stack is not used then all of SCRATCH_X is free.
244+
*/
245+
.stack1_dummy (NOLOAD):
246+
{
247+
*(.stack1*)
248+
} > SCRATCH_X
249+
.stack_dummy (NOLOAD):
250+
{
251+
KEEP(*(.stack*))
252+
} > SCRATCH_Y
253+
254+
.flash_end : {
255+
PROVIDE(__flash_binary_end = .);
256+
} > MAIN_ROM_REGION
257+
258+
/* stack limit is poorly named, but historically is maximum heap ptr */
259+
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
260+
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
261+
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
262+
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
263+
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
264+
PROVIDE(__stack = __StackTop);
265+
266+
/* Check if data + heap + stack exceeds RAM limit */
267+
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
268+
269+
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
270+
/* todo assert on extra code */
271+
}
272+

0 commit comments

Comments
 (0)