⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/pkg/sdk/
toolchain.rs

1//! Toolchain Management for VeridianOS SDK
2//!
3//! Provides toolchain registration, cross-compilation configuration, and linker
4//! setup for building user-space packages targeting VeridianOS. Supports
5//! x86_64, AArch64, and RISC-V architectures with appropriate defaults for
6//! each target.
7//!
8//! NOTE: Many types in this module are forward declarations for user-space
9//! APIs. They will be exercised when user-space process execution is
10//! functional. See TODO(user-space) markers for specific activation points.
11
12// User-space SDK forward declarations -- see module doc TODO(user-space)
13
14#[cfg(feature = "alloc")]
15use alloc::{
16    collections::BTreeMap,
17    format,
18    string::{String, ToString},
19    vec,
20    vec::Vec,
21};
22
23use crate::error::{KernelError, KernelResult};
24
25// ============================================================================
26// VeridianTarget - compile-time target definitions
27// ============================================================================
28
29/// Describes a supported VeridianOS build target at compile time.
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct VeridianTarget {
32    /// Target triple (e.g. "x86_64-veridian").
33    pub triple: &'static str,
34    /// Architecture short name (e.g. "x86_64").
35    pub arch: &'static str,
36    /// Architecture-specific compiler feature flags.
37    pub features: &'static str,
38}
39
40impl VeridianTarget {
41    /// x86_64 target with soft-float and no red zone for kernel safety.
42    pub const X86_64: VeridianTarget = VeridianTarget {
43        triple: "x86_64-veridian",
44        arch: "x86_64",
45        features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
46    };
47
48    /// AArch64 target using the Cortex-A57 baseline.
49    pub const AARCH64: VeridianTarget = VeridianTarget {
50        triple: "aarch64-veridian",
51        arch: "aarch64",
52        features: "+strict-align",
53    };
54
55    /// RISC-V 64-bit target with GC extensions.
56    pub const RISCV64: VeridianTarget = VeridianTarget {
57        triple: "riscv64gc-veridian",
58        arch: "riscv64",
59        features: "+m,+a,+f,+d,+c",
60    };
61
62    /// Look up a target definition by its triple string.
63    pub fn from_triple(triple: &str) -> Option<VeridianTarget> {
64        match triple {
65            "x86_64-veridian" => Some(Self::X86_64),
66            "aarch64-veridian" => Some(Self::AARCH64),
67            "riscv64gc-veridian" => Some(Self::RISCV64),
68            _ => None,
69        }
70    }
71
72    /// Return the target definition for the current compile-time architecture.
73    pub fn current() -> VeridianTarget {
74        #[cfg(target_arch = "x86_64")]
75        {
76            Self::X86_64
77        }
78        #[cfg(target_arch = "aarch64")]
79        {
80            Self::AARCH64
81        }
82        #[cfg(target_arch = "riscv64")]
83        {
84            Self::RISCV64
85        }
86    }
87}
88
89// ============================================================================
90// ToolchainComponent
91// ============================================================================
92
93/// Identifies a single component within a toolchain installation.
94#[cfg(feature = "alloc")]
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum ToolchainComponent {
97    /// A compiler for the given programming language (e.g. "c", "c++", "rust").
98    Compiler { language: String },
99    /// A system linker.
100    Linker,
101    /// An assembler.
102    Assembler,
103    /// A debugger (e.g. GDB, LLDB).
104    Debugger,
105    /// A profiling tool.
106    Profiler,
107}
108
109// ============================================================================
110// Toolchain
111// ============================================================================
112
113/// A registered toolchain containing one or more components.
114#[cfg(feature = "alloc")]
115#[derive(Debug, Clone)]
116pub struct Toolchain {
117    /// Human-readable name (e.g. "veridian-gcc-13").
118    pub name: String,
119    /// Version string (e.g. "13.2.0").
120    pub version: String,
121    /// Target triple this toolchain produces code for.
122    pub target_triple: String,
123    /// Filesystem path to the toolchain binary directory.
124    pub bin_path: String,
125    /// Filesystem path to the sysroot containing headers and libraries.
126    pub sysroot_path: String,
127    /// Components available in this toolchain.
128    pub components: Vec<ToolchainComponent>,
129}
130
131#[cfg(feature = "alloc")]
132impl Toolchain {
133    /// Create a new toolchain with the given identity and paths.
134    pub fn new(
135        name: &str,
136        version: &str,
137        target_triple: &str,
138        bin_path: &str,
139        sysroot_path: &str,
140    ) -> Self {
141        Self {
142            name: String::from(name),
143            version: String::from(version),
144            target_triple: String::from(target_triple),
145            bin_path: String::from(bin_path),
146            sysroot_path: String::from(sysroot_path),
147            components: Vec::new(),
148        }
149    }
150
151    /// Add a component to this toolchain.
152    pub fn add_component(&mut self, component: ToolchainComponent) {
153        if !self.components.contains(&component) {
154            self.components.push(component);
155        }
156    }
157
158    /// Check whether a specific component type is present.
159    pub fn has_component(&self, component: &ToolchainComponent) -> bool {
160        self.components.contains(component)
161    }
162}
163
164// ============================================================================
165// ToolchainRegistry
166// ============================================================================
167
168/// Registry of known toolchains, keyed by name.
169#[cfg(feature = "alloc")]
170#[derive(Debug, Clone)]
171pub struct ToolchainRegistry {
172    /// All registered toolchains.
173    toolchains: BTreeMap<String, Toolchain>,
174    /// Name of the default toolchain, if set.
175    default_name: Option<String>,
176}
177
178#[cfg(feature = "alloc")]
179impl ToolchainRegistry {
180    /// Create an empty registry.
181    pub fn new() -> Self {
182        Self {
183            toolchains: BTreeMap::new(),
184            default_name: None,
185        }
186    }
187
188    /// Register a toolchain. Returns an error if a toolchain with the same
189    /// name is already registered.
190    pub fn register(&mut self, toolchain: Toolchain) -> KernelResult<()> {
191        if self.toolchains.contains_key(&toolchain.name) {
192            return Err(KernelError::AlreadyExists {
193                resource: "toolchain",
194                id: 0,
195            });
196        }
197        self.toolchains.insert(toolchain.name.clone(), toolchain);
198        Ok(())
199    }
200
201    /// Look up a toolchain by name.
202    pub fn get(&self, name: &str) -> Option<&Toolchain> {
203        self.toolchains.get(name)
204    }
205
206    /// Return the names of all registered toolchains.
207    pub fn list(&self) -> Vec<&str> {
208        self.toolchains.keys().map(|k| k.as_str()).collect()
209    }
210
211    /// Remove a toolchain by name. Returns an error if it does not exist.
212    pub fn remove(&mut self, name: &str) -> KernelResult<Toolchain> {
213        // Clear default if it points to the removed toolchain.
214        if self.default_name.as_deref() == Some(name) {
215            self.default_name = None;
216        }
217        self.toolchains.remove(name).ok_or(KernelError::NotFound {
218            resource: "toolchain",
219            id: 0,
220        })
221    }
222
223    /// Set the default toolchain by name. The toolchain must already be
224    /// registered.
225    pub fn set_default(&mut self, name: &str) -> KernelResult<()> {
226        if !self.toolchains.contains_key(name) {
227            return Err(KernelError::NotFound {
228                resource: "toolchain",
229                id: 0,
230            });
231        }
232        self.default_name = Some(String::from(name));
233        Ok(())
234    }
235
236    /// Return a reference to the default toolchain, if one has been set.
237    pub fn get_default(&self) -> Option<&Toolchain> {
238        self.default_name
239            .as_deref()
240            .and_then(|name| self.toolchains.get(name))
241    }
242}
243
244#[cfg(feature = "alloc")]
245impl Default for ToolchainRegistry {
246    fn default() -> Self {
247        Self::new()
248    }
249}
250
251// ============================================================================
252// CrossCompilerConfig
253// ============================================================================
254
255/// Cross-compilation tool paths for a specific target.
256#[cfg(feature = "alloc")]
257#[derive(Debug, Clone)]
258pub struct CrossCompilerConfig {
259    /// Path to the C compiler.
260    pub cc: String,
261    /// Path to the C++ compiler.
262    pub cxx: String,
263    /// Path to the archiver.
264    pub ar: String,
265    /// Path to the linker.
266    pub ld: String,
267    /// Path to the ranlib tool.
268    pub ranlib: String,
269    /// Path to the strip tool.
270    pub strip: String,
271}
272
273#[cfg(feature = "alloc")]
274impl CrossCompilerConfig {
275    /// Produce a cross-compiler configuration with sensible defaults for the
276    /// given target triple. Uses GNU-style tool naming conventions.
277    pub fn for_target(target: &str) -> Self {
278        let prefix = match target {
279            "x86_64-veridian" | "x86_64-unknown-none" => "x86_64-veridian",
280            "aarch64-veridian" | "aarch64-unknown-none" => "aarch64-veridian",
281            "riscv64gc-veridian" | "riscv64gc-unknown-none-elf" => "riscv64gc-veridian",
282            other => other,
283        };
284        let sysroot = super::get_sysroot();
285
286        Self {
287            cc: format!("{}/bin/{}-gcc", sysroot, prefix),
288            cxx: format!("{}/bin/{}-g++", sysroot, prefix),
289            ar: format!("{}/bin/{}-ar", sysroot, prefix),
290            ld: format!("{}/bin/{}-ld", sysroot, prefix),
291            ranlib: format!("{}/bin/{}-ranlib", sysroot, prefix),
292            strip: format!("{}/bin/{}-strip", sysroot, prefix),
293        }
294    }
295
296    /// Convert the configuration into environment variable key-value pairs
297    /// suitable for passing to a build system.
298    pub fn to_env_vars(&self) -> BTreeMap<String, String> {
299        let mut env = BTreeMap::new();
300        env.insert(String::from("CC"), self.cc.clone());
301        env.insert(String::from("CXX"), self.cxx.clone());
302        env.insert(String::from("AR"), self.ar.clone());
303        env.insert(String::from("LD"), self.ld.clone());
304        env.insert(String::from("RANLIB"), self.ranlib.clone());
305        env.insert(String::from("STRIP"), self.strip.clone());
306        env
307    }
308}
309
310/// Generate a complete set of cross-compilation environment variables for the
311/// given target, including compiler paths, flags, and pkg-config hints.
312#[cfg(feature = "alloc")]
313pub fn generate_cross_env(target: &str) -> BTreeMap<String, String> {
314    let cross = CrossCompilerConfig::for_target(target);
315    let mut env = cross.to_env_vars();
316
317    let sysroot = super::get_sysroot();
318    let target_info = VeridianTarget::from_triple(target);
319
320    // Common compiler flags
321    env.insert(
322        String::from("CFLAGS"),
323        format!("--sysroot={} -ffreestanding -nostdlib", sysroot),
324    );
325    env.insert(
326        String::from("CXXFLAGS"),
327        format!("--sysroot={} -ffreestanding -nostdlib", sysroot),
328    );
329    env.insert(
330        String::from("LDFLAGS"),
331        format!("-L{}/lib/{} -nostdlib", sysroot, target),
332    );
333
334    // pkg-config integration
335    env.insert(
336        String::from("PKG_CONFIG_SYSROOT_DIR"),
337        String::from(sysroot),
338    );
339    env.insert(
340        String::from("PKG_CONFIG_PATH"),
341        format!("{}/lib/{}/pkgconfig", sysroot, target),
342    );
343
344    // Architecture-specific features
345    if let Some(info) = target_info {
346        env.insert(String::from("VERIDIAN_ARCH"), String::from(info.arch));
347        env.insert(String::from("VERIDIAN_TARGET"), String::from(info.triple));
348        env.insert(
349            String::from("VERIDIAN_FEATURES"),
350            String::from(info.features),
351        );
352    }
353
354    env
355}
356
357// ============================================================================
358// LinkerConfig
359// ============================================================================
360
361/// Linker configuration for a specific target architecture.
362#[cfg(feature = "alloc")]
363#[derive(Debug, Clone)]
364pub struct LinkerConfig {
365    /// Path to the linker binary.
366    pub linker_path: String,
367    /// Optional path to a custom linker script.
368    pub linker_script: Option<String>,
369    /// Library search paths passed via `-L`.
370    pub search_paths: Vec<String>,
371    /// Additional linker flags.
372    pub flags: Vec<String>,
373}
374
375#[cfg(feature = "alloc")]
376impl LinkerConfig {
377    /// Produce a linker configuration with sensible defaults for the given
378    /// target triple.
379    pub fn for_target(target: &str) -> Self {
380        let sysroot = super::get_sysroot();
381        let cross = CrossCompilerConfig::for_target(target);
382        let lib_dir = format!("{}/lib/{}", sysroot, target);
383
384        let mut flags = vec![String::from("--gc-sections")];
385
386        // Architecture-specific linker flags
387        match target {
388            "x86_64-veridian" | "x86_64-unknown-none" => {
389                flags.push(String::from("-z"));
390                flags.push(String::from("max-page-size=4096"));
391            }
392            "aarch64-veridian" | "aarch64-unknown-none" => {
393                flags.push(String::from("-z"));
394                flags.push(String::from("max-page-size=65536"));
395            }
396            "riscv64gc-veridian" | "riscv64gc-unknown-none-elf" => {
397                flags.push(String::from("-z"));
398                flags.push(String::from("max-page-size=4096"));
399            }
400            _ => {}
401        }
402
403        Self {
404            linker_path: cross.ld,
405            linker_script: None,
406            search_paths: vec![lib_dir],
407            flags,
408        }
409    }
410}
411
412// ============================================================================
413// Linker Script Generation
414// ============================================================================
415
416/// Generate a basic VeridianOS linker script for the given target architecture.
417///
418/// The script defines entry points and section layout appropriate for each
419/// supported architecture:
420/// - x86_64: kernel mapped at `0xFFFFFFFF80100000` (higher-half)
421/// - aarch64: loaded at `0x40080000` (QEMU virt)
422/// - riscv64: loaded at `0x80200000` (OpenSBI payload)
423#[cfg(feature = "alloc")]
424pub fn generate_linker_script(target: &str) -> String {
425    let (entry, origin) = match target {
426        "x86_64-veridian" | "x86_64-unknown-none" => ("_start", "0xFFFFFFFF80100000"),
427        "aarch64-veridian" | "aarch64-unknown-none" => ("_start", "0x40080000"),
428        "riscv64gc-veridian" | "riscv64gc-unknown-none-elf" => ("_start", "0x80200000"),
429        _ => ("_start", "0x100000"),
430    };
431
432    let mut s = String::new();
433    s.push_str("/* VeridianOS linker script - auto-generated */\n");
434    s.push_str(&format!("ENTRY({})\n\n", entry));
435    s.push_str("SECTIONS\n{\n");
436    s.push_str(&format!("    . = {};\n\n", origin));
437
438    // .text section
439    s.push_str("    .text : ALIGN(4096)\n    {\n");
440    s.push_str("        _text_start = .;\n");
441    s.push_str("        *(.text.boot)\n");
442    s.push_str("        *(.text .text.*)\n");
443    s.push_str("        _text_end = .;\n");
444    s.push_str("    }\n\n");
445
446    // .rodata section
447    s.push_str("    .rodata : ALIGN(4096)\n    {\n");
448    s.push_str("        _rodata_start = .;\n");
449    s.push_str("        *(.rodata .rodata.*)\n");
450    s.push_str("        _rodata_end = .;\n");
451    s.push_str("    }\n\n");
452
453    // .data section
454    s.push_str("    .data : ALIGN(4096)\n    {\n");
455    s.push_str("        _data_start = .;\n");
456    s.push_str("        *(.data .data.*)\n");
457    s.push_str("        _data_end = .;\n");
458    s.push_str("    }\n\n");
459
460    // .bss section
461    s.push_str("    .bss : ALIGN(4096)\n    {\n");
462    s.push_str("        _bss_start = .;\n");
463    s.push_str("        *(.bss .bss.*)\n");
464    s.push_str("        *(COMMON)\n");
465    s.push_str("        _bss_end = .;\n");
466    s.push_str("    }\n\n");
467
468    s.push_str("    _kernel_end = .;\n\n");
469    s.push_str("    /DISCARD/ :\n    {\n");
470    s.push_str("        *(.comment)\n");
471    s.push_str("        *(.note.*)\n");
472    s.push_str("        *(.eh_frame*)\n");
473    s.push_str("    }\n");
474    s.push_str("}\n");
475
476    s
477}
478
479// ============================================================================
480// CMake Toolchain File Generation
481// ============================================================================
482
483/// Generates a CMake toolchain file for cross-compiling to VeridianOS.
484#[cfg(feature = "alloc")]
485#[derive(Debug, Clone)]
486pub struct CMakeToolchainFile {
487    /// Target triple to generate the toolchain file for.
488    pub target: String,
489}
490
491#[cfg(feature = "alloc")]
492impl CMakeToolchainFile {
493    /// Create a new generator for the given target triple.
494    pub fn new(target: &str) -> Self {
495        Self {
496            target: target.to_string(),
497        }
498    }
499
500    /// Generate the contents of a CMake toolchain file.
501    ///
502    /// The output sets `CMAKE_SYSTEM_NAME`, `CMAKE_SYSTEM_PROCESSOR`,
503    /// `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`, `CMAKE_FIND_ROOT_PATH`,
504    /// and related variables so that CMake can cross-compile for VeridianOS.
505    pub fn generate(&self) -> String {
506        let cross = CrossCompilerConfig::for_target(&self.target);
507        let sysroot = super::get_sysroot();
508
509        let processor = match self.target.as_str() {
510            "x86_64-veridian" | "x86_64-unknown-none" => "x86_64",
511            "aarch64-veridian" | "aarch64-unknown-none" => "aarch64",
512            "riscv64gc-veridian" | "riscv64gc-unknown-none-elf" => "riscv64",
513            _ => "unknown",
514        };
515
516        let mut s = String::new();
517        s.push_str("# VeridianOS CMake toolchain file - auto-generated\n\n");
518        s.push_str("set(CMAKE_SYSTEM_NAME VeridianOS)\n");
519        s.push_str(&format!("set(CMAKE_SYSTEM_PROCESSOR {})\n\n", processor));
520
521        s.push_str(&format!("set(CMAKE_C_COMPILER {})\n", cross.cc));
522        s.push_str(&format!("set(CMAKE_CXX_COMPILER {})\n", cross.cxx));
523        s.push_str(&format!("set(CMAKE_AR {})\n", cross.ar));
524        s.push_str(&format!("set(CMAKE_RANLIB {})\n", cross.ranlib));
525        s.push_str(&format!("set(CMAKE_STRIP {})\n", cross.strip));
526        s.push_str(&format!("set(CMAKE_LINKER {})\n\n", cross.ld));
527
528        s.push_str(&format!("set(CMAKE_SYSROOT {})\n", sysroot));
529        s.push_str(&format!("set(CMAKE_FIND_ROOT_PATH {})\n\n", sysroot));
530
531        s.push_str("set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n");
532        s.push_str("set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n");
533        s.push_str("set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\n");
534        s.push_str("set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n\n");
535
536        s.push_str("set(CMAKE_C_FLAGS_INIT \"-ffreestanding -nostdlib\")\n");
537        s.push_str("set(CMAKE_CXX_FLAGS_INIT \"-ffreestanding -nostdlib\")\n");
538
539        s
540    }
541}