Merge branch 'userspace_fexec' into 'master'
Userspace fexec See merge request redox-os/relibc!343
This commit is contained in:
@@ -13,3 +13,7 @@
|
||||
[submodule "pthreads-emb"]
|
||||
path = pthreads-emb
|
||||
url = https://gitlab.redox-os.org/redox-os/pthreads-emb.git
|
||||
[submodule "compiler-builtins"]
|
||||
path = compiler-builtins
|
||||
url = https://gitlab.redox-os.org/redox-os/compiler-builtins.git
|
||||
branch = relibc_fix_dup_symbols
|
||||
|
||||
Generated
+74
-53
@@ -42,11 +42,11 @@ checksum = "2db2df1ebc842c41fd2c4ae5b5a577faf63bd5151b953db752fc686812bee318"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.16",
|
||||
"proc-macro2 1.0.42",
|
||||
"quote 1.0.20",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.89",
|
||||
"syn 1.0.98",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
@@ -108,9 +108,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
@@ -146,9 +146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
@@ -165,33 +165,34 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.121"
|
||||
version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.16"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -204,9 +205,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -227,16 +228,16 @@ version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.2",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -250,11 +251,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.16"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.36",
|
||||
"proc-macro2 1.0.42",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -297,6 +298,15 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "redox-exec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"plain",
|
||||
"redox_syscall 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
@@ -312,6 +322,15 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca8ce969b6629faa6f60a71f84022b0217e5ca8bcaeb6d11654506a55ebf8fed"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "relibc"
|
||||
version = "0.2.5"
|
||||
@@ -324,12 +343,14 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"memoffset",
|
||||
"plain",
|
||||
"posix-regex",
|
||||
"ralloc",
|
||||
"rand",
|
||||
"redox_syscall 0.2.15",
|
||||
"redox-exec",
|
||||
"redox_syscall 0.3.0",
|
||||
"sc",
|
||||
"spin 0.9.2",
|
||||
"spin 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -361,9 +382,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
|
||||
[[package]]
|
||||
name = "sc"
|
||||
@@ -421,29 +442,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.16",
|
||||
"syn 1.0.89",
|
||||
"proc-macro2 1.0.42",
|
||||
"quote 1.0.20",
|
||||
"syn 1.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.79"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -458,9 +479,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.2"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
|
||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
@@ -479,18 +500,18 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30",
|
||||
"quote 0.6.13",
|
||||
"unicode-xid 0.1.0",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.89"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.16",
|
||||
"unicode-xid 0.2.2",
|
||||
"proc-macro2 1.0.42",
|
||||
"quote 1.0.20",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -518,9 +539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -531,6 +552,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e92e959f029e4f8ee25d70d15ab58d2b46f98a17bc238b9265ff0c26f6f3d67f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
@@ -543,12 +570,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
|
||||
+4
-2
@@ -9,7 +9,7 @@ name = "relibc"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[workspace]
|
||||
members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so"]
|
||||
members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "src/platform/redox/redox-exec"]
|
||||
exclude = ["core_io", "ralloc", "tests"]
|
||||
|
||||
[build-dependencies]
|
||||
@@ -24,6 +24,7 @@ memoffset = "0.5.1"
|
||||
posix-regex = { path = "posix-regex", features = ["no_std"] }
|
||||
rand = { version = "0.5.5", default-features = false }
|
||||
memchr = { version = "2.2.0", default-features = false }
|
||||
plain = "0.2"
|
||||
|
||||
[dependencies.goblin]
|
||||
version = "0.0.21"
|
||||
@@ -39,8 +40,9 @@ optional = true
|
||||
sc = "0.2.3"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox_syscall = "0.2.15"
|
||||
redox_syscall = "0.3"
|
||||
spin = "0.9.0"
|
||||
redox-exec = { path = "src/platform/redox/redox-exec" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -2,13 +2,11 @@ TARGET?=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-t
|
||||
|
||||
CARGO?=cargo
|
||||
CARGO_TEST?=$(CARGO)
|
||||
CARGOFLAGS?=-Z build-std=core,alloc,compiler_builtins
|
||||
CARGO_COMMON_FLAGS=-Z build-std=core,alloc,compiler_builtins
|
||||
CARGOFLAGS?=$(CARGO_COMMON_FLAGS)
|
||||
RUSTCFLAGS?=
|
||||
export OBJCOPY?=objcopy
|
||||
|
||||
# When using xargo, build it in local location
|
||||
export XARGO_HOME=$(CURDIR)/target/xargo
|
||||
|
||||
BUILD="target/$(TARGET)"
|
||||
CARGOFLAGS+="--target=$(TARGET)"
|
||||
|
||||
@@ -51,21 +49,7 @@ SRC=\
|
||||
Cargo.* \
|
||||
$(shell find src -type f)
|
||||
|
||||
# FIXME: Remove the following line. It's only required since xargo automatically links with compiler_builtins, which conflicts with the compiler_builtins that rustc always links with.
|
||||
WEAKEN_SYMBOLS=\
|
||||
-W __divti3 \
|
||||
-W __fixdfti \
|
||||
-W __floattidf \
|
||||
-W __muloti4 \
|
||||
-W __udivti3 \
|
||||
-W __umodti3 \
|
||||
-W __rust_probestack \
|
||||
-W __rust_alloc \
|
||||
-W __rust_alloc_zeroed \
|
||||
-W __rust_dealloc \
|
||||
-W __rust_realloc \
|
||||
-W __rdl_oom \
|
||||
-W __rg_oom
|
||||
BUILTINS_VERSION=0.1.70
|
||||
|
||||
.PHONY: all clean fmt install install-headers libs submodules test
|
||||
|
||||
@@ -146,8 +130,7 @@ $(BUILD)/debug/libc.so: $(BUILD)/debug/librelibc.a $(BUILD)/pthreads-emb/libpthr
|
||||
|
||||
$(BUILD)/debug/librelibc.a: $(SRC)
|
||||
CARGO_INCREMENTAL=0 $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
|
||||
# FIXME: Remove the following line. It's only required since xargo automatically links with compiler_builtins, which conflicts with the compiler_builtins that rustc always links with.
|
||||
$(OBJCOPY) $@ $(WEAKEN_SYMBOLS)
|
||||
./renamesyms.sh $@ $(BUILD)/debug/deps/
|
||||
touch $@
|
||||
|
||||
$(BUILD)/debug/crt0.o: $(SRC)
|
||||
@@ -185,7 +168,9 @@ $(BUILD)/release/libc.so: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/lib
|
||||
|
||||
$(BUILD)/release/librelibc.a: $(SRC)
|
||||
CARGO_INCREMENTAL=0 $(CARGO) rustc --release $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
|
||||
$(OBJCOPY) $@ $(WEAKEN_SYMBOLS)
|
||||
# TODO: Better to only allow a certain whitelisted set of symbols? Perhaps
|
||||
# use some cbindgen hook, specify them manually, or grep for #[no_mangle].
|
||||
./renamesyms.sh $@ $(BUILD)/release/deps/
|
||||
touch $@
|
||||
|
||||
$(BUILD)/release/crt0.o: $(SRC)
|
||||
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
target=$1
|
||||
deps_dir=$2
|
||||
|
||||
if [ -z "$target" ] || [ -z "$deps_dir" ]; then
|
||||
echo "Usage:\n\t./renamesyms.sh TARGET DEPS_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
symbols_file=`mktemp`
|
||||
special_syms="__rg_alloc __rg_dealloc __rg_realloc __rg_alloc_zeroed"
|
||||
|
||||
for dep in `find $deps_dir -type f -name "*.rlib"`; do
|
||||
nm --format=posix -g "$dep" 2>/dev/null | sed 's/.*:.*//g' | awk '{if ($2 == "T") print $1}' | sed 's/^\(.*\)$/\1 __relibc_\1/g' >> $symbols_file
|
||||
done
|
||||
|
||||
for special_sym in $special_syms; do
|
||||
echo "$special_sym __relibc_$special_sym" >> $symbols_file
|
||||
done
|
||||
|
||||
sorted_file=`mktemp`
|
||||
sort -u "$symbols_file" > "$sorted_file"
|
||||
rm -f "$symbols_file"
|
||||
|
||||
objcopy --redefine-syms="$sorted_file" "$target"
|
||||
|
||||
rm -f "$sorted_file"
|
||||
+1
-1
@@ -1 +1 @@
|
||||
nightly-2021-06-15
|
||||
nightly-2022-03-18
|
||||
|
||||
@@ -8,10 +8,22 @@ use core::arch::global_asm;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
global_asm!("
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
mov rdi, rsp
|
||||
and rsp, 0xFFFFFFFFFFFFFFF0
|
||||
|
||||
sub rsp, 8
|
||||
|
||||
mov DWORD PTR [rsp], 0x00001F80
|
||||
ldmxcsr [rsp]
|
||||
mov WORD PTR [rsp], 0x031F
|
||||
fldcw [rsp]
|
||||
|
||||
add rsp, 8
|
||||
|
||||
call relibc_start
|
||||
.size _start, . - _start
|
||||
");
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
global_asm!("
|
||||
|
||||
+60
-71
@@ -324,16 +324,7 @@ pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut
|
||||
}
|
||||
|
||||
unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
|
||||
for (i, item) in platform::inner_environ.iter().enumerate() {
|
||||
let mut item = *item;
|
||||
if item.is_null() {
|
||||
assert_eq!(
|
||||
i,
|
||||
platform::inner_environ.len() - 1,
|
||||
"an early null pointer in environ vector"
|
||||
);
|
||||
break;
|
||||
}
|
||||
for (i, mut item) in platform::environ_iter().enumerate() {
|
||||
let mut search = search;
|
||||
loop {
|
||||
let end_of_query = *search == 0 || *search == b'=' as c_char;
|
||||
@@ -341,7 +332,7 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
|
||||
if *item == b'=' as c_char || end_of_query {
|
||||
if *item == b'=' as c_char && end_of_query {
|
||||
// Both keys env here
|
||||
return Some((i, item.offset(1)));
|
||||
return Some((i, item.add(1)));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -351,10 +342,11 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
|
||||
break;
|
||||
}
|
||||
|
||||
item = item.offset(1);
|
||||
search = search.offset(1);
|
||||
item = item.add(1);
|
||||
search = search.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -694,22 +686,33 @@ pub extern "C" fn ptsname(fildes: c_int) -> *mut c_char {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn put_new_env(insert: *mut c_char) {
|
||||
// XXX: Another problem is that `environ` can be set to any pointer, which means there is a
|
||||
// chance of a memory leak. But we can check if it was the same as before, like musl does.
|
||||
if platform::environ == platform::OUR_ENVIRON.as_mut_ptr() {
|
||||
*platform::OUR_ENVIRON.last_mut().unwrap() = insert;
|
||||
platform::OUR_ENVIRON.push(core::ptr::null_mut());
|
||||
// Likely a no-op but is needed due to Stacked Borrows.
|
||||
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
|
||||
} else {
|
||||
platform::OUR_ENVIRON.clear();
|
||||
platform::OUR_ENVIRON.extend(platform::environ_iter());
|
||||
platform::OUR_ENVIRON.push(insert);
|
||||
platform::OUR_ENVIRON.push(core::ptr::null_mut());
|
||||
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int {
|
||||
assert_ne!(insert, ptr::null_mut(), "putenv(NULL)");
|
||||
if let Some((i, _)) = find_env(insert) {
|
||||
//TODO: find out why this crashes: platform::free(platform::inner_environ[i] as *mut c_void);
|
||||
platform::inner_environ[i] = insert;
|
||||
// XXX: The POSIX manual states that environment variables can be *set* via the `environ`
|
||||
// global variable. While we can check if a pointer belongs to our allocator, or check
|
||||
// `environ` against a vector which we control, it is likely not worth the effort.
|
||||
platform::environ.add(i).write(insert);
|
||||
} else {
|
||||
let i = platform::inner_environ.len() - 1;
|
||||
assert_eq!(
|
||||
platform::inner_environ[i],
|
||||
ptr::null_mut(),
|
||||
"environ did not end with null"
|
||||
);
|
||||
platform::inner_environ[i] = insert;
|
||||
platform::inner_environ.push(ptr::null_mut());
|
||||
platform::environ = platform::inner_environ.as_mut_ptr();
|
||||
put_new_env(insert);
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -850,69 +853,45 @@ pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort {
|
||||
rand48::SEED48_XSUBI.as_mut_ptr()
|
||||
}
|
||||
|
||||
unsafe fn copy_kv(existing: *mut c_char, key: *const c_char, value: *const c_char, key_len: usize, value_len: usize) {
|
||||
core::ptr::copy_nonoverlapping(key, existing, key_len);
|
||||
core::ptr::write(existing.add(key_len), b'=' as c_char);
|
||||
core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setenv(
|
||||
mut key: *const c_char,
|
||||
mut value: *const c_char,
|
||||
overwrite: c_int,
|
||||
) -> c_int {
|
||||
let mut key_len = 0;
|
||||
while *key.offset(key_len) != 0 {
|
||||
key_len += 1;
|
||||
}
|
||||
let mut value_len = 0;
|
||||
while *value.offset(value_len) != 0 {
|
||||
value_len += 1;
|
||||
}
|
||||
let key_len = strlen(key);
|
||||
let value_len = strlen(value);
|
||||
|
||||
let index = if let Some((i, existing)) = find_env(key) {
|
||||
if let Some((i, existing)) = find_env(key) {
|
||||
if overwrite == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut existing_len = 0;
|
||||
while *existing.offset(existing_len) != 0 {
|
||||
existing_len += 1;
|
||||
}
|
||||
let existing_len = strlen(existing);
|
||||
|
||||
if existing_len >= value_len {
|
||||
// Reuse existing element's allocation
|
||||
for i in 0..=value_len {
|
||||
*existing.offset(i) = *value.offset(i);
|
||||
}
|
||||
return 0;
|
||||
core::ptr::copy_nonoverlapping(value, existing, value_len);
|
||||
} else {
|
||||
i
|
||||
// Reuse platform::environ slot, but allocate a new pointer.
|
||||
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
|
||||
copy_kv(ptr, key, value, key_len, value_len);
|
||||
platform::environ.add(i).write(ptr);
|
||||
}
|
||||
} else {
|
||||
let i = platform::inner_environ.len() - 1;
|
||||
assert_eq!(
|
||||
platform::inner_environ[i],
|
||||
ptr::null_mut(),
|
||||
"environ did not end with null"
|
||||
);
|
||||
platform::inner_environ.push(ptr::null_mut());
|
||||
platform::environ = platform::inner_environ.as_mut_ptr();
|
||||
i
|
||||
};
|
||||
// Expand platform::environ and allocate a new pointer.
|
||||
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
|
||||
copy_kv(ptr, key, value, key_len, value_len);
|
||||
put_new_env(ptr);
|
||||
}
|
||||
|
||||
//platform::free(platform::inner_environ[index] as *mut c_void);
|
||||
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
|
||||
platform::inner_environ[index] = ptr;
|
||||
|
||||
while *key != 0 {
|
||||
*ptr = *key;
|
||||
ptr = ptr.offset(1);
|
||||
key = key.offset(1);
|
||||
}
|
||||
*ptr = b'=' as c_char;
|
||||
ptr = ptr.offset(1);
|
||||
while *value != 0 {
|
||||
*ptr = *value;
|
||||
ptr = ptr.offset(1);
|
||||
value = value.offset(1);
|
||||
}
|
||||
*ptr = 0;
|
||||
|
||||
0
|
||||
}
|
||||
@@ -1174,9 +1153,19 @@ pub extern "C" fn unlockpt(fildes: c_int) -> c_int {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int {
|
||||
if let Some((i, _)) = find_env(key) {
|
||||
// No need to worry about updating the pointer, this does not
|
||||
// reallocate in any way. And the final null is already shifted back.
|
||||
platform::inner_environ.remove(i);
|
||||
if platform::environ == platform::OUR_ENVIRON.as_mut_ptr() {
|
||||
// No need to worry about updating the pointer, this does not
|
||||
// reallocate in any way. And the final null is already shifted back.
|
||||
platform::OUR_ENVIRON.remove(i);
|
||||
|
||||
// My UB paranoia.
|
||||
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
|
||||
} else {
|
||||
platform::OUR_ENVIRON.clear();
|
||||
platform::OUR_ENVIRON.extend(platform::environ_iter().enumerate().filter(|&(j, _)| j != i).map(|(_, v)| v));
|
||||
platform::OUR_ENVIRON.push(core::ptr::null_mut());
|
||||
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
@@ -2,33 +2,7 @@
|
||||
|
||||
use crate::platform::types::*;
|
||||
|
||||
pub const AT_NULL: usize = 0; /* End of vector */
|
||||
pub const AT_IGNORE: usize = 1; /* Entry should be ignored */
|
||||
pub const AT_EXECFD: usize = 2; /* File descriptor of program */
|
||||
pub const AT_PHDR: usize = 3; /* Program headers for program */
|
||||
pub const AT_PHENT: usize = 4; /* Size of program header entry */
|
||||
pub const AT_PHNUM: usize = 5; /* Number of program headers */
|
||||
pub const AT_PAGESZ: usize = 6; /* System page size */
|
||||
pub const AT_BASE: usize = 7; /* Base address of interpreter */
|
||||
pub const AT_FLAGS: usize = 8; /* Flags */
|
||||
pub const AT_ENTRY: usize = 9; /* Entry point of program */
|
||||
pub const AT_NOTELF: usize = 10; /* Program is not ELF */
|
||||
pub const AT_UID: usize = 11; /* Real uid */
|
||||
pub const AT_EUID: usize = 12; /* Effective uid */
|
||||
pub const AT_GID: usize = 13; /* Real gid */
|
||||
pub const AT_EGID: usize = 14; /* Effective gid */
|
||||
pub const AT_CLKTCK: usize = 17; /* Frequency of times() */
|
||||
pub const AT_PLATFORM: usize = 15; /* String identifying platform. */
|
||||
pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */
|
||||
pub const AT_FPUCW: usize = 18; /* Used FPU control word. */
|
||||
pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */
|
||||
pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */
|
||||
pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */
|
||||
pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */
|
||||
pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
|
||||
pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
|
||||
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
|
||||
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
|
||||
pub use crate::platform::auxv_defs::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn getauxval(_t: c_ulong) -> c_ulong {
|
||||
|
||||
@@ -472,7 +472,15 @@ impl Linker {
|
||||
}
|
||||
|
||||
fn run_init(&self, objects: &Vec<DSO>) {
|
||||
use crate::platform::{self, types::*};
|
||||
|
||||
for obj in objects.iter().rev() {
|
||||
if let Some((symbol, true)) = obj.get_sym("__relibc_init_environ") {
|
||||
unsafe {
|
||||
symbol.as_ptr().cast::<*mut *mut c_char>().write(platform::environ);
|
||||
}
|
||||
}
|
||||
|
||||
obj.run_init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ pub fn static_init(sp: &'static Stack) {
|
||||
tcb.masters_len = mem::size_of::<Master>();
|
||||
tcb.copy_masters().expect_notls("failed to copy TLS master data");
|
||||
tcb.activate();
|
||||
|
||||
}
|
||||
|
||||
//TODO: Warning on multiple TLS sections?
|
||||
|
||||
@@ -167,6 +167,22 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
|
||||
(argv, envs, auxv)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
crate::platform::OUR_ENVIRON = envs.iter().map(|(k, v)| {
|
||||
let mut var = Vec::with_capacity(k.len() + v.len() + 2);
|
||||
var.extend(k.as_bytes());
|
||||
var.push(b'=');
|
||||
var.extend(v.as_bytes());
|
||||
var.push(b'\0');
|
||||
let mut var = var.into_boxed_slice();
|
||||
let ptr = var.as_mut_ptr();
|
||||
core::mem::forget(var);
|
||||
ptr.cast()
|
||||
}).chain(core::iter::once(core::ptr::null_mut())).collect::<Vec<_>>();
|
||||
|
||||
crate::platform::environ = crate::platform::OUR_ENVIRON.as_mut_ptr();
|
||||
}
|
||||
|
||||
let is_manual = if let Some(img_entry) = auxv.get(&AT_ENTRY) {
|
||||
*img_entry == ld_entry
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(unused_variables)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(box_into_pin)]
|
||||
#![feature(c_variadic)]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
pub const AT_NULL: usize = 0; /* End of vector */
|
||||
pub const AT_IGNORE: usize = 1; /* Entry should be ignored */
|
||||
pub const AT_EXECFD: usize = 2; /* File descriptor of program */
|
||||
pub const AT_PHDR: usize = 3; /* Program headers for program */
|
||||
pub const AT_PHENT: usize = 4; /* Size of program header entry */
|
||||
pub const AT_PHNUM: usize = 5; /* Number of program headers */
|
||||
pub const AT_PAGESZ: usize = 6; /* System page size */
|
||||
pub const AT_BASE: usize = 7; /* Base address of interpreter */
|
||||
pub const AT_FLAGS: usize = 8; /* Flags */
|
||||
pub const AT_ENTRY: usize = 9; /* Entry point of program */
|
||||
pub const AT_NOTELF: usize = 10; /* Program is not ELF */
|
||||
pub const AT_UID: usize = 11; /* Real uid */
|
||||
pub const AT_EUID: usize = 12; /* Effective uid */
|
||||
pub const AT_GID: usize = 13; /* Real gid */
|
||||
pub const AT_EGID: usize = 14; /* Effective gid */
|
||||
pub const AT_CLKTCK: usize = 17; /* Frequency of times() */
|
||||
pub const AT_PLATFORM: usize = 15; /* String identifying platform. */
|
||||
pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */
|
||||
pub const AT_FPUCW: usize = 18; /* Used FPU control word. */
|
||||
pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */
|
||||
pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */
|
||||
pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */
|
||||
pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */
|
||||
pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
|
||||
pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
|
||||
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
|
||||
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
|
||||
+24
-2
@@ -34,6 +34,12 @@ mod pte;
|
||||
pub use self::rlb::{Line, RawLineBuffer};
|
||||
pub mod rlb;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod auxv_defs;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub use redox_exec::auxv_defs;
|
||||
|
||||
use self::types::*;
|
||||
pub mod types;
|
||||
|
||||
@@ -54,8 +60,24 @@ pub static mut program_invocation_short_name: *mut c_char = ptr::null_mut();
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[no_mangle]
|
||||
pub static mut environ: *mut *mut c_char = ptr::null_mut();
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static mut inner_environ: Vec<*mut c_char> = Vec::new();
|
||||
|
||||
pub static mut OUR_ENVIRON: Vec<*mut c_char> = Vec::new();
|
||||
|
||||
pub fn environ_iter() -> impl Iterator<Item = *mut c_char> + 'static {
|
||||
unsafe {
|
||||
let mut ptrs = environ;
|
||||
|
||||
core::iter::from_fn(move || {
|
||||
let ptr = ptrs.read();
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
ptrs = ptrs.add(1);
|
||||
Some(ptr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WriteByte: fmt::Write {
|
||||
fn write_u8(&mut self, byte: u8) -> fmt::Result;
|
||||
|
||||
@@ -118,7 +118,6 @@ pub unsafe extern "C" fn pte_osThreadCreate(
|
||||
if stack_base as isize == -1 {
|
||||
return PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
ptr::write_bytes(stack_base as *mut u8, 0, stack_size);
|
||||
let stack_end = stack_base.add(stack_size);
|
||||
let mut stack = stack_end as *mut usize;
|
||||
{
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
use core::arch::global_asm;
|
||||
use core::mem::size_of;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use syscall::data::Map;
|
||||
use syscall::flag::{MapFlags, O_CLOEXEC};
|
||||
use syscall::error::{Error, Result, EINVAL, ENAMETOOLONG};
|
||||
use syscall::SIGCONT;
|
||||
|
||||
use super::extra::{create_set_addr_space_buf, FdGuard};
|
||||
|
||||
fn new_context() -> Result<(FdGuard, usize)> {
|
||||
// Create a new context (fields such as uid/gid will be inherited from the current context).
|
||||
let fd = FdGuard::new(syscall::open("thisproc:new/open_via_dup", O_CLOEXEC)?);
|
||||
|
||||
// Extract pid.
|
||||
let mut buffer = [0_u8; 64];
|
||||
let len = syscall::fpath(*fd, &mut buffer)?;
|
||||
let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
|
||||
|
||||
let colon_idx = buffer.iter().position(|c| *c == b':').ok_or(Error::new(EINVAL))?;
|
||||
let slash_idx = buffer.iter().skip(colon_idx).position(|c| *c == b'/').ok_or(Error::new(EINVAL))? + colon_idx;
|
||||
let pid_bytes = buffer.get(colon_idx + 1..slash_idx).ok_or(Error::new(EINVAL))?;
|
||||
let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?;
|
||||
let pid = pid_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?;
|
||||
|
||||
Ok((fd, pid))
|
||||
}
|
||||
|
||||
fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
|
||||
let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?);
|
||||
let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?);
|
||||
|
||||
// TODO: Max path size?
|
||||
let mut buf = [0_u8; 256];
|
||||
let len = syscall::read(*cur_name_fd, &mut buf)?;
|
||||
let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
|
||||
|
||||
syscall::write(*new_name_fd, &buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
|
||||
// Copy environment registers.
|
||||
{
|
||||
let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?);
|
||||
let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?);
|
||||
|
||||
let mut env_regs = syscall::EnvRegisters::default();
|
||||
let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?;
|
||||
let _ = syscall::write(*new_env_regs_fd, &env_regs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
|
||||
pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> {
|
||||
let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
|
||||
let (new_pid_fd, new_pid) = new_context()?;
|
||||
|
||||
// Allocate a new signal stack.
|
||||
{
|
||||
let sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?);
|
||||
|
||||
const SIGSTACK_SIZE: usize = 1024 * 256;
|
||||
|
||||
// TODO: Put sigstack at high addresses?
|
||||
let target_sigstack = syscall::fmap(!0, &Map { address: 0, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, offset: 0, size: SIGSTACK_SIZE })? + SIGSTACK_SIZE;
|
||||
|
||||
let _ = syscall::write(*sigstack_fd, &usize::to_ne_bytes(target_sigstack))?;
|
||||
}
|
||||
|
||||
copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
|
||||
copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
|
||||
|
||||
// Reuse existing address space
|
||||
{
|
||||
let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
|
||||
let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
|
||||
|
||||
let buf = create_set_addr_space_buf(*cur_addr_space_fd, __relibc_internal_pte_clone_ret as usize, stack as usize);
|
||||
let _ = syscall::write(*new_addr_space_sel_fd, &buf)?;
|
||||
}
|
||||
|
||||
// Reuse file table
|
||||
{
|
||||
let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
|
||||
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
|
||||
|
||||
let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*cur_filetable_fd))?;
|
||||
}
|
||||
|
||||
// Reuse sigactions (on Linux, CLONE_THREAD requires CLONE_SIGHAND which implies the sigactions
|
||||
// table is reused).
|
||||
{
|
||||
let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);
|
||||
let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?);
|
||||
|
||||
let _ = syscall::write(*new_sigaction_sel_fd, &usize::to_ne_bytes(*cur_sigaction_fd))?;
|
||||
}
|
||||
|
||||
copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
|
||||
|
||||
// Unblock context.
|
||||
syscall::kill(new_pid, SIGCONT)?;
|
||||
let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
/// Spawns a new context which will not share the same address space as the current one. File
|
||||
/// descriptors from other schemes are reobtained with `dup`, and grants referencing such file
|
||||
/// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW.
|
||||
pub fn fork_impl() -> Result<usize> {
|
||||
unsafe {
|
||||
Error::demux(__relibc_internal_fork_wrapper())
|
||||
}
|
||||
}
|
||||
|
||||
fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
|
||||
let (cur_filetable_fd, new_pid_fd, new_pid);
|
||||
|
||||
{
|
||||
let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
|
||||
(new_pid_fd, new_pid) = new_context()?;
|
||||
|
||||
// Do not allocate new signal stack, but copy existing address (all memory will be re-mapped
|
||||
// CoW later).
|
||||
{
|
||||
let cur_sigstack_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigstack")?);
|
||||
let new_sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?);
|
||||
|
||||
let mut sigstack_buf = usize::to_ne_bytes(0);
|
||||
|
||||
let _ = syscall::read(*cur_sigstack_fd, &mut sigstack_buf);
|
||||
let _ = syscall::write(*new_sigstack_fd, &sigstack_buf);
|
||||
}
|
||||
|
||||
copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
|
||||
copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
|
||||
|
||||
{
|
||||
let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);
|
||||
let new_sigaction_fd = FdGuard::new(syscall::dup(*cur_sigaction_fd, b"copy")?);
|
||||
let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?);
|
||||
|
||||
let _ = syscall::write(*new_sigaction_sel_fd, &usize::to_ne_bytes(*new_sigaction_fd))?;
|
||||
}
|
||||
|
||||
// Copy existing files into new file table, but do not reuse the same file table (i.e. new
|
||||
// parent FDs will not show up for the child).
|
||||
{
|
||||
cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
|
||||
|
||||
// This must be done before the address space is copied.
|
||||
unsafe {
|
||||
initial_rsp.write(*cur_filetable_fd);
|
||||
initial_rsp.add(1).write(*new_pid_fd);
|
||||
}
|
||||
}
|
||||
|
||||
// CoW-duplicate address space.
|
||||
{
|
||||
let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
|
||||
|
||||
// FIXME: Find mappings which use external file descriptors
|
||||
|
||||
let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?);
|
||||
|
||||
let mut buf = vec! [0_u8; 4096];
|
||||
let mut bytes_read = 0;
|
||||
|
||||
loop {
|
||||
let new_bytes_read = syscall::read(*cur_addr_space_fd, &mut buf[bytes_read..])?;
|
||||
|
||||
if new_bytes_read == 0 { break }
|
||||
|
||||
bytes_read += new_bytes_read;
|
||||
}
|
||||
let bytes = &buf[..bytes_read];
|
||||
|
||||
for struct_bytes in bytes.array_chunks::<{size_of::<usize>() * 4}>() {
|
||||
let mut words = struct_bytes.array_chunks::<{size_of::<usize>()}>().copied().map(usize::from_ne_bytes);
|
||||
|
||||
let addr = words.next().unwrap();
|
||||
let size = words.next().unwrap();
|
||||
let flags = words.next().unwrap();
|
||||
let offset = words.next().unwrap();
|
||||
|
||||
if flags & 0x8000_0000 == 0 {
|
||||
continue;
|
||||
}
|
||||
let map_flags = MapFlags::from_bits_truncate(flags);
|
||||
|
||||
let grant_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, format!("grant-{:x}", addr).as_bytes())?);
|
||||
redox_exec::mmap_remote(&new_addr_space_fd, &grant_fd, offset, addr, size, map_flags)?;
|
||||
}
|
||||
let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
|
||||
|
||||
let buf = create_set_addr_space_buf(*new_addr_space_fd, __relibc_internal_fork_ret as usize, initial_rsp as usize);
|
||||
let _ = syscall::write(*new_addr_space_sel_fd, &buf)?;
|
||||
}
|
||||
copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
|
||||
}
|
||||
// Copy the file table. We do this last to ensure that all previously used file descriptors are
|
||||
// closed. The only exception -- the filetable selection fd and the current filetable fd --
|
||||
// will be closed by the child process.
|
||||
{
|
||||
// TODO: Use cross_scheme_links or something similar to avoid copying the file table in the
|
||||
// kernel.
|
||||
let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?);
|
||||
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
|
||||
let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd));
|
||||
}
|
||||
|
||||
// Unblock context.
|
||||
syscall::kill(new_pid, SIGCONT)?;
|
||||
|
||||
// XXX: Killing with SIGCONT will put (pid, 65536) at key (pid, pgid) into the waitpid of this
|
||||
// context. This means that if pgid is changed (as it is in ion for example), the pgid message
|
||||
// in syscall::exit() will not be inserted as the key comparator thinks they're equal as their
|
||||
// PIDs are. So, we have to call this to clear the waitpid queue to prevent deadlocks.
|
||||
let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED);
|
||||
|
||||
Ok(new_pid)
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize {
|
||||
Error::mux(fork_inner(initial_rsp))
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "sysv64" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
|
||||
let _ = syscall::close(cur_filetable_fd);
|
||||
let _ = syscall::close(new_pid_fd);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
core::arch::global_asm!("
|
||||
.p2align 6
|
||||
.globl __relibc_internal_fork_wrapper
|
||||
.type __relibc_internal_fork_wrapper, @function
|
||||
__relibc_internal_fork_wrapper:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
push rbx
|
||||
push rbp
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
sub rsp, 32
|
||||
|
||||
stmxcsr [rsp+16]
|
||||
fnstcw [rsp+24]
|
||||
|
||||
mov rdi, rsp
|
||||
call __relibc_internal_fork_impl
|
||||
jmp 2f
|
||||
|
||||
.size __relibc_internal_fork_wrapper, . - __relibc_internal_fork_wrapper
|
||||
|
||||
.p2align 6
|
||||
.type __relibc_internal_fork_ret, @function
|
||||
__relibc_internal_fork_ret:
|
||||
mov rdi, [rsp]
|
||||
mov rsi, [rsp + 8]
|
||||
call __relibc_internal_fork_hook
|
||||
|
||||
ldmxcsr [rsp+16]
|
||||
fldcw [rsp+24]
|
||||
|
||||
xor rax, rax
|
||||
|
||||
.p2align 4
|
||||
2:
|
||||
add rsp, 32
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbp
|
||||
pop rbx
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
.size __relibc_internal_fork_ret, . - __relibc_internal_fork_ret
|
||||
|
||||
.globl __relibc_internal_pte_clone_ret
|
||||
.type __relibc_internal_pte_clone_ret, @function
|
||||
.p2align 6
|
||||
__relibc_internal_pte_clone_ret:
|
||||
# Load registers
|
||||
pop rax
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
|
||||
sub rsp, 8
|
||||
|
||||
mov DWORD PTR [rsp], 0x00001F80
|
||||
ldmxcsr [rsp]
|
||||
mov WORD PTR [rsp], 0x031F
|
||||
fldcw [rsp]
|
||||
|
||||
add rsp, 8
|
||||
|
||||
# Call entry point
|
||||
call rax
|
||||
|
||||
ret
|
||||
.size __relibc_internal_pte_clone_ret, . - __relibc_internal_pte_clone_ret
|
||||
");
|
||||
|
||||
extern "sysv64" {
|
||||
fn __relibc_internal_fork_wrapper() -> usize;
|
||||
fn __relibc_internal_fork_ret();
|
||||
fn __relibc_internal_pte_clone_ret();
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
use crate::c_str::{CStr, CString};
|
||||
use crate::core_io::{BufReader, prelude::*, SeekFrom};
|
||||
use crate::fs::File;
|
||||
use crate::header::{fcntl, string::strlen};
|
||||
use crate::platform::{sys::{S_ISUID, S_ISGID}, types::*};
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::flag::*;
|
||||
use syscall::error::*;
|
||||
use redox_exec::{FdGuard, FexecResult};
|
||||
|
||||
fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_args_envs_size: usize, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
|
||||
let fd = *file;
|
||||
core::mem::forget(file);
|
||||
let image_file = FdGuard::new(fd as usize);
|
||||
|
||||
let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0)?);
|
||||
let memory = FdGuard::new(syscall::open("memory:", 0)?);
|
||||
|
||||
let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, &memory, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, interp_override)? {
|
||||
FexecResult::Normal { addrspace_handle } => addrspace_handle,
|
||||
FexecResult::Interp { image_file, open_via_dup, path, interp_override: new_interp_override } => {
|
||||
drop(image_file);
|
||||
drop(open_via_dup);
|
||||
drop(memory);
|
||||
|
||||
// According to elf(5), PT_INTERP requires that the interpreter path be
|
||||
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
|
||||
let path_cstr = CStr::from_bytes_with_nul(&path).map_err(|_| Error::new(ENOEXEC))?;
|
||||
|
||||
return execve(path_cstr, ArgEnv::Parsed { total_args_envs_size, args, envs }, Some(new_interp_override));
|
||||
}
|
||||
};
|
||||
drop(memory);
|
||||
|
||||
// Dropping this FD will cause the address space switch.
|
||||
drop(addrspace_selection_fd);
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
pub enum ArgEnv<'a> {
|
||||
C { argv: *const *mut c_char, envp: *const *mut c_char },
|
||||
Parsed { args: &'a [&'a [u8]], envs: &'a [&'a [u8]], total_args_envs_size: usize },
|
||||
}
|
||||
pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
|
||||
// NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we
|
||||
// have even read it!
|
||||
let mut image_file = File::open(path, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?;
|
||||
|
||||
// With execve now being implemented in userspace, we need to check ourselves that this
|
||||
// file is actually executable. While checking for read permission is unnecessary as the
|
||||
// scheme will not allow us to read otherwise, the execute bit is completely unenforced. We
|
||||
// have the permission to mmap executable memory and fill it with the program even if it is
|
||||
// unset, so the best we can do is check that nothing is executed by accident.
|
||||
//
|
||||
// TODO: At some point we might have capabilities limiting the ability to allocate
|
||||
// executable memory, and in that case we might use the `escalate:` scheme as we already do
|
||||
// when the binary needs setuid/setgid.
|
||||
|
||||
let mut stat = Stat::default();
|
||||
syscall::fstat(*image_file as usize, &mut stat)?;
|
||||
let uid = syscall::getuid()?;
|
||||
let gid = syscall::getuid()?;
|
||||
|
||||
let mode = if uid == stat.st_uid as usize {
|
||||
(stat.st_mode >> 3 * 2) & 0o7
|
||||
} else if gid == stat.st_gid as usize {
|
||||
(stat.st_mode >> 3 * 1) & 0o7
|
||||
} else {
|
||||
stat.st_mode & 0o7
|
||||
};
|
||||
|
||||
if mode & 0o1 == 0o0 {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
let wants_setugid = stat.st_mode & ((S_ISUID | S_ISGID) as u16) != 0;
|
||||
|
||||
// Count arguments
|
||||
let mut len = 0;
|
||||
|
||||
match arg_env {
|
||||
ArgEnv::C { argv, .. } => unsafe {
|
||||
while !(*argv.add(len)).is_null() {
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
ArgEnv::Parsed { args, .. } => len = args.len(),
|
||||
}
|
||||
|
||||
let mut args: Vec<&[u8]> = Vec::with_capacity(len);
|
||||
|
||||
// Read shebang (for example #!/bin/sh)
|
||||
let mut _interpreter_path = None;
|
||||
let is_interpreted = {
|
||||
let mut read = 0;
|
||||
let mut shebang = [0; 2];
|
||||
|
||||
while read < 2 {
|
||||
match image_file.read(&mut shebang).map_err(|_| Error::new(ENOEXEC))? {
|
||||
0 => break,
|
||||
i => read += i,
|
||||
}
|
||||
}
|
||||
shebang == *b"#!"
|
||||
};
|
||||
// Since the fexec implementation is almost fully done in userspace, the kernel can no
|
||||
// longer set UID/GID accordingly, and this code checking for them before using
|
||||
// hypothetical interfaces to upgrade UID/GID, can not be trusted. So we ask the
|
||||
// `escalate:` scheme for help. Note that `escalate:` can be deliberately excluded from the
|
||||
// scheme namespace to deny privilege escalation (such as su/sudo/doas) for untrusted
|
||||
// processes.
|
||||
//
|
||||
// According to execve(2), Linux and most other UNIXes ignore setuid/setgid for interpreted
|
||||
// executables and thereby simply keep the privileges as is. For compatibility we do that
|
||||
// too.
|
||||
|
||||
if is_interpreted {
|
||||
// TODO: Does this support prepending args to the interpreter? E.g.
|
||||
// #!/usr/bin/env python3
|
||||
|
||||
// So, this file is interpreted.
|
||||
// Then, read the actual interpreter:
|
||||
let mut interpreter = Vec::new();
|
||||
BufReader::new(&mut image_file).read_until(b'\n', &mut interpreter).map_err(|_| Error::new(EIO))?;
|
||||
if interpreter.ends_with(&[b'\n']) {
|
||||
interpreter.pop().unwrap();
|
||||
}
|
||||
let cstring = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?;
|
||||
image_file = File::open(&cstring, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?;
|
||||
|
||||
// Make sure path is kept alive long enough, and push it to the arguments
|
||||
_interpreter_path = Some(cstring);
|
||||
let path_ref = _interpreter_path.as_ref().unwrap();
|
||||
args.push(path_ref.as_bytes());
|
||||
} else {
|
||||
image_file.seek(SeekFrom::Start(0)).map_err(|_| Error::new(EIO))?;
|
||||
}
|
||||
|
||||
let (total_args_envs_size, args, envs): (usize, Vec<_>, Vec<_>) = match arg_env {
|
||||
ArgEnv::C { mut argv, mut envp } => unsafe {
|
||||
let mut args_envs_size_without_nul = 0;
|
||||
|
||||
// Arguments
|
||||
while !argv.read().is_null() {
|
||||
let arg = argv.read();
|
||||
|
||||
let len = strlen(arg);
|
||||
args.push(core::slice::from_raw_parts(arg as *const u8, len));
|
||||
args_envs_size_without_nul += len;
|
||||
argv = argv.add(1);
|
||||
}
|
||||
|
||||
// Environment variables
|
||||
let mut len = 0;
|
||||
while !envp.add(len).read().is_null() {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let mut envs: Vec<&[u8]> = Vec::with_capacity(len);
|
||||
while !envp.read().is_null() {
|
||||
let env = envp.read();
|
||||
|
||||
let len = strlen(env);
|
||||
envs.push(core::slice::from_raw_parts(env as *const u8, len));
|
||||
args_envs_size_without_nul += len;
|
||||
envp = envp.add(1);
|
||||
}
|
||||
(args_envs_size_without_nul + args.len() + envs.len(), args, envs)
|
||||
}
|
||||
ArgEnv::Parsed { args: new_args, envs, total_args_envs_size } => {
|
||||
let prev_size: usize = args.iter().map(|a| a.len()).sum();
|
||||
args.extend(new_args);
|
||||
(total_args_envs_size + prev_size, args, Vec::from(envs))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Close all O_CLOEXEC file descriptors. TODO: close_range?
|
||||
{
|
||||
// NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded
|
||||
// scenarios. While execve() is undefined according to POSIX if there exist sibling
|
||||
// threads, it could still be allowed by keeping certain file descriptors and instead
|
||||
// set the active file table.
|
||||
let files_fd = File::new(syscall::open("thisproc:current/filetable", O_RDONLY)? as c_int);
|
||||
for line in BufReader::new(files_fd).lines() {
|
||||
let line = match line {
|
||||
Ok(l) => l,
|
||||
Err(_) => break,
|
||||
};
|
||||
let fd = match line.parse::<usize>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let flags = syscall::fcntl(fd, F_GETFD, 0)?;
|
||||
|
||||
if flags & O_CLOEXEC == O_CLOEXEC {
|
||||
let _ = syscall::close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !is_interpreted && wants_setugid {
|
||||
// Make sure the last file descriptor not covered by O_CLOEXEC is not leaked.
|
||||
drop(image_file);
|
||||
|
||||
// We are now going to invoke `escalate:` rather than loading the program ourselves.
|
||||
let escalate_fd = FdGuard::new(syscall::open("escalate:", O_WRONLY)?);
|
||||
|
||||
// First, we write the path.
|
||||
//
|
||||
// TODO: For improved security, use a hypothetical SYS_DUP_FORWARD syscall to give the
|
||||
// scheme our file descriptor. It can check through the kernel-overwritten stat.st_dev
|
||||
// field that it pertains to a "trusted" scheme (i.e. of at least the privilege the
|
||||
// new uid/gid has), although for now only root can open schemes. Passing a file
|
||||
// descriptor and not a path will allow escalated to run in a limited namespace.
|
||||
//
|
||||
// TODO: Plus, at this point fexecve is not implemented (but specified in
|
||||
// POSIX.1-2008), and to avoid bad syscalls such as fpath, passing a file descriptor
|
||||
// would be better.
|
||||
let _ = syscall::write(*escalate_fd, path.to_bytes());
|
||||
|
||||
// Second, we write the flattened args and envs with NUL characters separating
|
||||
// individual items. This can be copied directly into the new executable's memory.
|
||||
let _ = syscall::write(*escalate_fd, &flatten_with_nul(args))?;
|
||||
let _ = syscall::write(*escalate_fd, &flatten_with_nul(envs))?;
|
||||
|
||||
// Closing will notify the scheme, and from that point we will no longer have control
|
||||
// over this process (unless it fails). We do this manually since drop cannot handle
|
||||
// errors.
|
||||
let fd = *escalate_fd as usize;
|
||||
core::mem::forget(escalate_fd);
|
||||
|
||||
syscall::close(fd)?;
|
||||
|
||||
unreachable!()
|
||||
} else {
|
||||
fexec_impl(image_file, path.to_bytes(), &args, &envs, total_args_envs_size, interp_override)
|
||||
}
|
||||
}
|
||||
fn flatten_with_nul<T>(iter: impl IntoIterator<Item = T>) -> Box<[u8]> where T: AsRef<[u8]> {
|
||||
let mut vec = Vec::new();
|
||||
for item in iter {
|
||||
vec.extend(item.as_ref());
|
||||
vec.push(b'\0');
|
||||
}
|
||||
vec.into_boxed_slice()
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use core::{ptr, slice};
|
||||
use core::arch::global_asm;
|
||||
use core::{mem::size_of, ptr, slice};
|
||||
|
||||
use crate::platform::{sys::e, types::*};
|
||||
|
||||
@@ -49,62 +48,4 @@ pub unsafe extern "C" fn redox_physunmap(virtual_address: *mut c_void) -> c_int
|
||||
e(syscall::physunmap(virtual_address as usize)) as c_int
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn pte_clone_inner(stack: usize) -> usize;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
global_asm!("
|
||||
.globl pte_clone_inner
|
||||
.type pte_clone_inner, @function
|
||||
|
||||
pte_clone_inner:
|
||||
# Move the 1st argument `stack` of this function into the second argument to clone.
|
||||
mov rsi, rdi
|
||||
mov rax, {SYS_CLONE}
|
||||
mov rdi, {flags}
|
||||
|
||||
# Call clone syscall
|
||||
syscall
|
||||
|
||||
# Check if child or parent
|
||||
test rax, rax
|
||||
jnz 2f
|
||||
|
||||
# Load registers
|
||||
pop rax
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
|
||||
# Call entry point
|
||||
call rax
|
||||
|
||||
# Exit
|
||||
mov rax, 1
|
||||
xor rdi, rdi
|
||||
syscall
|
||||
|
||||
# Invalid instruction on failure to exit
|
||||
ud2
|
||||
|
||||
# Return PID if parent
|
||||
2:
|
||||
ret
|
||||
|
||||
.size pte_clone_inner, . - pte_clone_inner
|
||||
|
||||
",
|
||||
|
||||
flags = const(
|
||||
syscall::CLONE_VM.bits()
|
||||
| syscall::CLONE_FS.bits()
|
||||
| syscall::CLONE_FILES.bits()
|
||||
| syscall::CLONE_SIGHAND.bits()
|
||||
| syscall::CLONE_STACK.bits()
|
||||
),
|
||||
SYS_CLONE = const(syscall::SYS_CLONE),
|
||||
);
|
||||
pub use redox_exec::{create_set_addr_space_buf, FdGuard};
|
||||
|
||||
+19
-141
@@ -14,10 +14,11 @@ use crate::{
|
||||
dirent::dirent,
|
||||
errno::{EINVAL, EIO, ENOMEM, EPERM, ERANGE},
|
||||
fcntl,
|
||||
string::strlen,
|
||||
sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
|
||||
sys_random,
|
||||
sys_resource::{rlimit, RLIM_INFINITY},
|
||||
sys_stat::stat,
|
||||
sys_stat::{stat, S_ISGID, S_ISUID},
|
||||
sys_statvfs::statvfs,
|
||||
sys_time::{timeval, timezone},
|
||||
sys_utsname::{utsname, UTSLENGTH},
|
||||
@@ -33,7 +34,14 @@ use super::{errno, types::*, Pal, Read};
|
||||
static mut BRK_CUR: *mut c_void = ptr::null_mut();
|
||||
static mut BRK_END: *mut c_void = ptr::null_mut();
|
||||
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
fn round_up_to_page_size(val: usize) -> usize {
|
||||
(val + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE
|
||||
}
|
||||
|
||||
mod clone;
|
||||
mod epoll;
|
||||
mod exec;
|
||||
mod extra;
|
||||
mod ptrace;
|
||||
mod signal;
|
||||
@@ -205,140 +213,10 @@ impl Pal for Sys {
|
||||
|
||||
unsafe fn execve(
|
||||
path: &CStr,
|
||||
mut argv: *const *mut c_char,
|
||||
mut envp: *const *mut c_char,
|
||||
argv: *const *mut c_char,
|
||||
envp: *const *mut c_char,
|
||||
) -> c_int {
|
||||
let mut file = match File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
|
||||
Ok(file) => file,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let fd = *file as usize;
|
||||
|
||||
// Count arguments
|
||||
let mut len = 0;
|
||||
while !(*argv.offset(len)).is_null() {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let mut args: Vec<[usize; 2]> = Vec::with_capacity(len as usize);
|
||||
|
||||
// Read shebang (for example #!/bin/sh)
|
||||
let interpreter = {
|
||||
let mut reader = BufReader::new(&mut file);
|
||||
|
||||
let mut shebang = [0; 2];
|
||||
let mut read = 0;
|
||||
|
||||
while read < 2 {
|
||||
match reader.read(&mut shebang) {
|
||||
Ok(0) => break,
|
||||
Ok(i) => read += i,
|
||||
Err(_) => return -1,
|
||||
}
|
||||
}
|
||||
|
||||
if &shebang == b"#!" {
|
||||
// So, this file is interpreted.
|
||||
// That means the actual file descriptor passed to `fexec` won't be this file.
|
||||
// So we need to check ourselves that this file is actually be executable.
|
||||
|
||||
let mut stat = redox_stat::default();
|
||||
if e(syscall::fstat(fd, &mut stat)) == !0 {
|
||||
return -1;
|
||||
}
|
||||
let uid = e(syscall::getuid());
|
||||
if uid == !0 {
|
||||
return -1;
|
||||
}
|
||||
let gid = e(syscall::getuid());
|
||||
if gid == !0 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let mode = if uid == stat.st_uid as usize {
|
||||
(stat.st_mode >> 3 * 2) & 0o7
|
||||
} else if gid == stat.st_gid as usize {
|
||||
(stat.st_mode >> 3 * 1) & 0o7
|
||||
} else {
|
||||
stat.st_mode & 0o7
|
||||
};
|
||||
|
||||
if mode & 0o1 == 0o0 {
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Then, read the actual interpreter:
|
||||
let mut interpreter = Vec::new();
|
||||
match reader.read_until(b'\n', &mut interpreter) {
|
||||
Err(_) => return -1,
|
||||
Ok(_) => {
|
||||
if interpreter.ends_with(&[b'\n']) {
|
||||
interpreter.pop().unwrap();
|
||||
}
|
||||
// TODO: Returning the interpreter here is actually a
|
||||
// hack. Preferrably we should reassign `file =`
|
||||
// directly from here. Just wait until NLL comes
|
||||
// around...
|
||||
Some(interpreter)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut _interpreter_path = None;
|
||||
if let Some(interpreter) = interpreter {
|
||||
let cstring = match CString::new(interpreter) {
|
||||
Ok(cstring) => cstring,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
file = match File::open(&cstring, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
|
||||
Ok(file) => file,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
|
||||
// Make sure path is kept alive long enough, and push it to the arguments
|
||||
_interpreter_path = Some(cstring);
|
||||
let path_ref = _interpreter_path.as_ref().unwrap();
|
||||
args.push([path_ref.as_ptr() as usize, path_ref.to_bytes().len()]);
|
||||
} else {
|
||||
if file.seek(SeekFrom::Start(0)).is_err() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments
|
||||
while !(*argv).is_null() {
|
||||
let arg = *argv;
|
||||
|
||||
let mut len = 0;
|
||||
while *arg.offset(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
args.push([arg as usize, len as usize]);
|
||||
argv = argv.offset(1);
|
||||
}
|
||||
|
||||
// Environment variables
|
||||
let mut len = 0;
|
||||
while !(*envp.offset(len)).is_null() {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let mut envs: Vec<[usize; 2]> = Vec::with_capacity(len as usize);
|
||||
while !(*envp).is_null() {
|
||||
let env = *envp;
|
||||
|
||||
let mut len = 0;
|
||||
while *env.offset(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
envs.push([env as usize, len as usize]);
|
||||
envp = envp.offset(1);
|
||||
}
|
||||
|
||||
e(syscall::fexec(*file as usize, &args, &envs)) as c_int
|
||||
e(self::exec::execve(path, self::exec::ArgEnv::C { argv, envp }, None)) as c_int
|
||||
}
|
||||
|
||||
fn fchdir(fd: c_int) -> c_int {
|
||||
@@ -375,7 +253,7 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn fork() -> pid_t {
|
||||
e(unsafe { syscall::clone(syscall::CloneFlags::empty()) }) as pid_t
|
||||
e(clone::fork_impl()) as pid_t
|
||||
}
|
||||
|
||||
fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
|
||||
@@ -607,7 +485,7 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn getpagesize() -> usize {
|
||||
4096
|
||||
PAGE_SIZE
|
||||
}
|
||||
|
||||
fn getpgid(pid: pid_t) -> pid_t {
|
||||
@@ -754,7 +632,7 @@ impl Pal for Sys {
|
||||
) -> *mut c_void {
|
||||
let map = Map {
|
||||
offset: off as usize,
|
||||
size: len,
|
||||
size: round_up_to_page_size(len),
|
||||
flags: syscall::MapFlags::from_bits_truncate(
|
||||
((prot as usize) << 16) | ((flags as usize) & 0xFFFF),
|
||||
),
|
||||
@@ -771,7 +649,7 @@ impl Pal for Sys {
|
||||
unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> c_int {
|
||||
e(syscall::mprotect(
|
||||
addr as usize,
|
||||
len,
|
||||
round_up_to_page_size(len),
|
||||
syscall::MapFlags::from_bits((prot as usize) << 16)
|
||||
.expect("mprotect: invalid bit pattern"),
|
||||
)) as c_int
|
||||
@@ -783,7 +661,7 @@ impl Pal for Sys {
|
||||
/* TODO
|
||||
e(syscall::msync(
|
||||
addr as usize,
|
||||
len,
|
||||
round_up_to_page_size(len),
|
||||
flags
|
||||
)) as c_int
|
||||
*/
|
||||
@@ -800,7 +678,7 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
unsafe fn munmap(addr: *mut c_void, len: usize) -> c_int {
|
||||
if e(syscall::funmap(addr as usize, len)) == !0 {
|
||||
if e(syscall::funmap(addr as usize, round_up_to_page_size(len))) == !0 {
|
||||
return !0;
|
||||
}
|
||||
0
|
||||
@@ -858,7 +736,7 @@ impl Pal for Sys {
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe fn pte_clone(stack: *mut usize) -> pid_t {
|
||||
e(syscall::Error::demux(extra::pte_clone_inner(stack as usize))) as pid_t
|
||||
e(clone::pte_clone_impl(stack)) as pid_t
|
||||
}
|
||||
|
||||
fn read(fd: c_int, buf: &mut [u8]) -> ssize_t {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "redox-exec"
|
||||
authors = ["4lDO2 <4lDO2@protonmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
redox_syscall = "0.3"
|
||||
# TODO: Update
|
||||
goblin = { version = "0.0.21", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
||||
plain = "0.2"
|
||||
@@ -0,0 +1,412 @@
|
||||
#![no_std]
|
||||
|
||||
#![feature(array_chunks, map_first_last)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::mem::size_of;
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
vec,
|
||||
};
|
||||
|
||||
use syscall::{
|
||||
error::*,
|
||||
flag::{MapFlags, SEEK_SET},
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
|
||||
pub enum FexecResult {
|
||||
Normal { addrspace_handle: FdGuard },
|
||||
Interp { path: Box<[u8]>, image_file: FdGuard, open_via_dup: FdGuard, interp_override: InterpOverride },
|
||||
}
|
||||
pub struct InterpOverride {
|
||||
phs: Box<[u8]>,
|
||||
at_entry: usize,
|
||||
at_phnum: usize,
|
||||
at_phent: usize,
|
||||
name: Box<[u8]>,
|
||||
tree: BTreeMap<usize, usize>,
|
||||
}
|
||||
|
||||
pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
|
||||
where
|
||||
A: IntoIterator,
|
||||
E: IntoIterator,
|
||||
A::Item: AsRef<[u8]>,
|
||||
E::Item: AsRef<[u8]>,
|
||||
{
|
||||
use goblin::elf64::{header::Header, program_header::program_header64::{ProgramHeader, PT_LOAD, PT_INTERP, PF_W, PF_X}};
|
||||
|
||||
// Here, we do the minimum part of loading an application, which is what the kernel used to do.
|
||||
// We load the executable into memory (albeit at different offsets in this executable), fix
|
||||
// some misalignments, and then execute the SYS_EXEC syscall to replace the program memory
|
||||
// entirely.
|
||||
|
||||
let mut header_bytes = [0_u8; size_of::<Header>()];
|
||||
read_all(*image_file, Some(0), &mut header_bytes)?;
|
||||
let header = Header::from_bytes(&header_bytes);
|
||||
|
||||
let grants_fd = {
|
||||
let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?);
|
||||
FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?)
|
||||
};
|
||||
let memory_fd = FdGuard::new(syscall::dup(*grants_fd, b"mem")?);
|
||||
|
||||
// Never allow more than 1 MiB of program headers.
|
||||
const MAX_PH_SIZE: usize = 1024 * 1024;
|
||||
let phentsize = u64::from(header.e_phentsize) as usize;
|
||||
let phnum = u64::from(header.e_phnum) as usize;
|
||||
let pheaders_size = phentsize.saturating_mul(phnum).saturating_add(size_of::<Header>());
|
||||
|
||||
if pheaders_size > MAX_PH_SIZE {
|
||||
return Err(Error::new(E2BIG));
|
||||
}
|
||||
let mut phs_raw = vec! [0_u8; pheaders_size];
|
||||
phs_raw[..size_of::<Header>()].copy_from_slice(&header_bytes);
|
||||
let phs = &mut phs_raw[size_of::<Header>()..];
|
||||
|
||||
// TODO: Remove clone, but this would require more as_refs and as_muts
|
||||
let mut tree = interp_override.as_mut().map_or_else(|| {
|
||||
core::iter::once((0, PAGE_SIZE)).collect::<BTreeMap<_, _>>()
|
||||
}, |o| core::mem::take(&mut o.tree));
|
||||
|
||||
const BUFSZ: usize = 1024 * 256;
|
||||
let mut buf = vec! [0_u8; BUFSZ];
|
||||
|
||||
read_all(*image_file as usize, Some(header.e_phoff), phs).map_err(|_| Error::new(EIO))?;
|
||||
|
||||
for ph_idx in 0..phnum {
|
||||
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
|
||||
let segment: &ProgramHeader = plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
|
||||
let mut flags = syscall::PROT_READ;
|
||||
|
||||
// W ^ X. If it is executable, do not allow it to be writable, even if requested
|
||||
if segment.p_flags & PF_X == PF_X {
|
||||
flags |= syscall::PROT_EXEC;
|
||||
} else if segment.p_flags & PF_W == PF_W {
|
||||
flags |= syscall::PROT_WRITE;
|
||||
}
|
||||
|
||||
let voff = segment.p_vaddr as usize % PAGE_SIZE;
|
||||
let vaddr = segment.p_vaddr as usize - voff;
|
||||
let size =
|
||||
(segment.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
|
||||
|
||||
if segment.p_filesz > segment.p_memsz {
|
||||
return Err(Error::new(ENOEXEC));
|
||||
}
|
||||
#[forbid(unreachable_patterns)]
|
||||
match segment.p_type {
|
||||
// PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice.
|
||||
PT_INTERP => {
|
||||
let mut interp = vec! [0_u8; segment.p_filesz as usize];
|
||||
read_all(*image_file as usize, Some(segment.p_offset), &mut interp)?;
|
||||
|
||||
return Ok(FexecResult::Interp {
|
||||
path: interp.into_boxed_slice(),
|
||||
image_file,
|
||||
open_via_dup,
|
||||
interp_override: InterpOverride {
|
||||
at_entry: header.e_entry as usize,
|
||||
at_phnum: phnum,
|
||||
at_phent: phentsize,
|
||||
phs: phs_raw.into_boxed_slice(),
|
||||
name: path.into(),
|
||||
tree,
|
||||
}
|
||||
});
|
||||
}
|
||||
PT_LOAD => {
|
||||
allocate_remote(&grants_fd, memory_scheme_fd, vaddr, size, syscall::PROT_READ | syscall::PROT_WRITE)?;
|
||||
syscall::lseek(*image_file as usize, segment.p_offset as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
|
||||
syscall::lseek(*memory_fd, segment.p_vaddr as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
|
||||
|
||||
for size in core::iter::repeat(buf.len()).take((segment.p_filesz as usize) / buf.len()).chain(Some((segment.p_filesz as usize) % buf.len())) {
|
||||
read_all(*image_file as usize, None, &mut buf[..size]).map_err(|_| Error::new(EIO))?;
|
||||
let _ = syscall::write(*memory_fd, &buf[..size]).map_err(|_| Error::new(EIO))?;
|
||||
}
|
||||
mprotect_remote(&grants_fd, vaddr, size, flags)?;
|
||||
|
||||
if !tree.range(..=vaddr).next_back().filter(|(start, size)| **start + **size > vaddr).is_some() {
|
||||
tree.insert(vaddr, size);
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
||||
const STACK_TOP: usize = 1 << 47;
|
||||
const STACK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
allocate_remote(&grants_fd, memory_scheme_fd, STACK_TOP - STACK_SIZE, STACK_SIZE, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
|
||||
tree.insert(STACK_TOP - STACK_SIZE, STACK_SIZE);
|
||||
|
||||
let mut sp = STACK_TOP - 256;
|
||||
|
||||
let mut push = |word: usize| {
|
||||
sp -= size_of::<usize>();
|
||||
write_all(*memory_fd, Some(sp as u64), &usize::to_ne_bytes(word))
|
||||
};
|
||||
|
||||
let pheaders_to_convey = if let Some(ref r#override) = interp_override {
|
||||
&*r#override.phs
|
||||
} else {
|
||||
&*phs_raw
|
||||
};
|
||||
let pheaders_size_aligned = (pheaders_to_convey.len()+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
|
||||
let pheaders = find_free_target_addr(&tree, pheaders_size_aligned).ok_or(Error::new(ENOMEM))?;
|
||||
tree.insert(pheaders, pheaders_size_aligned);
|
||||
allocate_remote(&grants_fd, memory_scheme_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
|
||||
write_all(*memory_fd, Some(pheaders as u64), &pheaders_to_convey)?;
|
||||
mprotect_remote(&grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?;
|
||||
|
||||
push(0)?;
|
||||
push(AT_NULL)?;
|
||||
push(header.e_entry as usize)?;
|
||||
if let Some(ref r#override) = interp_override {
|
||||
push(AT_BASE)?;
|
||||
push(r#override.at_entry)?;
|
||||
}
|
||||
push(AT_ENTRY)?;
|
||||
push(pheaders + size_of::<Header>())?;
|
||||
push(AT_PHDR)?;
|
||||
push(interp_override.as_ref().map_or(header.e_phnum as usize, |o| o.at_phnum))?;
|
||||
push(AT_PHNUM)?;
|
||||
push(interp_override.as_ref().map_or(header.e_phentsize as usize, |o| o.at_phent))?;
|
||||
push(AT_PHENT)?;
|
||||
|
||||
let args_envs_size_aligned = (total_args_envs_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
|
||||
let target_args_env_address = find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?;
|
||||
allocate_remote(&grants_fd, memory_scheme_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
|
||||
tree.insert(target_args_env_address, args_envs_size_aligned);
|
||||
|
||||
let mut offset = 0;
|
||||
|
||||
let mut argc = 0;
|
||||
|
||||
{
|
||||
let mut append = |source_slice: &[u8]| {
|
||||
let address = target_args_env_address + offset;
|
||||
write_all(*memory_fd, Some(address as u64), source_slice)?;
|
||||
offset += source_slice.len() + 1;
|
||||
Ok(address)
|
||||
};
|
||||
|
||||
push(0)?;
|
||||
|
||||
for env in envs {
|
||||
push(append(env.as_ref())?)?;
|
||||
}
|
||||
|
||||
push(0)?;
|
||||
|
||||
for arg in args {
|
||||
push(append(arg.as_ref())?)?;
|
||||
argc += 1;
|
||||
}
|
||||
}
|
||||
|
||||
push(argc)?;
|
||||
|
||||
unsafe { deactivate_tcb(*open_via_dup)?; }
|
||||
|
||||
{
|
||||
let current_sigaction_fd = FdGuard::new(syscall::dup(*open_via_dup, b"sigactions")?);
|
||||
let empty_sigaction_fd = FdGuard::new(syscall::dup(*current_sigaction_fd, b"empty")?);
|
||||
let sigaction_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-sigactions")?);
|
||||
|
||||
let _ = syscall::write(*sigaction_selection_fd, &usize::to_ne_bytes(*empty_sigaction_fd))?;
|
||||
}
|
||||
|
||||
// TODO: Restore old name if exec failed?
|
||||
if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) {
|
||||
let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name));
|
||||
}
|
||||
if interp_override.is_some() {
|
||||
let mmap_min_fd = FdGuard::new(syscall::dup(*grants_fd, b"mmap-min-addr")?);
|
||||
let last_addr = tree.iter().rev().nth(1).map_or(0, |(off, len)| *off + *len);
|
||||
let aligned_last_addr = (last_addr + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
|
||||
let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr));
|
||||
}
|
||||
|
||||
let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?);
|
||||
|
||||
let _ = syscall::write(*addrspace_selection_fd, &create_set_addr_space_buf(*grants_fd, header.e_entry as usize, sp));
|
||||
|
||||
Ok(FexecResult::Normal { addrspace_handle: addrspace_selection_fd })
|
||||
}
|
||||
fn write_usizes<const N: usize>(fd: &FdGuard, usizes: [usize; N]) -> Result<()> {
|
||||
let _ = syscall::write(**fd, unsafe { plain::as_bytes(&usizes) });
|
||||
Ok(())
|
||||
}
|
||||
fn allocate_remote(addrspace_fd: &FdGuard, memory_scheme_fd: &FdGuard, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
|
||||
mmap_remote(addrspace_fd, memory_scheme_fd, 0, dst_addr, len, flags)
|
||||
}
|
||||
pub fn mmap_remote(addrspace_fd: &FdGuard, fd: &FdGuard, offset: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
|
||||
write_usizes(addrspace_fd, [
|
||||
// op
|
||||
syscall::flag::ADDRSPACE_OP_MMAP,
|
||||
// fd
|
||||
**fd,
|
||||
// "offset"
|
||||
offset,
|
||||
// address
|
||||
dst_addr,
|
||||
// size
|
||||
len,
|
||||
// flags
|
||||
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
|
||||
])
|
||||
}
|
||||
pub fn mprotect_remote(addrspace_fd: &FdGuard, addr: usize, len: usize, flags: MapFlags) -> Result<()> {
|
||||
write_usizes(addrspace_fd, [
|
||||
// op
|
||||
syscall::flag::ADDRSPACE_OP_MPROTECT,
|
||||
// address
|
||||
addr,
|
||||
// size
|
||||
len,
|
||||
// flags
|
||||
flags.bits(),
|
||||
])
|
||||
}
|
||||
pub fn munmap_remote(addrspace_fd: &FdGuard, addr: usize, len: usize) -> Result<()> {
|
||||
write_usizes(addrspace_fd, [
|
||||
// op
|
||||
syscall::flag::ADDRSPACE_OP_MUNMAP,
|
||||
// address
|
||||
addr,
|
||||
// size
|
||||
len,
|
||||
])
|
||||
}
|
||||
pub fn munmap_transfer(src: &FdGuard, dst: &FdGuard, src_addr: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
|
||||
write_usizes(dst, [
|
||||
// op
|
||||
syscall::flag::ADDRSPACE_OP_TRANSFER,
|
||||
// fd
|
||||
**src,
|
||||
// "offset" (source address)
|
||||
src_addr,
|
||||
// address
|
||||
dst_addr,
|
||||
// size
|
||||
len,
|
||||
// flags
|
||||
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
|
||||
])
|
||||
}
|
||||
fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> {
|
||||
if let Some(offset) = offset {
|
||||
syscall::lseek(fd, offset as isize, SEEK_SET)?;
|
||||
}
|
||||
|
||||
let mut total_bytes_read = 0;
|
||||
|
||||
while total_bytes_read < buf.len() {
|
||||
total_bytes_read += match syscall::read(fd, &mut buf[total_bytes_read..])? {
|
||||
0 => return Err(Error::new(ENOEXEC)),
|
||||
bytes_read => bytes_read,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn write_all(fd: usize, offset: Option<u64>, buf: &[u8]) -> Result<()> {
|
||||
if let Some(offset) = offset {
|
||||
syscall::lseek(fd, offset as isize, SEEK_SET)?;
|
||||
}
|
||||
|
||||
let mut total_bytes_written = 0;
|
||||
|
||||
while total_bytes_written < buf.len() {
|
||||
total_bytes_written += match syscall::write(fd, &buf[total_bytes_written..])? {
|
||||
0 => return Err(Error::new(EIO)),
|
||||
bytes_written => bytes_written,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: With the introduction of remote mmaps, remove this and let the kernel handle address
|
||||
// allocation.
|
||||
fn find_free_target_addr(tree: &BTreeMap<usize, usize>, size: usize) -> Option<usize> {
|
||||
let mut iterator = tree.iter().peekable();
|
||||
|
||||
// Ignore the space between zero and the first region, to avoid null pointers.
|
||||
while let Some((cur_address, entry_size)) = iterator.next() {
|
||||
let end = *cur_address + entry_size;
|
||||
|
||||
if let Some((next_address, _)) = iterator.peek() {
|
||||
if **next_address - end > size {
|
||||
return Some(end);
|
||||
}
|
||||
}
|
||||
// No need to check last entry, since the stack will always be put at the highest
|
||||
// possible address.
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
||||
/// is already initialized as if it was a thread.
|
||||
#[cfg(all(target_os = "redox", target_arch = "x86_64"))]
|
||||
pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> {
|
||||
let mut env = syscall::EnvRegisters::default();
|
||||
|
||||
let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?);
|
||||
|
||||
env.fsbase = 0;
|
||||
env.gsbase = 0;
|
||||
|
||||
let _ = syscall::write(*file, &mut env)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct FdGuard {
|
||||
fd: usize,
|
||||
taken: bool,
|
||||
}
|
||||
impl FdGuard {
|
||||
pub fn new(fd: usize) -> Self {
|
||||
Self {
|
||||
fd, taken: false,
|
||||
}
|
||||
}
|
||||
pub fn take(&mut self) -> usize {
|
||||
self.taken = true;
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
impl core::ops::Deref for FdGuard {
|
||||
type Target = usize;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FdGuard {
|
||||
fn drop(&mut self) {
|
||||
if !self.taken {
|
||||
let _ = syscall::close(self.fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn create_set_addr_space_buf(space: usize, ip: usize, sp: usize) -> [u8; size_of::<usize>() * 3] {
|
||||
let mut buf = [0_u8; 3 * size_of::<usize>()];
|
||||
let mut chunks = buf.array_chunks_mut::<{size_of::<usize>()}>();
|
||||
*chunks.next().unwrap() = usize::to_ne_bytes(space);
|
||||
*chunks.next().unwrap() = usize::to_ne_bytes(sp);
|
||||
*chunks.next().unwrap() = usize::to_ne_bytes(ip);
|
||||
buf
|
||||
}
|
||||
|
||||
#[path = "../../../auxv_defs.rs"]
|
||||
pub mod auxv_defs;
|
||||
|
||||
use auxv_defs::*;
|
||||
+39
-8
@@ -67,7 +67,16 @@ static INIT_ARRAY: [extern "C" fn(); 1] = [init_array];
|
||||
|
||||
static mut init_complete: bool = false;
|
||||
|
||||
#[used]
|
||||
#[no_mangle]
|
||||
static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut();
|
||||
|
||||
fn alloc_init() {
|
||||
unsafe {
|
||||
if init_complete {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
if let Some(tcb) = ld_so::tcb::Tcb::current() {
|
||||
if tcb.mspace != 0 {
|
||||
@@ -96,6 +105,12 @@ extern "C" fn init_array() {
|
||||
alloc_init();
|
||||
io_init();
|
||||
|
||||
unsafe {
|
||||
if platform::environ.is_null() {
|
||||
platform::environ = __relibc_init_environ;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn pthread_init();
|
||||
}
|
||||
@@ -112,6 +127,15 @@ fn io_init() {
|
||||
stdio::stderr = stdio::default_stderr.get();
|
||||
}
|
||||
}
|
||||
fn setup_sigstack() {
|
||||
use syscall::{Map, MapFlags};
|
||||
const SIGSTACK_SIZE: usize = 1024 * 256;
|
||||
let sigstack = unsafe { syscall::fmap(!0, &Map { address: 0, offset: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: SIGSTACK_SIZE }) }.expect("failed to allocate sigstack") + SIGSTACK_SIZE;
|
||||
|
||||
let fd = syscall::open("thisproc:current/sigstack", syscall::O_WRONLY | syscall::O_CLOEXEC).expect("failed to open thisproc:current/sigstack");
|
||||
syscall::write(fd, &usize::to_ne_bytes(sigstack)).expect("failed to write to thisproc:current/sigstack");
|
||||
let _ = syscall::close(fd);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[no_mangle]
|
||||
@@ -146,15 +170,22 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
|
||||
platform::program_invocation_name = *arg;
|
||||
platform::program_invocation_short_name = libgen::basename(*arg);
|
||||
}
|
||||
|
||||
// Set up envp
|
||||
let envp = sp.envp();
|
||||
let mut len = 0;
|
||||
while !(*envp.add(len)).is_null() {
|
||||
len += 1;
|
||||
// We check for NULL here since ld.so might already have initialized it for us, and we don't
|
||||
// want to overwrite it if constructors in .init_array of dependency libraries have called
|
||||
// setenv.
|
||||
if platform::environ.is_null() {
|
||||
// Set up envp
|
||||
let envp = sp.envp();
|
||||
let mut len = 0;
|
||||
while !(*envp.add(len)).is_null() {
|
||||
len += 1;
|
||||
}
|
||||
platform::OUR_ENVIRON = copy_string_array(envp, len);
|
||||
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
|
||||
}
|
||||
platform::inner_environ = copy_string_array(envp, len);
|
||||
platform::environ = platform::inner_environ.as_mut_ptr();
|
||||
|
||||
// Setup signal stack, otherwise we cannot handle any signals besides SIG_IGN/SIG_DFL behavior.
|
||||
setup_sigstack();
|
||||
|
||||
init_array();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user