├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── ported_objects ├── rustfmt.toml ├── scripts ├── build_and_test.sh ├── check_format.sh ├── clean_all.sh ├── examine.sh ├── frankenstein.sh ├── musl.sh ├── rusl.sh ├── test_musl.sh └── test_rusl.sh └── src ├── exit ├── _Exit.rs ├── exit.rs ├── mod.rs └── quick_exit.rs ├── lib.rs ├── malloc ├── expand_heap.rs ├── malloc.rs └── mod.rs ├── mmap.rs ├── platform └── linux-x86_64 │ ├── atomic.rs │ ├── c_types.rs │ ├── environ.rs │ ├── errno.rs │ ├── malloc.rs │ ├── mman.rs │ ├── mod.rs │ ├── pthread.rs │ └── signal.rs ├── string ├── bcmp.rs ├── bcopy.rs ├── bzero.rs ├── index.rs ├── memrchr.rs ├── mod.rs ├── rindex.rs ├── stpcpy.rs ├── strchr.rs ├── strchrnul.rs ├── strcmp.rs ├── strcpy.rs ├── strdup.rs ├── strlen.rs ├── strrchr.rs └── strspn.rs ├── syscall_mgt.rs ├── thread ├── mod.rs ├── pthread.rs └── vmlock.rs ├── time ├── clock.rs └── mod.rs └── unistd ├── _exit.rs ├── access.rs ├── acct.rs ├── alarm.rs ├── chdir.rs ├── chown.rs ├── close.rs ├── ctermid.rs ├── dup.rs ├── dup2.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | 4 | bld 5 | *.a 6 | *.o 7 | *.lo 8 | libc-test/src/common/options.h 9 | rusl_failures 10 | musl_failures 11 | *.bk 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "musl"] 2 | path = musl 3 | url = git://git.musl-libc.org/musl 4 | [submodule "libc-test"] 5 | path = libc-test 6 | url = git://repo.or.cz/libc-test.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: 3 | directories: 4 | - $HOME/.cargo/bin 5 | rust: 6 | - nightly 7 | before_script: 8 | - rustup component add --toolchain nightly rustfmt-preview 9 | - export PATH=$PATH:$HOME/.cargo/bin/ 10 | script: 11 | - scripts/check_format.sh 12 | - scripts/build_and_test.sh 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusl" 3 | version = "0.1.0" 4 | authors = ["Adam Perry "] 5 | 6 | [lib] 7 | name = "rusl" 8 | crate-type = ["staticlib", "cdylib"] 9 | 10 | [dependencies] 11 | lazy_static = { version = "1.0.0", features = ["spin_no_std"] } 12 | rlibc = "1.0.0" 13 | spin = "0.4.6" 14 | sc = "0.2.2" 15 | va_list = { version = "0.1.0", features = ["no_std"] } 16 | compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-builtins", rev = "0ba07e49264a54cb5bbd4856fcea083bb3fbec15" } 17 | 18 | [profile.dev] 19 | panic = "abort" 20 | 21 | [profile.release] 22 | debug = true 23 | panic = "abort" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The contents of the musl/ and libc-test/ directories are re-distributed under their original licenses. The remainder of this repository's contents are available 2 | under the MIT license: 3 | 4 | The MIT License (MIT) 5 | Copyright (c) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the 8 | Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 9 | Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 14 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rusl 2 | 3 | [![Build Status](https://img.shields.io/travis/dikaiosune/rusl/master.svg?style=flat-square)](https://travis-ci.org/dikaiosune/rusl) [![License](https://img.shields.io/badge/license-MIT-lightgray.svg?style=flat-square)](https://github.com/dikaiosune/rusl/blob/master/LICENSE) 4 | 5 | This is a super experimental attempt at re-implementing [musl](http://musl-libc.org) in Rust. 6 | 7 | ## Blog post(s) 8 | 9 | * [Baby Steps: Slowly Porting musl to Rust](http://blog.adamperry.me/rust/2016/06/11/baby-steps-porting-musl-to-rust/) 10 | 11 | ## Running tests 12 | 13 | If you'd like to run the tests (right now rusl only support x86_64 Linux), clone the repo and run: 14 | 15 | ``` 16 | scripts/clean_all.sh && scripts/build_and_test.sh 17 | ``` 18 | 19 | ## Contributing 20 | 21 | If you want to contribute -- thanks, I'm flattered! Please be aware that this 22 | just a hobby project, so reviews may not be very rapid. Also, while CI is 23 | configured, please please please make sure your code builds and tests pass using 24 | a recent nightly on an x86_64 Linux box/VM/VPS. It will save a lot of time 25 | before the submission gets to the review phase. 26 | 27 | CI is now set up for Pull Requests, so that needs to pass before a PR is 28 | reviewed. There aren't enough contributions right now to write a style guide, 29 | but before submitting anything please read some of the existing code to get a 30 | feeling for the current style before submitting anything, and also run 31 | [rustfmt](https://github.com/rust-lang-nursery/rustfmt) on your submission using 32 | this repository's configuration. Since this is just my hobby project at the 33 | moment, please be patient if I nitpick your PR -- there are no practical goals 34 | here so I'd like to keep things nice (even if it's at the expense of time spent 35 | or contributions lost). 36 | 37 | Please see issue #6 if you'd like help figuring out a starting point for 38 | contributions. 39 | 40 | ### Tips & Caveats 41 | 42 | As you port musl functions to Rust, you'll eventually expose one or more `pub` 43 | functions in `rusl`. For example, `malloc` from `musl/src/malloc/malloc.c` is 44 | ported to `malloc` in `src/malloc/malloc.rs`. Other (non-public, `static`) 45 | functions from `malloc.c` might get ported but won't necessarily be exposed as 46 | `pub`lic. With new `pub` functions `librusl.a` will contain symbols which 47 | conflict with object files in musl's `libc.a`, and the build will fail. The 48 | solution is to put the name of the object file with the conflicting symbols in a 49 | file called `ported_objects` living in the root of the repo. It describes all 50 | the object files which should be deleted from musl's `libc.a` so we don't get 51 | symbol conflicts during linking. One small caveat is that a few functions 52 | (`memset`, `memcmp`, and friends) are provided by `rlibc`, not `rusl`, but we 53 | still need to remove the symbols from musl's `libc.a` before mixing in `rusl` 54 | symbols. 55 | 56 | Effectively, this means you should not expose new `pub`lic functions in `rusl` 57 | until an entire C source file has been ported. Don't worry; let build failures 58 | be your guide. 59 | 60 | You can use `nm bld/usr/lib/libc.a | less` to verify the object filename 61 | providing a given symbol. Sometimes they don't match (i.e., `vmlock.c` --> 62 | `__vm_lock`). 63 | 64 | ## Documentation 65 | 66 | Since musl doesn't include documentation itself, this project won't make any effort to add new documentation beyond any comments necessary to clarify implementation-specific behavior. For documentation of C language functions and the POSIX C standard library, see: 67 | 68 | * [C99 language standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 69 | * [POSIX System Interface Functions](http://pubs.opengroup.org/onlinepubs/9699919799/functions/contents.html) 70 | 71 | ## A little background 72 | 73 | [musl](http://www.musl-libc.org/) is, according to its homepage, "a new standard library to power a new generation of Linux-based devices. **musl** is *lightweight, fast, simple, free,* and strives to be *correct* in the sense of standards-conformance and safety." 74 | 75 | [Rust](https://www.rust-lang.org) is, according to its homepage, "a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety." 76 | 77 | Seems like a nice match! In fact, there's been some great work done to [make Rust executables statically compile with musl as the libc implementation on Linux](http://blog.rust-lang.org/2016/05/13/rustup.html), allowing for fully static Rust Linux binaries (a la Go). musl has also [been cited as an excellent codebase](http://blog.regehr.org/archives/1393) for someone to read when learning C. But wait, if musl's C code is already so great, why rewrite it in Rust? 78 | 79 | 1. Because I don't know C very well, and I wanted to tinker with a codebase mostly free from preprocessor magic and with sane design and formatting decisions. As an added bonus, musl's source is very well chunked into smaller files with few interdependencies, which means that an incremental porting approach can work well. 80 | 2. musl has a [pretty big test suite](http://wiki.musl-libc.org/wiki/Libc-Test). As a systems programming (whatever that means) novice, I don't want to trust in my non-existent psychic powers to detect regressions that result from my actions. 81 | 3. I thought it'd be fun to have C programs call `malloc()` and execute Rust (and right now, the test suite currently does and passes!). It could also (if I get a heck of a lot further than I have) be a cool test case for demonstrating Rust's potential to improve our current computing infrastructure and ecosystem. 82 | 83 | ## Goals and non-goals 84 | 85 | It's obviously stupid for one junior programmer to try to build their own C standard library, right? Yeah, it is. But there are still a few reasons I'm liking this: 86 | 87 | * Learning: I don't know C and its domains very well. In my day job I write back-end web code, data analysis scripts, and bioinformatics tools. Mostly in Python. This seems like a great way to learn about C, how software interacts with the kernel, and what goes into making a standard library. 88 | * Showing that Rust can do this kind of work (allocators, pthread implementations, etc.), and showing myself that I'm capable of learning how to :). 89 | * Experimenting with ways to incrementally consume a C library or program from the inside out using Rust's solid C interop support. 90 | * I'm starting to get a more realistic idea of Rust's current limitations when operating with C (I'm looking at you, unstable inline assembly and unimplemented untagged union support). 91 | 92 | There are also a whole bunch of things I have no intention of doing: 93 | 94 | * Using [rust-bindgen](https://github.com/crabtw/rust-bindgen) or some other automated tool to generate type declarations or translate code for me. As a learning project, it's much better if I hand-translate everything (which so far is just a tiny portion of the overall codebase). 95 | * Ever having this code used in production. Ever. No really, ever. The test suite that I'm using has gaps (if you're a POSIX aficionado and a musl fan, I'm sure they'd love help improving the coverage), and while I feel pretty confident about higher-level work I do, I'm dumb as a rock when it comes to pointer math. 96 | * Supporting any platform other than Linux on x86_64. Or really supporting any platform other than my laptop or desktop. 97 | * Trying to improve upon what musl actually does. If a bug gets fixed accidentally, then cool. But right now I'm just trying to replicate its behavior nearly exactly in Rust. 98 | 99 | ## LICENSE 100 | 101 | Like musl, rusl is made available under the MIT license. 102 | -------------------------------------------------------------------------------- /ported_objects: -------------------------------------------------------------------------------- 1 | _Exit.o 2 | _exit.o 3 | __errno_location.o 4 | __wait.o 5 | access.o 6 | acct.o 7 | alarm.o 8 | bcmp.o 9 | bcopy.o 10 | bzero.o 11 | chdir.o 12 | chown.o 13 | clock.o 14 | clock_getcpuclockid.o 15 | clock_getres.o 16 | clock_gettime.o 17 | clock_nanosleep.o 18 | clock_settime.o 19 | close.o 20 | ctermid.o 21 | dup.o 22 | dup2.o 23 | exit.o 24 | expand_heap.o 25 | index.o 26 | madvise.o 27 | malloc.o 28 | memcmp.o 29 | memcpy.o 30 | memmove.o 31 | memrchr.o 32 | memset.o 33 | mincore.o 34 | mlock.o 35 | mlockall.o 36 | mmap.o 37 | mremap.o 38 | munmap.o 39 | quick_exit.o 40 | rindex.o 41 | stpcpy.o 42 | strchr.o 43 | strchrnul.o 44 | strcmp.o 45 | strcpy.o 46 | strdup.o 47 | strlen.o 48 | strrchr.o 49 | strspn.o 50 | vmlock.o 51 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imported_names = true 2 | reorder_imports = true 3 | reorder_imports_in_group = true 4 | wrap_comments = true 5 | -------------------------------------------------------------------------------- /scripts/build_and_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | SCRIPT_DIR=${BASE_DIR}/scripts 9 | 10 | mkdir -p "${BUILD_DIR}"/usr 11 | "${SCRIPT_DIR}"/musl.sh 12 | "${SCRIPT_DIR}"/rusl.sh 13 | "${SCRIPT_DIR}"/test_musl.sh 14 | "${SCRIPT_DIR}"/frankenstein.sh 15 | "${SCRIPT_DIR}"/test_rusl.sh 16 | 17 | echo 18 | echo 19 | echo 20 | echo 21 | echo "Tests that failed on rusl but not on vanilla musl:" 22 | echo 23 | 24 | # check to see if any rusl failures are new failures 25 | # we need to invert the exit code of grep here, if nothing's found it's a good thing 26 | if ! grep -v -F -x -f "${BASE_DIR}"/musl_failures "${BASE_DIR}"/rusl_failures; then 27 | true 28 | else 29 | false 30 | fi 31 | -------------------------------------------------------------------------------- /scripts/check_format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | 8 | cd "${BASE_DIR}" 9 | 10 | # check to see if any rusl failures are new failures 11 | # we need to invert the exit code of grep here, if nothing's found it's a good thing 12 | 13 | if ! rustfmt --write-mode diff src/lib.rs | grep -c "Diff at line"; then 14 | true 15 | else 16 | echo "Failed style check." 17 | false 18 | fi 19 | -------------------------------------------------------------------------------- /scripts/clean_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | BASE_DIR=${DIR}/.. 5 | BUILD_DIR=${BASE_DIR}/bld 6 | MUSL_SRC_DIR=${BASE_DIR}/musl 7 | TESTS_SRC_DIR=${BASE_DIR}/libc-test 8 | 9 | rm -dr "${BUILD_DIR}" 10 | cd "${MUSL_SRC_DIR}" && make clean 11 | cd "${TESTS_SRC_DIR}" && make cleanall && rm "${TESTS_SRC_DIR}"/config.mak 12 | cd "${BASE_DIR}" && cargo clean 13 | -------------------------------------------------------------------------------- /scripts/examine.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # examine disassembly 4 | # $1 -- function name; rest -- object files 5 | # i.e. 6 | # scripts/examine.sh bzero bld/usr/lib/libc.a 7 | # 0000000000000000 : 8 | # 0: 48 89 f2 mov %rsi,%rdx 9 | # 3: 31 f6 xor %esi,%esi 10 | # 5: e9 00 00 00 00 jmpq a 11 | # 0: 12 | 13 | fn=$1; shift 1 14 | exec objdump -d "$@" | 15 | awk " /^[[:xdigit:]].*<$fn>/,/^\$/ { print \$0 }" | 16 | awk -F: -F' ' 'NR==1 { offset=strtonum("0x"$1); print $0; } 17 | NR!=1 { split($0,a,":"); rhs=a[2]; n=strtonum("0x"$1); $1=sprintf("%x", n-offset); printf "%4s:%s\n", $1,rhs }' 18 | -------------------------------------------------------------------------------- /scripts/frankenstein.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | 9 | # move the two archives into a tempdir 10 | cd "${BUILD_DIR}"/usr/lib 11 | LIB_RUSL_DIR="${BUILD_DIR}"/usr/librusl 12 | mkdir -p "${LIB_RUSL_DIR}" 13 | cp "${BASE_DIR}"/target/release/librusl.a "${LIB_RUSL_DIR}"/ 14 | cp -t "${LIB_RUSL_DIR}"/ *.a *.specs # get .o files in a bit 15 | 16 | cd "${LIB_RUSL_DIR}" 17 | 18 | PORTED_OBJECTS=() 19 | while IFS= read -r line; do 20 | PORTED_OBJECTS+=(${line}) 21 | done < "${BASE_DIR}"/ported_objects 22 | 23 | # delete all ported object files from libc.a 24 | ar -dv libc.a "${PORTED_OBJECTS[@]}" 25 | 26 | # add in all .o files from librusl.a 27 | ar -x librusl.a 28 | ar -r libc.a *.o 29 | 30 | rm *.o 31 | # get remaining .o files from lib 32 | cp -t ./ ../lib/*.o 33 | -------------------------------------------------------------------------------- /scripts/musl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | MUSL_SRC_DIR=${BASE_DIR}/musl 9 | NUM_CPUS=$(grep -c ^processor /proc/cpuinfo) 10 | 11 | cd "${MUSL_SRC_DIR}" 12 | ./configure --prefix="${BUILD_DIR}"/usr \ 13 | --disable-shared \ 14 | --enable-static \ 15 | --disable-visibility 16 | make -j "${NUM_CPUS}" 17 | make install 18 | -------------------------------------------------------------------------------- /scripts/rusl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | 9 | cd ${BASE_DIR} 10 | cargo build --release 11 | -------------------------------------------------------------------------------- /scripts/test_musl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | TESTS_SRC_DIR=${BASE_DIR}/libc-test 9 | NUM_CPUS=$(grep -c ^processor /proc/cpuinfo) 10 | 11 | cd "${TESTS_SRC_DIR}" 12 | 13 | cp config.mak.def config.mak 14 | echo "CFLAGS += -static -isystem ${BUILD_DIR}/usr/include -B${BUILD_DIR}/usr/lib -L${BUILD_DIR}/usr/lib" >> config.mak 15 | echo "LDFLAGS += -static -isystem ${BUILD_DIR}/usr/include -B${BUILD_DIR}/usr/lib -L${BUILD_DIR}/usr/lib" >> config.mak 16 | make clean 17 | make -j "${NUM_CPUS}" CC="${BUILD_DIR}"/usr/bin/musl-gcc 18 | grep FAIL < "${TESTS_SRC_DIR}"/src/REPORT > "${BASE_DIR}"/musl_failures 19 | -------------------------------------------------------------------------------- /scripts/test_rusl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BASE_DIR=${DIR}/.. 7 | BUILD_DIR=${BASE_DIR}/bld 8 | TESTS_SRC_DIR=${BASE_DIR}/libc-test 9 | NUM_CPUS=$(grep -c ^processor /proc/cpuinfo) 10 | 11 | cd "${TESTS_SRC_DIR}" 12 | 13 | cp config.mak.def config.mak 14 | echo "CFLAGS += -static -isystem ${BUILD_DIR}/usr/include -B${BUILD_DIR}/usr/librusl -L${BUILD_DIR}/usr/librusl" >> config.mak 15 | echo "LDFLAGS += -static -isystem ${BUILD_DIR}/usr/include -B${BUILD_DIR}/usr/librusl -L${BUILD_DIR}/usr/librusl" >> config.mak 16 | make clean 17 | make -j "${NUM_CPUS}" CC="${BUILD_DIR}"/usr/bin/musl-gcc 18 | grep FAIL < "${TESTS_SRC_DIR}"/src/REPORT > "${BASE_DIR}"/rusl_failures 19 | -------------------------------------------------------------------------------- /src/exit/_Exit.rs: -------------------------------------------------------------------------------- 1 | use c_types::c_int; 2 | 3 | #[no_mangle] 4 | pub extern "C" fn _Exit(ec: c_int) -> ! { 5 | unsafe { 6 | syscall!(EXIT_GROUP, ec); 7 | syscall!(EXIT, ec); 8 | } 9 | // The loop, while technically unreachable, ensures this function 10 | // is divergent. The syscall above returns (), so this code 11 | // wouldn't typecheck if we didn't have this infinite loop. See 12 | // this musl changeset for similar discussion: 13 | // http://git.musl-libc.org/cgit/musl/commit/src/exit/_Exit.c?id=0c05bd3a9c165cf2f0b9d6fa23a1f96532ddcdb3 14 | loop {} 15 | } 16 | -------------------------------------------------------------------------------- /src/exit/exit.rs: -------------------------------------------------------------------------------- 1 | use super::_Exit::_Exit; 2 | use c_types::c_int; 3 | 4 | #[linkage = "weak"] 5 | #[no_mangle] 6 | pub extern "C" fn __funcs_on_exit() {} 7 | 8 | #[linkage = "weak"] 9 | #[no_mangle] 10 | pub extern "C" fn __stdio_exit() {} 11 | 12 | #[linkage = "weak"] 13 | #[no_mangle] 14 | pub extern "C" fn _fini() {} 15 | 16 | #[linkage = "weak"] 17 | #[no_mangle] 18 | pub extern "C" fn __libc_exit_fini() { 19 | // TODO dark magic goes here, seemingly for dynlink's benefit 20 | // See http://git.musl-libc.org/cgit/musl/commit/src/exit/exit.c?id=7586360badcae6e73f04eb1b8189ce630281c4b2 21 | // and http://git.musl-libc.org/cgit/musl/commit/src/exit/exit.c?id=19caa25d0a8e587bb89b79c3f629085548709dd4 22 | // for interesting discussion about how musl handles this function. 23 | // Really, we should iterate through __fini_array_start..__fini_array_end, 24 | // executing each function. But it requires annoying pointer 25 | // manipulation that I wasn't sure we needed yet, since we aren't 26 | // porting dynlink right now. 27 | _fini() 28 | } 29 | 30 | #[no_mangle] 31 | pub extern "C" fn exit(code: c_int) -> ! { 32 | __funcs_on_exit(); 33 | __libc_exit_fini(); 34 | __stdio_exit(); 35 | // The functions above are weakly-aliased because the dynlinker 36 | // might provide defs for them. Meanwhile, we provide dummy fns. 37 | _Exit(code) 38 | } 39 | -------------------------------------------------------------------------------- /src/exit/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_snake_case)] 2 | pub mod _Exit; 3 | pub mod exit; 4 | pub mod quick_exit; 5 | -------------------------------------------------------------------------------- /src/exit/quick_exit.rs: -------------------------------------------------------------------------------- 1 | use super::_Exit::_Exit; 2 | use c_types::c_int; 3 | 4 | #[linkage = "weak"] 5 | #[no_mangle] 6 | pub extern "C" fn __funcs_on_quick_exit() {} 7 | 8 | #[no_mangle] 9 | pub extern "C" fn quick_exit(code: c_int) { 10 | __funcs_on_quick_exit(); 11 | _Exit(code) 12 | } 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm, const_fn, lang_items, linkage, compiler_builtins_lib)] 3 | #![feature(pointer_methods)] 4 | #![allow(non_camel_case_types)] 5 | #![cfg_attr(feature = "cargo-clippy", allow(inline_always))] 6 | 7 | extern crate compiler_builtins; 8 | #[macro_use] 9 | extern crate lazy_static; 10 | extern crate rlibc; 11 | extern crate sc as syscall; 12 | extern crate spin; 13 | extern crate va_list; 14 | 15 | pub use rlibc::*; 16 | 17 | #[macro_use] 18 | pub mod syscall_mgt; 19 | 20 | pub mod exit; 21 | pub mod malloc; 22 | pub mod mmap; 23 | pub mod string; 24 | pub mod thread; 25 | pub mod time; 26 | pub mod unistd; 27 | 28 | #[cfg(all(target_os = "linux", target_arch = "x86_64"))] 29 | #[path = "platform/linux-x86_64/mod.rs"] 30 | pub mod platform; 31 | 32 | pub use platform::atomic; 33 | pub use platform::c_types; 34 | pub use platform::environ; 35 | pub use platform::errno; 36 | pub use platform::mman; 37 | pub use platform::pthread; 38 | pub use platform::signal; 39 | 40 | #[cfg(not(test))] 41 | #[lang = "panic_fmt"] 42 | #[no_mangle] 43 | pub extern "C" fn panic_fmt() {} 44 | -------------------------------------------------------------------------------- /src/malloc/expand_heap.rs: -------------------------------------------------------------------------------- 1 | use core::num::Wrapping; 2 | use core::ptr; 3 | use core::usize; 4 | 5 | use c_types::*; 6 | use environ::AUXV_PTR; 7 | use errno::{set_errno, ENOMEM}; 8 | use mmap::__mmap; 9 | use platform::mman::*; 10 | 11 | /// Comment from original musl C function: 12 | /// 13 | /// This function returns true if the interval [old,new] 14 | /// intersects the 'len'-sized interval below &libc.auxv 15 | /// (interpreted as the main-thread stack) or below &b 16 | /// (the current stack). It is used to defend against 17 | /// buggy brk implementations that can cross the stack. 18 | #[no_mangle] 19 | pub extern "C" fn traverses_stack_p(old: usize, new: usize) -> c_int { 20 | let len = 8usize << 20; 21 | 22 | let b = *AUXV_PTR; 23 | let a = if b > len { b - len } else { 0 }; 24 | 25 | if new > a && old < b { 26 | return 1; 27 | } 28 | 29 | let b = (&b as *const usize) as usize; 30 | let a = if b > len { b - len } else { 0 }; 31 | 32 | if new > a && old < b { 33 | return 1; 34 | } 35 | 36 | 0 37 | } 38 | 39 | // Expand the heap in-place if brk can be used, or otherwise via mmap, 40 | // using an exponential lower bound on growth by mmap to make 41 | // fragmentation asymptotically irrelevant. The size argument is both 42 | // an input and an output, since the caller needs to know the size 43 | // allocated, which will be larger than requested due to page alignment 44 | // and mmap minimum size rules. The caller is responsible for locking 45 | // to prevent concurrent calls. 46 | 47 | #[no_mangle] 48 | pub unsafe extern "C" fn __expand_heap(pn: *mut size_t) -> *mut c_void { 49 | static mut BRK: usize = 0; 50 | static mut MMAP_STEP: c_uint = 0; 51 | 52 | let mut n = *pn; 53 | 54 | if n > ((usize::MAX / 2) - PAGE_SIZE as usize) { 55 | set_errno(ENOMEM); 56 | return ptr::null_mut(); 57 | } 58 | 59 | n += (-Wrapping(n)).0 & (PAGE_SIZE as usize - 1); 60 | 61 | if BRK == 0 { 62 | BRK = syscall!(BRK, 0); 63 | BRK += (-Wrapping(BRK)).0 & (PAGE_SIZE - 1) as usize; 64 | } 65 | 66 | if n < (usize::MAX - BRK) && traverses_stack_p(BRK, BRK + n) == 0 67 | && syscall!(BRK, BRK + n) == BRK + n 68 | { 69 | *pn = n; 70 | BRK += n; 71 | return (BRK - n) as *mut c_void; 72 | } 73 | 74 | let min = (PAGE_SIZE << (MMAP_STEP / 2)) as usize; 75 | 76 | if n < min { 77 | n = min; 78 | } 79 | 80 | let area = __mmap( 81 | ptr::null_mut(), 82 | n, 83 | PROT_READ | PROT_WRITE, 84 | MAP_PRIVATE | MAP_ANONYMOUS, 85 | -1, 86 | 0, 87 | ); 88 | 89 | if area == MAP_FAILED { 90 | return ptr::null_mut(); 91 | } 92 | 93 | *pn = n; 94 | MMAP_STEP += 1; 95 | 96 | area 97 | } 98 | -------------------------------------------------------------------------------- /src/malloc/malloc.rs: -------------------------------------------------------------------------------- 1 | use core::isize; 2 | use core::mem::transmute; 3 | use core::ptr; 4 | 5 | use memcpy; 6 | 7 | use spin::Mutex; 8 | 9 | use c_types::*; 10 | use errno::{set_errno, ENOMEM}; 11 | use malloc::expand_heap::__expand_heap; 12 | use mmap::{__madvise, __mmap, __munmap, mremap_helper}; 13 | use platform::atomic::{a_crash, a_store, a_swap, a_and_64, a_ctz_64, a_or_64}; 14 | use platform::malloc::*; 15 | use platform::mman::*; 16 | use thread::{__wait, __wake}; 17 | 18 | pub const MMAP_THRESHOLD: usize = 0x1c00 * SIZE_ALIGN; 19 | pub const DONTCARE: usize = 16; 20 | pub const RECLAIM: usize = 163_840; 21 | 22 | static mut HEAP: Heap = Heap::new(); 23 | 24 | pub struct Chunk { 25 | psize: usize, 26 | csize: usize, 27 | next: *mut Chunk, 28 | prev: *mut Chunk, 29 | } 30 | 31 | impl Chunk { 32 | pub unsafe fn trim(&mut self, n: usize) { 33 | let n1 = self.size(); 34 | 35 | if n >= n1 - DONTCARE { 36 | return; 37 | } 38 | 39 | let next = self.next(); 40 | let split = (self as *const Self as *mut u8).offset(n as isize) as *mut Chunk; 41 | 42 | (*split).psize = n | 1; 43 | (*split).csize = (n1 - n) | 1; 44 | 45 | (*next).psize = (n1 - n) | 1; 46 | 47 | self.csize = n | 1; 48 | 49 | free((*split).as_mem()); 50 | } 51 | 52 | pub fn is_mmapped(&self) -> bool { 53 | (self.csize & 1) == 0 54 | } 55 | 56 | pub unsafe fn from_mem(ptr: *mut c_void) -> *mut Chunk { 57 | ((ptr as *mut u8).offset(-(OVERHEAD as isize))) as *mut Chunk 58 | } 59 | 60 | pub unsafe fn as_mem(&self) -> *mut c_void { 61 | (self as *const Chunk as *const u8).offset(OVERHEAD as isize) as *mut c_void 62 | } 63 | 64 | pub fn size(&self) -> usize { 65 | self.csize & ((-2i64) as usize) 66 | } 67 | 68 | pub fn psize(&self) -> usize { 69 | self.psize & ((-2i64) as usize) 70 | } 71 | 72 | unsafe fn previous(&self) -> *mut Chunk { 73 | (self as *const Self as *const u8).offset(-(self.psize() as isize)) as *mut Chunk 74 | } 75 | 76 | unsafe fn next(&self) -> *mut Chunk { 77 | (self as *const Self as *const u8).offset(self.size() as isize) as *mut Chunk 78 | } 79 | } 80 | 81 | pub struct Bin { 82 | lock: [c_int; 2], 83 | head: *mut Chunk, 84 | tail: *mut Chunk, 85 | } 86 | 87 | pub struct Heap { 88 | binmap: u64, 89 | bins: [Bin; 64], 90 | } 91 | 92 | impl Bin { 93 | const fn new() -> Self { 94 | Bin { 95 | lock: [0; 2], 96 | head: ptr::null::() as *mut Chunk, 97 | tail: ptr::null::() as *mut Chunk, 98 | } 99 | } 100 | } 101 | 102 | macro_rules! array { 103 | (@accum (0, $($_es:expr),*) -> ($($body:tt)*)) 104 | => {array!(@as_expr [$($body)*])}; 105 | (@accum (2, $($es:expr),*) -> ($($body:tt)*)) 106 | => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)* $($es,)*))}; 107 | (@accum (4, $($es:expr),*) -> ($($body:tt)*)) 108 | => {array!(@accum (2, $($es,)* $($es),*) -> ($($body)*))}; 109 | (@accum (8, $($es:expr),*) -> ($($body:tt)*)) 110 | => {array!(@accum (4, $($es,)* $($es),*) -> ($($body)*))}; 111 | (@accum (16, $($es:expr),*) -> ($($body:tt)*)) 112 | => {array!(@accum (8, $($es,)* $($es),*) -> ($($body)*))}; 113 | (@accum (32, $($es:expr),*) -> ($($body:tt)*)) 114 | => {array!(@accum (16, $($es,)* $($es),*) -> ($($body)*))}; 115 | (@accum (64, $($es:expr),*) -> ($($body:tt)*)) 116 | => {array!(@accum (32, $($es,)* $($es),*) -> ($($body)*))}; 117 | 118 | (@as_expr $e:expr) => {$e}; 119 | 120 | [$e:expr; $n:tt] => { array!(@accum ($n, $e) -> ()) }; 121 | } 122 | 123 | impl Heap { 124 | const fn new() -> Self { 125 | Heap { 126 | binmap: 0, 127 | bins: array![Bin::new(); 64], 128 | } 129 | } 130 | 131 | pub unsafe fn allocate(&self, mut n: usize) -> *mut c_void { 132 | let mut c: *mut Chunk; 133 | 134 | if self.adjust_size(&mut n) < 0 { 135 | return ptr::null_mut(); 136 | } 137 | 138 | if n > MMAP_THRESHOLD { 139 | let len = n + OVERHEAD + PAGE_SIZE as usize - 1 & (-PAGE_SIZE) as usize; 140 | let base = __mmap( 141 | ptr::null_mut(), 142 | len, 143 | PROT_READ | PROT_WRITE, 144 | MAP_PRIVATE | MAP_ANONYMOUS, 145 | -1, 146 | 0, 147 | ) as *mut u8; 148 | 149 | if base == ((-1isize) as usize) as *mut u8 { 150 | return ptr::null_mut(); 151 | } 152 | 153 | c = base.offset((SIZE_ALIGN - OVERHEAD) as isize) as *mut Chunk; 154 | (*c).csize = len - (SIZE_ALIGN - OVERHEAD); 155 | (*c).psize = SIZE_ALIGN - OVERHEAD; 156 | return (*c).as_mem(); 157 | } 158 | 159 | let i = self.bin_index_up(n); 160 | loop { 161 | let mask = self.binmap & (-((1usize << i) as isize)) as u64; 162 | if mask == 0 { 163 | c = self.expand(n); 164 | if c.is_null() { 165 | return ptr::null_mut(); 166 | } 167 | if self.alloc_rev(c) { 168 | let x = c; 169 | c = (*c).previous(); 170 | 171 | let new = (*x).csize + (*c).size(); 172 | (*c).csize = new; 173 | (*(*x).next()).psize = new; 174 | } 175 | break; 176 | } 177 | 178 | let j = a_ctz_64(mask) as c_int; 179 | lock_bin(j); 180 | c = self.bins[j as usize].head; 181 | 182 | if c != self.bin_to_chunk(j as usize) { 183 | if !self.pretrim(c, n, i, j) { 184 | self.unbin(c, j); 185 | } 186 | unlock_bin(j); 187 | break; 188 | } 189 | unlock_bin(j); 190 | } 191 | 192 | // Now patch up in case we over-allocated 193 | (*c).trim(n); 194 | 195 | (*c).as_mem() 196 | } 197 | 198 | pub unsafe fn reallocate(&self, p: *mut c_void, mut n: usize) -> *mut c_void { 199 | if p.is_null() { 200 | return malloc(n); 201 | } 202 | 203 | if self.adjust_size(&mut n) < 0 { 204 | return ptr::null_mut(); 205 | } 206 | 207 | let s = Chunk::from_mem(p); 208 | let n0 = (*s).size(); 209 | 210 | if (*s).is_mmapped() { 211 | let extra = (*s).psize; 212 | let base = (s as *mut u8).offset(-(extra as isize)); 213 | let old_len = n0 + extra; 214 | let new_len = n + extra; 215 | 216 | // Crash on realloc of freed chunk 217 | if extra & 1 != 0 { 218 | a_crash(); 219 | } 220 | 221 | let new = malloc(n); 222 | if new_len < PAGE_SIZE as usize && !new.is_null() { 223 | memcpy(new as *mut u8, p as *const u8, n - OVERHEAD); 224 | free(p); 225 | return new; 226 | } 227 | 228 | let new_len = (new_len + PAGE_SIZE as usize - 1) & (-PAGE_SIZE) as usize; 229 | 230 | if old_len == new_len { 231 | return p; 232 | } 233 | 234 | let base = mremap_helper(base as *mut c_void, old_len, new_len, MREMAP_MAYMOVE, None); 235 | 236 | if base as usize == (-1isize) as usize { 237 | return if new_len < old_len { 238 | p 239 | } else { 240 | ptr::null_mut() 241 | }; 242 | } 243 | 244 | let s = base.offset(extra as isize) as *mut Chunk; 245 | (*s).csize = new_len - extra; 246 | 247 | return (*s).as_mem(); 248 | } 249 | 250 | let mut next = (*s).next(); 251 | 252 | // Crash on corrupted footer (likely from buffer overflow) 253 | if (*next).psize != (*s).csize { 254 | a_crash(); 255 | } 256 | 257 | // Merge adjacent chunks if we need more space. This is not 258 | // a waste of time even if we fail to get enough space, because our 259 | // subsequent call to free would otherwise have to do the merge. 260 | let mut n1 = n0; 261 | if n > n1 && self.alloc_fwd(next) { 262 | n1 += (*next).size(); 263 | next = (*next).next(); 264 | } 265 | 266 | (*s).csize = n1 | 1; 267 | (*next).psize = n1 | 1; 268 | 269 | // If we got enough space, split off the excess and return 270 | if n <= n1 { 271 | (*s).trim(n); 272 | return (*s).as_mem(); 273 | } 274 | 275 | // As a last resort, allocate a new chunk and copy to it. 276 | let new = malloc(n - OVERHEAD); 277 | if new.is_null() { 278 | return ptr::null_mut(); 279 | } 280 | 281 | memcpy(new as *mut u8, p as *const u8, n0 - OVERHEAD); 282 | free((*s).as_mem()); 283 | 284 | new 285 | } 286 | 287 | pub unsafe fn free_ptr(&self, ptr: *mut c_void) { 288 | static FREE_LOCK: Mutex<()> = Mutex::new(()); 289 | 290 | if ptr.is_null() { 291 | return; 292 | } 293 | 294 | let mut s = Chunk::from_mem(ptr); 295 | let mut next: *mut Chunk; 296 | let mut final_size: usize; 297 | let new_size: usize; 298 | let mut size: usize; 299 | 300 | let mut reclaim = false; 301 | let mut i: c_int; 302 | 303 | if (*s).is_mmapped() { 304 | let extra = (*s).psize as isize; 305 | let base = (s as *mut u8).offset(-extra); 306 | let len = (*s).size() + extra as usize; 307 | 308 | // crash on double free 309 | if extra & 1 != 0 { 310 | a_crash(); 311 | } 312 | __munmap(base as *mut c_void, len); 313 | return; 314 | } 315 | 316 | new_size = (*s).size(); 317 | final_size = new_size; 318 | next = (*s).next(); 319 | 320 | // crash on corrupted footer (likely from buffer overflow) 321 | if (*next).psize != (*s).csize { 322 | a_crash(); 323 | } 324 | 325 | { 326 | let mut _held_lock = None; 327 | loop { 328 | if (*s).psize & (*next).csize & 1 != 0 { 329 | (*s).csize = final_size | 1; 330 | (*next).psize = final_size | 1; 331 | 332 | i = self.bin_index(final_size); 333 | lock_bin(i); 334 | _held_lock = Some(FREE_LOCK.lock()); 335 | 336 | if (*s).psize & (*next).csize & 1 != 0 { 337 | break; 338 | } 339 | 340 | _held_lock = None; 341 | unlock_bin(i); 342 | } 343 | 344 | if self.alloc_rev(s) { 345 | s = (*s).previous(); 346 | size = (*s).size(); 347 | final_size += size; 348 | 349 | if new_size + size > RECLAIM && (new_size + size ^ size) > size { 350 | reclaim = true; 351 | } 352 | } 353 | 354 | if self.alloc_fwd(next) { 355 | size = (*next).size(); 356 | final_size += size; 357 | if new_size + size > RECLAIM && (new_size + size ^ size) > size { 358 | reclaim = true; 359 | } 360 | next = (*next).next(); 361 | } 362 | } 363 | 364 | if (self.binmap & 1u64 << i) == 0 { 365 | a_or_64(&self.binmap as *const _ as *mut _, 1u64 << i); 366 | } 367 | 368 | (*s).csize = final_size; 369 | (*next).psize = final_size; 370 | } 371 | 372 | (*s).next = self.bin_to_chunk(i as usize); 373 | (*s).prev = self.bins[i as usize].tail; 374 | (*(*s).next).prev = s; 375 | (*(*s).prev).next = s; 376 | 377 | // replace middle of large chunks with fresh zero pages 378 | if reclaim { 379 | let a = s as usize + SIZE_ALIGN + PAGE_SIZE as usize - 1 & (-PAGE_SIZE) as usize; 380 | let b = next as usize - SIZE_ALIGN & (-PAGE_SIZE) as usize; 381 | __madvise(a as *mut c_void, b - a, MADV_DONTNEED); 382 | } 383 | 384 | unlock_bin(i); 385 | } 386 | 387 | unsafe fn expand(&self, mut n: usize) -> *mut Chunk { 388 | static mut HEAP_LOCK: Mutex<*mut c_void> = Mutex::new(ptr::null_mut()); 389 | 390 | let mut p: *mut c_void; 391 | let mut w: *mut Chunk; 392 | 393 | // The argument n already accounts for the caller's chunk 394 | // overhead needs, but if the heap can't be extended in-place, 395 | // we need room for an extra zero-sized sentinel chunk. 396 | n += SIZE_ALIGN; 397 | 398 | let mut end = HEAP_LOCK.lock(); 399 | 400 | p = __expand_heap(&mut n as *mut usize); 401 | 402 | if p.is_null() { 403 | return ptr::null_mut(); 404 | } 405 | 406 | // If not just expanding existing space, we need to make a 407 | // new sentinel chunk below the allocated space. 408 | if p != *end { 409 | // Valid/safe because of the prologue increment. 410 | n -= SIZE_ALIGN; 411 | p = (p as *mut u8).offset(SIZE_ALIGN as isize) as *mut c_void; 412 | w = Chunk::from_mem(p); 413 | (*w).psize = 1; 414 | } 415 | 416 | // Record new heap end and fill in footer. 417 | *end = (p as *mut u8).offset(n as isize) as *mut c_void; 418 | w = Chunk::from_mem(*end); 419 | (*w).psize = n | 1; 420 | (*w).csize = 1; 421 | 422 | // Fill in header, which may be new or may be replacing a 423 | // zero-size sentinel header at the old end-of-heap. 424 | w = Chunk::from_mem(p); 425 | (*w).csize = n | 1; 426 | 427 | w 428 | } 429 | 430 | unsafe fn alloc_fwd(&self, c: *mut Chunk) -> bool { 431 | let mut i: c_int; 432 | let mut k: usize; 433 | 434 | while { 435 | k = (*c).csize; 436 | k & 1 == 0 437 | } { 438 | i = self.bin_index(k); 439 | lock_bin(i); 440 | if (*c).csize == k { 441 | self.unbin(c, i); 442 | unlock_bin(i); 443 | return true; 444 | } 445 | unlock_bin(i); 446 | } 447 | 448 | false 449 | } 450 | 451 | unsafe fn alloc_rev(&self, c: *mut Chunk) -> bool { 452 | let mut i: c_int; 453 | let mut k: usize; 454 | 455 | while { 456 | k = (*c).psize; 457 | k & 1 == 0 458 | } { 459 | i = self.bin_index(k); 460 | lock_bin(i); 461 | if (*c).psize == k { 462 | self.unbin((*c).previous(), i); 463 | unlock_bin(i); 464 | return true; 465 | } 466 | unlock_bin(i); 467 | } 468 | false 469 | } 470 | 471 | unsafe fn unbin(&self, c: *mut Chunk, i: c_int) { 472 | if (*c).prev == (*c).next { 473 | a_and_64(&self.binmap as *const _ as *mut _, !(1u64 << i)); 474 | } 475 | 476 | (*(*c).prev).next = (*c).next; 477 | (*(*c).next).prev = (*c).prev; 478 | (*c).csize |= 1; 479 | (*(*c).next()).psize |= 1; 480 | } 481 | 482 | unsafe fn bin_to_chunk(&self, i: usize) -> *mut Chunk { 483 | Chunk::from_mem(((&self.bins[i].head) as *const *mut Chunk as usize) as *mut c_void) 484 | } 485 | 486 | fn bin_index(&self, x: usize) -> i32 { 487 | let x = (x / SIZE_ALIGN) - 1; 488 | 489 | if x <= 32 { 490 | x as c_int 491 | } else if x > 0x1c00 { 492 | 63 493 | } else { 494 | unsafe { ((transmute::((x as c_int) as c_float) >> 21) - 496) as c_int } 495 | } 496 | } 497 | 498 | fn bin_index_up(&self, x: usize) -> c_int { 499 | let x = (x / SIZE_ALIGN) - 1; 500 | 501 | if x <= 32 { 502 | x as c_int 503 | } else { 504 | unsafe { 505 | ((transmute::((x as c_int) as c_float) + 0x1fffff >> 21) - 496) 506 | as c_int 507 | } 508 | } 509 | } 510 | 511 | // pretrim - trims a chunk _prior_ to removing it from its bin. 512 | // Must be called with i as the ideal bin for size n, j the bin 513 | // for the _free_ chunk self, and bin j locked. 514 | unsafe fn pretrim(&self, s: *mut Chunk, n: usize, i: c_int, j: c_int) -> bool { 515 | // We cannot pretrim if it would require re-binning. 516 | if j < 40 || (j < i + 3 && j != 63) { 517 | return false; 518 | } 519 | 520 | let n1 = (*s).size(); 521 | 522 | if n1 - n <= MMAP_THRESHOLD { 523 | return false; 524 | } 525 | 526 | if self.bin_index(n1 - n) != j { 527 | return false; 528 | } 529 | 530 | let next = (*s).next(); 531 | let split = (s as *mut u8).offset(n as isize) as *mut Chunk; 532 | 533 | (*split).prev = (*s).prev; 534 | (*split).next = (*s).next; 535 | (*(*split).prev).next = split; 536 | (*(*split).next).prev = split; 537 | (*split).psize = n | 1; 538 | (*split).csize = n1 - n; 539 | 540 | (*next).psize = n1 - n; 541 | 542 | (*s).csize = n | 1; 543 | 544 | true 545 | } 546 | 547 | unsafe fn adjust_size(&self, n: *mut usize) -> c_int { 548 | // Result of pointer difference must fit in ptrdiff_t. 549 | if *n - 1 > isize::MAX as usize - SIZE_ALIGN as usize - PAGE_SIZE as usize { 550 | if *n != 0 { 551 | set_errno(ENOMEM); 552 | -1 553 | } else { 554 | *n = SIZE_ALIGN; 555 | 0 556 | } 557 | } else { 558 | *n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK; 559 | 0 560 | } 561 | } 562 | } 563 | 564 | #[no_mangle] 565 | pub unsafe extern "C" fn malloc(n: usize) -> *mut c_void { 566 | HEAP.allocate(n) 567 | } 568 | 569 | #[no_mangle] 570 | pub unsafe extern "C" fn __malloc0(n: usize) -> *mut c_void { 571 | let p = malloc(n); 572 | 573 | let chunk = Chunk::from_mem(p); 574 | 575 | if !p.is_null() && !(*chunk).is_mmapped() { 576 | for i in 0..n { 577 | *(p as *mut u8).offset(i as isize) = 0; 578 | } 579 | } 580 | 581 | p 582 | } 583 | 584 | #[no_mangle] 585 | pub unsafe extern "C" fn free(p: *mut c_void) { 586 | HEAP.free_ptr(p); 587 | } 588 | 589 | #[no_mangle] 590 | pub unsafe extern "C" fn realloc(p: *mut c_void, n: usize) -> *mut c_void { 591 | HEAP.reallocate(p, n) 592 | } 593 | 594 | unsafe fn lock(lock: *mut c_int) { 595 | while a_swap(lock, 1) != 0 { 596 | __wait(lock, lock.offset(1), 1, 1); 597 | } 598 | } 599 | 600 | unsafe fn unlock(lock: *mut c_int) { 601 | if *lock != 0 { 602 | a_store(lock, 0); 603 | if *lock.offset(1) != 0 { 604 | __wake(lock as *mut c_void, 1, 1); 605 | } 606 | } 607 | } 608 | 609 | unsafe fn unlock_bin(i: c_int) { 610 | unlock(&mut HEAP.bins[i as usize].lock[0]); 611 | } 612 | 613 | unsafe fn lock_bin(i: c_int) { 614 | let i = i as usize; 615 | lock(&mut HEAP.bins[i].lock[0]); 616 | 617 | if HEAP.bins[i].head.is_null() { 618 | HEAP.bins[i].tail = HEAP.bin_to_chunk(i); 619 | HEAP.bins[i].head = HEAP.bins[i].tail; 620 | } 621 | } 622 | -------------------------------------------------------------------------------- /src/malloc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod expand_heap; 2 | pub mod malloc; 3 | 4 | pub unsafe extern "C" fn __brk(new_break: usize) -> usize { 5 | syscall!(BRK, new_break) 6 | } 7 | -------------------------------------------------------------------------------- /src/mmap.rs: -------------------------------------------------------------------------------- 1 | use core::isize; 2 | 3 | use va_list::VaList; 4 | 5 | use c_types::*; 6 | use errno::{set_errno, EINVAL, ENOMEM, EPERM}; 7 | use platform::mman::*; 8 | use thread::vmlock::__vm_wait; 9 | 10 | use syscall_mgt::syscall_return; 11 | 12 | #[no_mangle] 13 | pub unsafe extern "C" fn __munmap(start: *mut c_void, len: size_t) -> c_int { 14 | __vm_wait(); 15 | syscall!(MUNMAP, start, len) as c_int 16 | } 17 | 18 | #[no_mangle] 19 | pub unsafe extern "C" fn __mmap( 20 | start: *mut c_void, 21 | len: size_t, 22 | prot: c_int, 23 | flags: c_int, 24 | fd: c_int, 25 | off: off_t, 26 | ) -> *mut c_void { 27 | if (off & (PAGE_SIZE - 1)) != 0 { 28 | set_errno(EINVAL); 29 | return MAP_FAILED; 30 | } 31 | 32 | if len >= isize::MAX as usize { 33 | set_errno(ENOMEM); 34 | return MAP_FAILED; 35 | } 36 | 37 | if flags & MAP_FIXED != 0 { 38 | __vm_wait(); 39 | } 40 | 41 | // Ported this fixup from 42 | // http://git.musl-libc.org/cgit/musl/commit/src/mman/mmap.c?id=da438ee1fc516c41ba1790cef7be551a9e244397 43 | let mut ret = syscall!(MMAP, start, len, prot, flags, fd, off); 44 | if ret == -EPERM as usize && start.is_null() && (flags & MAP_ANON) != 0 45 | && (flags & MAP_FIXED) == 0 46 | { 47 | ret = -ENOMEM as usize; 48 | } 49 | syscall_return(ret) as *mut c_void 50 | } 51 | 52 | #[no_mangle] 53 | pub unsafe extern "C" fn __mremap( 54 | old_address: *mut c_void, 55 | old_len: size_t, 56 | new_len: size_t, 57 | flags: c_int, 58 | mut args: VaList, 59 | ) -> *mut c_void { 60 | let new_address = if flags & MREMAP_FIXED != 0 { 61 | __vm_wait(); 62 | Some(args.get::()) 63 | } else { 64 | None 65 | }; 66 | 67 | mremap_helper(old_address, old_len, new_len, flags, new_address) 68 | } 69 | 70 | pub unsafe fn mremap_helper( 71 | old_address: *mut c_void, 72 | old_len: size_t, 73 | new_len: size_t, 74 | flags: c_int, 75 | new_address: Option, 76 | ) -> *mut c_void { 77 | if new_len >= isize::MAX as usize { 78 | set_errno(ENOMEM); 79 | return MAP_FAILED; 80 | } 81 | 82 | let new_address = new_address.unwrap_or(0); 83 | 84 | syscall!(MREMAP, old_address, old_len, new_len, flags, new_address) as *mut c_void 85 | } 86 | 87 | #[linkage = "weak"] 88 | #[no_mangle] 89 | pub unsafe extern "C" fn madvise(address: *mut c_void, len: usize, advice: c_int) -> c_int { 90 | syscall!(MADVISE, address, len, advice) as c_int 91 | } 92 | 93 | #[no_mangle] 94 | pub unsafe extern "C" fn __madvise(address: *mut c_void, len: usize, advice: c_int) -> c_int { 95 | madvise(address, len, advice) 96 | } 97 | 98 | #[no_mangle] 99 | pub unsafe extern "C" fn mincore(address: *mut c_void, len: usize, vec: *mut u8) -> c_int { 100 | syscall!(MINCORE, address, len, vec) as c_int 101 | } 102 | 103 | #[no_mangle] 104 | pub unsafe extern "C" fn mlock(address: *const c_void, len: usize) -> c_int { 105 | syscall!(MLOCK, address, len) as c_int 106 | } 107 | 108 | #[no_mangle] 109 | pub unsafe extern "C" fn mlockall(flags: c_int) -> c_int { 110 | syscall!(MLOCKALL, flags) as c_int 111 | } 112 | 113 | // aliases 114 | #[linkage = "weak"] 115 | #[no_mangle] 116 | pub unsafe extern "C" fn munmap(start: *mut c_void, len: size_t) -> c_int { 117 | __munmap(start, len) 118 | } 119 | 120 | #[linkage = "weak"] 121 | #[no_mangle] 122 | pub unsafe extern "C" fn mmap( 123 | start: *mut c_void, 124 | len: size_t, 125 | prot: c_int, 126 | flags: c_int, 127 | fd: c_int, 128 | off: off_t, 129 | ) -> *mut c_void { 130 | __mmap(start, len, prot, flags, fd, off) 131 | } 132 | 133 | #[linkage = "weak"] 134 | #[no_mangle] 135 | pub unsafe extern "C" fn mmap64( 136 | start: *mut c_void, 137 | len: size_t, 138 | prot: c_int, 139 | flags: c_int, 140 | fd: c_int, 141 | off: off_t, 142 | ) -> *mut c_void { 143 | __mmap(start, len, prot, flags, fd, off) 144 | } 145 | 146 | #[linkage = "weak"] 147 | #[no_mangle] 148 | pub unsafe extern "C" fn mremap( 149 | old_address: *mut c_void, 150 | old_len: size_t, 151 | new_len: size_t, 152 | flags: c_int, 153 | args: VaList, 154 | ) -> *mut c_void { 155 | __mremap(old_address, old_len, new_len, flags, args) 156 | } 157 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/atomic.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use core::sync::atomic::{AtomicPtr, Ordering}; 3 | 4 | #[inline(always)] 5 | #[no_mangle] 6 | pub unsafe extern "C" fn a_cas(p: *mut c_int, mut t: c_int, s: c_int) -> c_int { 7 | asm!("lock ; cmpxchgl $3, $1" : 8 | "=A"(t), "=*m"(p) : 9 | "A"(t), "r"(s) : 10 | "memory" : 11 | "volatile"); 12 | t 13 | } 14 | 15 | #[inline(always)] 16 | #[no_mangle] 17 | pub unsafe extern "C" fn a_cas_p(p: *mut c_int, mut t: *mut c_int, s: *mut c_int) -> *mut c_void { 18 | asm!("lock ; cmpxchg $3, $1" : 19 | "=A"(t), "=*m"(p) : 20 | "A"(t), "r"(s) : 21 | "memory" : 22 | "volatile"); 23 | t as *mut c_void 24 | } 25 | 26 | #[inline(always)] 27 | #[no_mangle] 28 | pub unsafe extern "C" fn a_swap(p: *mut c_int, mut v: c_int) -> c_int { 29 | *AtomicPtr::new(p).swap(&mut v, Ordering::Relaxed) 30 | } 31 | 32 | #[inline(always)] 33 | #[no_mangle] 34 | pub unsafe extern "C" fn a_store(p: *mut c_int, x: c_int) { 35 | asm!("mov $1, $0 ; lock ; orl $$0, (%rsp)" 36 | : "=*m"(p) : "r"(x) : "memory" : "volatile"); 37 | } 38 | 39 | #[inline(always)] 40 | #[no_mangle] 41 | pub unsafe extern "C" fn a_inc(p: *mut c_int) { 42 | asm!( 43 | "lock ; incl $0" 44 | :"=*m"(p) 45 | :"m"(*p) 46 | :"memory" 47 | :"volatile" 48 | ); 49 | } 50 | 51 | #[inline(always)] 52 | #[no_mangle] 53 | pub unsafe extern "C" fn a_dec(p: *mut c_int) { 54 | asm!( 55 | "lock ; decl $0" 56 | :"=*m"(p) 57 | :"m"(*p) 58 | :"memory" 59 | :"volatile" 60 | ); 61 | } 62 | 63 | #[inline(always)] 64 | #[no_mangle] 65 | pub unsafe extern "C" fn a_fetch_add(p: *mut c_int, mut v: c_int) -> c_int { 66 | asm!("lock ; xadd $0, $1" 67 | : "=r"(v), "=*m"(p) : "0"(v) : "memory" : "volatile"); 68 | v 69 | } 70 | 71 | #[inline(always)] 72 | #[no_mangle] 73 | pub unsafe extern "C" fn a_and(p: *mut c_int, v: c_int) { 74 | asm!("lock ; and $1, $0" 75 | : "=*m"(p) : "r"(v) : "memory" : "volatile"); 76 | } 77 | 78 | #[inline(always)] 79 | #[no_mangle] 80 | pub unsafe extern "C" fn a_or(p: *mut c_int, v: c_int) { 81 | asm!("lock ; or $1, $0" 82 | : "=*m"(p) : "r"(v) : "memory" : "volatile"); 83 | } 84 | 85 | #[inline(always)] 86 | #[no_mangle] 87 | pub unsafe extern "C" fn a_and_64(p: *mut u64, v: u64) { 88 | asm!("lock ; and $1, $0" 89 | : "=*m"(p) : "r"(v) : "memory" : "volatile"); 90 | } 91 | 92 | #[inline(always)] 93 | #[no_mangle] 94 | pub unsafe extern "C" fn a_or_64(p: *mut u64, v: u64) { 95 | asm!("lock ; or $1, $0" 96 | : "=*m"(p) : "r"(v) : "memory" : "volatile"); 97 | } 98 | 99 | #[inline(always)] 100 | #[no_mangle] 101 | pub unsafe extern "C" fn a_crash() { 102 | asm!("hlt" ::: "memory" : "volatile"); 103 | } 104 | 105 | #[inline(always)] 106 | #[no_mangle] 107 | pub unsafe extern "C" fn a_spin() { 108 | asm!("pause" ::: "memory" : "volatile"); 109 | } 110 | 111 | #[inline(always)] 112 | #[no_mangle] 113 | pub unsafe extern "C" fn a_barrier() { 114 | asm!("" ::: "memory" : "volatile"); 115 | } 116 | 117 | #[inline(always)] 118 | #[no_mangle] 119 | pub unsafe extern "C" fn a_ctz_64(mut x: u64) -> u64 { 120 | asm!("bsf $1, $0" : "=r"(x) : "r"(x)); 121 | x 122 | } 123 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/c_types.rs: -------------------------------------------------------------------------------- 1 | // most of these were copied from the rust-lang libc crate 2 | use core::i32; 3 | 4 | #[repr(u8)] 5 | pub enum c_void { 6 | // Two dummy variants so the #[repr] attribute can be used. 7 | #[doc(hidden)] __variant1, 8 | #[doc(hidden)] __variant2, 9 | } 10 | 11 | pub type int8_t = i8; 12 | pub type int16_t = i16; 13 | pub type int32_t = i32; 14 | pub type int64_t = i64; 15 | pub type uint8_t = u8; 16 | pub type uint16_t = u16; 17 | pub type uint32_t = u32; 18 | pub type uint64_t = u64; 19 | 20 | pub type c_schar = i8; 21 | pub type c_uchar = u8; 22 | pub type c_short = i16; 23 | pub type c_ushort = u16; 24 | pub type c_int = i32; 25 | pub type c_uint = u32; 26 | pub type c_float = f32; 27 | pub type c_double = f64; 28 | pub type c_longlong = i64; 29 | pub type c_ulonglong = u64; 30 | pub type intmax_t = i64; 31 | pub type uintmax_t = u64; 32 | 33 | pub const INT_BYTES: usize = 4; 34 | pub const LONG_BYTES: usize = 8; 35 | pub const C_INT_MAX: i32 = i32::MAX; 36 | 37 | pub type size_t = usize; 38 | pub type ptrdiff_t = isize; 39 | pub type intptr_t = isize; 40 | pub type uintptr_t = usize; 41 | pub type ssize_t = isize; 42 | pub type off_t = i64; 43 | 44 | pub type pid_t = i32; 45 | pub type uid_t = u32; 46 | pub type gid_t = u32; 47 | pub type in_addr_t = u32; 48 | pub type in_port_t = u16; 49 | pub type sighandler_t = size_t; 50 | pub type cc_t = c_uchar; 51 | 52 | pub type clock_t = u64; 53 | pub type time_t = i64; 54 | pub type clockid_t = c_int; 55 | pub type suseconds_t = c_longlong; 56 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/environ.rs: -------------------------------------------------------------------------------- 1 | lazy_static! { 2 | // this init is behind a spinlock, should be fine 3 | pub static ref AUXV_PTR: usize = unsafe { auxv_ptr() as usize }; 4 | } 5 | 6 | unsafe fn environ() -> *mut *const *const i8 { 7 | extern "C" { 8 | static mut environ: *const *const i8; 9 | } 10 | &mut environ 11 | } 12 | 13 | unsafe fn auxv_ptr() -> *mut *const *const i8 { 14 | // using sample at bottom of 15 | // http://articles.manugarg.com/aboutelfauxiliaryvectors 16 | // basically, the auxv pointer starts at the end of the envp block 17 | // so we get envp, go until it's NULL, and then go one past 18 | let mut ptr = environ(); 19 | 20 | while *ptr as usize != 0 { 21 | ptr = ptr.offset(1); 22 | } 23 | 24 | // *ptr is now NULL, go one past 25 | ptr = ptr.offset(1); 26 | 27 | ptr 28 | } 29 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/errno.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use pthread::__pthread_self; 3 | 4 | #[inline(always)] 5 | pub unsafe fn set_errno(e: c_int) { 6 | *__errno_location() = e; 7 | } 8 | 9 | #[no_mangle] 10 | pub unsafe extern "C" fn __errno_location() -> *mut c_int { 11 | &mut ((*__pthread_self()).errno_val) 12 | } 13 | 14 | // TODO(adam) convert to a repr(u32) enum 15 | pub const EPERM: c_int = 1; 16 | pub const ENOENT: c_int = 2; 17 | pub const ESRCH: c_int = 3; 18 | pub const EINTR: c_int = 4; 19 | pub const EIO: c_int = 5; 20 | pub const ENXIO: c_int = 6; 21 | pub const E2BIG: c_int = 7; 22 | pub const ENOEXEC: c_int = 8; 23 | pub const EBADF: c_int = 9; 24 | pub const ECHILD: c_int = 10; 25 | pub const EAGAIN: c_int = 11; 26 | pub const ENOMEM: c_int = 12; 27 | pub const EACCES: c_int = 13; 28 | pub const EFAULT: c_int = 14; 29 | pub const ENOTBLK: c_int = 15; 30 | pub const EBUSY: c_int = 16; 31 | pub const EEXIST: c_int = 17; 32 | pub const EXDEV: c_int = 18; 33 | pub const ENODEV: c_int = 19; 34 | pub const ENOTDIR: c_int = 20; 35 | pub const EISDIR: c_int = 21; 36 | pub const EINVAL: c_int = 22; 37 | pub const ENFILE: c_int = 23; 38 | pub const EMFILE: c_int = 24; 39 | pub const ENOTTY: c_int = 25; 40 | pub const ETXTBSY: c_int = 26; 41 | pub const EFBIG: c_int = 27; 42 | pub const ENOSPC: c_int = 28; 43 | pub const ESPIPE: c_int = 29; 44 | pub const EROFS: c_int = 30; 45 | pub const EMLINK: c_int = 31; 46 | pub const EPIPE: c_int = 32; 47 | pub const EDOM: c_int = 33; 48 | pub const ERANGE: c_int = 34; 49 | pub const EDEADLK: c_int = 35; 50 | pub const ENAMETOOLONG: c_int = 36; 51 | pub const ENOLCK: c_int = 37; 52 | pub const ENOSYS: c_int = 38; 53 | pub const ENOTEMPTY: c_int = 39; 54 | pub const ELOOP: c_int = 40; 55 | pub const EWOULDBLOCK: c_int = EAGAIN; 56 | pub const ENOMSG: c_int = 42; 57 | pub const EIDRM: c_int = 43; 58 | pub const ECHRNG: c_int = 44; 59 | pub const EL2NSYNC: c_int = 45; 60 | pub const EL3HLT: c_int = 46; 61 | pub const EL3RST: c_int = 47; 62 | pub const ELNRNG: c_int = 48; 63 | pub const EUNATCH: c_int = 49; 64 | pub const ENOCSI: c_int = 50; 65 | pub const EL2HLT: c_int = 51; 66 | pub const EBADE: c_int = 52; 67 | pub const EBADR: c_int = 53; 68 | pub const EXFULL: c_int = 54; 69 | pub const ENOANO: c_int = 55; 70 | pub const EBADRQC: c_int = 56; 71 | pub const EBADSLT: c_int = 57; 72 | pub const EDEADLOCK: c_int = EDEADLK; 73 | pub const EBFONT: c_int = 59; 74 | pub const ENOSTR: c_int = 60; 75 | pub const ENODATA: c_int = 61; 76 | pub const ETIME: c_int = 62; 77 | pub const ENOSR: c_int = 63; 78 | pub const ENONET: c_int = 64; 79 | pub const ENOPKG: c_int = 65; 80 | pub const EREMOTE: c_int = 66; 81 | pub const ENOLINK: c_int = 67; 82 | pub const EADV: c_int = 68; 83 | pub const ESRMNT: c_int = 69; 84 | pub const ECOMM: c_int = 70; 85 | pub const EPROTO: c_int = 71; 86 | pub const EMULTIHOP: c_int = 72; 87 | pub const EDOTDOT: c_int = 73; 88 | pub const EBADMSG: c_int = 74; 89 | pub const EOVERFLOW: c_int = 75; 90 | pub const ENOTUNIQ: c_int = 76; 91 | pub const EBADFD: c_int = 77; 92 | pub const EREMCHG: c_int = 78; 93 | pub const ELIBACC: c_int = 79; 94 | pub const ELIBBAD: c_int = 80; 95 | pub const ELIBSCN: c_int = 81; 96 | pub const ELIBMAX: c_int = 82; 97 | pub const ELIBEXEC: c_int = 83; 98 | pub const EILSEQ: c_int = 84; 99 | pub const ERESTART: c_int = 85; 100 | pub const ESTRPIPE: c_int = 86; 101 | pub const EUSERS: c_int = 87; 102 | pub const ENOTSOCK: c_int = 88; 103 | pub const EDESTADDRREQ: c_int = 89; 104 | pub const EMSGSIZE: c_int = 90; 105 | pub const EPROTOTYPE: c_int = 91; 106 | pub const ENOPROTOOPT: c_int = 92; 107 | pub const EPROTONOSUPPORT: c_int = 93; 108 | pub const ESOCKTNOSUPPORT: c_int = 94; 109 | pub const EOPNOTSUPP: c_int = 95; 110 | pub const ENOTSUP: c_int = EOPNOTSUPP; 111 | pub const EPFNOSUPPORT: c_int = 96; 112 | pub const EAFNOSUPPORT: c_int = 97; 113 | pub const EADDRINUSE: c_int = 98; 114 | pub const EADDRNOTAVAIL: c_int = 99; 115 | pub const ENETDOWN: c_int = 100; 116 | pub const ENETUNREACH: c_int = 101; 117 | pub const ENETRESET: c_int = 102; 118 | pub const ECONNABORTED: c_int = 103; 119 | pub const ECONNRESET: c_int = 104; 120 | pub const ENOBUFS: c_int = 105; 121 | pub const EISCONN: c_int = 106; 122 | pub const ENOTCONN: c_int = 107; 123 | pub const ESHUTDOWN: c_int = 108; 124 | pub const ETOOMANYREFS: c_int = 109; 125 | pub const ETIMEDOUT: c_int = 110; 126 | pub const ECONNREFUSED: c_int = 111; 127 | pub const EHOSTDOWN: c_int = 112; 128 | pub const EHOSTUNREACH: c_int = 113; 129 | pub const EALREADY: c_int = 114; 130 | pub const EINPROGRESS: c_int = 115; 131 | pub const ESTALE: c_int = 116; 132 | pub const EUCLEAN: c_int = 117; 133 | pub const ENOTNAM: c_int = 118; 134 | pub const ENAVAIL: c_int = 119; 135 | pub const EISNAM: c_int = 120; 136 | pub const EREMOTEIO: c_int = 121; 137 | pub const EDQUOT: c_int = 122; 138 | pub const ENOMEDIUM: c_int = 123; 139 | pub const EMEDIUMTYPE: c_int = 124; 140 | pub const ECANCELED: c_int = 125; 141 | pub const ENOKEY: c_int = 126; 142 | pub const EKEYEXPIRED: c_int = 127; 143 | pub const EKEYREVOKED: c_int = 128; 144 | pub const EKEYREJECTED: c_int = 129; 145 | pub const EOWNERDEAD: c_int = 130; 146 | pub const ENOTRECOVERABLE: c_int = 131; 147 | pub const ERFKILL: c_int = 132; 148 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/malloc.rs: -------------------------------------------------------------------------------- 1 | pub const SIZE_ALIGN: usize = 32; 2 | pub const SIZE_MASK: usize = 0xffffffffffffffe0; 3 | pub const OVERHEAD: usize = 16; 4 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/mman.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | pub const PAGE_SIZE: off_t = 4096; 4 | 5 | pub const MAP_FAILED: *mut c_void = 0xffffffffffffffff as *mut c_void; 6 | 7 | pub const PROT_NONE: c_int = 0; 8 | pub const PROT_READ: c_int = 1; 9 | pub const PROT_WRITE: c_int = 2; 10 | pub const PROT_EXEC: c_int = 4; 11 | pub const PROT_GROWSDOWN: c_int = 0x01000000; 12 | pub const PROT_GROWSUP: c_int = 0x02000000; 13 | 14 | pub const MAP_SHARED: c_int = 0x01; 15 | pub const MAP_PRIVATE: c_int = 0x02; 16 | pub const MAP_FIXED: c_int = 0x10; 17 | 18 | pub const MAP_TYPE: c_int = 0x0f; 19 | pub const MAP_FILE: c_int = 0x00; 20 | pub const MAP_ANON: c_int = 0x20; 21 | pub const MAP_ANONYMOUS: c_int = MAP_ANON; 22 | pub const MAP_32BIT: c_int = 0x40; 23 | pub const MAP_NORESERVE: c_int = 0x4000; 24 | pub const MAP_GROWSDOWN: c_int = 0x0100; 25 | pub const MAP_DENYWRITE: c_int = 0x0800; 26 | pub const MAP_EXECUTABLE: c_int = 0x1000; 27 | pub const MAP_LOCKED: c_int = 0x2000; 28 | pub const MAP_POPULATE: c_int = 0x8000; 29 | pub const MAP_NONBLOCK: c_int = 0x10000; 30 | pub const MAP_STACK: c_int = 0x20000; 31 | pub const MAP_HUGETLB: c_int = 0x40000; 32 | 33 | pub const POSIX_MADV_NORMAL: c_int = 0; 34 | pub const POSIX_MADV_RANDOM: c_int = 1; 35 | pub const POSIX_MADV_SEQUENTIAL: c_int = 2; 36 | pub const POSIX_MADV_WILLNEED: c_int = 3; 37 | pub const POSIX_MADV_DONTNEED: c_int = 0; 38 | 39 | pub const MS_ASYNC: c_int = 1; 40 | pub const MS_INVALIDATE: c_int = 2; 41 | pub const MS_SYNC: c_int = 4; 42 | 43 | pub const MCL_CURRENT: c_int = 1; 44 | pub const MCL_FUTURE: c_int = 2; 45 | pub const MCL_ONFAULT: c_int = 4; 46 | 47 | pub const MADV_NORMAL: c_int = 0; 48 | pub const MADV_RANDOM: c_int = 1; 49 | pub const MADV_SEQUENTIAL: c_int = 2; 50 | pub const MADV_WILLNEED: c_int = 3; 51 | pub const MADV_DONTNEED: c_int = 4; 52 | pub const MADV_REMOVE: c_int = 9; 53 | pub const MADV_DONTFORK: c_int = 10; 54 | pub const MADV_DOFORK: c_int = 11; 55 | pub const MADV_MERGEABLE: c_int = 12; 56 | pub const MADV_UNMERGEABLE: c_int = 13; 57 | pub const MADV_HUGEPAGE: c_int = 14; 58 | pub const MADV_NOHUGEPAGE: c_int = 15; 59 | pub const MADV_DONTDUMP: c_int = 16; 60 | pub const MADV_DODUMP: c_int = 17; 61 | pub const MADV_HWPOISON: c_int = 100; 62 | pub const MADV_SOFT_OFFLINE: c_int = 101; 63 | 64 | pub const MREMAP_MAYMOVE: c_int = 1; 65 | pub const MREMAP_FIXED: c_int = 2; 66 | 67 | pub const MLOCK_ONFAULT: c_int = 0x01; 68 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod atomic; 2 | pub mod c_types; 3 | pub mod environ; 4 | pub mod errno; 5 | pub mod malloc; 6 | pub mod mman; 7 | pub mod pthread; 8 | pub mod signal; 9 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/pthread.rs: -------------------------------------------------------------------------------- 1 | use thread::pthread::pthread; 2 | 3 | #[inline(always)] 4 | #[no_mangle] 5 | pub unsafe extern "C" fn __pthread_self() -> *mut pthread { 6 | let slf: *mut pthread; 7 | asm!("mov %fs:0, $0" : "=r" (slf) ::: "volatile"); 8 | slf 9 | } 10 | -------------------------------------------------------------------------------- /src/platform/linux-x86_64/signal.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | pub const NSIG: c_int = 65; 4 | -------------------------------------------------------------------------------- /src/string/bcmp.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_int, c_void, size_t}; 2 | use memcmp; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: size_t) -> c_int { 6 | memcmp((s1 as *const u8), (s2 as *const u8), n) as c_int 7 | } 8 | -------------------------------------------------------------------------------- /src/string/bcopy.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_void, size_t}; 2 | use memmove; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn bcopy(s1: *const c_void, s2: *mut c_void, n: size_t) { 6 | memmove(s2 as *mut u8, s1 as *const u8, n); 7 | } 8 | -------------------------------------------------------------------------------- /src/string/bzero.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_void, size_t}; 2 | use memset; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn bzero(s: *mut c_void, n: size_t) { 6 | memset(s as *mut u8, 0, n); 7 | } 8 | -------------------------------------------------------------------------------- /src/string/index.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_int, c_schar}; 2 | use string::strchr::strchr; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn index(s: *const c_schar, c: c_int) -> *const c_schar { 6 | strchr(s, c) 7 | } 8 | -------------------------------------------------------------------------------- /src/string/memrchr.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_int, c_void, size_t}; 2 | use core::ptr; 3 | 4 | #[linkage = "weak"] 5 | #[no_mangle] 6 | pub unsafe extern "C" fn memrchr(m: *const c_void, c: c_int, n: size_t) -> *const c_void { 7 | let mut i = n as isize; 8 | let s = m as *const u8; 9 | let c = c as u8; 10 | while i > 0 { 11 | i -= 1; 12 | if *s.offset(i) == c { 13 | return s.offset(i) as *const c_void; 14 | } 15 | } 16 | ptr::null() 17 | } 18 | 19 | #[no_mangle] 20 | pub unsafe extern "C" fn __memrchr(m: *const c_void, c: c_int, n: size_t) -> *const c_void { 21 | memrchr(m, c, n) 22 | } 23 | -------------------------------------------------------------------------------- /src/string/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bcmp; 2 | pub mod bcopy; 3 | pub mod bzero; 4 | pub mod index; 5 | pub mod memrchr; 6 | pub mod rindex; 7 | pub mod stpcpy; 8 | pub mod strcpy; 9 | pub mod strchr; 10 | pub mod strchrnul; 11 | pub mod strdup; 12 | pub mod strlen; 13 | pub mod strcmp; 14 | pub mod strrchr; 15 | pub mod strspn; 16 | 17 | #[cfg_attr(rustfmt, rustfmt_skip)] 18 | const ONES: usize = 0x0101010101010101_usize; 19 | const HIGHS: usize = 0x8080808080808080_usize; 20 | 21 | #[cfg_attr(rustfmt, rustfmt_skip)] 22 | #[inline(always)] 23 | fn has_zero(x: usize) -> bool { 24 | (x.wrapping_sub(ONES) & !x & HIGHS) != 0 25 | } 26 | -------------------------------------------------------------------------------- /src/string/rindex.rs: -------------------------------------------------------------------------------- 1 | use super::strrchr::strrchr; 2 | use c_types::{c_int, c_schar}; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn rindex(s: *const c_schar, c: c_int) -> *const c_schar { 6 | strrchr(s, c) 7 | } 8 | -------------------------------------------------------------------------------- /src/string/stpcpy.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[linkage = "weak"] 4 | #[no_mangle] 5 | pub unsafe extern "C" fn stpcpy(dest: *mut c_schar, source: *const c_schar) -> *mut c_schar { 6 | // TODO(adam) compare and copy as word-size chunks 7 | 8 | for i in 0.. { 9 | *dest.offset(i) = *source.offset(i); 10 | if *dest.offset(i) == 0 { 11 | break; 12 | } 13 | } 14 | 15 | dest 16 | } 17 | -------------------------------------------------------------------------------- /src/string/strchr.rs: -------------------------------------------------------------------------------- 1 | use c_types::{c_int, c_schar, c_uchar}; 2 | use core::ptr; 3 | use string::strchrnul::__strchrnul; 4 | 5 | #[no_mangle] 6 | pub unsafe extern "C" fn strchr(s: *const c_schar, c: c_int) -> *const c_schar { 7 | let r = __strchrnul(s, c); 8 | if *(r as *const c_uchar) == c as c_uchar { 9 | r 10 | } else { 11 | ptr::null() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/string/strchrnul.rs: -------------------------------------------------------------------------------- 1 | use super::{has_zero, ONES}; 2 | use c_types::{c_int, c_schar, c_uchar, size_t, uintptr_t}; 3 | use core::mem; 4 | use string::strlen::strlen; 5 | 6 | #[no_mangle] 7 | pub unsafe extern "C" fn __strchrnul(s: *const c_schar, c: c_int) -> *const c_schar { 8 | strchrnul(s, c) 9 | } 10 | 11 | #[linkage = "weak"] 12 | #[no_mangle] 13 | pub unsafe extern "C" fn strchrnul(s: *const c_schar, c: c_int) -> *const c_schar { 14 | let mut _s = s; 15 | let c = c as c_uchar; 16 | if c == 0 { 17 | return _s.add(strlen(_s)); 18 | } 19 | 20 | while _s as uintptr_t % mem::size_of::() != 0 { 21 | if *_s == 0 || *(_s as *const c_uchar) == c { 22 | return _s; 23 | } 24 | _s = _s.offset(1); 25 | } 26 | 27 | let k: *const size_t = (ONES * c as size_t) as *const size_t; 28 | let mut w: *const size_t = _s as *const size_t; 29 | while !has_zero(*w) && !has_zero(*w ^ (k as size_t)) { 30 | w = w.offset(1); 31 | } 32 | 33 | _s = w as *const c_schar; 34 | while *_s != 0 && *(_s as *const c_uchar) != c { 35 | _s = _s.offset(1); 36 | } 37 | _s 38 | } 39 | -------------------------------------------------------------------------------- /src/string/strcmp.rs: -------------------------------------------------------------------------------- 1 | use core::i32; 2 | 3 | use c_types::*; 4 | 5 | #[no_mangle] 6 | pub unsafe extern "C" fn strcmp(l: *const c_schar, r: *const c_schar) -> c_int { 7 | // TODO(adam) convert to checking word-size chunks 8 | for i in 0.. { 9 | let lc = *l.offset(i); 10 | let rc = *r.offset(i); 11 | if lc == 0 || lc != rc { 12 | return (lc - rc) as c_int; 13 | } 14 | } 15 | i32::MAX 16 | } 17 | -------------------------------------------------------------------------------- /src/string/strcpy.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use string::stpcpy::stpcpy; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn strcpy(dest: *mut c_schar, source: *const c_schar) -> *mut c_schar { 6 | stpcpy(dest, source); 7 | dest 8 | } 9 | -------------------------------------------------------------------------------- /src/string/strdup.rs: -------------------------------------------------------------------------------- 1 | use super::strlen::strlen; 2 | use c_types::c_schar; 3 | use core::ptr; 4 | use malloc::malloc::malloc; 5 | use memcpy; 6 | 7 | #[linkage = "weak"] 8 | #[no_mangle] 9 | pub unsafe extern "C" fn strdup(s: *const c_schar) -> *const c_schar { 10 | let l = strlen(s); 11 | let d = malloc(l + 1); 12 | if d.is_null() { 13 | ptr::null() 14 | } else { 15 | memcpy(d as *mut u8, s as *const u8, l + 1) as *const c_schar 16 | } 17 | } 18 | 19 | #[no_mangle] 20 | pub unsafe extern "C" fn __strdup(s: *const c_schar) -> *const c_schar { 21 | strdup(s) 22 | } 23 | -------------------------------------------------------------------------------- /src/string/strlen.rs: -------------------------------------------------------------------------------- 1 | use super::has_zero; 2 | use core::mem; 3 | 4 | use c_types::{c_schar, size_t, uintptr_t}; 5 | 6 | #[no_mangle] 7 | pub unsafe extern "C" fn strlen(s: *const c_schar) -> size_t { 8 | let mut t = s; 9 | while t as uintptr_t % mem::size_of::() != 0 { 10 | if *t == 0 { 11 | return t as size_t - s as size_t; 12 | } 13 | t = t.offset(1) 14 | } 15 | 16 | let mut w = t as *const size_t; 17 | while !has_zero(*w) { 18 | w = w.offset(1) 19 | } 20 | 21 | t = w as *const c_schar; 22 | while *t != 0 { 23 | t = t.offset(1) 24 | } 25 | t as size_t - s as size_t 26 | } 27 | -------------------------------------------------------------------------------- /src/string/strrchr.rs: -------------------------------------------------------------------------------- 1 | use super::memrchr::memrchr; 2 | use super::strlen::strlen; 3 | use c_types::{c_int, c_schar, c_void}; 4 | 5 | #[no_mangle] 6 | pub unsafe extern "C" fn strrchr(s: *const c_schar, c: c_int) -> *const c_schar { 7 | memrchr(s as *const c_void, c, strlen(s) + 1) as *const c_schar 8 | } 9 | -------------------------------------------------------------------------------- /src/string/strspn.rs: -------------------------------------------------------------------------------- 1 | use core::usize; 2 | 3 | use c_types::*; 4 | 5 | #[no_mangle] 6 | pub unsafe extern "C" fn strspn(string: *const c_schar, chars: *const c_schar) -> size_t { 7 | if *chars.offset(0) == 0 { 8 | return 0; 9 | } 10 | if *chars.offset(1) == 0 { 11 | let mut i = 0; 12 | loop { 13 | if *string.offset(i) != *chars { 14 | return i as usize; 15 | } 16 | i += 1; 17 | } 18 | } 19 | 20 | let mut bit_array = AsciiBitArray::new(); 21 | let mut i = 0; 22 | loop { 23 | let c = *chars.offset(i); 24 | if c == 0 { 25 | break; 26 | } 27 | bit_array.set_bit(c as usize); 28 | i += 1; 29 | } 30 | 31 | let mut j = 0; 32 | loop { 33 | let s = *string.offset(j); 34 | if s == 0 || !bit_array.get_bit(s as usize) { 35 | break; 36 | } 37 | j += 1; 38 | } 39 | 40 | j as usize 41 | } 42 | 43 | const ASCII_MAX_VALUE: usize = 127; 44 | const U32_N_BITS: usize = 8 * 4; 45 | const BIT_ARRAY_LEN: usize = 1 + ASCII_MAX_VALUE / U32_N_BITS; 46 | 47 | struct AsciiBitArray { 48 | bit_array: [u32; BIT_ARRAY_LEN], 49 | } 50 | 51 | impl AsciiBitArray { 52 | fn new() -> AsciiBitArray { 53 | AsciiBitArray { 54 | bit_array: [0; BIT_ARRAY_LEN], 55 | } 56 | } 57 | 58 | fn set_bit(&mut self, index: usize) { 59 | let value = (1u32 << (index % U32_N_BITS)) as u32; 60 | let byte_index = index % BIT_ARRAY_LEN / U32_N_BITS; 61 | 62 | self.bit_array[byte_index] |= value; 63 | } 64 | 65 | fn get_bit(&self, index: usize) -> bool { 66 | let value = (1u32 << (index % U32_N_BITS)) as u32; 67 | let byte_index = index % BIT_ARRAY_LEN / U32_N_BITS; 68 | 69 | self.bit_array[byte_index] & value > 0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/syscall_mgt.rs: -------------------------------------------------------------------------------- 1 | use core::mem::transmute; 2 | 3 | use c_types::*; 4 | use errno::set_errno; 5 | 6 | // from musl/src/internal/syscall_ret.c 7 | pub unsafe fn syscall_return(code: usize) -> usize { 8 | let max_err: usize = transmute(-4096i64); 9 | if code > max_err { 10 | set_errno(-(code as c_int)); 11 | (-1isize) as usize 12 | } else { 13 | code 14 | } 15 | } 16 | 17 | // from the syscall.rs crate, just added the return handling 18 | #[macro_export] 19 | macro_rules! syscall { 20 | ($nr:ident) 21 | => ( ::syscall_mgt::syscall_return(::syscall::syscall0( 22 | ::syscall::nr::$nr)) ); 23 | 24 | ($nr:ident, $a1:expr) 25 | => ( ::syscall_mgt::syscall_return(::syscall::syscall1( 26 | ::syscall::nr::$nr, 27 | $a1 as usize)) ); 28 | 29 | ($nr:ident, $a1:expr, $a2:expr) 30 | => ( ::syscall_mgt::syscall_return(::syscall::syscall2( 31 | ::syscall::nr::$nr, 32 | $a1 as usize, $a2 as usize)) ); 33 | 34 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr) 35 | => ( ::syscall_mgt::syscall_return(::syscall::syscall3( 36 | ::syscall::nr::$nr, 37 | $a1 as usize, $a2 as usize, $a3 as usize)) ); 38 | 39 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) 40 | => ( ::syscall_mgt::syscall_return(::syscall::syscall4( 41 | ::syscall::nr::$nr, 42 | $a1 as usize, $a2 as usize, $a3 as usize, 43 | $a4 as usize)) ); 44 | 45 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) 46 | => ( ::syscall_mgt::syscall_return(::syscall::syscall5( 47 | ::syscall::nr::$nr, 48 | $a1 as usize, $a2 as usize, $a3 as usize, 49 | $a4 as usize, $a5 as usize)) ); 50 | 51 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) 52 | => ( ::syscall_mgt::syscall_return(::syscall::syscall6( 53 | ::syscall::nr::$nr, 54 | $a1 as usize, $a2 as usize, $a3 as usize, 55 | $a4 as usize, $a5 as usize, $a6 as usize)) ); 56 | 57 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr, $a7:expr) 58 | => ( ::syscall_mgt::syscall_return(::syscall::syscall7( 59 | ::syscall::nr::$nr, 60 | $a1 as usize, $a2 as usize, $a3 as usize, 61 | $a4 as usize, $a5 as usize, $a6 as usize, 62 | $a7 as usize)) ); 63 | } 64 | -------------------------------------------------------------------------------- /src/thread/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pthread; 2 | pub mod vmlock; 3 | 4 | use atomic::{a_dec, a_inc, a_spin}; 5 | use c_types::*; 6 | use errno::ENOSYS; 7 | 8 | pub const FUTEX_WAIT: c_int = 0; 9 | pub const FUTEX_WAKE: c_int = 1; 10 | pub const FUTEX_FD: c_int = 2; 11 | pub const FUTEX_REQUEUE: c_int = 3; 12 | pub const FUTEX_CMP_REQUEUE: c_int = 4; 13 | pub const FUTEX_WAKE_OP: c_int = 5; 14 | pub const FUTEX_LOCK_PI: c_int = 6; 15 | pub const FUTEX_UNLOCK_PI: c_int = 7; 16 | pub const FUTEX_TRYLOCK_PI: c_int = 8; 17 | pub const FUTEX_WAIT_BITSET: c_int = 9; 18 | pub const FUTEX_PRIVATE: c_int = 128; 19 | pub const FUTEX_CLOCK_REALTIME: c_int = 256; 20 | 21 | #[no_mangle] 22 | pub unsafe extern "C" fn __wake(address: *mut c_void, count: c_int, private: c_int) { 23 | let private = if private != 0 { 128 } else { private }; 24 | 25 | let count = if count < 0 { C_INT_MAX } else { count }; 26 | 27 | let res = syscall!(FUTEX, address, FUTEX_WAKE | private, count); 28 | 29 | if res == ENOSYS as usize { 30 | syscall!(FUTEX, address, FUTEX_WAKE, count); 31 | } 32 | } 33 | 34 | #[no_mangle] 35 | pub unsafe extern "C" fn __wait( 36 | address: *mut c_int, 37 | waiters: *mut c_int, 38 | val: c_int, 39 | private: c_int, 40 | ) { 41 | let private = if private != 0 { FUTEX_PRIVATE } else { private }; 42 | 43 | for _ in 0..100 { 44 | if !waiters.is_null() || *waiters != 0 { 45 | if *address == val { 46 | a_spin(); 47 | } else { 48 | return; 49 | } 50 | } 51 | } 52 | 53 | if !waiters.is_null() { 54 | a_inc(waiters); 55 | } 56 | 57 | while *address == val { 58 | let first = syscall!(FUTEX, address, FUTEX_WAIT | private, val, 0); 59 | 60 | if first as c_int == -ENOSYS { 61 | syscall!(FUTEX, address, FUTEX_WAIT, val, 0); 62 | } 63 | } 64 | 65 | if !waiters.is_null() { 66 | a_dec(waiters); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/thread/pthread.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use signal::NSIG; 3 | 4 | pub use platform::pthread::*; 5 | 6 | const SIGMASK_LEN: usize = (NSIG as usize / 8) / LONG_BYTES; 7 | 8 | #[repr(C)] 9 | pub struct pthread { 10 | this: *mut pthread, 11 | 12 | dtv: *mut *mut c_void, 13 | unused1: *mut c_void, 14 | unused2: *mut c_void, 15 | 16 | sysinfo: usize, 17 | canary: usize, 18 | canary2: usize, 19 | tid: pid_t, 20 | pid: pid_t, 21 | 22 | tsd_used: c_int, 23 | pub errno_val: c_int, 24 | 25 | // TODO(adam) make these atomic 26 | cancel: c_int, 27 | canceldisable: c_int, 28 | cancelasync: c_int, 29 | 30 | detached: c_int, 31 | map_base: *mut c_uchar, 32 | map_size: usize, 33 | stack: *const c_void, 34 | stack_size: usize, 35 | 36 | start_arg: *mut c_void, 37 | // void *(*start)(void *); 38 | start: *mut c_void, 39 | result: *mut c_void, 40 | 41 | cancelbuf: *mut __ptcb, 42 | tsd: *mut c_void, 43 | 44 | // TODO(adam) waiting on proper union support to implement correctly 45 | // pthread_attr_t attr; 46 | attr: [c_int; 14], 47 | dead: c_int, 48 | robust_list: rb_list, 49 | 50 | unblock_cancel: c_int, 51 | timer_id: c_int, 52 | 53 | // TODO(adam) track down declaration of locale_t when relevant 54 | locale: *mut c_void, 55 | killlock: [c_int; 2], 56 | exitlock: [c_int; 2], 57 | startlock: [c_int; 2], 58 | 59 | sigmask: [usize; SIGMASK_LEN], 60 | dlerror_buf: *mut c_schar, 61 | dlerror_flag: c_int, 62 | stdio_locks: *mut c_void, 63 | canary_at_end: usize, 64 | dtv_copy: *mut *mut c_void, 65 | } 66 | 67 | #[repr(C)] 68 | pub struct rb_list { 69 | head: *mut c_void, 70 | off: c_longlong, 71 | pending: *mut c_void, 72 | } 73 | 74 | #[repr(C)] 75 | pub struct __ptcb { 76 | // void (*__f)(void *); 77 | __f: *mut c_void, 78 | __x: *mut c_void, 79 | __next: *mut __ptcb, 80 | } 81 | -------------------------------------------------------------------------------- /src/thread/vmlock.rs: -------------------------------------------------------------------------------- 1 | use atomic::{a_fetch_add, a_inc}; 2 | use c_types::*; 3 | use thread::{__wait, __wake}; 4 | 5 | static mut LOCK: [c_int; 2] = [0, 0]; 6 | 7 | #[linkage = "weak"] 8 | #[no_mangle] 9 | pub unsafe extern "C" fn __vm_wait() { 10 | let mut tmp = LOCK[0]; 11 | while tmp != 0 { 12 | __wait( 13 | &mut LOCK[0] as *mut c_int, 14 | &mut LOCK[1] as *mut c_int, 15 | tmp, 16 | 1, 17 | ); 18 | tmp = LOCK[0]; 19 | } 20 | } 21 | 22 | #[no_mangle] 23 | pub unsafe extern "C" fn __vm_lock() { 24 | a_inc(&mut LOCK[0] as *mut c_int); 25 | } 26 | 27 | #[no_mangle] 28 | pub unsafe extern "C" fn __vm_unlock() { 29 | let result = a_fetch_add(&mut LOCK[0] as *mut c_int, -1); 30 | 31 | if result == 1 && LOCK[1] != 0 { 32 | __wake((&mut LOCK[0] as *mut c_int) as *mut c_void, -1, 1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/time/clock.rs: -------------------------------------------------------------------------------- 1 | use core::u64; 2 | 3 | use c_types::*; 4 | use errno::{EINVAL, ENOSYS}; 5 | 6 | use time::timespec; 7 | 8 | pub const CLOCK_REALTIME: clockid_t = 0; 9 | pub const CLOCK_MONOTONIC: clockid_t = 1; 10 | pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; 11 | pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3; 12 | pub const CLOCK_MONOTONIC_RAW: clockid_t = 4; 13 | pub const CLOCK_REALTIME_COARSE: clockid_t = 5; 14 | pub const CLOCK_MONOTONIC_COARSE: clockid_t = 6; 15 | pub const CLOCK_BOOTTIME: clockid_t = 7; 16 | pub const CLOCK_REALTIME_ALARM: clockid_t = 8; 17 | pub const CLOCK_BOOTTIME_ALARM: clockid_t = 9; 18 | pub const CLOCK_SGI_CYCLE: clockid_t = 10; 19 | pub const CLOCK_TAI: clockid_t = 11; 20 | 21 | #[no_mangle] 22 | pub unsafe extern "C" fn __clock_gettime(clock: clockid_t, spec: &mut timespec) -> c_int { 23 | clock_gettime(clock, spec) 24 | } 25 | 26 | #[linkage = "weak"] 27 | #[no_mangle] 28 | pub unsafe extern "C" fn clock_gettime(clock: clockid_t, spec: &mut timespec) -> c_int { 29 | let mut r = syscall!(CLOCK_GETTIME, clock, spec as *mut timespec) as c_int; 30 | 31 | if r == -ENOSYS { 32 | if clock == CLOCK_REALTIME { 33 | syscall!(GETTIMEOFDAY, spec as *mut timespec, 0); 34 | spec.tv_nsec *= 1000; 35 | return 0; 36 | } 37 | r = -EINVAL; 38 | } 39 | 40 | r 41 | } 42 | 43 | #[no_mangle] 44 | pub unsafe extern "C" fn clock() -> clock_t { 45 | let mut spec = timespec { 46 | tv_sec: 0, 47 | tv_nsec: 0, 48 | }; 49 | 50 | if clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mut spec) != 0 { 51 | return u64::MAX; 52 | } 53 | 54 | if spec.tv_sec as u64 > u64::MAX / 1_000_000 55 | || (spec.tv_nsec / 1000) as u64 > (u64::MAX - 1_000_000) * spec.tv_sec as u64 56 | { 57 | return u64::MAX; 58 | } 59 | 60 | (spec.tv_sec * 1000000 + spec.tv_nsec / 1000) as clock_t 61 | } 62 | 63 | #[no_mangle] 64 | pub unsafe extern "C" fn clock_getcpuclockid(pid: pid_t, clock: *mut clockid_t) -> c_int { 65 | let mut spec = timespec { 66 | tv_sec: 0, 67 | tv_nsec: 0, 68 | }; 69 | 70 | let id = ((-pid - 1) * 8) + 2; 71 | let r = syscall!(CLOCK_GETRES, id, &mut spec as *mut timespec); 72 | if r != 0 { 73 | -(r as c_int) 74 | } else { 75 | *clock = id; 76 | 0 77 | } 78 | } 79 | 80 | #[no_mangle] 81 | pub unsafe extern "C" fn clock_getres(clock: clockid_t, spec: &mut timespec) -> c_int { 82 | syscall!(CLOCK_GETRES, clock, spec as *mut timespec) as i32 83 | } 84 | 85 | #[no_mangle] 86 | pub unsafe extern "C" fn clock_nanosleep( 87 | clock: clockid_t, 88 | flags: c_int, 89 | req: ×pec, 90 | rem: &mut timespec, 91 | ) -> c_int { 92 | -(syscall!( 93 | CLOCK_NANOSLEEP, 94 | clock, 95 | flags, 96 | req as *const timespec, 97 | rem as *mut timespec 98 | ) as c_int) 99 | } 100 | 101 | #[no_mangle] 102 | pub unsafe extern "C" fn clock_settime(clock: clockid_t, spec: ×pec) -> c_int { 103 | syscall!(CLOCK_SETTIME, clock, spec as *const timespec) as c_int 104 | } 105 | -------------------------------------------------------------------------------- /src/time/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clock; 2 | 3 | use c_types::*; 4 | 5 | #[repr(C)] 6 | pub struct tm { 7 | tm_sec: c_int, 8 | tm_min: c_int, 9 | tm_hour: c_int, 10 | tm_mday: c_int, 11 | tm_mon: c_int, 12 | tm_year: c_int, 13 | tm_wday: c_int, 14 | tm_yday: c_int, 15 | tm_isdst: c_int, 16 | __tm_gmtoff: c_longlong, 17 | __tm_zone: *const c_schar, 18 | } 19 | 20 | #[repr(C)] 21 | pub struct timespec { 22 | tv_sec: time_t, 23 | tv_nsec: c_longlong, 24 | } 25 | 26 | #[repr(C)] 27 | pub struct itimerspec { 28 | interval: timespec, 29 | value: timespec, 30 | } 31 | 32 | pub const ITIMER_REAL: isize = 0; 33 | pub const ITIMER_VIRTUAL: isize = 1; 34 | pub const ITIMER_PROF: isize = 2; 35 | 36 | #[repr(C)] 37 | pub struct timeval { 38 | pub tv_sec: time_t, 39 | pub tv_usec: suseconds_t, 40 | } 41 | 42 | #[repr(C)] 43 | pub struct itimerval { 44 | pub it_interval: timeval, 45 | pub it_value: timeval, 46 | } 47 | 48 | #[repr(C)] 49 | pub struct sigevent; 50 | -------------------------------------------------------------------------------- /src/unistd/_exit.rs: -------------------------------------------------------------------------------- 1 | use c_types::c_int; 2 | use exit::_Exit::_Exit; 3 | 4 | #[no_mangle] 5 | pub extern "C" fn _exit(status: c_int) -> ! { 6 | _Exit(status) 7 | } 8 | -------------------------------------------------------------------------------- /src/unistd/access.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[no_mangle] 4 | pub unsafe extern "C" fn access(filename: *const c_schar, amode: c_int) -> c_int { 5 | syscall!(ACCESS, filename, amode) as i32 6 | } 7 | -------------------------------------------------------------------------------- /src/unistd/acct.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[no_mangle] 4 | pub unsafe extern "C" fn acct(filename: *const c_schar) -> c_int { 5 | syscall!(ACCT, filename) as i32 6 | } 7 | -------------------------------------------------------------------------------- /src/unistd/alarm.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | use time::{itimerval, timeval, ITIMER_REAL}; 4 | 5 | #[no_mangle] 6 | pub unsafe extern "C" fn alarm(seconds: c_uint) -> c_uint { 7 | let mut it = itimerval { 8 | it_interval: timeval { 9 | tv_sec: 0, 10 | tv_usec: 0, 11 | }, 12 | it_value: timeval { 13 | tv_sec: seconds as time_t, 14 | tv_usec: 0, 15 | }, 16 | }; 17 | 18 | syscall!( 19 | SETITIMER, 20 | ITIMER_REAL, 21 | &mut it as *mut itimerval, 22 | &mut it as *mut itimerval 23 | ); 24 | 25 | (it.it_value.tv_sec + !!it.it_value.tv_usec) as c_uint 26 | } 27 | -------------------------------------------------------------------------------- /src/unistd/chdir.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[no_mangle] 4 | pub unsafe extern "C" fn chdir(path: *const c_schar) -> c_int { 5 | syscall!(CHDIR, path) as c_int 6 | } 7 | -------------------------------------------------------------------------------- /src/unistd/chown.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[no_mangle] 4 | pub unsafe extern "C" fn chown(path: *const c_schar, uid: uid_t, gid: gid_t) -> c_int { 5 | syscall!(CHOWN, path, uid, gid) as c_int 6 | } 7 | -------------------------------------------------------------------------------- /src/unistd/close.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use errno::EINTR; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn close(fd: c_int) -> c_int { 6 | let mut r = syscall!(CLOSE, fd) as c_int; 7 | if r == -EINTR { 8 | r = 0; 9 | } 10 | r 11 | } 12 | -------------------------------------------------------------------------------- /src/unistd/ctermid.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | use string::strcpy::strcpy; 4 | 5 | const DEV_TTY: &'static [u8] = b"/dev/tty\0"; 6 | 7 | #[no_mangle] 8 | pub unsafe extern "C" fn ctermid(s: *mut c_schar) -> *mut c_schar { 9 | match s as usize { 10 | 0 => DEV_TTY.as_ptr() as *mut i8, 11 | _ => strcpy(s, DEV_TTY.as_ptr() as *const i8), 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/unistd/dup.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | 3 | #[no_mangle] 4 | pub extern "C" fn dup(fd: c_int) -> c_int { 5 | unsafe { syscall!(DUP, fd) as c_int } 6 | } 7 | -------------------------------------------------------------------------------- /src/unistd/dup2.rs: -------------------------------------------------------------------------------- 1 | use c_types::*; 2 | use platform::errno::EBUSY; 3 | use syscall_mgt::syscall_return; 4 | 5 | // TODO impl for non-dup2 systems (e.g. aarch64, or1k) 6 | #[no_mangle] 7 | pub extern "C" fn dup2(old: c_int, new: c_int) -> c_int { 8 | let mut r; 9 | loop { 10 | r = unsafe { syscall!(DUP2, old, new) }; 11 | if (r as c_int) != -EBUSY { 12 | break; 13 | } 14 | } 15 | unsafe { syscall_return(r) as c_int } 16 | } 17 | -------------------------------------------------------------------------------- /src/unistd/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod _exit; 2 | pub mod access; 3 | pub mod acct; 4 | pub mod alarm; 5 | pub mod chdir; 6 | pub mod chown; 7 | pub mod close; 8 | pub mod ctermid; 9 | pub mod dup; 10 | pub mod dup2; 11 | --------------------------------------------------------------------------------