From eecadf700d35adcd6f888157f0e21b339209d652 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Sun, 27 Nov 2022 10:57:28 -0500 Subject: [PATCH] Work on lcld --- binfmt/src/fmt.rs | 20 +++ binfmt/src/lib.rs | 36 ++++- config.toml | 2 +- lc-as/src/targ/clever.rs | 185 +++++++++++++++++++++- lcld/Cargo.toml | 9 +- lcld/build/config.rs | 57 +++++++ lcld/build/main.rs | 49 ++++++ lcld/config.toml | 19 ++- lcld/src/driver/ld/mod.rs | 300 +++++++++++++++++++++++++++++++++++- lcld/src/driver/ld64/mod.rs | 4 +- lcld/src/driver/link/mod.rs | 4 +- lcld/src/driver/mod.rs | 2 + lcld/src/driver/wasm/mod.rs | 6 + lcld/src/input.rs | 51 ++++++ lcld/src/link.rs | 43 ++++++ lcld/src/lto.rs | 26 ++++ lcld/src/main.rs | 55 +++++-- lcld/src/output.rs | 8 + lcld/src/script.rs | 0 lcld/src/targ.rs | 50 ++++++ 20 files changed, 890 insertions(+), 36 deletions(-) create mode 100644 lcld/build/config.rs create mode 100644 lcld/build/main.rs create mode 100644 lcld/src/driver/wasm/mod.rs create mode 100644 lcld/src/input.rs create mode 100644 lcld/src/link.rs create mode 100644 lcld/src/lto.rs create mode 100644 lcld/src/output.rs create mode 100644 lcld/src/script.rs create mode 100644 lcld/src/targ.rs diff --git a/binfmt/src/fmt.rs b/binfmt/src/fmt.rs index 24da1ab..54e4f8b 100644 --- a/binfmt/src/fmt.rs +++ b/binfmt/src/fmt.rs @@ -48,6 +48,26 @@ pub trait Binfmt { fn before_relocate(&self, _reloc: &mut Reloc, _symbol: &Symbol) {} } +impl core::fmt::Debug for dyn Binfmt{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name()) + } +} + +impl core::cmp::PartialEq for dyn Binfmt{ + fn eq(&self, rhs: &Self) -> bool{ + core::ptr::eq(self, rhs) // Binary Formats are unique and singleton + } +} + +impl core::cmp::Eq for dyn Binfmt{} + +impl core::hash::Hash for dyn Binfmt{ + fn hash(&self, state: &mut H) { + core::ptr::hash(self,state) + } +} + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum FileType { Exec, diff --git a/binfmt/src/lib.rs b/binfmt/src/lib.rs index 9943d27..1eef8b1 100644 --- a/binfmt/src/lib.rs +++ b/binfmt/src/lib.rs @@ -92,7 +92,7 @@ use std::{ ops::Deref, }; -use fmt::BinaryFile; +use fmt::{BinaryFile, Binfmt}; use target_tuples::Target; #[rustfmt::skip] @@ -116,15 +116,15 @@ define_formats![ binary ]; -pub fn formats() -> impl Iterator +pub fn formats() -> impl Iterator { - BINARY_FORMATS.iter().copied() + BINARY_FORMATS.iter().copied().map(|x|x as &dyn crate::fmt::Binfmt) } pub fn format_by_name( name: &str, -) -> Option<&'static (dyn crate::fmt::Binfmt + Sync + Send + 'static)> { - BINARY_FORMATS_BY_NAME.get(name).map(Deref::deref) +) -> Option<&'static (dyn crate::fmt::Binfmt)> { + BINARY_FORMATS_BY_NAME.get(name).map(Deref::deref).map(|x|x as &dyn crate::fmt::Binfmt) } pub fn def_vec_for(targ: &Target) -> &'static (dyn crate::fmt::Binfmt + Sync + Send + 'static) { @@ -147,17 +147,39 @@ pub fn def_vec_for(targ: &Target) -> &'static (dyn crate::fmt::Binfmt + Sync + S } } +pub fn identify_file(mut read: R) -> std::io::Result>{ + let begin = read.seek(std::io::SeekFrom::Current(0))?; + for fmt in crate::formats() { + if fmt==format_by_name("binary").unwrap(){ + break + } + #[allow(clippy::branches_sharing_code)] + // As much as I'd love to follow your suggestion clippy, I'd rather have the correct behaviour at runtime + // So shut it + if let Ok(true) = fmt.ident_file(&mut read) { + read.seek(std::io::SeekFrom::Start(begin))?; + + return Ok(Some(fmt)); + } else { + read.seek(std::io::SeekFrom::Start(begin))?; + } + } + + Ok(None) +} + pub fn open_file(mut read: R) -> std::io::Result> { + let begin = read.seek(std::io::SeekFrom::Current(0))?; for fmt in crate::formats() { #[allow(clippy::branches_sharing_code)] // As much as I'd love to follow your suggestion clippy, I'd rather have the correct behaviour at runtime // So shut it if let Ok(true) = fmt.ident_file(&mut read) { - read.rewind()?; + read.seek(std::io::SeekFrom::Start(begin))?; let file = fmt.read_file(&mut read)?.unwrap(); return Ok(file); } else { - read.rewind()?; + read.seek(std::io::SeekFrom::Start(begin))?; } } unreachable!("binary should be chosen last") diff --git a/config.toml b/config.toml index 27647be..fecec53 100644 --- a/config.toml +++ b/config.toml @@ -1,7 +1,7 @@ [targets] #default-target = "" -[paths] +[dirs] #prefix = "/usr/local" #exec_prefix = "" #bindir = "bin" diff --git a/lc-as/src/targ/clever.rs b/lc-as/src/targ/clever.rs index 0674e71..a4b808c 100644 --- a/lc-as/src/targ/clever.rs +++ b/lc-as/src/targ/clever.rs @@ -475,6 +475,29 @@ fn parse_insn( opc: &str, state: &mut crate::as_state::AsState, ) -> Option { + if opc=="nop"{ + let mut oprs = Vec::new(); + if let Some(Token::LineTerminator) | None = state.iter().peek(){ + }else{ + for _ in 0..3{ + oprs.push(parse_operand(state, false)?); + match state.iter().peek(){ + Some(Token::Sigil(s)) if s=="," => continue, + _ => break, + } + } + } + let opc = 0x010 | (oprs.len() as u16); + let opc = CleverOpcode::from_opcode(opc).unwrap(); + + return Some(CleverInstruction::new(opc, oprs)); + }else if opc=="ifjmp"{ + let opc = 0x7c8f; + return Some(CleverInstruction::new(CleverOpcode::from_opcode(opc).unwrap(), vec![])); + }else if opc=="fret"{ + let opc = 0x7c8e; + return Some(CleverInstruction::new(CleverOpcode::from_opcode(opc).unwrap(), vec![])); + } let opc = parse_mnemonic(opc)?; match opc.operands() { arch_ops::clever::CleverOperandKind::Normal(n) => { @@ -616,6 +639,29 @@ macro_rules! clever_mnemonics{ } } +fn parse_cc(opc: &mut u16, mnemonic: &str) -> Option<()>{ + match mnemonic { + "p" | "po" => (), + "c" | "b" => *opc |= 0x1, + "v" => *opc |= 0x2, + "z" | "e" | "eq" => *opc |= 0x3, + "lt" => *opc |= 0x4, + "le" => *opc |= 0x5, + "be" => *opc |= 0x6, + "mi" | "s" => *opc |= 0x7, + "pl" | "ns" => *opc |= 0x8, + "a" => *opc |= 0x9, + "gt" => *opc |= 0xA, + "ge" => *opc |= 0xB, + "nz" | "ne" => *opc |= 0xC, + "nv" => *opc |= 0xD, + "nc" | "ae" => *opc |= 0xE, + "np" | "pe" => *opc |= 0xF, + _ => None?, + } + Some(()) +} + fn parse_jmp(opc: &mut u16, mnemonic: &str) -> Option<()> { let pos = mnemonic.find("."); @@ -681,6 +727,57 @@ fn parse_l00f(opc: &mut u16, mnemonic: &str) -> Option<()> { } } +fn parse_uf(opc: &mut u16, mnemonic: &str) -> Option<()> { + if mnemonic.starts_with('.') { + for c in mnemonic.chars().skip(1) { + match c { + 'u' => *opc |= 0x2, + 'f' => *opc |= 0x1, + _ => None?, + } + } + + Some(()) + } else if mnemonic.is_empty() { + Some(()) + } else { + None + } +} + +fn parse_size_suffix(opc: &mut u16, mnemonic: &str) -> Option<()>{ + if mnemonic.starts_with('.') { + let suffix = &mnemonic[1..]; + match suffix{ + "8" | "byte" => (), + "16" | "half" => *opc |= 0x01, + "32" | "single" => *opc |= 0x02, + "64" | "double" => *opc |= 0x03, + _ => None? + } + Some(()) + }else{ + None + } +} + +fn parse_callsm(opc: &mut u16, mnemonic: &str) -> Option<()>{ + if mnemonic.starts_with('.') { + let suffix = &mnemonic[1..]; + if suffix=="v"{ + *opc |= 0x01; + }else{ + None? + } + Some(()) + }else if mnemonic.is_empty() { + Some(()) + } + else{ + None + } +} + clever_mnemonics! { ["jmp", 0x7c0, parse_none], ["j",0x700,parse_jmp], @@ -690,18 +787,102 @@ clever_mnemonics! { ["and", 0x003, parse_l00f], ["or" , 0x004, parse_l00f], ["xor", 0x005, parse_l00f], - ["mul", 0x006, parse_l00f], ["mov", 0x008, parse_none], ["lea", 0x009, parse_none], ["push", 0x014, parse_none], ["pop", 0x015, parse_none], - ["lsh", 0x030, parse_none], + ["stogpr",0x018,parse_none], + ["stoar",0x019,parse_none], + ["rstogpr",0x01a,parse_none], + ["rstoar",0x01b,parse_none], + ["pushgpr",0x01c,parse_none], + ["pushar",0x01d,parse_none], + ["popgpr",0x01e,parse_none], + ["popar",0x01f,parse_none], + ["movsx",0x020,parse_l00f], + ["bswap",0x021,parse_l00f], + ["movif",0x022,parse_uf], + ["movfi",0x024,parse_uf], + ["cvtf",0x026,parse_l00f], + ["repc",0x028,parse_none], + ["repi",0x029,parse_cc], + ["bcpy",0x02a,parse_none], + ["bsto",0x02b,parse_none], + ["bsca",0x02c,parse_none], + ["bcmp",0x02d,parse_none], + ["btst",0x02e,parse_none], + ["lsh", 0x030, parse_l00f], + ["rsh",0x031, parse_l00f], + ["arsh",0x032,parse_l00f], + ["lshc",0x033,parse_l00f], + ["rshc",0x034,parse_l00f], + ["lrot",0x035,parse_l00f], + ["rrot",0x036,parse_l00f], + ["bnot",0x046,parse_l00f], + ["neg",0x047,parse_l00f], + + ["cmovt",0x068,parse_cc], + ["cmov",0x069,parse_cc], ["cmp", 0x06c, parse_none], ["test", 0x06d, parse_none], + + ["round",0x100,parse_l00f], + ["ceil",0x101,parse_l00f], + ["floor",0x102,parse_l00f], + ["fabs",0x103,parse_l00f], + ["fneg",0x104,parse_l00f], + ["finv",0x105,parse_l00f], + ["fadd",0x106,parse_l00f], + ["fsub",0x107,parse_l00f], + ["fmul",0x108,parse_l00f], + ["fdiv",0x109,parse_l00f], + ["frem",0x10a,parse_l00f], + ["fma",0x10b,parse_l00f], + ["fcmpz",0x118,parse_none], + ["fcmp",0x119,parse_none], + ["exp",0x120,parse_l00f], + ["ln",0x121,parse_l00f], + + ["fraiseexcept",0x130,parse_none], + ["ftriggerexcept",0x131,parse_none], + + ["xchg",0x200,parse_none], + ["cmpxchg",0x201,parse_none], + ["wcmpxchg",0x202,parse_none], + ["fence",0x203,parse_none], + + ["rpoll",0x230,parse_none], + + ["vec",0x400,parse_size_suffix], + ["vmov",0x401,parse_none], + ["vshuffle",0x402,parse_size_suffix], + ["vextract",0x403,parse_none], + ["vcmp",0x404,parse_none], + ["vtest",0x405,parse_none], + ["vfcmp",0x406,parse_none], + ["call",0x7c1, parse_none], + ["fcall",0x7c2,parse_none], ["ret",0x7c3, parse_none], + ["int",0x7c4, parse_none], + ["ijmp",0x7c8,parse_none], ["icall",0x7c9,parse_none], + ["ifcall",0x7ca,parse_none], + ["jmpsm",0x7cb,parse_none], + ["callsm",0x7cc,parse_callsm], + ["retrsm",0x7cd,parse_none], + ["hlt", 0x801, parse_none], ["in", 0x806, parse_none], ["out", 0x807, parse_none], + ["storegf",0x808,parse_none], + ["rstregf",0x809,parse_none], + ["vmcreate",0xe00,parse_none], + ["vmdestroy",0xe01,parse_none], + + ["scret",0xfc6,parse_none], + ["reti",0xfc7,parse_none], + ["hcall",0xfcb,parse_none], + ["hret",0xfd6,parse_none], + ["hresume",0xfd7,parse_none], } diff --git a/lcld/Cargo.toml b/lcld/Cargo.toml index 22e8d5a..f291fd9 100644 --- a/lcld/Cargo.toml +++ b/lcld/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Connor Horman ", "Ray Redondo "] edition = "2018" license = "BSD-2-Clause-Patent" +build = "build/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -39,9 +40,15 @@ sosigning = [] lto = ["lto-formats"] [dependencies] -target-tuples = "0.5.5" +target-tuples = "0.5.10" binfmt = {path = "../binfmt", features = ["ar"]} arch-ops = {path = "../arch-ops"} +[build-dependencies] +target-tuples = "0.5.10" +serde = {version="1.0.147",features=["derive"]} +toml = "0.5" +unix_path={version="1.0.1",features=["serde"]} + [package.metadata.install-targets.lcld] aliases = ["ld.lc","lc-link","ld64.lc"] diff --git a/lcld/build/config.rs b/lcld/build/config.rs new file mode 100644 index 0000000..8b16ab7 --- /dev/null +++ b/lcld/build/config.rs @@ -0,0 +1,57 @@ +use std::{collections::HashMap}; + +use unix_path::PathBuf; + +use serde::{Deserialize}; +use target_tuples::Target; + +#[derive(Hash, PartialEq, Eq,Clone,Debug)] +pub struct WrappedTarget(pub Target); + +impl<'de> Deserialize<'de> for WrappedTarget{ + fn deserialize(de: D) -> Result + where + D: serde::Deserializer<'de> { + let string = <&str as Deserialize>::deserialize(de)?; + + let targ = string.parse().map_err(::custom)?; + + Ok(WrappedTarget(targ)) + } +} + +#[derive(Deserialize,Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Config{ + pub target: Targets, +} + +#[derive(Deserialize,Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Targets{ + pub default_target: Option, + pub host_target: Option, + #[serde(rename="default")] + pub default_cfg: TargetConfig, + #[serde(flatten)] + pub targets: HashMap +} + +#[derive(Deserialize,Debug)] +#[serde(rename_all = "kebab-case")] +pub struct TargetConfig{ + pub paths: PathConfig, + #[serde(default)] + pub sysroot: PathBuf, +} + +#[derive(Deserialize,Debug)] +#[serde(rename_all = "kebab-case")] +pub struct PathConfig{ + pub search_paths: Vec, + pub libdirs: Vec, + #[serde(default)] + pub use_target: bool, + #[serde(default)] + pub find_msvc: bool, +} \ No newline at end of file diff --git a/lcld/build/main.rs b/lcld/build/main.rs new file mode 100644 index 0000000..d43524d --- /dev/null +++ b/lcld/build/main.rs @@ -0,0 +1,49 @@ +use std::{io::{Read,Write, ErrorKind},path::PathBuf}; + + +mod config; + +fn main() -> Result<(),std::io::Error>{ + println!("cargo:rerun-if-changed={}","config.toml"); + + let mut file = std::fs::File::open("config.toml")?; + + let mut str = String::new(); + file.read_to_string(&mut str)?; + + + let cfg = toml::from_str::(&str).map_err(|e|std::io::Error::new(ErrorKind::InvalidData, e))?; + + let host = cfg.target.host_target.map(|t|t.0).unwrap_or_else(|| + target_tuples::Target::parse(&std::env::var("TARGET").unwrap()) + ); + + let target = cfg.target.default_target.map(|t|t.0).unwrap_or_else(|| + host.clone() + ); + + println!("cargo:rustc-env=HOST={}",host); + println!("cargo:rustc-env=default_target={}",target); + + let mut targ_output = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + targ_output.push("targ-generated.rs"); + + let mut file = std::fs::File::create(&targ_output)?; + + writeln!(file,r#" + + pub fn target_config(targ: &Target) -> TargetConfig<'static>{{ + target_tuples::match_targets!{{ + match (targ){{ + "#)?; + + for (id,cfg) in cfg.target.targets{ + writeln!(file,"{} => construct_cfg!({:?}),",id,cfg)?; + } + writeln!(file,"* => construct_cfg!({:?})",cfg.target.default_cfg)?; + writeln!(file,"}}}}}}")?; + + println!("cargo:rustc-env=config_targ_generated={}",targ_output.display()); + + Ok(()) +} \ No newline at end of file diff --git a/lcld/config.toml b/lcld/config.toml index 12491bd..8cdfe65 100644 --- a/lcld/config.toml +++ b/lcld/config.toml @@ -1,3 +1,18 @@ -[paths] -search_prefixes=["/","/usr","/usr/local"] +allow-sysroot=true +[target] +#default-target="" +#host-target="" + + + +[target.default.paths] +search-paths=["/","/usr","/usr/local"] +use-target=true +libdirs=["lib"] + +[target.'x86_86-*-windows-msvc'.paths] +search-paths=["/"] +use-target=false +libdirs=["lib"] +find-msvc=true \ No newline at end of file diff --git a/lcld/src/driver/ld/mod.rs b/lcld/src/driver/ld/mod.rs index ea8a124..768a0e6 100644 --- a/lcld/src/driver/ld/mod.rs +++ b/lcld/src/driver/ld/mod.rs @@ -1,8 +1,298 @@ -use std::io::{Error as IOError, ErrorKind}; +use std::{io::{Error as IOError, ErrorKind}, path::{PathBuf,Path}}; +use target_tuples::Target; + +use crate::{output::OutputType, targ::TargetInfo}; + +#[derive(Copy,Clone, Debug, Hash, PartialEq,Eq)] + +pub enum LibraryType{ + Static, + Dynamic, +} + +#[derive(Copy,Clone, Debug, Hash, PartialEq,Eq)] + +pub struct InputStatus{ + prefer_mode: LibraryType, + allow_static: bool, + allow_dynamic: bool, +} + +#[derive(Clone, Debug, Hash, PartialEq,Eq)] +pub enum InputSet{ + Single(PathBuf), + Group(Vec), + Library(String), + LinkStatic, + LinkDynamic, + AsNeeded, + NoAsNeeded, +} + + +fn find_library>(lib: &str, search_dirs: impl IntoIterator,info: &TargetInfo, status: &InputStatus) -> std::io::Result{ + let mut dynlib_paths = Vec::new(); + let mut staticlib_paths = Vec::new(); + for dir in search_dirs{ + if status.allow_dynamic{ + for suffix in info.dynamicsuffixes{ + let file = info.libprefix.to_string()+lib+*suffix; + let mut path = dir.as_ref().to_path_buf(); + path.push(file); + match std::fs::metadata(&path){ + Ok(_) => dynlib_paths.push(path), + Err(e) if e.kind()==ErrorKind::NotFound => {}, + Err(e) => Err(e)? + } + } + } + if status.allow_static{ + for suffix in info.staticsuffixes{ + let file = info.libprefix.to_string()+lib+*suffix; + let mut path = dir.as_ref().to_path_buf(); + path.push(file); + match std::fs::metadata(&path){ + Ok(_) => staticlib_paths.push(path), + Err(e) if e.kind()==ErrorKind::NotFound => {}, + Err(e) => Err(e)? + } + } + } + } + match (status.prefer_mode,staticlib_paths.is_empty(),dynlib_paths.is_empty()){ + (LibraryType::Dynamic,_,false) | (_,true,false) => Ok(dynlib_paths.into_iter().next().unwrap()), + (LibraryType::Static,false,_) | (_,false,true) => Ok(staticlib_paths.into_iter().next().unwrap()), + (_,true,true) => Err(std::io::Error::new(ErrorKind::NotFound,format!("Library {} not found",lib))) + } +} + + +fn print_input_and_ident<'a,R,P: AsRef>(p: &InputSet, search_dirs: &'a R,info: &TargetInfo, status: &mut InputStatus) -> std::io::Result<()> where &'a R: IntoIterator{ + match p{ + InputSet::Single(path) => { + let ty = crate::input::ident_input(path)?; + eprint!("{} {{{}}}",path.display(),ty); + }, + InputSet::Group(inputs) => { + let mut sep = "("; + for input in inputs{ + eprint!("{}",sep); + sep = " "; + print_input_and_ident(input, search_dirs,info,status)?; + } + eprint!(")"); + }, + InputSet::Library(lib) => { + let file = find_library(lib, search_dirs, info, status)?; + let ty = crate::input::ident_input(&file)?; + eprint!("-l{}: {} {{{}}}",lib,file.display(),ty); + + }, + InputSet::LinkStatic => status.prefer_mode = LibraryType::Static, + InputSet::LinkDynamic => status.prefer_mode = LibraryType::Dynamic, + _ => {} + } + Ok(()) +} + +#[allow(unused_variables,unused_assignments)] pub fn main() -> Result<(), IOError> { - Err(IOError::new( - ErrorKind::Other, - "Incomplete Driver, ld support is Work-In-Progress", - )) + let mut default_targ = target_tuples::from_env!("default_target"); + + let mut targ = None::; + + let mut args = std::env::args(); + + let prg_name = args.next().unwrap(); + + let mut output_file = "a.out".to_string(); + let mut output_type = None::; + let mut suppout = None::; + + let mut inputs = Vec::new(); + + let mut cur_group = None::>; + + let mut add_search_dirs = Vec::new(); + + + if let Some((left,_)) = prg_name.rsplit_once("-"){ + if let Ok(targ) = left.parse(){ + default_targ = targ; + } + } + + + while let Some(arg) = args.next(){ + match &*arg{ + "--version" => { + eprintln!("lcld (GNU Driver) v{}",env!("CARGO_PKG_VERSION")); + eprintln!("Copyright (C) 2020-2022 Lightning Creations"); + eprintln!("This program is released under the terms of the BSD 2 Clause + Patent License."); + eprintln!("A copy of the license is available in the source code or at https://github.com/LightningCreations/lc-binutils"); + eprintln!(); + eprint!("lcld is compiled with support for the following binary formats: "); + for fmt in binfmt::formats(){ + eprint!("{} ",fmt.name()); + } + eprintln!(); + std::process::exit(0) + } + "-shared" => { + output_type = Some(OutputType::Shared); + }, + "--target" => { + targ = Some(args.next().and_then(|s|s.parse().ok()).unwrap_or_else(|| { + eprintln!("{}: Expected a target after --target",prg_name); + std::process::exit(1) + })); + } + "-o" => { + output_file = args.next().unwrap_or_else(|| { + eprintln!("{}: Expected a file name after -o",prg_name); + std::process::exit(1) + }); + } + x if x.starts_with("-o") => { + output_file = x[2..].to_string(); + } + "-L" => { + add_search_dirs.push(args.next().unwrap_or_else(|| { + eprintln!("{}: Expected a file name after -o",prg_name); + std::process::exit(1) + })); + } + x if x.starts_with("-L") => { + add_search_dirs.push(x[2..].to_string()); + } + "-l" => { + if let Some(group) = &mut cur_group{ + group.push(InputSet::Library(args.next().unwrap_or_else(|| { + eprintln!("{}: Expected a library name after -l",prg_name); + std::process::exit(1) + }))) + }else{ + inputs.push(InputSet::Library(args.next().unwrap_or_else(|| { + eprintln!("{}: Expected a library name after -l",prg_name); + std::process::exit(1) + }))) + } + } + x if x.starts_with("-l") => { + if let Some(group) = &mut cur_group{ + group.push(InputSet::Library(x[2..].to_string())) + }else{ + inputs.push(InputSet::Library(x[2..].to_string())) + } + } + "--import-name" => { + suppout = Some(args.next().unwrap_or_else(|| { + eprintln!("{}: Expected a file name after --import-library",prg_name); + std::process::exit(1) + })) + } + "--start-group" | "-(" => { + if cur_group.is_some(){ + eprintln!("{}: Cannot nest groups",prg_name); + std::process::exit(1); + } + cur_group = Some(Vec::new()) + } + "--end-group" | "-)" => { + if let Some(group) = cur_group.take(){ + inputs.push(InputSet::Group(group)); + }else{ + eprintln!("{}: No group to end with --end-group",prg_name); + std::process::exit(1); + } + } + "--flavour" | "-flavour" | "--flavor" | "-flavor" => { + args.next(); // consume the argument to flavour, but we're committed on the unix driver now + } + x if x.starts_with("-") => todo!("opts"), + _ => { + if let Some(group) = &mut cur_group{ + group.push(InputSet::Single(PathBuf::from(arg))) + }else{ + inputs.push(InputSet::Single(PathBuf::from(arg))) + } + } + } + } + + let targ = if let Some(targ) = targ{ + targ + }else{ + default_targ + }; + + + if inputs.is_empty(){ + eprintln!("{}: Expected at least one input file",prg_name); + std::process::exit(1) + } + + let cfg = crate::targ::target_config(&targ); + + let info = &crate::targ::ELF_TARG; // todo, determine target based on the Binfmt + + let mut status = InputStatus{ + prefer_mode: LibraryType::Dynamic, + allow_static: true, + allow_dynamic: true, + }; + let mut search_dirs = Vec::new(); + cfg.search_paths.iter().map(|&r|r).map(Path::new) + .flat_map(|p| core::iter::repeat(p).zip(cfg.libdirs.iter().map(|&r|r))) + .for_each(|pair|{ + if cfg.use_target{ + search_dirs.extend(core::iter::once(pair).map(|(dir,stem)|{ + let mut file = dir.to_path_buf(); + file.push(stem); + file + }).chain(core::iter::repeat(pair) + .zip([ + targ.to_string(), + targ.arch().to_string()+"-"+targ.sys(), + targ.get_name().to_string() + ]) + .map(|((dir,stem),targ)|{ + let mut file = dir.to_path_buf(); + file.push(targ); + file.push(stem); + file + })).chain(core::iter::repeat(pair) + .zip([ + targ.to_string(), + targ.arch().to_string()+"-"+targ.sys(), + targ.get_name().to_string() + ]) + .map(|((dir,stem),targ)|{ + let mut file = dir.to_path_buf(); + file.push(stem); + file.push(targ); + file + }))); + }else{ + search_dirs.extend(core::iter::once(pair).map(|(dir,stem)|{ + let mut file = dir.to_path_buf(); + file.push(stem); + file + })); + } + }); + search_dirs.extend(add_search_dirs.into_iter().map(PathBuf::from)); + + eprintln!("{}: Search Paths: {:?}",prg_name,search_dirs); + + eprint!("{}: Input Files: ",prg_name); + for input in &inputs{ + print_input_and_ident(input, &search_dirs, info, &mut status)?; + eprint!(" "); + } + + eprintln!(); + + Ok(()) } diff --git a/lcld/src/driver/ld64/mod.rs b/lcld/src/driver/ld64/mod.rs index 9b07a5e..1371108 100644 --- a/lcld/src/driver/ld64/mod.rs +++ b/lcld/src/driver/ld64/mod.rs @@ -1,5 +1,5 @@ -use std::io::Error as IOError; +use std::io::{Error as IOError, ErrorKind}; pub fn main() -> Result<(), IOError> { - unimplemented!() + Err(std::io::Error::new(ErrorKind::Unsupported,"darwin driver not implemented")) } diff --git a/lcld/src/driver/link/mod.rs b/lcld/src/driver/link/mod.rs index 9b07a5e..bdd3c8d 100644 --- a/lcld/src/driver/link/mod.rs +++ b/lcld/src/driver/link/mod.rs @@ -1,5 +1,5 @@ -use std::io::Error as IOError; +use std::io::{Error as IOError, ErrorKind}; pub fn main() -> Result<(), IOError> { - unimplemented!() + Err(std::io::Error::new(ErrorKind::Unsupported,"link.exe driver not implemented")) } diff --git a/lcld/src/driver/mod.rs b/lcld/src/driver/mod.rs index 07d3b06..2152779 100644 --- a/lcld/src/driver/mod.rs +++ b/lcld/src/driver/mod.rs @@ -3,3 +3,5 @@ pub mod ld; pub mod ld64; pub mod link; + +pub mod wasm; \ No newline at end of file diff --git a/lcld/src/driver/wasm/mod.rs b/lcld/src/driver/wasm/mod.rs new file mode 100644 index 0000000..acaf4a5 --- /dev/null +++ b/lcld/src/driver/wasm/mod.rs @@ -0,0 +1,6 @@ +use std::io::ErrorKind; + + +pub fn main() -> std::io::Result<()>{ + Err(std::io::Error::new(ErrorKind::Unsupported,"wasm driver not implemented")) +} \ No newline at end of file diff --git a/lcld/src/input.rs b/lcld/src/input.rs new file mode 100644 index 0000000..a0a4f34 --- /dev/null +++ b/lcld/src/input.rs @@ -0,0 +1,51 @@ +use binfmt::fmt::Binfmt; + +use std::io::{Read,Seek, SeekFrom}; + +use std::fs::File; +use std::path::Path; + +use crate::lto::LtoProvider; + + +#[derive(Copy,Clone, Debug, Hash, PartialEq, Eq)] +pub enum InputFileType{ + Object(&'static dyn Binfmt), + Archive, + LtoInput(&'static dyn LtoProvider), + LinkerScript, +} + +impl core::fmt::Display for InputFileType{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result{ + match self{ + Self::Object(fmt) => f.write_str(fmt.name()), + Self::Archive => f.write_str("archive"), + Self::LtoInput(prov) => { + f.write_str("lto ")?; + f.write_str(prov.name()) + } + Self::LinkerScript => f.write_str("script") + } + } +} + + +pub fn ident_input(p: &Path) -> std::io::Result{ + let file = File::open(p)?; + + if let Some(binfmt) = binfmt::identify_file(&file)?{ + Ok(InputFileType::Object(binfmt)) + }else{ + let mut arch_buf = [0u8;8]; + (&file).read(&mut arch_buf)?; + (&file).seek(SeekFrom::Start(0))?; + if arch_buf==*b"!\n"{ + Ok(InputFileType::Archive) + }else{ + // todo: Identify Lto Input objects + Ok(InputFileType::LinkerScript) + } + } + +} diff --git a/lcld/src/link.rs b/lcld/src/link.rs new file mode 100644 index 0000000..a6eb8a3 --- /dev/null +++ b/lcld/src/link.rs @@ -0,0 +1,43 @@ +use std::path::PathBuf; + +use crate::input::InputFileType; + + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct InputFile{ + pub path: PathBuf, + pub ty: InputFileType, + pub as_needed: bool, +} + +#[derive(Copy,Clone, Debug, Hash, PartialEq, Eq)] +pub struct InputId(u32); + +#[derive(Copy,Clone, Debug, Hash, PartialEq, Eq)] +pub struct SectionId(u32); + +#[derive(Copy,Clone, Debug, Hash, PartialEq, Eq)] +pub enum SectionOffset{ + Absolute(u128), + Begin(u64), + End(i64), + AfterInput{ + input_spec_number: u32, + pos: i64, + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum SymbolDef{ + Object(InputId), + ArchiveMember(InputId,String), + ScriptDefined(SectionId, SectionOffset), + Undefined, +} + + +#[derive(Debug)] +pub struct Linker{ + input_file: Vec, + +} \ No newline at end of file diff --git a/lcld/src/lto.rs b/lcld/src/lto.rs new file mode 100644 index 0000000..1d42b57 --- /dev/null +++ b/lcld/src/lto.rs @@ -0,0 +1,26 @@ + + +pub trait LtoProvider{ + fn name(&self) -> &'static str; + +} + +impl core::fmt::Debug for dyn LtoProvider{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name()) + } +} + +impl core::cmp::PartialEq for dyn LtoProvider{ + fn eq(&self, other: &Self) -> bool { + core::ptr::eq(self,other) + } +} + +impl core::cmp::Eq for dyn LtoProvider{} + +impl core::hash::Hash for dyn LtoProvider{ + fn hash(&self, state: &mut H) { + core::ptr::hash(self,state) + } +} \ No newline at end of file diff --git a/lcld/src/main.rs b/lcld/src/main.rs index a8305fe..06fd7cd 100644 --- a/lcld/src/main.rs +++ b/lcld/src/main.rs @@ -2,34 +2,58 @@ use std::io::{Error as IOError, ErrorKind}; +pub mod targ; pub mod driver; +pub mod output; +pub mod script; +pub mod input; +pub mod lto; +pub mod link; pub enum Mode { Unix, Darwin, Windows, + Wasm, } fn main() { - let exe_name = std::env::current_exe() - .and_then(|p| { - p.file_name() - .ok_or_else(|| IOError::new(ErrorKind::Other, "No executable path was found")) - .map(|s| String::from(s.to_string_lossy())) - .map(|s| s.replace(".exe", "")) - }) - .unwrap_or_else(|e| { - println!("Error: {}", e); - std::process::exit(1); - }); - - let driver = match &*exe_name { + + let mut args = std::env::args(); + + let mut exe_name = args.next().unwrap(); + + if exe_name.ends_with(".exe"){ + exe_name.truncate(exe_name.len() - 4); + } + + let mut driver = match &*exe_name { x if x.ends_with("ld") || x.ends_with("ld.lc") => Some(Mode::Unix), x if x.ends_with("ld64") || x.ends_with("ld64.lc") => Some(Mode::Darwin), x if x.ends_with("link") => Some(Mode::Windows), _ => None, }; + match args.next().as_deref(){ + Some(x @ ("-flavor" | "-flavour" | "--flavor" | "--flavour")) => { + driver = match args.next().as_deref().unwrap_or_else(||{ + eprintln!("{}: Expected an argument for {}",exe_name,x); + std::process::exit(1) + }){ + "ld" | "ld.lc" | "gnu" | "unix" => Some(Mode::Unix), + "ld64" | "ld64.lc" | "darwin" => Some(Mode::Darwin), + "link" | "lc-link" | "windows" => Some(Mode::Windows), + "wasm" | "wasm-ld" | "wasm-ld.lc" => Some(Mode::Wasm), + x => { + eprintln!("{}: Unknown flavor {}",exe_name,x); + std::process::exit(1) + } + } + } + _ => {} + } + + match driver{ Some(Mode::Unix) => { driver::ld::main() @@ -40,11 +64,14 @@ fn main() { Some(Mode::Windows) => { driver::link::main() }, + Some(Mode::Wasm) => { + driver::wasm::main() + } None => { Err(IOError::new(ErrorKind::Other,"Generic driver for lcld. Run ld.lc (unix), ld64.lc (MacOS), or lc-link (Windows) for an actual driver")) } }.unwrap_or_else(|e| { - println!("Error: {}", e); + println!("{}: {}",exe_name, e); std::process::exit(1); }) } diff --git a/lcld/src/output.rs b/lcld/src/output.rs new file mode 100644 index 0000000..30a831f --- /dev/null +++ b/lcld/src/output.rs @@ -0,0 +1,8 @@ +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum OutputType{ + Relocatable, // perform partial link + StaticExecutable, // position-dependent executable + PieExecutable, // position-independent executable + Shared, // Shared object/dll + SharedAndLink, // dll+lib +} \ No newline at end of file diff --git a/lcld/src/script.rs b/lcld/src/script.rs new file mode 100644 index 0000000..e69de29 diff --git a/lcld/src/targ.rs b/lcld/src/targ.rs new file mode 100644 index 0000000..bda9f80 --- /dev/null +++ b/lcld/src/targ.rs @@ -0,0 +1,50 @@ + +use target_tuples::Target; + +use crate::output::OutputType; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct TargetConfig<'a>{ + pub search_paths: &'a [&'a str], + pub libdirs: &'a [&'a str], + pub use_target: bool, + pub sysroot: &'a str, + pub find_msvc: bool, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct TargetInfo<'a>{ + pub objsuffix: &'a str, + pub libprefix: &'a str, + pub staticsuffixes: &'a [&'a str], + pub dynamicsuffixes: &'a [&'a str], + pub output_dynsuffix: &'a str, + pub default_output: OutputType, + pub need_dylib_link: bool, + +} + +macro_rules! construct_cfg{ + (TargetConfig { paths: PathConfig { search_paths: [$($search_paths:expr),* $(,)?], libdirs: [$($libdirs:expr),* $(,)?], use_target: $usetarget:expr, find_msvc: $find_msvc:expr $(, $_field:ident : $_init:expr)* $(, ..)? }, sysroot: $sysroot:expr $(, $_ofield:ident : $_oinit:expr)* $(, ..)? }) => { + TargetConfig{ + search_paths: &[$($search_paths),*], + libdirs: &[$($libdirs),*], + use_target: $usetarget, + sysroot: $sysroot, + find_msvc: $find_msvc, + } + } +} + +include!(env!("config_targ_generated")); + + +pub static ELF_TARG: TargetInfo = TargetInfo{ + objsuffix: ".o", + libprefix: "lib", + staticsuffixes: &[".a"], + dynamicsuffixes: &[".so"], + output_dynsuffix: ".so", + default_output: OutputType::PieExecutable, + need_dylib_link: false +}; \ No newline at end of file