From 465da6e7cd6a81f37e6fe4bbe9caacc51d41a11f Mon Sep 17 00:00:00 2001 From: pantonshire Date: Sun, 25 Aug 2024 11:15:11 +0100 Subject: [PATCH] build asm in build.rs --- boot1/asm/prelude.s | 6 ++ boot1/build.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++ boot1/link.ld | 1 + 3 files changed, 160 insertions(+) create mode 100644 boot1/asm/prelude.s diff --git a/boot1/asm/prelude.s b/boot1/asm/prelude.s new file mode 100644 index 0000000..04b2e71 --- /dev/null +++ b/boot1/asm/prelude.s @@ -0,0 +1,6 @@ +%include "defines.s" + +[org BOOT1_LOADPOINT] +[bits 16] + +hlt diff --git a/boot1/build.rs b/boot1/build.rs index 3bd445e..d9b35c9 100644 --- a/boot1/build.rs +++ b/boot1/build.rs @@ -1,4 +1,157 @@ +use std::{env, ffi::{OsStr, OsString}, fmt, fs, io, path::{Path, PathBuf}, process::{self, Command}}; + fn main() { + let out_dir = env::var_os("OUT_DIR") + .expect("OUT_DIR not set"); + let out_dir = PathBuf::from(out_dir); + + let path = SearchPath::load(); + + let nasm_path = path.search("nasm") + .next() + .expect("failed to find nasm in PATH"); + let nasm = Nasm::new(nasm_path); + + build_asm(&nasm, &out_dir); + + emit_link_args(); + + rerun_if_changed("build.rs".as_ref()); +} + +fn emit_link_args() { println!("cargo::rustc-link-arg=-Tlink.ld"); } +fn rerun_if_changed(path: &Path) { + if let Some(path_str) = path.to_str() { + println!("cargo::rerun-if-changed={}", path_str); + } + else { + eprintln!("invalid unicode file path"); + } +} + +fn build_asm(nasm: &Nasm, out_dir: &Path) { + nasm.assemble( + &out_dir.join("prelude.bin"), + &["asm/prelude.s".as_ref()], + &["..".as_ref()], + ).unwrap(); +} + +struct Nasm { + bin_path: PathBuf, +} + +impl Nasm { + fn new(bin_path: PathBuf) -> Self { + Self { bin_path } + } + + fn assemble(&self, output: &Path, sources: &[&Path], includes: &[&Path]) -> Result<(), CmdError> + { + for source in sources { + rerun_if_changed(source); + } + + let mut cmd = Command::new(&self.bin_path); + cmd.arg("-f").arg("bin"); + + for include in includes { + let mut buf = OsString::new(); + buf.push("-I"); + buf.push(include); + cmd.arg(buf); + } + + cmd + .arg("-o") + .arg(output) + .args(sources); + + run_cmd(&mut cmd)?; + + Ok(()) + } +} + +struct SearchPath { + paths: Vec +} + +impl SearchPath { + fn load() -> Self { + let path_var = env::var_os("PATH").unwrap_or_default(); + let paths = env::split_paths(&path_var).collect(); + Self { paths } + } + + fn search<'a, 'b, 'c, T>(&'a self, bin: &'b T) -> impl Iterator + 'c + where + 'a: 'c, + 'b: 'c, + T: AsRef + ?Sized, + { + let bin = bin.as_ref(); + self.paths + .iter() + .filter_map(move |path| { + let path = path.join(bin); + fs::metadata(&path) + .ok() + .and_then(|meta| if meta.is_file() || meta.is_symlink() { + Some(path) + } else { + None + }) + }) + } +} + +fn run_cmd(cmd: &mut Command) -> Result { + use fmt::Write; + + cmd + .output() + .map_err(CmdErrorKind::Io) + .and_then(|out| if out.status.success() { + Ok(out) + } else { + Err(CmdErrorKind::Status(out)) + }) + .map_err(|err| { + let mut cmd_buf = String::new(); + write!(&mut cmd_buf, "{:?}", cmd).ok(); + CmdError::new(cmd_buf, err) + }) +} + +#[derive(Debug)] +struct CmdError { + cmd: String, + kind: CmdErrorKind, +} + +impl CmdError { + fn new(cmd: String, kind: CmdErrorKind) -> Self { + Self { cmd, kind } + } +} + +impl fmt::Display for CmdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "`{}` failed: ", self.cmd)?; + match &self.kind { + CmdErrorKind::Io(err) => err.fmt(f), + CmdErrorKind::Status(out) => write!(f, "exited with status {}", out.status), + } + } +} + +#[derive(Debug)] +enum CmdErrorKind { + Io(io::Error), + Status(process::Output), +} + diff --git a/boot1/link.ld b/boot1/link.ld index 28c7f94..c77e0b0 100644 --- a/boot1/link.ld +++ b/boot1/link.ld @@ -3,6 +3,7 @@ OUTPUT_FORMAT("binary") SECTIONS { .text : { *(.text); *(.text.*) } .data : { *(.data); *(.data.*) } + .bss : { *(.bss); *(.bss.*) } .rodata : { *(.rodata); *(.rodata.*) } }