├── .gitignore ├── simpletest ├── link.T ├── aarch64-nintendo-horizon-newlib.json ├── src │ └── main.rs ├── Xargo.toml └── Cargo.toml ├── .gitmodules ├── libtransistor-sys ├── Cargo.toml ├── src │ └── lib.rs └── build.rs ├── Cargo.toml ├── libtransistor ├── Cargo.toml └── src │ └── lib.rs ├── aarch64-nintendo-horizon-newlib.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simpletest/link.T: -------------------------------------------------------------------------------- 1 | ../libtransistor-sys/libtransistor/link.T -------------------------------------------------------------------------------- /simpletest/aarch64-nintendo-horizon-newlib.json: -------------------------------------------------------------------------------- 1 | ../aarch64-nintendo-horizon-newlib.json -------------------------------------------------------------------------------- /simpletest/src/main.rs: -------------------------------------------------------------------------------- 1 | //extern crate libtransistor_sys; 2 | 3 | fn main() { 4 | println!("Hello world"); 5 | } 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libtransistor-sys/libtransistor"] 2 | path = libtransistor-sys/libtransistor 3 | url = https://github.com/reswitched/libtransistor 4 | -------------------------------------------------------------------------------- /simpletest/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-nintendo-horizon-newlib.dependencies] 2 | alloc = {} 3 | alloc_system = {} 4 | std = {} 5 | 6 | #[target.aarch64-nintendo-horizon-newlib.dependencies.libtransistor-sys] 7 | #path = "../libtransistor-sys" 8 | -------------------------------------------------------------------------------- /libtransistor-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["roblabla "] 3 | name = "libtransistor-sys" 4 | version = "0.0.0" 5 | 6 | [dependencies] 7 | cty = "0.1.5" 8 | 9 | [build-dependencies] 10 | bindgen = "0.32.3" 11 | -------------------------------------------------------------------------------- /libtransistor-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | //#![crate_type = "rlib"] 2 | #![no_std] 3 | #![cfg_attr(feature = "libc", feature(libc))] 4 | #[cfg(not(feature = "libc"))] 5 | extern crate cty; 6 | #[cfg(feature = "libc")] 7 | extern crate libc as cty; 8 | 9 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 10 | //include!("bindings.rs"); 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = ["libtransistor-sys", "libtransistor", "simpletest"] 4 | 5 | [patch.crates-io] 6 | bindgen = { path = "../rust-bindgen" } 7 | ralloc = { git = "https://github.com/roblabla/ralloc" } 8 | ralloc_shim = { git = "https://github.com/roblabla/ralloc" } 9 | libtransistor-sys = { path = "libtransistor-sys" } 10 | -------------------------------------------------------------------------------- /libtransistor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["roblabla "] 3 | name = "libtransistor" 4 | version = "0.0.0" 5 | 6 | [dependencies] 7 | 8 | [dependencies.libtransistor-sys] 9 | optional = false 10 | path = "../libtransistor-sys/" 11 | 12 | [dependencies.ralloc] 13 | optional = false 14 | version = "1.0.0" 15 | default-features = false 16 | -------------------------------------------------------------------------------- /simpletest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["roblabla "] 3 | name = "simpletest" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | 8 | # For now, we build against libtransistor-sys to find the libc and stuff. 9 | # Eventually, the plan will be to have our stdlib in a nice standalone 10 | # repository, and be selfcontained (e.g. link the libc itself). Then the user 11 | # will just have to put it as a git dep in his Xargo.toml. 12 | [dependencies.libtransistor-sys] 13 | optional = false 14 | path = "../libtransistor-sys" 15 | -------------------------------------------------------------------------------- /aarch64-nintendo-horizon-newlib.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi-blacklist": [ 3 | "stdcall", 4 | "fastcall", 5 | "vectorcall", 6 | "thiscall", 7 | "win64", 8 | "sysv64" 9 | ], 10 | "arch": "aarch64", 11 | "crt-static-default": true, 12 | "crt-static-respected": true, 13 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 14 | "dynamic-linking": false, 15 | "env": "newlib", 16 | "executables": true, 17 | "has-elf-tls": false, 18 | "has-rpath": false, 19 | "linker-flavor": "ld", 20 | "llvm-target": "aarch64-unknown-none", 21 | "max-atomic-width": 128, 22 | "os": "horizon", 23 | "position-independent-executables": true, 24 | "pre-link-args": { 25 | "ld": [ 26 | "-Tlink.T" 27 | ] 28 | }, 29 | "relro-level": "full", 30 | "target-c-int-width": "32", 31 | "target-endian": "little", 32 | "target-pointer-width": "64", 33 | "vendor": "nintendo" 34 | } 35 | -------------------------------------------------------------------------------- /libtransistor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items, alloc, global_allocator, allocator_api)] 2 | #![no_std] 3 | extern crate alloc; 4 | extern crate ralloc; 5 | extern crate libtransistor_sys; 6 | 7 | use libtransistor_sys::*; 8 | 9 | // For now, we are the stdlib, so we provide the start method 10 | #[lang = "start"] 11 | unsafe fn start(main_ptr: *const u8, _argc: isize, _argv: *const *const u8) -> isize { 12 | let main : fn() = core::mem::transmute(main_ptr); 13 | main(); 14 | 0 15 | } 16 | 17 | // And the panic method 18 | #[lang = "eh_personality"] extern fn eh_personality() {} 19 | #[lang = "panic_fmt"] 20 | #[no_mangle] 21 | pub extern fn panic_fmt(msg: ::core::fmt::Arguments, file: &'static str, line: u32) -> ! { 22 | use core::fmt::Write; 23 | 24 | // Print the panic message 25 | let mut s = alloc::string::String::new(); 26 | let _ = s.write_fmt(msg); 27 | unsafe { 28 | svcOutputDebugString(s.as_bytes_mut().as_mut_ptr(), s.len() as u64); 29 | } 30 | // TODO: Crash the switch 31 | loop {} 32 | } 33 | 34 | // And the allocator 35 | #[global_allocator] 36 | static ALLOCATOR: ralloc::Allocator = ralloc::Allocator; 37 | 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | #[test] 42 | fn it_works() { 43 | assert_eq!(2 + 2, 4); 44 | } 45 | } 46 | 47 | pub mod sys { 48 | pub use libtransistor_sys::*; 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Libtransistor-rs 2 | 3 | `libtransistor-rs` allows you to write Homebrew apps for the Nintendo Switch in 4 | Rust ! 5 | 6 | # Usage 7 | 8 | First, you'll need to get hold of the switch-friendly rust fork. 9 | 10 | ``` 11 | git clone -b horizon https://github.com/roblabla/rust 12 | ``` 13 | 14 | Once you have this, clone libtransistor-rs. It will have a folder with a simple 15 | project you can use as a base to develop your homebrew. 16 | 17 | ``` 18 | git clone https://github.com/roblabla/libtransistor-rs 19 | ``` 20 | 21 | `simpletest` contains a simple hello world, while `libtransistor-sys` contains 22 | bindings to the `libtransistor` library, a global allocator so you may use Vecs 23 | and Hashmap, and links against `newlib` to satisfy the `libc` dependency. 24 | 25 | To build it, you'll need `xargo`, which you can install with 26 | `cargo install xargo`. Then it's as simple as 27 | 28 | ``` 29 | XARGO_RUST_SRC=path/to/rust/src xargo build --target=aarch64-nintendo-horizon-newlib 30 | ``` 31 | 32 | Your binary will arrive, with all its dependencies statically linked, in 33 | `target/aarch64-nintendo-horizon-newlib/debug/simpletest`. You'll need to run 34 | `elf2nxo.py` on it to get an `NRO` that you can load in the Mephisto emulator or 35 | on an actual nintendo switch. 36 | 37 | # TODO 38 | 39 | - Make idiomatic bindings to the libtransistor-sys crate. 40 | - Port libstd to the Switch 41 | - Find a way to globally install custom target definitions ? 42 | -------------------------------------------------------------------------------- /libtransistor-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::process::Command; 5 | use std::fs::File; 6 | use std::path::PathBuf; 7 | 8 | fn main() { 9 | let dir = env::var("LIBTRANSISTOR_HOME").expect("LIBTRANSISTOR_HOME musts be set"); 10 | 11 | let status = Command::new("make") 12 | .current_dir(&format!("{}", dir)) 13 | .status().expect("Make failed"); 14 | if !status.success() { 15 | panic!("Make failed"); 16 | } 17 | 18 | // Don't bother linking libc, liblibc takes care of it. 19 | // TODO: What if liblibc isn't linked ? 20 | // Solution: have libtransistor-sys use libc always, instead of cty. 21 | //println!("cargo:rustc-link-lib=static=c"); 22 | // TODO: compiler_builtins takes care of this ! 23 | //println!("cargo:rustc-link-lib=static=clang_rt.builtins-aarch64"); 24 | //println!("cargo:rustc-link-search=native={}/libtransistor/build/compiler-rt/lib/linux", dir); 25 | println!("cargo:rustc-link-lib=static=transistor.nro"); 26 | println!("cargo:rustc-link-search=native={}/build/lib", dir); 27 | 28 | let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be set")); 29 | 30 | /* 31 | * let status = Command::new("rustup").args(&["run", "nightly", "bindgen"]) 32 | .args(&["--blacklist-type", "u(8|16|32|64)"]) 33 | .arg("--use-core") 34 | .args(&["--ctype-prefix", "::libc"]) 35 | .arg("libtransistor/include/libtransistor/nx.h") 36 | .args(&["--", "-nostdinc"]) 37 | .args(&["-isystem", "/usr/lib/clang/5.0.0/include"]) 38 | .args(&["-isystem", "libtransistor/newlib/newlib/libc/include"]) 39 | .args(&["-isystem", "libtransistor/newlib/newlib/libc/sys/switch/include"]) 40 | .arg("-Ilibtransistor/include") 41 | .stdout(File::create(out_path.join("bindings.rs")).unwrap()) 42 | .status().unwrap(); 43 | if !status.success() { 44 | panic!("bindgen failed"); 45 | } 46 | */ 47 | 48 | 49 | // TODO: Avoid generating a ton of useless cruft from liblibc 50 | let bindings = bindgen::Builder::default() 51 | .header(format!("{}/include/libtransistor/nx.h", dir)) 52 | // Don't use host headers, to make sure we're building against newlib 53 | .clang_arg("-nostdlibinc") 54 | // Include the newlib/transistor headers, and the clang builtin headers 55 | .clang_args(&["-isystem", "/usr/lib/clang/5.0.0/include"]) 56 | .clang_args(&["-isystem", &format!("{}/newlib/newlib/libc/sys/switch/include", dir)]) 57 | .clang_args(&["-isystem", &format!("{}/build/newlib/aarch64-none-switch/newlib/targ-include", dir)]) 58 | .clang_args(&["-isystem", &format!("{}/newlib/newlib/libc/include", dir)]) 59 | .clang_arg(format!("-I{}/include", dir)) 60 | // We don't need to define those types, rust has them already anyways. 61 | // Blacklisting avoids a bug in bindgen where it creates cyclic references 62 | // (pub type u8 = u8) 63 | .blacklist_type("u(8|16|32|64)") 64 | .blacklist_type(".*va_list") 65 | .blacklist_type("dbg_vs?n?printf(cb)?") 66 | .use_core() 67 | .ctypes_prefix("cty") 68 | .rustfmt_bindings(true) 69 | .generate_comments(true) 70 | .generate() 71 | .expect("Unable to generate bindings"); 72 | 73 | bindings 74 | .write_to_file(out_path.join("bindings.rs")) 75 | .expect("Couldn't write bindings!"); 76 | } 77 | --------------------------------------------------------------------------------