aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-hal-internal/src/atomic_ring_buffer.rs4
-rw-r--r--embassy-stm32/src/adc/mod.rs1
-rw-r--r--embassy-stm32/src/cordic/mod.rs1
-rw-r--r--embassy-stm32/src/ospi/mod.rs11
-rw-r--r--embassy-time/src/queue_generic.rs118
-rw-r--r--rust-toolchain.toml2
-rw-r--r--tests/riscv32/Cargo.toml4
-rw-r--r--tests/riscv32/link.x214
8 files changed, 275 insertions, 80 deletions
diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs
index 34ceac852..40ad9dc7a 100644
--- a/embassy-hal-internal/src/atomic_ring_buffer.rs
+++ b/embassy-hal-internal/src/atomic_ring_buffer.rs
@@ -478,8 +478,12 @@ mod tests {
478 478
479 #[test] 479 #[test]
480 fn zero_len() { 480 fn zero_len() {
481 let mut b = [0; 0];
482
481 let rb = RingBuffer::new(); 483 let rb = RingBuffer::new();
482 unsafe { 484 unsafe {
485 rb.init(b.as_mut_ptr(), b.len());
486
483 assert_eq!(rb.is_empty(), true); 487 assert_eq!(rb.is_empty(), true);
484 assert_eq!(rb.is_full(), true); 488 assert_eq!(rb.is_full(), true);
485 489
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 8ef68490b..c753d046c 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -52,6 +52,7 @@ trait SealedInstance {
52 #[allow(unused)] 52 #[allow(unused)]
53 fn regs() -> crate::pac::adc::Adc; 53 fn regs() -> crate::pac::adc::Adc;
54 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] 54 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
55 #[allow(unused)]
55 fn common_regs() -> crate::pac::adccommon::AdcCommon; 56 fn common_regs() -> crate::pac::adccommon::AdcCommon;
56 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 57 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
57 fn state() -> &'static State; 58 fn state() -> &'static State;
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs
index 9ac10e714..29b11735e 100644
--- a/embassy-stm32/src/cordic/mod.rs
+++ b/embassy-stm32/src/cordic/mod.rs
@@ -47,6 +47,7 @@ trait SealedInstance {
47 } 47 }
48 48
49 /// Enable global interrupt 49 /// Enable global interrupt
50 #[allow(unused)]
50 fn enable_irq(&self) { 51 fn enable_irq(&self) {
51 Self::regs().csr().modify(|v| v.set_ien(true)) 52 Self::regs().csr().modify(|v| v.set_ien(true))
52 } 53 }
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 398c3298f..c25ac4d24 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -969,17 +969,6 @@ fn finish_dma(regs: Regs) {
969 }); 969 });
970} 970}
971 971
972trait RegsExt {
973 fn dr_ptr<W>(&self) -> *mut W;
974}
975
976impl RegsExt for Regs {
977 fn dr_ptr<W>(&self) -> *mut W {
978 let dr = self.dr();
979 dr.as_ptr() as *mut W
980 }
981}
982
983pub(crate) trait SealedInstance { 972pub(crate) trait SealedInstance {
984 const REGS: Regs; 973 const REGS: Regs;
985} 974}
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
index cf7a986d5..4882afd3e 100644
--- a/embassy-time/src/queue_generic.rs
+++ b/embassy-time/src/queue_generic.rs
@@ -177,9 +177,10 @@ embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new())
177#[cfg(test)] 177#[cfg(test)]
178#[cfg(feature = "mock-driver")] 178#[cfg(feature = "mock-driver")]
179mod tests { 179mod tests {
180 use core::cell::Cell; 180 use core::sync::atomic::{AtomicBool, Ordering};
181 use core::task::{RawWaker, RawWakerVTable, Waker}; 181 use core::task::Waker;
182 use std::rc::Rc; 182 use std::sync::Arc;
183 use std::task::Wake;
183 184
184 use serial_test::serial; 185 use serial_test::serial;
185 186
@@ -188,44 +189,28 @@ mod tests {
188 use crate::{Duration, Instant}; 189 use crate::{Duration, Instant};
189 190
190 struct TestWaker { 191 struct TestWaker {
191 pub awoken: Rc<Cell<bool>>, 192 pub awoken: AtomicBool,
192 pub waker: Waker,
193 } 193 }
194 194
195 impl TestWaker { 195 impl Wake for TestWaker {
196 fn new() -> Self { 196 fn wake(self: Arc<Self>) {
197 let flag = Rc::new(Cell::new(false)); 197 self.awoken.store(true, Ordering::Relaxed);
198 198 }
199 const VTABLE: RawWakerVTable = RawWakerVTable::new(
200 |data: *const ()| {
201 unsafe {
202 Rc::increment_strong_count(data as *const Cell<bool>);
203 }
204 199
205 RawWaker::new(data as _, &VTABLE) 200 fn wake_by_ref(self: &Arc<Self>) {
206 }, 201 self.awoken.store(true, Ordering::Relaxed);
207 |data: *const ()| unsafe {
208 let data = data as *const Cell<bool>;
209 data.as_ref().unwrap().set(true);
210 Rc::decrement_strong_count(data);
211 },
212 |data: *const ()| unsafe {
213 (data as *const Cell<bool>).as_ref().unwrap().set(true);
214 },
215 |data: *const ()| unsafe {
216 Rc::decrement_strong_count(data);
217 },
218 );
219
220 let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE);
221
222 Self {
223 awoken: flag.clone(),
224 waker: unsafe { Waker::from_raw(raw) },
225 }
226 } 202 }
227 } 203 }
228 204
205 fn test_waker() -> (Arc<TestWaker>, Waker) {
206 let arc = Arc::new(TestWaker {
207 awoken: AtomicBool::new(false),
208 });
209 let waker = Waker::from(arc.clone());
210
211 (arc, waker)
212 }
213
229 fn setup() { 214 fn setup() {
230 MockDriver::get().reset(); 215 MockDriver::get().reset();
231 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None); 216 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
@@ -249,11 +234,11 @@ mod tests {
249 234
250 assert_eq!(queue_len(), 0); 235 assert_eq!(queue_len(), 0);
251 236
252 let waker = TestWaker::new(); 237 let (flag, waker) = test_waker();
253 238
254 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); 239 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
255 240
256 assert!(!waker.awoken.get()); 241 assert!(!flag.awoken.load(Ordering::Relaxed));
257 assert_eq!(queue_len(), 1); 242 assert_eq!(queue_len(), 1);
258 } 243 }
259 244
@@ -262,23 +247,23 @@ mod tests {
262 fn test_schedule_same() { 247 fn test_schedule_same() {
263 setup(); 248 setup();
264 249
265 let waker = TestWaker::new(); 250 let (_flag, waker) = test_waker();
266 251
267 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); 252 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
268 253
269 assert_eq!(queue_len(), 1); 254 assert_eq!(queue_len(), 1);
270 255
271 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); 256 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
272 257
273 assert_eq!(queue_len(), 1); 258 assert_eq!(queue_len(), 1);
274 259
275 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); 260 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
276 261
277 assert_eq!(queue_len(), 1); 262 assert_eq!(queue_len(), 1);
278 263
279 let waker2 = TestWaker::new(); 264 let (_flag2, waker2) = test_waker();
280 265
281 QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker); 266 QUEUE.schedule_wake(Instant::from_secs(100), &waker2);
282 267
283 assert_eq!(queue_len(), 2); 268 assert_eq!(queue_len(), 2);
284 } 269 }
@@ -288,21 +273,21 @@ mod tests {
288 fn test_trigger() { 273 fn test_trigger() {
289 setup(); 274 setup();
290 275
291 let waker = TestWaker::new(); 276 let (flag, waker) = test_waker();
292 277
293 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); 278 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
294 279
295 assert!(!waker.awoken.get()); 280 assert!(!flag.awoken.load(Ordering::Relaxed));
296 281
297 MockDriver::get().advance(Duration::from_secs(99)); 282 MockDriver::get().advance(Duration::from_secs(99));
298 283
299 assert!(!waker.awoken.get()); 284 assert!(!flag.awoken.load(Ordering::Relaxed));
300 285
301 assert_eq!(queue_len(), 1); 286 assert_eq!(queue_len(), 1);
302 287
303 MockDriver::get().advance(Duration::from_secs(1)); 288 MockDriver::get().advance(Duration::from_secs(1));
304 289
305 assert!(waker.awoken.get()); 290 assert!(flag.awoken.load(Ordering::Relaxed));
306 291
307 assert_eq!(queue_len(), 0); 292 assert_eq!(queue_len(), 0);
308 } 293 }
@@ -312,18 +297,18 @@ mod tests {
312 fn test_immediate_trigger() { 297 fn test_immediate_trigger() {
313 setup(); 298 setup();
314 299
315 let waker = TestWaker::new(); 300 let (flag, waker) = test_waker();
316 301
317 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); 302 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
318 303
319 MockDriver::get().advance(Duration::from_secs(50)); 304 MockDriver::get().advance(Duration::from_secs(50));
320 305
321 let waker2 = TestWaker::new(); 306 let (flag2, waker2) = test_waker();
322 307
323 QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker); 308 QUEUE.schedule_wake(Instant::from_secs(40), &waker2);
324 309
325 assert!(!waker.awoken.get()); 310 assert!(!flag.awoken.load(Ordering::Relaxed));
326 assert!(waker2.awoken.get()); 311 assert!(flag2.awoken.load(Ordering::Relaxed));
327 assert_eq!(queue_len(), 1); 312 assert_eq!(queue_len(), 1);
328 } 313 }
329 314
@@ -333,30 +318,31 @@ mod tests {
333 setup(); 318 setup();
334 319
335 for i in 1..super::QUEUE_SIZE { 320 for i in 1..super::QUEUE_SIZE {
336 let waker = TestWaker::new(); 321 let (flag, waker) = test_waker();
337 322
338 QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker); 323 QUEUE.schedule_wake(Instant::from_secs(310), &waker);
339 324
340 assert_eq!(queue_len(), i); 325 assert_eq!(queue_len(), i);
341 assert!(!waker.awoken.get()); 326 assert!(!flag.awoken.load(Ordering::Relaxed));
342 } 327 }
343 328
344 let first_waker = TestWaker::new(); 329 let (flag, waker) = test_waker();
345 330
346 QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker); 331 QUEUE.schedule_wake(Instant::from_secs(300), &waker);
347 332
348 assert_eq!(queue_len(), super::QUEUE_SIZE); 333 assert_eq!(queue_len(), super::QUEUE_SIZE);
349 assert!(!first_waker.awoken.get()); 334 assert!(!flag.awoken.load(Ordering::Relaxed));
350 335
351 let second_waker = TestWaker::new(); 336 let (flag2, waker2) = test_waker();
352 337
353 QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker); 338 QUEUE.schedule_wake(Instant::from_secs(305), &waker2);
354 339
355 assert_eq!(queue_len(), super::QUEUE_SIZE); 340 assert_eq!(queue_len(), super::QUEUE_SIZE);
356 assert!(first_waker.awoken.get()); 341 assert!(flag.awoken.load(Ordering::Relaxed));
357 342
358 QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker); 343 let (_flag3, waker3) = test_waker();
344 QUEUE.schedule_wake(Instant::from_secs(320), &waker3);
359 assert_eq!(queue_len(), super::QUEUE_SIZE); 345 assert_eq!(queue_len(), super::QUEUE_SIZE);
360 assert!(second_waker.awoken.get()); 346 assert!(flag2.awoken.load(Ordering::Relaxed));
361 } 347 }
362} 348}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 2f5d17069..57185e217 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,5 +1,5 @@
1[toolchain] 1[toolchain]
2channel = "1.77" 2channel = "1.78"
3components = [ "rust-src", "rustfmt", "llvm-tools" ] 3components = [ "rust-src", "rustfmt", "llvm-tools" ]
4targets = [ 4targets = [
5 "thumbv7em-none-eabi", 5 "thumbv7em-none-eabi",
diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml
index 38fb2deec..94eda3c09 100644
--- a/tests/riscv32/Cargo.toml
+++ b/tests/riscv32/Cargo.toml
@@ -11,8 +11,8 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature
11embassy-time = { version = "0.3.0", path = "../../embassy-time" } 11embassy-time = { version = "0.3.0", path = "../../embassy-time" }
12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
13 13
14riscv-rt = "0.11" 14riscv-rt = "0.12.2"
15riscv = { version = "0.10", features = ["critical-section-single-hart"] } 15riscv = { version = "0.11.1", features = ["critical-section-single-hart"] }
16 16
17 17
18[profile.dev] 18[profile.dev]
diff --git a/tests/riscv32/link.x b/tests/riscv32/link.x
new file mode 100644
index 000000000..4076b0c68
--- /dev/null
+++ b/tests/riscv32/link.x
@@ -0,0 +1,214 @@
1/* # EMBASSY notes
2 This file is a workaround for https://github.com/rust-embedded/riscv/issues/196
3 Remove when fixed upstream.
4*/
5/* # Developer notes
6
7- Symbols that start with a double underscore (__) are considered "private"
8
9- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
10 overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
11 static mut _heap_size }`).
12
13- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
14 symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
15 needed" by any of the preceding objects (linker arguments)
16
17- `PROVIDE` is used to provide default values that can be overridden by a user linker script
18
19- In this linker script, you may find symbols that look like `${...}` (e.g., `4`).
20 These are wildcards used by the `build.rs` script to adapt to different target particularities.
21 Check `build.rs` for more details about these symbols.
22
23- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
24 the LMA of .data are all `4`-byte aligned. These alignments are assumed by the RAM
25 initialization routine. There's also a second benefit: `4`-byte aligned boundaries
26 means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
27*/
28
29PROVIDE(_stext = ORIGIN(REGION_TEXT));
30PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
31PROVIDE(_max_hart_id = 0);
32PROVIDE(_hart_stack_size = 2K);
33PROVIDE(_heap_size = 0);
34
35PROVIDE(InstructionMisaligned = ExceptionHandler);
36PROVIDE(InstructionFault = ExceptionHandler);
37PROVIDE(IllegalInstruction = ExceptionHandler);
38PROVIDE(Breakpoint = ExceptionHandler);
39PROVIDE(LoadMisaligned = ExceptionHandler);
40PROVIDE(LoadFault = ExceptionHandler);
41PROVIDE(StoreMisaligned = ExceptionHandler);
42PROVIDE(StoreFault = ExceptionHandler);;
43PROVIDE(UserEnvCall = ExceptionHandler);
44PROVIDE(SupervisorEnvCall = ExceptionHandler);
45PROVIDE(MachineEnvCall = ExceptionHandler);
46PROVIDE(InstructionPageFault = ExceptionHandler);
47PROVIDE(LoadPageFault = ExceptionHandler);
48PROVIDE(StorePageFault = ExceptionHandler);
49
50PROVIDE(SupervisorSoft = DefaultHandler);
51PROVIDE(MachineSoft = DefaultHandler);
52PROVIDE(SupervisorTimer = DefaultHandler);
53PROVIDE(MachineTimer = DefaultHandler);
54PROVIDE(SupervisorExternal = DefaultHandler);
55PROVIDE(MachineExternal = DefaultHandler);
56
57PROVIDE(DefaultHandler = DefaultInterruptHandler);
58PROVIDE(ExceptionHandler = DefaultExceptionHandler);
59
60/* # Pre-initialization function */
61/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
62 then the function this points to will be called before the RAM is initialized. */
63PROVIDE(__pre_init = default_pre_init);
64
65/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
66PROVIDE(_setup_interrupts = default_setup_interrupts);
67
68/* # Multi-processing hook function
69 fn _mp_hook() -> bool;
70
71 This function is called from all the harts and must return true only for one hart,
72 which will perform memory initialization. For other harts it must return false
73 and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
74*/
75PROVIDE(_mp_hook = default_mp_hook);
76
77/* # Start trap function override
78 By default uses the riscv crates default trap handler
79 but by providing the `_start_trap` symbol external crates can override.
80*/
81PROVIDE(_start_trap = default_start_trap);
82
83SECTIONS
84{
85 .text.dummy (NOLOAD) :
86 {
87 /* This section is intended to make _stext address work */
88 . = ABSOLUTE(_stext);
89 } > REGION_TEXT
90
91 .text _stext :
92 {
93 /* Put reset handler first in .text section so it ends up as the entry */
94 /* point of the program. */
95 KEEP(*(.init));
96 KEEP(*(.init.rust));
97 . = ALIGN(4);
98 *(.trap);
99 *(.trap.rust);
100 *(.text.abort);
101 *(.text .text.*);
102 } > REGION_TEXT
103
104 .eh_frame : { KEEP(*(.eh_frame)) } > REGION_TEXT
105 .eh_frame_hdr : { *(.eh_frame_hdr) } > REGION_TEXT
106
107 .rodata : ALIGN(4)
108 {
109 *(.srodata .srodata.*);
110 *(.rodata .rodata.*);
111
112 /* 4-byte align the end (VMA) of this section.
113 This is required by LLD to ensure the LMA of the following .data
114 section will have the correct alignment. */
115 . = ALIGN(4);
116 } > REGION_RODATA
117
118 .data : ALIGN(4)
119 {
120 _sidata = LOADADDR(.data);
121 _sdata = .;
122 /* Must be called __global_pointer$ for linker relaxations to work. */
123 PROVIDE(__global_pointer$ = . + 0x800);
124 *(.sdata .sdata.* .sdata2 .sdata2.*);
125 *(.data .data.*);
126 . = ALIGN(4);
127 _edata = .;
128 } > REGION_DATA AT > REGION_RODATA
129
130 .bss (NOLOAD) : ALIGN(4)
131 {
132 _sbss = .;
133 *(.sbss .sbss.* .bss .bss.*);
134 . = ALIGN(4);
135 _ebss = .;
136 } > REGION_BSS
137
138 /* fictitious region that represents the memory available for the heap */
139 .heap (NOLOAD) :
140 {
141 _sheap = .;
142 . += _heap_size;
143 . = ALIGN(4);
144 _eheap = .;
145 } > REGION_HEAP
146
147 /* fictitious region that represents the memory available for the stack */
148 .stack (NOLOAD) :
149 {
150 _estack = .;
151 . = ABSOLUTE(_stack_start);
152 _sstack = .;
153 } > REGION_STACK
154
155 /* fake output .got section */
156 /* Dynamic relocations are unsupported. This section is only used to detect
157 relocatable code in the input files and raise an error if relocatable code
158 is found */
159 .got (INFO) :
160 {
161 KEEP(*(.got .got.*));
162 }
163}
164
165/* Do not exceed this mark in the error messages above | */
166ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
167ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
168
169ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
170ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
171
172ASSERT(ORIGIN(REGION_DATA) % 4 == 0, "
173ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned");
174
175ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
176ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
177
178ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
179ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
180
181ASSERT(ORIGIN(REGION_STACK) % 4 == 0, "
182ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
183
184ASSERT(_stext % 4 == 0, "
185ERROR(riscv-rt): `_stext` must be 4-byte aligned");
186
187ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, "
188BUG(riscv-rt): .data is not 4-byte aligned");
189
190ASSERT(_sidata % 4 == 0, "
191BUG(riscv-rt): the LMA of .data is not 4-byte aligned");
192
193ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, "
194BUG(riscv-rt): .bss is not 4-byte aligned");
195
196ASSERT(_sheap % 4 == 0, "
197BUG(riscv-rt): start of .heap is not 4-byte aligned");
198
199ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), "
200ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.
201Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'");
202
203ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, "
204ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
205Consider changing `_max_hart_id` or `_hart_stack_size`.");
206
207ASSERT(SIZEOF(.got) == 0, "
208.got section detected in the input files. Dynamic relocations are not
209supported. If you are linking to C code compiled using the `gcc` crate
210then modify your build script to compile the C code _without_ the
211-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
212details.");
213
214/* Do not exceed this mark in the error messages above | */