synth_backend/
linker_script.rs1use synth_core::Result;
6
7#[derive(Debug, Clone)]
9pub struct MemoryRegion {
10 pub name: String,
12 pub origin: u32,
14 pub length: u32,
16 pub attributes: String,
18}
19
20pub struct LinkerScriptGenerator {
22 regions: Vec<MemoryRegion>,
24 entry_point: String,
26 stack_size: u32,
28 heap_size: u32,
30 wasm_memory_size: u32,
32 meld_integration: bool,
34}
35
36impl Default for LinkerScriptGenerator {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl LinkerScriptGenerator {
43 pub fn new_stm32() -> Self {
45 let regions = vec![
46 MemoryRegion {
47 name: "FLASH".to_string(),
48 origin: 0x08000000,
49 length: 512 * 1024, attributes: "rx".to_string(),
51 },
52 MemoryRegion {
53 name: "RAM".to_string(),
54 origin: 0x20000000,
55 length: 128 * 1024, attributes: "rwx".to_string(),
57 },
58 ];
59
60 Self {
61 regions,
62 entry_point: "Reset_Handler".to_string(),
63 stack_size: 4096, heap_size: 8192, wasm_memory_size: 0,
66 meld_integration: false,
67 }
68 }
69
70 pub fn new() -> Self {
72 Self {
73 regions: Vec::new(),
74 entry_point: "Reset_Handler".to_string(),
75 stack_size: 4096,
76 heap_size: 0,
77 wasm_memory_size: 0,
78 meld_integration: false,
79 }
80 }
81
82 pub fn add_region(&mut self, region: MemoryRegion) -> &mut Self {
84 self.regions.push(region);
85 self
86 }
87
88 pub fn with_entry_point(mut self, entry: String) -> Self {
90 self.entry_point = entry;
91 self
92 }
93
94 pub fn with_stack_size(mut self, size: u32) -> Self {
96 self.stack_size = size;
97 self
98 }
99
100 pub fn with_heap_size(mut self, size: u32) -> Self {
102 self.heap_size = size;
103 self
104 }
105
106 pub fn with_wasm_memory(mut self, size: u32) -> Self {
108 self.wasm_memory_size = size;
109 self
110 }
111
112 pub fn with_meld_integration(mut self) -> Self {
114 self.meld_integration = true;
115 self
116 }
117
118 pub fn generate(&self) -> Result<String> {
120 let mut script = String::new();
121
122 script.push_str("/* Generated Linker Script for ARM Cortex-M */\n");
124 script.push_str("/* Generated by Synth */\n\n");
125
126 script.push_str(&format!("ENTRY({})\n\n", self.entry_point));
128
129 if self.meld_integration {
131 script.push_str("/* Meld runtime symbols (provided by meld static library) */\n");
132 script.push_str("EXTERN(__meld_dispatch_import)\n");
133 script.push_str("EXTERN(__meld_get_memory_base)\n\n");
134 }
135
136 script.push_str(&format!("_stack_size = 0x{:X};\n", self.stack_size));
138 if self.heap_size > 0 {
139 script.push_str(&format!("_heap_size = 0x{:X};\n", self.heap_size));
140 }
141 script.push('\n');
142
143 script.push_str("MEMORY\n{\n");
145 for region in &self.regions {
146 script.push_str(&format!(
147 " {} ({}): ORIGIN = 0x{:08X}, LENGTH = 0x{:X}\n",
148 region.name, region.attributes, region.origin, region.length
149 ));
150 }
151 script.push_str("}\n\n");
152
153 script.push_str("SECTIONS\n{\n");
155
156 script.push_str(" .isr_vector :\n");
158 script.push_str(" {\n");
159 script.push_str(" . = ALIGN(256); /* Vector table must be aligned to next power-of-2 >= table size */\n");
160 script.push_str(" KEEP(*(.isr_vector))\n");
161 script.push_str(" . = ALIGN(4);\n");
162 script.push_str(" } >FLASH\n\n");
163
164 script.push_str(" .text :\n");
166 script.push_str(" {\n");
167 script.push_str(" . = ALIGN(4);\n");
168 script.push_str(" *(.text)\n");
169 script.push_str(" *(.text*)\n");
170 if self.meld_integration {
171 script.push_str(" *(.text.synth.*) /* Synth-compiled component code */\n");
172 script.push_str(" *(.text.meld.*) /* Meld runtime */\n");
173 }
174 script.push_str(" *(.glue_7) /* ARM/Thumb interworking */\n");
175 script.push_str(" *(.glue_7t)\n");
176 script.push_str(" *(.eh_frame)\n");
177 script.push_str(" KEEP (*(.init))\n");
178 script.push_str(" KEEP (*(.fini))\n");
179 script.push_str(" . = ALIGN(4);\n");
180 script.push_str(" _etext = .;\n");
181 script.push_str(" } >FLASH\n\n");
182
183 if self.meld_integration {
185 script.push_str(" .meld_import_table :\n");
186 script.push_str(" {\n");
187 script.push_str(" . = ALIGN(4);\n");
188 script.push_str(" __meld_import_table_start = .;\n");
189 script.push_str(" KEEP(*(.meld.imports))\n");
190 script.push_str(" __meld_import_table_end = .;\n");
191 script.push_str(" } >FLASH\n\n");
192 }
193
194 script.push_str(" .rodata :\n");
196 script.push_str(" {\n");
197 script.push_str(" . = ALIGN(4);\n");
198 script.push_str(" *(.rodata)\n");
199 script.push_str(" *(.rodata*)\n");
200 script.push_str(" . = ALIGN(4);\n");
201 script.push_str(" } >FLASH\n\n");
202
203 script.push_str(" .ARM.extab :\n");
205 script.push_str(" {\n");
206 script.push_str(" *(.ARM.extab* .gnu.linkonce.armextab.*)\n");
207 script.push_str(" } >FLASH\n\n");
208
209 script.push_str(" .ARM.exidx :\n");
210 script.push_str(" {\n");
211 script.push_str(" __exidx_start = .;\n");
212 script.push_str(" *(.ARM.exidx* .gnu.linkonce.armexidx.*)\n");
213 script.push_str(" __exidx_end = .;\n");
214 script.push_str(" } >FLASH\n\n");
215
216 script.push_str(" .preinit_array :\n");
218 script.push_str(" {\n");
219 script.push_str(" PROVIDE_HIDDEN (__preinit_array_start = .);\n");
220 script.push_str(" KEEP (*(.preinit_array*))\n");
221 script.push_str(" PROVIDE_HIDDEN (__preinit_array_end = .);\n");
222 script.push_str(" } >FLASH\n\n");
223
224 script.push_str(" .init_array :\n");
225 script.push_str(" {\n");
226 script.push_str(" PROVIDE_HIDDEN (__init_array_start = .);\n");
227 script.push_str(" KEEP (*(SORT(.init_array.*)))\n");
228 script.push_str(" KEEP (*(.init_array*))\n");
229 script.push_str(" PROVIDE_HIDDEN (__init_array_end = .);\n");
230 script.push_str(" } >FLASH\n\n");
231
232 script.push_str(" .fini_array :\n");
233 script.push_str(" {\n");
234 script.push_str(" PROVIDE_HIDDEN (__fini_array_start = .);\n");
235 script.push_str(" KEEP (*(SORT(.fini_array.*)))\n");
236 script.push_str(" KEEP (*(.fini_array*))\n");
237 script.push_str(" PROVIDE_HIDDEN (__fini_array_end = .);\n");
238 script.push_str(" } >FLASH\n\n");
239
240 script.push_str(" _sidata = LOADADDR(.data);\n\n");
242
243 script.push_str(" .data :\n");
245 script.push_str(" {\n");
246 script.push_str(" . = ALIGN(4);\n");
247 script.push_str(" _sdata = .; /* Start of data section */\n");
248 script.push_str(" *(.data)\n");
249 script.push_str(" *(.data*)\n");
250 script.push_str(" . = ALIGN(4);\n");
251 script.push_str(" _edata = .; /* End of data section */\n");
252 script.push_str(" } >RAM AT> FLASH\n\n");
253
254 script.push_str(" .bss :\n");
256 script.push_str(" {\n");
257 script.push_str(" . = ALIGN(4);\n");
258 script.push_str(" _sbss = .; /* Start of BSS section */\n");
259 script.push_str(" __bss_start__ = _sbss;\n");
260 script.push_str(" *(.bss)\n");
261 script.push_str(" *(.bss*)\n");
262 script.push_str(" *(COMMON)\n");
263 script.push_str(" . = ALIGN(4);\n");
264 script.push_str(" _ebss = .; /* End of BSS section */\n");
265 script.push_str(" __bss_end__ = _ebss;\n");
266 script.push_str(" } >RAM\n\n");
267
268 if self.wasm_memory_size > 0 {
270 script.push_str(&format!(
271 " __WASM_MEMORY_SIZE = 0x{:X};\n",
272 self.wasm_memory_size
273 ));
274 script.push_str(" .wasm_linear_memory (NOLOAD) :\n");
275 script.push_str(" {\n");
276 script.push_str(" . = ALIGN(4);\n");
277 script.push_str(" __wasm_memory_start = .;\n");
278 script.push_str(" . = . + __WASM_MEMORY_SIZE;\n");
279 script.push_str(" __wasm_memory_end = .;\n");
280 script.push_str(" } >RAM\n\n");
281 }
282
283 if self.heap_size > 0 {
285 script.push_str(" .heap :\n");
286 script.push_str(" {\n");
287 script.push_str(" . = ALIGN(4);\n");
288 script.push_str(" _sheap = .;\n");
289 script.push_str(" . = . + _heap_size;\n");
290 script.push_str(" . = ALIGN(4);\n");
291 script.push_str(" _eheap = .;\n");
292 script.push_str(" } >RAM\n\n");
293 }
294
295 script.push_str(" .stack :\n");
297 script.push_str(" {\n");
298 script.push_str(" . = ALIGN(8);\n");
299 script.push_str(" _sstack = .;\n");
300 script.push_str(" . = . + _stack_size;\n");
301 script.push_str(" . = ALIGN(8);\n");
302 script.push_str(" _estack = .;\n");
303 script.push_str(" } >RAM\n\n");
304
305 script.push_str(" /DISCARD/ :\n");
307 script.push_str(" {\n");
308 script.push_str(" libc.a ( * )\n");
309 script.push_str(" libm.a ( * )\n");
310 script.push_str(" libgcc.a ( * )\n");
311 script.push_str(" }\n\n");
312
313 script.push_str(" .ARM.attributes 0 : { *(.ARM.attributes) }\n");
315
316 script.push_str("}\n");
317
318 Ok(script)
319 }
320
321 pub fn generate_to_file(&self, path: &str) -> Result<()> {
323 let script = self.generate()?;
324 std::fs::write(path, script)?;
325 Ok(())
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn test_linker_script_generation() {
335 let generator = LinkerScriptGenerator::new_stm32();
336 let script = generator.generate().expect("Failed to generate");
337
338 assert!(script.contains("ENTRY(Reset_Handler)"));
339 assert!(script.contains("MEMORY"));
340 assert!(script.contains("FLASH"));
341 assert!(script.contains("RAM"));
342 assert!(script.contains("SECTIONS"));
343 }
344
345 #[test]
346 fn test_custom_memory_regions() {
347 let mut generator = LinkerScriptGenerator::new();
348 generator.add_region(MemoryRegion {
349 name: "FLASH".to_string(),
350 origin: 0x08000000,
351 length: 1024 * 1024,
352 attributes: "rx".to_string(),
353 });
354
355 let script = generator.generate().expect("Failed to generate");
356 assert!(script.contains("FLASH"));
357 assert!(script.contains("0x08000000"));
358 }
359
360 #[test]
361 fn test_entry_point() {
362 let generator = LinkerScriptGenerator::new_stm32().with_entry_point("main".to_string());
363
364 let script = generator.generate().expect("Failed to generate");
365 assert!(script.contains("ENTRY(main)"));
366 }
367
368 #[test]
369 fn test_stack_configuration() {
370 let generator = LinkerScriptGenerator::new_stm32().with_stack_size(8192);
371
372 let script = generator.generate().expect("Failed to generate");
373 assert!(script.contains("_stack_size = 0x2000")); }
375
376 #[test]
377 fn test_heap_configuration() {
378 let generator = LinkerScriptGenerator::new_stm32().with_heap_size(16384);
379
380 let script = generator.generate().expect("Failed to generate");
381 assert!(script.contains("_heap_size = 0x4000")); assert!(script.contains(".heap"));
383 }
384
385 #[test]
386 fn test_section_alignment() {
387 let generator = LinkerScriptGenerator::new_stm32();
388 let script = generator.generate().expect("Failed to generate");
389
390 assert!(script.contains("ALIGN(256)"));
392 assert!(script.contains("ALIGN(4)"));
394 }
395
396 #[test]
397 fn test_data_section_initialization() {
398 let generator = LinkerScriptGenerator::new_stm32();
399 let script = generator.generate().expect("Failed to generate");
400
401 assert!(script.contains("_sidata"));
403 assert!(script.contains("_sdata"));
404 assert!(script.contains("_edata"));
405 assert!(script.contains(">RAM AT> FLASH"));
406 }
407
408 #[test]
409 fn test_bss_section() {
410 let generator = LinkerScriptGenerator::new_stm32();
411 let script = generator.generate().expect("Failed to generate");
412
413 assert!(script.contains(".bss"));
414 assert!(script.contains("_sbss"));
415 assert!(script.contains("_ebss"));
416 }
417
418 #[test]
419 fn test_meld_integration_sections() {
420 let generator = LinkerScriptGenerator::new_stm32()
421 .with_meld_integration()
422 .with_wasm_memory(64 * 1024); let script = generator.generate().expect("Failed to generate");
425
426 assert!(script.contains("EXTERN(__meld_dispatch_import)"));
428 assert!(script.contains("EXTERN(__meld_get_memory_base)"));
429
430 assert!(script.contains(".meld_import_table"));
432 assert!(script.contains("__meld_import_table_start"));
433 assert!(script.contains("__meld_import_table_end"));
434
435 assert!(script.contains("*(.text.synth.*)"));
437 assert!(script.contains("*(.text.meld.*)"));
438
439 assert!(script.contains(".wasm_linear_memory"));
441 assert!(script.contains("__wasm_memory_start"));
442 assert!(script.contains("__wasm_memory_end"));
443 assert!(script.contains("__WASM_MEMORY_SIZE = 0x10000")); }
445
446 #[test]
447 fn test_isr_vector_section() {
448 let generator = LinkerScriptGenerator::new_stm32();
449 let script = generator.generate().expect("Failed to generate");
450
451 assert!(script.contains(".isr_vector"));
452 assert!(script.contains("KEEP(*(.isr_vector))"));
453 }
454}