diff --git a/.gitignore b/.gitignore index 880ac59..87f3c62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ +target/ .DS_Store diff --git a/.travis.yml b/.travis.yml index 3f76a7c..be9b00a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,10 @@ sudo: false dist: xenial -language: c +language: rust + +rust: + - nightly cache: directories: @@ -19,6 +22,11 @@ env: - ARCH="i386" SRC="ucore" # - ARCH="x86_64" SRC="ucore" - ARCH="aarch64" SRC="ucore" + - ARCH="riscv32" SRC="rust" + - ARCH="riscv64" SRC="rust" + - ARCH="i386" SRC="rust" + - ARCH="x86_64" SRC="rust" + - ARCH="aarch64" SRC="rust" - ARCH="x86_64" SRC="biscuit" install: @@ -48,12 +56,13 @@ install: - if [ $ARCH = x86_64 ]; then if [ $TRAVIS_OS_NAME = osx ]; then brew tap altkatz/homebrew-gcc_cross_compilers; - brew install x64-elf-gcc; + travis_wait brew install x64-elf-gcc; fi; fi +before_script: + - rustup component add rust-src + - (test -x $HOME/.cargo/bin/cargo-xbuild || cargo install cargo-xbuild) + script: - - cd $SRC - - mkdir build && cd build - - cmake -DARCH=$ARCH .. - - make + - make $SRC arch=$ARCH diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55146a0 --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +# arch = {riscv32, riscv64, x86_64, aarch64} +# mode = {debug, release} +arch ?= riscv32 +mode ?= debug +out_dir ?= build/$(arch) +out_img ?= build/$(arch).img + +rust_src_dir := rust/src/bin +rust_bin_path := rust/target/$(arch)-rcore/$(mode) +rust_bins := $(patsubst $(rust_src_dir)/%.rs, $(rust_bin_path)/%, $(wildcard $(rust_src_dir)/*.rs)) +ucore_bin_path := ucore/build/$(arch) +biscuit_bin_path := biscuit/build/$(arch) + +rust_build_args := --target targets/$(arch)-rcore.json +cmake_build_args := -DARCH=$(arch) + +ifeq ($(mode), release) +rust_build_args += --release +cmake_build_args += -DCMAKE_BUILD_TYPE=Release +endif + + +.PHONY: all clean build rust ucore biscuit bin + +all: build + +rust: + @echo Building rust programs + @cd rust && cargo xbuild $(rust_build_args) + @rm -rf $(out_dir)/rust && mkdir -p $(out_dir)/rust + @cp $(rust_bins) $(out_dir)/rust + +ucore: +ifneq ($(arch), x86_64) + @echo Building ucore programs + @mkdir -p ucore/build + @cd ucore/build && cmake $(cmake_build_args) .. && make + @rm -rf $(out_dir)/ucore && mkdir -p $(out_dir)/ucore + @cp $(ucore_bin_path)/* $(out_dir)/ucore +endif + +biscuit: +ifeq ($(arch), x86_64) + @echo Building biscuit programs + @mkdir -p biscuit/build + @cd biscuit/build && cmake $(cmake_build_args) .. && make + @rm -rf $(out_dir)/biscuit && mkdir -p $(out_dir)/biscuit + @cp $(biscuit_bin_path)/* $(out_dir)/biscuit +endif + +build: rust ucore biscuit + +sfsimg: $(out_img) + +$(out_img): build mksfs + @mksfs zip $(out_dir) $@ + +mksfs: +ifeq ($(shell which mksfs),) + @cargo install --git https://github.com/wangrunji0408/SimpleFileSystem-Rust --features="std" +endif + +clean: + @cd rust && cargo clean + @rm -rf biscuit/build ucore/build + @rm -rf $(out_dir) diff --git a/README.md b/README.md index 7d5ae17..edd20aa 100644 --- a/README.md +++ b/README.md @@ -6,32 +6,22 @@ User programs for [rCore OS](https://github.com/wangrunji0408/RustOS). -Now it has 2 parts: +Now it has 3 parts: * `ucore`: C-lang, from the original [ucore_os_lab](https://github.com/chyyuu/ucore_os_plus) * `biscuit`: C/C++, from [Biscuit](https://github.com/mit-pdos/biscuit), based on a partial libc `litc`. (WIP) +* `rust`: Simple no_std Rust programs. ## Build -For ucore: - ```bash -cd ucore -mkdir build && cd build -cmake -DARCH={i386,x86_64,riscv32,riscv64,aarch64} .. -make +make {ucore,biscuit,rust,all} arch={i386,x86_64,riscv32,riscv64,aarch64} ``` -For biscuit: +Output to `build/$(arch)` -```bash -cd biscuit -mkdir build && cd build -cmake -DARCH=x86_64 .. -make -``` ## Notice -* The syscall ids have been set compatible with Linux64. +* The syscall ids have been set compatible with [Linux64](https://syscalls64.paolostivanin.com). * So the ucore part can not run on the original ucore. \ No newline at end of file diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..7e2e08e --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "linked_list_allocator" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rcore-user" +version = "0.1.0" +dependencies = [ + "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "spin" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "655d57c71827fe0891ce72231b6aa5e14033dae3f604609e6a6f807267c1678d" +"checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..6122d8e --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rcore-user" +version = "0.1.0" +authors = ["Runji Wang "] +edition = "2018" + +[dependencies] +linked_list_allocator = "0.6" \ No newline at end of file diff --git a/rust/src/bin/fantastic_text.rs b/rust/src/bin/fantastic_text.rs new file mode 100644 index 0000000..b1403ed --- /dev/null +++ b/rust/src/bin/fantastic_text.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate rcore_user; + +macro_rules! color_text { + ($text:expr, $color:expr) => {{ + format_args!("\x1b[{}m{}\x1b[0m", $color, $text) + }}; +} + +// IMPORTANT: Must define main() like this +#[no_mangle] +pub fn main() { + println!( + "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}", + color_text!("H", 31), + color_text!("e", 32), + color_text!("l", 33), + color_text!("l", 34), + color_text!("o", 35), + color_text!("R", 36), + color_text!("u", 37), + color_text!("s", 90), + color_text!("t", 91), + color_text!("u", 92), + color_text!("C", 93), + color_text!("o", 94), + color_text!("r", 95), + color_text!("e", 96), + color_text!("!", 97), + ); + + let text = "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m"; + println!("\x1b[47m{}\x1b[0m", color_text!(text, 30)); + for i in 31..38 { + println!("{}", color_text!(text, i)); + } + for i in 90..98 { + println!("{}", color_text!(text, i)); + } +} diff --git a/rust/src/bin/hello_rust.rs b/rust/src/bin/hello_rust.rs new file mode 100644 index 0000000..56f7d23 --- /dev/null +++ b/rust/src/bin/hello_rust.rs @@ -0,0 +1,13 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate rcore_user; + +// IMPORTANT: Must define main() like this +#[no_mangle] +pub fn main() { + println!("Hello Rust uCore!"); + println!("I am process {}.", rcore_user::syscall::sys_getpid()); + println!("hello pass."); +} diff --git a/rust/src/bin/sh.rs b/rust/src/bin/sh.rs new file mode 100644 index 0000000..43ba90e --- /dev/null +++ b/rust/src/bin/sh.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] +#![feature(alloc)] + +extern crate alloc; +#[macro_use] +extern crate rcore_user; + +use alloc::vec::Vec; + +use rcore_user::io::get_line; +use rcore_user::syscall::{sys_exec, sys_fork, sys_wait}; + +// IMPORTANT: Must define main() like this +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + loop { + print!(">> "); + let cmd = get_line(); + // split cmd, make argc & argv + let cmd = cmd.replace(' ', "\0") + "\0"; + let ptrs: Vec<*const u8> = cmd.split('\0') + .filter(|s| !s.is_empty()).map(|s| s.as_ptr()).collect(); + if ptrs.is_empty() { + continue; + } + + let pid = sys_fork(); + assert!(pid >= 0); + if pid == 0 { + return sys_exec(ptrs[0], ptrs.len(), ptrs.as_ptr()); + } else { + let mut code: i32 = 0; + sys_wait(pid as usize, &mut code); + println!("\n[Process exited with code {}]", code); + } + } +} diff --git a/rust/src/io.rs b/rust/src/io.rs new file mode 100644 index 0000000..cf6c4a1 --- /dev/null +++ b/rust/src/io.rs @@ -0,0 +1,92 @@ +use alloc::string::String; +use core::fmt::{self, Write}; +use core::option::Option; + +use crate::syscall::{sys_read, sys_write}; + +pub const STDIN: usize = 0; +pub const STDOUT: usize = 1; + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + $crate::io::print(format_args!($($arg)*)); + }); +} + +#[macro_export] +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +pub fn print(args: fmt::Arguments) { + StdOut.write_fmt(args).unwrap(); +} + +pub fn getc() -> Option { + let mut c = 0u8; + let ret = sys_read(STDIN, &mut c, 1); + match ret { + 1 => Some(c), + 0 => None, + _ => panic!(), + } +} + +pub fn get_line() -> String { + let mut s = String::new(); + loop { + let ret = getc(); + match ret { + None => return s, + Some(byte) => { + let c = byte as char; + match c { + '\x08' | '\x7f' /* '\b' */ => { + if s.pop().is_some() { + print!("\x08 \x08"); + } + } + ' '...'\x7e' => { + s.push(c); + print!("{}", c); + } + '\n' | '\r' => { + print!("\n"); + return s; + } + _ => {} + } + } + } + } +} + +pub fn putc(c: u8) { + sys_write(STDOUT, &c, 1); +} + +struct StdOut; + +impl fmt::Write for StdOut { + fn write_str(&mut self, s: &str) -> fmt::Result { + if sys_write(STDOUT, s.as_ptr(), s.len()) >= 0 { + Ok(()) + } else { + Err(fmt::Error::default()) + } + } +} + +/* VFS flags */ +// TODO: use bitflags +// flags for open: choose one of these +pub const O_RDONLY: usize = 0; // open for reading only +pub const O_WRONLY: usize = 1; // open for writing only +pub const O_RDWR: usize = 2; // open for reading and writing +// then or in any of these: +pub const O_CREAT: usize = 0x00000004; // create file if it does not exist +pub const O_EXCL: usize = 0x00000008; // error if O_CREAT and the file exists +pub const O_TRUNC: usize = 0x00000010; // truncate file upon open +pub const O_APPEND: usize = 0x00000020; // append on each write \ No newline at end of file diff --git a/rust/src/lang_items.rs b/rust/src/lang_items.rs new file mode 100644 index 0000000..f125f8c --- /dev/null +++ b/rust/src/lang_items.rs @@ -0,0 +1,70 @@ +use crate::syscall::{sys_close, sys_dup2, sys_exit, sys_open}; +use crate::io::{O_RDONLY, O_WRONLY, STDIN, STDOUT}; +use crate::ALLOCATOR; + +use core::alloc::Layout; +use core::panic::PanicInfo; + +#[linkage = "weak"] +#[no_mangle] +fn main() { + panic!("No main() linked"); +} + +fn initfd(fd2: usize, path: &str, open_flags: usize) -> i32 { + let fd1 = sys_open(path, open_flags); + if fd1 < 0 { + return fd1; + } + let mut ret = fd1; + let fd1 = fd1 as usize; + if fd1 != fd2 { + sys_close(fd2); + ret = sys_dup2(fd1, fd2); + sys_close(fd1); + } + return ret; +} + +fn init_heap() { + const HEAP_SIZE: usize = 0x1000; + static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; + unsafe { ALLOCATOR.lock().init(HEAP.as_ptr() as usize, HEAP_SIZE); } +} + +#[no_mangle] +pub extern "C" fn _start(_argc: isize, _argv: *const *const u8) -> ! { + let fd = initfd(STDIN, "stdin:", O_RDONLY); + if fd < 0 { + panic!("open failed: {}.", fd); + } + let fd = initfd(STDOUT, "stdout:", O_WRONLY); + if fd < 0 { + panic!("open failed: {}.", fd); + } + + init_heap(); + main(); + sys_exit(0) +} + +#[lang = "eh_personality"] +fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + let location = info.location().unwrap(); + let message = info.message().unwrap(); + println!("\n\nPANIC in {} at line {}\n {}", location.file(), location.line(), message); + sys_exit(1) +} + +#[lang = "oom"] +fn oom(_: Layout) -> ! { + panic!("out of memory"); +} + +#[no_mangle] +pub extern "C" fn abort() -> ! { + sys_exit(2) +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..3863e78 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,19 @@ +#![no_std] +#![feature(asm)] +#![feature(alloc)] +#![feature(lang_items)] +#![feature(panic_info_message)] +#![feature(linkage)] +#![feature(compiler_builtins_lib)] + +extern crate alloc; + +#[macro_use] +pub mod io; +pub mod syscall; +pub mod lang_items; + +use linked_list_allocator::LockedHeap; + +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); \ No newline at end of file diff --git a/rust/src/syscall.rs b/rust/src/syscall.rs new file mode 100644 index 0000000..b674201 --- /dev/null +++ b/rust/src/syscall.rs @@ -0,0 +1,133 @@ +#[inline(always)] +fn sys_call(syscall_id: SyscallId, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> i32 { + let id = syscall_id as usize; + let ret: i32; + + unsafe { + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + asm!("ecall" + : "={x10}" (ret) + : "{x10}" (id), "{x11}" (arg0), "{x12}" (arg1), "{x13}" (arg2), "{x14}" (arg3), "{x15}" (arg4), "{x16}" (arg5) + : "memory" + : "volatile"); + #[cfg(target_arch = "x86")] + asm!("int 0x80" + : "={eax}" (ret) + : "{eax}" (id), "{edx}" (arg0), "{ecx}" (arg1), "{ebx}" (arg2), "{edi}" (arg3), "{esi}" (arg4) + : "memory" + : "intel" "volatile"); + #[cfg(target_arch = "x86_64")] + asm!("syscall" + : "={rax}" (ret) + : "{rax}" (id), "{rdi}" (arg0), "{rsi}" (arg1), "{rdx}" (arg2), "{rcx}" (arg3), "{r8}" (arg4), "{r9}" (arg5) + : "memory" + : "intel" "volatile"); + #[cfg(target_arch = "aarch64")] + asm!("svc 0" + : "={x0}" (ret) + : "{x8}" (id), "{x0}" (arg0), "{x1}" (arg1), "{x2}" (arg2), "{x3}" (arg3), "{x4}" (arg4), "{x5}" (arg5) + : "memory" + : "volatile"); + } + ret +} + +pub fn sys_exit(code: usize) -> ! { + sys_call(SyscallId::Exit, code, 0, 0, 0, 0, 0); + unreachable!() +} + + +pub fn sys_exec(name: *const u8, argc: usize, argv: *const *const u8) -> i32 { + sys_call(SyscallId::Exec, name as usize, argc, argv as usize, 0, 0, 0) +} + +pub fn sys_write(fd: usize, base: *const u8, len: usize) -> i32 { + sys_call(SyscallId::Write, fd, base as usize, len, 0, 0, 0) +} + +pub fn sys_read(fd: usize, base: *mut u8, len: usize) -> i32 { + sys_call(SyscallId::Read, fd, base as usize, len, 0, 0, 0) +} + +pub fn sys_open(path: &str, flags: usize) -> i32 { + // UNSAFE: append '\0' to the string + use core::mem::replace; + let end = unsafe { &mut *(path.as_ptr().offset(path.len() as isize) as *mut u8) }; + let backup = replace(end, 0); + let ret = sys_call(SyscallId::Open, path.as_ptr() as usize, flags, 0, 0, 0, 0); + *end = backup; + ret +} + +pub fn sys_close(fd: usize) -> i32 { + sys_call(SyscallId::Close, fd, 0, 0, 0, 0, 0) +} + +pub fn sys_dup2(fd1: usize, fd2: usize) -> i32 { + sys_call(SyscallId::Dup2, fd1, fd2, 0, 0, 0, 0) +} + +/// Fork the current process. Return the child's PID. +pub fn sys_fork() -> i32 { + sys_call(SyscallId::Fork, 0, 0, 0, 0, 0, 0) +} + +/// Wait the process exit. +/// Return the PID. Store exit code to `code` if it's not null. +pub fn sys_wait(pid: usize, code: *mut i32) -> i32 { + sys_call(SyscallId::Wait, pid, code as usize, 0, 0, 0, 0) +} + +pub fn sys_yield() -> i32 { + sys_call(SyscallId::Yield, 0, 0, 0, 0, 0, 0) +} + +/// Kill the process +pub fn sys_kill(pid: usize) -> i32 { + sys_call(SyscallId::Kill, pid, 0, 0, 0, 0, 0) +} + +/// Get the current process id +pub fn sys_getpid() -> i32 { + sys_call(SyscallId::GetPid, 0, 0, 0, 0, 0, 0) +} + +pub fn sys_sleep(time: usize) -> i32 { + sys_call(SyscallId::Sleep, time, 0, 0, 0, 0, 0) +} + +pub fn sys_get_time() -> i32 { + sys_call(SyscallId::GetTime, 0, 0, 0, 0, 0, 0) +} + +pub fn sys_set_priority(priority: usize) -> i32 { + sys_call(SyscallId::SetPriority, priority, 0, 0, 0, 0, 0) +} + +#[allow(dead_code)] +enum SyscallId { + Exit = 60, + Fork = 57, + Wait = 61, + Exec = 59, + Clone = 56, + Yield = 24, + Sleep = 35, + Kill = 62, + GetTime = 96, + GetPid = 39, + Mmap = 9, + Munmap = 11, + Open = 2, + Close = 3, + Read = 0, + Write = 1, + Seek = 8, + Fstat = 4, + Fsync = 74, + GetCwd = 79, + GetDirEntry = 78, + Dup2 = 33, + SetPriority = 141, +} diff --git a/rust/targets/aarch64-rcore.json b/rust/targets/aarch64-rcore.json new file mode 100644 index 0000000..a4adac2 --- /dev/null +++ b/rust/targets/aarch64-rcore.json @@ -0,0 +1,35 @@ +{ + "abi-blacklist": [ + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "win64", + "sysv64" + ], + "arch": "aarch64", + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "executables": true, + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "linker-is-gnu": true, + "pre-link-args": { + "ld.lld": [ + "--image-base=0xffff000000000000" + ] + }, + "llvm-target": "aarch64-unknown-none", + "no-compiler-rt": true, + "features": "+a53,+strict-align,-neon", + "max-atomic-width": 128, + "os": "none", + "panic": "abort", + "panic-strategy": "abort", + "relocation-model": "static", + "position-independent-executables": true, + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "64", + "target-family": "unix", + "disable-redzone": true +} diff --git a/rust/targets/i386-rcore.json b/rust/targets/i386-rcore.json new file mode 100644 index 0000000..35ee84a --- /dev/null +++ b/rust/targets/i386-rcore.json @@ -0,0 +1,16 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "llvm-target": "i386", + "max-atomic-width": 64, + "os": "none", + "panic-strategy": "abort", + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "32", + "features": "-mmx,-sse,+soft-float" +} \ No newline at end of file diff --git a/rust/targets/riscv32-rcore.json b/rust/targets/riscv32-rcore.json new file mode 100644 index 0000000..aa03e0c --- /dev/null +++ b/rust/targets/riscv32-rcore.json @@ -0,0 +1,30 @@ +{ + "llvm-target": "riscv32", + "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "os": "none", + "arch": "riscv32", + "cpu": "generic-rv32", + "features": "+m,+a,+c", + "max-atomic-width": "32", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "executables": true, + "panic-strategy": "abort", + "relocation-model": "static", + "abi-blacklist": [ + "cdecl", + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "aapcs", + "win64", + "sysv64", + "ptx-kernel", + "msp430-interrupt", + "x86-interrupt" + ] +} diff --git a/rust/targets/riscv64-rcore.json b/rust/targets/riscv64-rcore.json new file mode 100644 index 0000000..fe95ada --- /dev/null +++ b/rust/targets/riscv64-rcore.json @@ -0,0 +1,30 @@ +{ + "llvm-target": "riscv64", + "data-layout": "e-m:e-p:64:64-i64:64-n64-S128", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "arch": "riscv64", + "cpu": "generic-rv64", + "features": "+m,+a,+c", + "max-atomic-width": "64", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "executables": true, + "panic-strategy": "abort", + "relocation-model": "static", + "abi-blacklist": [ + "cdecl", + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "aapcs", + "win64", + "sysv64", + "ptx-kernel", + "msp430-interrupt", + "x86-interrupt" + ] +} diff --git a/rust/targets/x86_64-rcore.json b/rust/targets/x86_64-rcore.json new file mode 100644 index 0000000..6510efa --- /dev/null +++ b/rust/targets/x86_64-rcore.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" +}