Skip to content

Commit 0e30a22

Browse files
committed
refactor(riscv-rt): template linker scripts with minilink
1 parent 72a7ed9 commit 0e30a22

File tree

5 files changed

+86
-110
lines changed

5 files changed

+86
-110
lines changed

riscv-rt/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ targets = [
2222

2323
[build-dependencies]
2424
riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.2" }
25+
minilink = "0.2"
2526

2627
[dependencies]
2728
riscv = { path = "../riscv", version = "0.14.0" }

riscv-rt/build.rs

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,19 @@
11
// NOTE: Adapted from cortex-m/build.rs
22

3-
use riscv_target_parser::RiscvTarget;
4-
use std::{env, fs, io, path::PathBuf};
3+
use riscv_target_parser::{RiscvTarget, Width};
4+
use std::env;
55

66
// List of all possible RISC-V configurations to check for in risv-rt
77
const RISCV_CFG: [&str; 4] = ["riscvi", "riscvm", "riscvf", "riscvd"];
88

9-
fn add_linker_script(arch_width: u32) -> io::Result<()> {
10-
// Read the file to a string and replace all occurrences of ${ARCH_WIDTH} with the arch width
11-
let mut content = fs::read_to_string("link.x.in")?;
12-
content = content.replace("${ARCH_WIDTH}", &arch_width.to_string());
13-
14-
// Get target-dependent linker configuration and replace ${INCLUDE_LINKER_FILES} with it
15-
let mut include_content = String::new();
16-
17-
// If no-exceptions is disabled, include the exceptions.x files
18-
if env::var_os("CARGO_FEATURE_NO_EXCEPTIONS").is_none() {
19-
let exceptions_content = fs::read_to_string("exceptions.x")?;
20-
include_content.push_str(&(exceptions_content + "\n"));
21-
}
22-
// If no-interrupts is disabled, include the interrupts.x files
23-
if env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none() {
24-
let interrupts_content = fs::read_to_string("interrupts.x")?;
25-
include_content.push_str(&(interrupts_content + "\n"));
26-
}
27-
// If device is enabled, include the device.x file (usually, provided by PACs)
28-
if env::var_os("CARGO_FEATURE_DEVICE").is_some() {
29-
include_content.push_str("/* Device-specific exception and interrupt handlers */\n");
30-
include_content.push_str("INCLUDE device.x\n");
9+
fn add_linker_script(arch_width: Width) {
10+
// `CARGO_CFG_TARGET_POINTER_WIDTH` technically be used, but instruction
11+
// alignment and pointer width aren't necessarily the same things.
12+
unsafe {
13+
std::env::set_var("CARGO_CFG_ARCH_WIDTH", arch_width.to_string());
3114
}
32-
// If memory is enabled, include the memory.x file (usually, provided by BSPs)
33-
if env::var_os("CARGO_FEATURE_MEMORY").is_some() {
34-
include_content.push_str("/* Device-specific memory layout */\n");
35-
include_content.push_str("INCLUDE memory.x\n");
36-
}
37-
38-
content = content.replace("${INCLUDE_LINKER_FILES}", &include_content);
3915

40-
// Put the linker script somewhere the linker can find it
41-
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
42-
fs::write(out_dir.join("link.x"), content)?;
43-
println!("cargo:rustc-link-search={}", out_dir.display());
44-
println!("cargo:rerun-if-changed=link.x");
45-
46-
Ok(())
16+
minilink::register_template("./link.x.in", "link.x");
4717
}
4818

4919
fn main() {
@@ -86,6 +56,7 @@ fn main() {
8656
println!("cargo:rustc-cfg={flag}");
8757
}
8858
}
89-
add_linker_script(width.into()).unwrap();
59+
60+
add_linker_script(width);
9061
}
9162
}

riscv-rt/exceptions.x

Lines changed: 0 additions & 23 deletions
This file was deleted.

riscv-rt/interrupts.x

Lines changed: 0 additions & 25 deletions
This file was deleted.

riscv-rt/link.x.in

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,9 @@
1212

1313
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
1414

15-
- In this linker script, you may find symbols that look like `${...}` (e.g., `${ARCH_WIDTH}`).
16-
These are wildcards used by the `build.rs` script to adapt to different target particularities.
17-
Check `build.rs` for more details about these symbols.
18-
1915
- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
20-
the LMA of .data are all `${ARCH_WIDTH}`-byte aligned. These alignments are assumed by the RAM
21-
initialization routine. There's also a second benefit: `${ARCH_WIDTH}`-byte aligned boundaries
16+
the LMA of .data are all `{{ cfg.arch_width }}`-byte aligned. These alignments are assumed by the RAM
17+
initialization routine. There's also a second benefit: `{{ cfg.arch_width }}`-byte aligned boundaries
2218
means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
2319
*/
2420

@@ -67,7 +63,63 @@ PROVIDE(DefaultHandler = abort);
6763
to avoid compilation errors in direct mode, not to allow users to overwrite the symbol. */
6864
PROVIDE(_start_DefaultHandler_trap = _start_trap);
6965

70-
${INCLUDE_LINKER_FILES}
66+
{% if not contains(cfg.feature, "no-exceptions") %}
67+
/* # EXCEPTION HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA
68+
69+
It is possible to define a special handler for each exception type.
70+
By default, all exceptions are handled by ExceptionHandler. However,
71+
users can override these alias by defining the symbol themselves
72+
*/
73+
PROVIDE(InstructionMisaligned = ExceptionHandler);
74+
PROVIDE(InstructionFault = ExceptionHandler);
75+
PROVIDE(IllegalInstruction = ExceptionHandler);
76+
PROVIDE(Breakpoint = ExceptionHandler);
77+
PROVIDE(LoadMisaligned = ExceptionHandler);
78+
PROVIDE(LoadFault = ExceptionHandler);
79+
PROVIDE(StoreMisaligned = ExceptionHandler);
80+
PROVIDE(StoreFault = ExceptionHandler);
81+
PROVIDE(UserEnvCall = ExceptionHandler);
82+
PROVIDE(SupervisorEnvCall = ExceptionHandler);
83+
PROVIDE(MachineEnvCall = ExceptionHandler);
84+
PROVIDE(InstructionPageFault = ExceptionHandler);
85+
PROVIDE(LoadPageFault = ExceptionHandler);
86+
PROVIDE(StorePageFault = ExceptionHandler);
87+
{% endif %}
88+
89+
{% if not contains(cfg.feature, "no-interrupts") %}
90+
/* # CORE INTERRUPT HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA
91+
92+
It is possible to define a special handler for each interrupt type.
93+
By default, all interrupts are handled by DefaultHandler. However, users can
94+
override these alias by defining the symbol themselves
95+
*/
96+
PROVIDE(SupervisorSoft = DefaultHandler);
97+
PROVIDE(MachineSoft = DefaultHandler);
98+
PROVIDE(SupervisorTimer = DefaultHandler);
99+
PROVIDE(MachineTimer = DefaultHandler);
100+
PROVIDE(SupervisorExternal = DefaultHandler);
101+
PROVIDE(MachineExternal = DefaultHandler);
102+
103+
/* When vectored trap mode is enabled, each interrupt source must implement its own
104+
trap entry point. By default, all interrupts start in _DefaultHandler_trap.
105+
However, users can override these alias by defining the symbol themselves */
106+
PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap);
107+
PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap);
108+
PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap);
109+
PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap);
110+
PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap);
111+
PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap);
112+
{% endif %}
113+
114+
{% if contains(cfg.feature, "device")%}
115+
/* Device-specific exception and interrupt handlers, usually provided by PACs */
116+
INCLUDE device.x
117+
{% endif %}
118+
119+
{% if contains(cfg.feature, "memory")%}
120+
/* Device-specific memory layout, usually provided by BSPs */
121+
INCLUDE memory.x
122+
{% endif %}
71123

72124
PROVIDE(_stext = ORIGIN(REGION_TEXT));
73125
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
@@ -108,16 +160,16 @@ SECTIONS
108160
*(.srodata .srodata.*);
109161
*(.rodata .rodata.*);
110162

111-
/* ${ARCH_WIDTH}-byte align the end (VMA) of this section.
163+
/* {{ cfg.arch_width }}-byte align the end (VMA) of this section.
112164
This is required by LLD to ensure the LMA of the following .data
113165
section will have the correct alignment. */
114-
. = ALIGN(${ARCH_WIDTH});
166+
. = ALIGN({{ cfg.arch_width }});
115167
__erodata = .;
116168
} > REGION_RODATA
117169

118-
.data : ALIGN(${ARCH_WIDTH})
170+
.data : ALIGN({{ cfg.arch_width }})
119171
{
120-
. = ALIGN(${ARCH_WIDTH});
172+
. = ALIGN({{ cfg.arch_width }});
121173
__sdata = .;
122174

123175
/* Must be called __global_pointer$ for linker relaxations to work. */
@@ -130,15 +182,15 @@ SECTIONS
130182
/* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
131183
* use the .data loading mechanism by pushing __edata. Note: do not change
132184
* output region or load region in those user sections! */
133-
. = ALIGN(${ARCH_WIDTH});
185+
. = ALIGN({{ cfg.arch_width }});
134186
__edata = .;
135187

136188
/* LMA of .data */
137189
__sidata = LOADADDR(.data);
138190

139-
.bss (NOLOAD) : ALIGN(${ARCH_WIDTH})
191+
.bss (NOLOAD) : ALIGN({{ cfg.arch_width }})
140192
{
141-
. = ALIGN(${ARCH_WIDTH});
193+
. = ALIGN({{ cfg.arch_width }});
142194
__sbss = .;
143195

144196
*(.sbss .sbss.* .bss .bss.*);
@@ -147,7 +199,7 @@ SECTIONS
147199
/* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
148200
* use the .bss zeroing mechanism by pushing __ebss. Note: do not change
149201
* output region or load region in those user sections! */
150-
. = ALIGN(${ARCH_WIDTH});
202+
. = ALIGN({{ cfg.arch_width }});
151203
__ebss = .;
152204

153205
/* fictitious region that represents the memory available for the heap */
@@ -184,8 +236,8 @@ ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
184236
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
185237
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
186238

187-
ASSERT(ORIGIN(REGION_DATA) % ${ARCH_WIDTH} == 0, "
188-
ERROR(riscv-rt): the start of the REGION_DATA must be ${ARCH_WIDTH}-byte aligned");
239+
ASSERT(ORIGIN(REGION_DATA) % {{ cfg.arch_width }} == 0, "
240+
ERROR(riscv-rt): the start of the REGION_DATA must be {{ cfg.arch_width }}-byte aligned");
189241

190242
ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
191243
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
@@ -196,14 +248,14 @@ ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
196248
ASSERT(_stext % 4 == 0, "
197249
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
198250

199-
ASSERT(__sdata % ${ARCH_WIDTH} == 0 && __edata % ${ARCH_WIDTH} == 0, "
200-
BUG(riscv-rt): .data is not ${ARCH_WIDTH}-byte aligned");
251+
ASSERT(__sdata % {{ cfg.arch_width }} == 0 && __edata % {{ cfg.arch_width }} == 0, "
252+
BUG(riscv-rt): .data is not {{ cfg.arch_width }}-byte aligned");
201253

202-
ASSERT(__sidata % ${ARCH_WIDTH} == 0, "
203-
BUG(riscv-rt): the LMA of .data is not ${ARCH_WIDTH}-byte aligned");
254+
ASSERT(__sidata % {{ cfg.arch_width }} == 0, "
255+
BUG(riscv-rt): the LMA of .data is not {{ cfg.arch_width }}-byte aligned");
204256

205-
ASSERT(__sbss % ${ARCH_WIDTH} == 0 && __ebss % ${ARCH_WIDTH} == 0, "
206-
BUG(riscv-rt): .bss is not ${ARCH_WIDTH}-byte aligned");
257+
ASSERT(__sbss % {{ cfg.arch_width }} == 0 && __ebss % {{ cfg.arch_width }} == 0, "
258+
BUG(riscv-rt): .bss is not {{ cfg.arch_width }}-byte aligned");
207259

208260
ASSERT(__sheap % 4 == 0, "
209261
BUG(riscv-rt): start of .heap is not 4-byte aligned");

0 commit comments

Comments
 (0)