* silverfish is a submodule now * memory modules from silverfish runtime is copied in to the aWsm runtime. * runtime/tests/Makefile compiles different tests and silverfish/code_benches..master
parent
b2d0ecc66f
commit
9b0ec6f7dc
@ -0,0 +1,7 @@
|
||||
[submodule "silverfish"]
|
||||
path = silverfish
|
||||
url = https://github.com/gwsystems/silverfish.git
|
||||
ignore = dirty
|
||||
[submodule "runtime/jsmn"]
|
||||
path = runtime/jsmn
|
||||
url = https://github.com/zserge/jsmn.git
|
@ -0,0 +1,74 @@
|
||||
# Inspired by lucet's Dockerfile.
|
||||
|
||||
# using ubuntu 18 docker image
|
||||
FROM ubuntu:bionic
|
||||
|
||||
# install some basic packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
curl \
|
||||
git \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
cmake \
|
||||
ca-certificates \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
gcc \
|
||||
g++ \
|
||||
clang-6.0 \
|
||||
clang-tools-6.0 \
|
||||
llvm-6.0 \
|
||||
llvm-6.0-dev \
|
||||
libc++-dev \
|
||||
libc++abi-dev \
|
||||
lld-6.0 \
|
||||
lldb-6.0 \
|
||||
libclang-6.0-dev \
|
||||
libclang-common-6.0-dev \
|
||||
vim \
|
||||
apache2 \
|
||||
subversion \
|
||||
libapache2-mod-svn \
|
||||
libsvn-dev \
|
||||
binutils-dev \
|
||||
build-essential \
|
||||
automake \
|
||||
libtool \
|
||||
strace \
|
||||
less \
|
||||
libuv1-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Enable apache2 for svn
|
||||
RUN a2enmod dav
|
||||
RUN a2enmod dav_svn
|
||||
RUN service apache2 restart
|
||||
RUN pip3 install numpy
|
||||
|
||||
# set to use our installed clang version
|
||||
RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-6.0 100
|
||||
RUN update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-6.0 100
|
||||
|
||||
# set LD_LIBRARY_PATH
|
||||
ENV LD_LIBRARY_PATH=/usr/local/lib
|
||||
|
||||
# install rust - commands copied straight from lucet's dockerfile.
|
||||
# so we have exactly the same rust version as lucet!
|
||||
RUN curl -sS -L -O https://static.rust-lang.org/dist/rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz \
|
||||
&& tar xzf rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz \
|
||||
&& cd rust-1.35.0-x86_64-unknown-linux-gnu \
|
||||
&& ./install.sh \
|
||||
&& cd .. \
|
||||
&& rm -rf rust-1.35.0-x86_64-unknown-linux-gnu rust-1.35.0-x86_64-unknown-linux-gnu.tar.gz
|
||||
ENV PATH=/usr/local/bin:$PATH
|
||||
RUN cargo install --root /usr/local cargo-audit cargo-watch
|
||||
|
||||
## copied again from lucet for when we want to use wasi-sdk
|
||||
#RUN curl -sS -L -O https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-5/wasi-sdk_5.0_amd64.deb \
|
||||
# && dpkg -i wasi-sdk_5.0_amd64.deb && rm -f wasi-sdk_5.0_amd64.deb
|
||||
#
|
||||
#ENV WASI_SDK=/opt/wasi-sdk
|
||||
ENV PATH=/opt/awsm/bin:$PATH
|
||||
|
@ -0,0 +1,38 @@
|
||||
SFCC='silverfish'
|
||||
ROOT=${ROOT:-$(cd "$(dirname ${BASH_SOURCE:-$0})" && pwd)}
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
@echo "Building wasmception toolchain, takes a while."
|
||||
@cd ${SFCC} && make -C wasmception && cd ${ROOT}
|
||||
@echo "Building silverfish compiler (release)"
|
||||
@cd ${SFCC} && cargo build --release && cd ${ROOT}
|
||||
|
||||
.PHONY: build-dev
|
||||
build-dev:
|
||||
@echo "Building wasmception toolchain, takes a while."
|
||||
@cd ${SFCC} && make -C wasmception && cd ${ROOT}
|
||||
@echo "Building silverfish compiler (default==debug)"
|
||||
@cd ${SFCC} && cargo build && cd ${ROOT}
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo "Cleaning silverfish compiler"
|
||||
@cd ${SFCC} && cargo clean && cd ${ROOT}
|
||||
|
||||
# wasmception is too slow to recompile,
|
||||
# so lets not make that part of the "silverfish" cleanup
|
||||
.PHONY: wclean
|
||||
wclean:
|
||||
@echo "Cleaning wasmception toolchain"
|
||||
@cd ${SFCC} && make -C wasmception clean && cd ${ROOT}
|
||||
|
||||
.PHONY: runtime
|
||||
runtime:
|
||||
@echo "Building runtime!"
|
||||
make -C runtime
|
||||
|
||||
.PHONY: install
|
||||
install: build
|
||||
@./install.sh wasmception
|
||||
|
@ -0,0 +1,144 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# This environment file and dockerfile are
|
||||
# inspired by lucet's devenv_xxx.sh scripts
|
||||
#
|
||||
HOST_ROOT=${HOST_ROOT:-$(cd "$(dirname ${BASH_SOURCE:-$0})" && pwd)}
|
||||
SYS_NAME='awsm'
|
||||
HOST_SYS_MOUNT=${HOST_SYS_MOUNT:-"/${SYS_NAME}"}
|
||||
SYS_WASMCEPTION='silverfish/wasmception'
|
||||
SYS_DOC_NAME=${SYS_NAME}
|
||||
SYS_DOC_DEVNAME=${SYS_DOC_NAME}'-dev'
|
||||
SYS_DOC_TAG='latest'
|
||||
SYS_DOC_NAMETAG=${SYS_DOC_NAME}:${SYS_DOC_TAG}
|
||||
SYS_DOC_DEVNAMETAG=${SYS_DOC_DEVNAME}:${SYS_DOC_TAG}
|
||||
SYS_BUILD_TIMEOUT=10
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage $0 <setup/run/stop>"
|
||||
}
|
||||
|
||||
countdown()
|
||||
{
|
||||
tmp_cnt=$1
|
||||
while [ ${tmp_cnt} -gt 0 ]; do
|
||||
printf "${tmp_cnt}."
|
||||
# sleep 1
|
||||
tmp_cnt=$((tmp_cnt - 1))
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
envsetup()
|
||||
{
|
||||
if docker image inspect ${SYS_DOC_NAMETAG} > /dev/null; then
|
||||
echo "${SYS_DOC_NAMETAG} image exists, remove it first!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Setting up ${SYS_NAME}"
|
||||
git submodule update --init --recursive 2>/dev/null ||:
|
||||
|
||||
# Perhaps change in the wasmception (forked) repo,
|
||||
# Gregor already forked every mainline repo to modify something or the other!
|
||||
#
|
||||
# That said, you may comment this if you're not behind a firewall!
|
||||
# http:// doesn't work for me at my current work place.
|
||||
echo "Changing http:// to https:// in ${SYS_WASMCEPTION}"
|
||||
sed -i 's/http:\/\//https:\/\//' ${SYS_WASMCEPTION}/Makefile
|
||||
|
||||
if docker image inspect ${SYS_DOC_DEVNAMETAG} >> /dev/null; then
|
||||
echo "${SYS_DOC_DEVNAME} image exists, rebuilding it"
|
||||
echo "(you have ${SYS_BUILD_TIMEOUT}secs to stop the rebuild)"
|
||||
countdown ${SYS_BUILD_TIMEOUT}
|
||||
fi
|
||||
|
||||
echo "Building ${SYS_DOC_DEVNAMETAG}"
|
||||
docker build -t ${SYS_DOC_DEVNAMETAG} .
|
||||
|
||||
echo "Creating ${SYS_DOC_NAMETAG} on top of ${SYS_DOC_DEVNAMETAG}"
|
||||
docker run --privileged --name=${SYS_DOC_DEVNAME} --detach --mount type=bind,src="$(cd $(dirname ${0}); pwd -P),target=/${SYS_NAME}" \
|
||||
${SYS_DOC_DEVNAMETAG} /bin/sleep 99999999 > /dev/null
|
||||
|
||||
echo "Building ${SYS_NAME}"
|
||||
docker exec -t -w ${HOST_SYS_MOUNT} ${SYS_DOC_DEVNAME} make install
|
||||
|
||||
echo "Tagging the new image"
|
||||
docker container commit ${SYS_DOC_DEVNAME} ${SYS_DOC_NAMETAG}
|
||||
|
||||
echo "Cleaning up ${SYS_DOC_DEVNAME}"
|
||||
docker kill ${SYS_DOC_DEVNAME}
|
||||
docker rm ${SYS_DOC_DEVNAME}
|
||||
|
||||
echo "Done!"
|
||||
}
|
||||
|
||||
envrun()
|
||||
{
|
||||
if ! docker image inspect ${SYS_DOC_NAMETAG} > /dev/null; then
|
||||
envbuild
|
||||
fi
|
||||
|
||||
if docker ps -f name=${SYS_DOC_NAME} --format '{{.Names}}' | grep -q "^${SYS_DOC_NAME}" ; then
|
||||
echo "Container is running" >&2
|
||||
else
|
||||
echo "Starting ${SYS_DOC_NAME}"
|
||||
docker run --privileged --security-opt seccomp:unconfined --name=${SYS_DOC_NAME} --detach --mount type=bind,src="$(cd $(dirname ${0}); pwd -P),target=/${SYS_NAME}" \
|
||||
${SYS_DOC_NAMETAG} /bin/sleep 99999999 > /dev/null
|
||||
fi
|
||||
|
||||
echo "Running shell"
|
||||
docker exec -t -i -w "${HOST_SYS_MOUNT}" ${SYS_DOC_NAME} /bin/bash
|
||||
}
|
||||
|
||||
envstop()
|
||||
{
|
||||
echo "Stopping container"
|
||||
docker stop ${SYS_DOC_NAME}
|
||||
echo "Removing container"
|
||||
docker rm ${SYS_DOC_NAME}
|
||||
}
|
||||
|
||||
envrm()
|
||||
{
|
||||
envstop
|
||||
docker rmi ${SYS_DOC_NAME}
|
||||
}
|
||||
|
||||
envrma()
|
||||
{
|
||||
envrm
|
||||
docker rmi ${SYS_DOC_DEVNAME}
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage $0
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
run)
|
||||
envrun
|
||||
;;
|
||||
stop)
|
||||
envstop
|
||||
;;
|
||||
setup)
|
||||
envsetup
|
||||
;;
|
||||
rm)
|
||||
envrm
|
||||
;;
|
||||
rma)
|
||||
envrma
|
||||
;;
|
||||
*)
|
||||
echo "invalid option: $1"
|
||||
usage $0
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo
|
||||
echo "done!"
|
@ -0,0 +1,77 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Inspired by Lucet's install.sh
|
||||
|
||||
echo "Setting up toolchain environment"
|
||||
SYS_SRC_PREFIX=${SYS_SRC_PREFIX:-"$(
|
||||
cd $(dirname $(dirname ${0}))
|
||||
pwd -P
|
||||
)"}
|
||||
|
||||
if [ ! -x "${SYS_SRC_PREFIX}/install.sh" ]; then
|
||||
echo "Unable to find the install script" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SYS_NAME='awsm'
|
||||
SILVERFISH='silverfish'
|
||||
SYS_PREFIX=${SYS_PREFIX:-"/opt/${SYS_NAME}"}
|
||||
SYS_SRC_PREFIX=${SYS_SRC_PREFIX:-"/${SYS_NAME}"}
|
||||
SYS_SF_REL_DIR=${SYS_SF_REL_DIR:-"${SYS_SRC_PREFIX}/${SILVERFISH}/target/release"}
|
||||
SYS_BIN_DIR=${SYS_BIN_DIR:-"${SYS_PREFIX}/bin"}
|
||||
SYS_LIB_DIR=${SYS_LIB_DIR:-"${SYS_PREFIX}/lib"}
|
||||
|
||||
#use wasmception
|
||||
if [ $# -eq 0 ] || [ "$1" = "wasmception" ]; then
|
||||
echo "Setting up for wasmception"
|
||||
WASM_PREFIX=${WASM_PREFIX:-"${SYS_SRC_PREFIX}/${SILVERFISH}/wasmception"}
|
||||
WASM_BIN=${WASM_BIN:-"${WASM_PREFIX}/dist/bin"}
|
||||
WASM_SYSROOT=${WASM_SYSROOT:-"${WASM_PREFIX}/sysroot"}
|
||||
WASM_TARGET=${WASM_TARGET:-"wasm32-unknown-unknown-wasm"}
|
||||
WASM_BIN_PREFIX=${WASM_BIN_PREFIX:-"$WASM_TARGET"}
|
||||
WASM_TOOLS=ar
|
||||
elif [ "$1" = "wasi" ]; then
|
||||
echo "Setting up for wasi-sdk"
|
||||
#use wasi-sdk
|
||||
WASM_PREFIX=${WASM_PREFIX:-${WASM_SDK:-"/opt/wasi-sdk"}}
|
||||
WASM_BIN=${WASM_BIN:-"${WASM_PREFIX}/bin"}
|
||||
WASM_SYSROOT=${WASM_SYSROOT:-"${WASM_PREFIX}/share/sysroot"}
|
||||
WASM_TARGET=${WASM_TARGET:-"wasm32-wasi"}
|
||||
WASM_BIN_PREFIX=${WASM_BIN_PREFIX:-"$WASM_TARGET"}
|
||||
WASM_TOOLS=ar dwarfdump nm ranlib size
|
||||
fi
|
||||
|
||||
# silverfish compiler binary!
|
||||
BINS=${SILVERFISH}
|
||||
DEVSRC=${SYS_BIN_DIR}/'devenv_src.sh'
|
||||
|
||||
rm -f ${SYS_BIN_DIR}/*
|
||||
install -d -v "$SYS_BIN_DIR" || exit 1
|
||||
for bin in $BINS; do
|
||||
ln -sfv "${SYS_SF_REL_DIR}/${bin}" "${SYS_BIN_DIR}/${bin}"
|
||||
done
|
||||
|
||||
for file in clang clang++; do
|
||||
wrapper_file="$(mktemp)"
|
||||
cat >"$wrapper_file" <<EOT
|
||||
#! /bin/sh
|
||||
|
||||
exec "${WASM_BIN}/${file}" --target="$WASM_TARGET" --sysroot="$WASM_SYSROOT" "\$@"
|
||||
EOT
|
||||
install -p -v "$wrapper_file" "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-${file}"
|
||||
rm -f "$wrapper_file"
|
||||
done
|
||||
|
||||
#for file in ar dwarfdump nm ranlib size; do
|
||||
for file in ${WASM_TOOLS}; do
|
||||
ln -sfv "${WASM_BIN}/llvm-${file}" "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-${file}"
|
||||
done
|
||||
|
||||
for file in ld; do
|
||||
ln -sfv "${WASM_BIN}/wasm-${file}" "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-${file}"
|
||||
done
|
||||
|
||||
ln -svf "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-clang" "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-gcc"
|
||||
ln -svf "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-clang++" "${SYS_BIN_DIR}/${WASM_BIN_PREFIX}-g++"
|
||||
|
||||
echo "Done!"
|
@ -0,0 +1,55 @@
|
||||
CC=clang
|
||||
|
||||
RTDIR=src/
|
||||
CFILES=${RTDIR}/*.c ${RTDIR}/libc/*.c ${RTDIR}/memory/common.c
|
||||
RUNTIME=bin/awsmrt
|
||||
INC=include/
|
||||
|
||||
OPTFLAGS = -O3
|
||||
OPTFLAGS += -flto -g
|
||||
LDFLAGS = -Wl,--export-dynamic -ldl -lm
|
||||
USE_MEM = USE_MEM_VM
|
||||
CFLAGS = ${OPTFLAGS} -D${USE_MEM} -I${INC} -pthread
|
||||
CFLAGS += -DX86_64
|
||||
CFLAGS += -DDEBUG
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
#CFLAGS += -DNOSTDIO
|
||||
CFLAGS += -DUSE_UVIO
|
||||
#CFLAGS += -DUSE_SYSCALL
|
||||
#CFLAGS += -DPREEMPT_DISABLE
|
||||
|
||||
MAKE= make --no-print-directory
|
||||
|
||||
JSMN=jsmn
|
||||
JSMNINC=-I${JSMN}
|
||||
JSMNCFLAGS=${JSMNINC} -DJSMN_STATIC -DJSMN_STRICT
|
||||
|
||||
ifeq ($(USE_MEM),USE_MEM_GENERIC)
|
||||
CFILES += ${RTDIR}/memory/generic.c
|
||||
else ifeq ($(USE_MEM),USE_MEM_VM)
|
||||
CFILES += ${RTDIR}/memory/64bit_nix.c
|
||||
endif
|
||||
|
||||
all: clean runtime tools
|
||||
|
||||
runtime:
|
||||
@echo "Compiling runtime"
|
||||
@mkdir -p bin/
|
||||
@${CC} ${CFLAGS} ${LDFLAGS} ${CFILES} ${JSMNCFLAGS} -L/usr/lib/ -luv $^ -o ${RUNTIME}
|
||||
|
||||
tools:
|
||||
# @echo "Compiling tools"
|
||||
@${MAKE} -C tools
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up runtime"
|
||||
@rm -f ${RUNTIME}
|
||||
# @echo "Cleaning up tools"
|
||||
@${MAKE} -C tools clean
|
||||
|
||||
fetch:
|
||||
@git submodule update --init --recursive
|
||||
|
||||
init: fetch clean runtime tools
|
||||
|
||||
.PHONY: all init clean fetch libuv runtime tools
|
@ -0,0 +1,199 @@
|
||||
/* code from https://github.com/gwsystems/silverfish/blob/master/runtime/runtime.c */
|
||||
#include <assert.h>
|
||||
#include <types.h>
|
||||
|
||||
// TODO: Throughout here we use `assert` for error conditions, which isn't optimal
|
||||
// Instead we should use `unlikely` branches to a single trapping function (which should optimize better)
|
||||
// The below functions are for implementing WASM instructions
|
||||
|
||||
// ROTL and ROTR helper functions
|
||||
INLINE u32
|
||||
rotl_u32(u32 n, u32 c_u32)
|
||||
{
|
||||
// WASM requires a modulus here (usually a single bitwise op, but it means we need no assert)
|
||||
unsigned int c = c_u32 % (CHAR_BIT * sizeof(n));
|
||||
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2.
|
||||
|
||||
c &= mask;
|
||||
return (n << c) | (n >> ((-c) & mask));
|
||||
}
|
||||
|
||||
INLINE u32
|
||||
rotr_u32(u32 n, u32 c_u32)
|
||||
{
|
||||
// WASM requires a modulus here (usually a single bitwise op, but it means we need no assert)
|
||||
unsigned int c = c_u32 % (CHAR_BIT * sizeof(n));
|
||||
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1);
|
||||
|
||||
c &= mask;
|
||||
return (n>>c) | (n << ((-c) & mask));
|
||||
}
|
||||
|
||||
INLINE u64
|
||||
rotl_u64(u64 n, u64 c_u64)
|
||||
{
|
||||
// WASM requires a modulus here (usually a single bitwise op, but it means we need no assert)
|
||||
unsigned int c = c_u64 % (CHAR_BIT * sizeof(n));
|
||||
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2.
|
||||
|
||||
c &= mask;
|
||||
return (n << c) | (n >> ((-c) & mask));
|
||||
}
|
||||
|
||||
INLINE u64
|
||||
rotr_u64(u64 n, u64 c_u64)
|
||||
{
|
||||
// WASM requires a modulus here (usually a single bitwise op, but it means we need no assert)
|
||||
unsigned int c = c_u64 % (CHAR_BIT * sizeof(n));
|
||||
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1);
|
||||
|
||||
c &= mask;
|
||||
return (n >> c) | (n << ((-c) & mask));
|
||||
}
|
||||
|
||||
// Now safe division and remainder
|
||||
INLINE u32
|
||||
u32_div(u32 a, u32 b)
|
||||
{
|
||||
assert(b);
|
||||
return a / b;
|
||||
}
|
||||
|
||||
INLINE u32
|
||||
u32_rem(u32 a, u32 b)
|
||||
{
|
||||
assert(b);
|
||||
return a % b;
|
||||
}
|
||||
|
||||
INLINE i32
|
||||
i32_div(i32 a, i32 b)
|
||||
{
|
||||
assert(b && (a != INT32_MIN || b != -1));
|
||||
return a / b;
|
||||
}
|
||||
|
||||
INLINE i32
|
||||
i32_rem(i32 a, i32 b)
|
||||
{
|
||||
assert(b && (a != INT32_MIN || b != -1));
|
||||
return a % b;
|
||||
}
|
||||
|
||||
INLINE u64
|
||||
u64_div(u64 a, u64 b)
|
||||
{
|
||||
assert(b);
|
||||
return a / b;
|
||||
}
|
||||
|
||||
INLINE u64
|
||||
u64_rem(u64 a, u64 b)
|
||||
{
|
||||
assert(b);
|
||||
return a % b;
|
||||
}
|
||||
|
||||
INLINE i64
|
||||
i64_div(i64 a, i64 b)
|
||||
{
|
||||
assert(b && (a != INT64_MIN || b != -1));
|
||||
return a / b;
|
||||
}
|
||||
|
||||
INLINE i64
|
||||
i64_rem(i64 a, i64 b)
|
||||
{
|
||||
assert(b && (a != INT64_MIN || b != -1));
|
||||
return a % b;
|
||||
}
|
||||
|
||||
// float to integer conversion methods
|
||||
// In C, float => int conversions always truncate
|
||||
// If a int2float(int::min_value) <= float <= int2float(int::max_value), it must always be safe to truncate it
|
||||
u32
|
||||
u32_trunc_f32(float f)
|
||||
{
|
||||
assert(0 <= f && f <= UINT32_MAX);
|
||||
return (u32) f;
|
||||
}
|
||||
|
||||
i32
|
||||
i32_trunc_f32(float f)
|
||||
{
|
||||
assert(INT32_MIN <= f && f <= INT32_MAX );
|
||||
return (i32) f;
|
||||
}
|
||||
|
||||
u32
|
||||
u32_trunc_f64(double f)
|
||||
{
|
||||
assert(0 <= f && f <= UINT32_MAX);
|
||||
return (u32) f;
|
||||
}
|
||||
|
||||
i32
|
||||
i32_trunc_f64(double f)
|
||||
{
|
||||
assert(INT32_MIN <= f && f <= INT32_MAX );
|
||||
return (i32) f;
|
||||
}
|
||||
|
||||
u64
|
||||
u64_trunc_f32(float f)
|
||||
{
|
||||
assert(0 <= f && f <= UINT64_MAX);
|
||||
return (u64) f;
|
||||
}
|
||||
|
||||
i64
|
||||
i64_trunc_f32(float f)
|
||||
{
|
||||
assert(INT64_MIN <= f && f <= INT64_MAX);
|
||||
return (i64) f;
|
||||
}
|
||||
|
||||
u64
|
||||
u64_trunc_f64(double f)
|
||||
{
|
||||
assert(0 <= f && f <= UINT64_MAX);
|
||||
return (u64) f;
|
||||
}
|
||||
|
||||
i64
|
||||
i64_trunc_f64(double f)
|
||||
{
|
||||
assert(INT64_MIN <= f && f <= INT64_MAX);
|
||||
return (i64) f;
|
||||
}
|
||||
|
||||
// Float => Float truncation functions
|
||||
INLINE float
|
||||
f32_trunc_f32(float f)
|
||||
{
|
||||
return trunc(f);
|
||||
}
|
||||
|
||||
INLINE float
|
||||
f32_min(float a, float b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
INLINE float
|
||||
f32_max(float a, float b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
INLINE double
|
||||
f64_min(double a, double b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
INLINE double
|
||||
f64_max(double a, double b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/* https://github.com/gwsystems/silverfish/blob/master/runtime/memory/64bit_nix.c */
|
||||
#include <types.h>
|
||||
|
||||
#ifdef USE_MEM_VM
|
||||
|
||||
// All of these are pretty generic
|
||||
INLINE float
|
||||
get_f32(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(float *)address;
|
||||
}
|
||||
|
||||
INLINE double
|
||||
get_f64(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(double *)address;
|
||||
}
|
||||
|
||||
INLINE i8
|
||||
get_i8(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(i8 *)address;
|
||||
}
|
||||
|
||||
INLINE i16
|
||||
get_i16(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(i16 *)address;
|
||||
}
|
||||
|
||||
INLINE i32
|
||||
get_i32(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(i32 *)address;
|
||||
}
|
||||
|
||||
INLINE i64
|
||||
get_i64(i32 offset)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
return *(i64 *)address;
|
||||
}
|
||||
|
||||
// Now setting routines
|
||||
INLINE void
|
||||
set_f32(i32 offset, float v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(float *)address = v;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_f64(i32 offset, double v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(double *)address = v;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i8(i32 offset, i8 v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(i8 *)address = v;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i16(i32 offset, i16 v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(i16 *)address = v;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i32(i32 offset, i32 v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(i32 *)address = v;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i64(i32 offset, i64 v)
|
||||
{
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
void *address = &mem_as_chars[offset];
|
||||
|
||||
*(i64 *)address = v;
|
||||
}
|
||||
|
||||
// Table handling functionality
|
||||
INLINE char *
|
||||
get_function_from_table(u32 idx, u32 type_id)
|
||||
{
|
||||
assert(idx < INDIRECT_TABLE_SIZE);
|
||||
|
||||
struct indirect_table_entry f = module_indirect_table[idx];
|
||||
|
||||
assert(f.type_id == type_id && f.func_pointer);
|
||||
|
||||
return f.func_pointer;
|
||||
}
|
||||
#else
|
||||
#error "Incorrect memory module!"
|
||||
#endif
|
@ -0,0 +1,37 @@
|
||||
#ifndef ARCH_AARCH64_CONTEXT_H
|
||||
#define ARCH_AARCH64_CONTEXT_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
typedef uint64_t reg_t;
|
||||
#define ARCH_NREGS 31 // TODO: aarch64 context-switch assembly and registers!
|
||||
|
||||
/*
|
||||
* This is the slowpath switch to a preempted sandbox!
|
||||
* SIGUSR1 on the current thread and restore mcontext there!
|
||||
*/
|
||||
extern void __attribute__((noreturn)) sandbox_switch_preempt(void);
|
||||
|
||||
struct arch_context {
|
||||
reg_t regs[ARCH_NREGS];
|
||||
mcontext_t mctx;
|
||||
};
|
||||
|
||||
typedef struct arch_context arch_context_t;
|
||||
extern __thread arch_context_t base_context;
|
||||
|
||||
static inline void
|
||||
arch_context_init(arch_context_t *actx, reg_t ip, reg_t sp)
|
||||
{
|
||||
memset(&actx->mctx, 0, sizeof(mcontext_t));
|
||||
memset((void *)actx->regs, 0, sizeof(reg_t) * ARCH_NREGS);
|
||||
}
|
||||
|
||||
static inline int
|
||||
arch_context_switch(arch_context_t *ca, arch_context_t *na)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* ARCH_AARCH64_CONTEXT_H */
|
@ -0,0 +1,14 @@
|
||||
#ifndef ARCH_CONTEXT_H
|
||||
#define ARCH_CONTEXT_H
|
||||
|
||||
#if defined(AARCH64)
|
||||
#include "aarch64/context.h"
|
||||
#elif defined(X86_64)
|
||||
#include "x86_64/context.h"
|
||||
#else
|
||||
#warning "Architecture not set. Using x86_64"
|
||||
#define X86_64
|
||||
#include "x86_64/context.h"
|
||||
#endif
|
||||
|
||||
#endif /* ARCH_CONTEXT_H */
|
@ -0,0 +1,155 @@
|
||||
#ifndef ARCH_X86_64_CONTEXT_H
|
||||
#define ARCH_X86_64_CONTEXT_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
/*
|
||||
* Register strict ordering>
|
||||
* rax = (regs + 0) = 0(%%reg)
|
||||
* rbx = (regs + 1) = 8(%%reg)
|
||||
* rcx = (regs + 2) = 16(%%reg)
|
||||
* rdx = (regs + 3) = 24(%%reg)
|
||||
* rbp = (regs + 4) = 32(%%reg)
|
||||
* rsp = (regs + 5) = 40(%%reg)
|
||||
* rsi = (regs + 6) = 48(%%reg)
|
||||
* rdi = (regs + 7) = 56(%%reg)
|
||||
* r8 = (regs + 8) = 64(%%reg)
|
||||
* r9 = (regs + 9) = 72(%%reg)
|
||||
* r10 = (regs + 10) = 80(%%reg)
|
||||
* r11 = (regs + 11) = 88(%%reg)
|
||||
* r12 = (regs + 12) = 96(%%reg)
|
||||
* r13 = (regs + 13) = 104(%%reg)
|
||||
* r14 = (regs + 14) = 112(%%reg)
|
||||
* r15 = (regs + 15) = 120(%%reg)
|
||||
* rip = (regs + 16) = 128(%%reg)
|
||||
*/
|
||||
|
||||
typedef uint64_t reg_t;
|
||||
#define ARCH_NREGS (16 /* GP registers */ + 1 /* for IP */)
|
||||
|
||||
/*
|
||||
* This is the slowpath switch to a preempted sandbox!
|
||||
* SIGUSR1 on the current thread and restore mcontext there!
|
||||
*/
|
||||
extern void __attribute__((noreturn)) sandbox_switch_preempt(void);
|
||||
|
||||
struct arch_context {
|
||||
reg_t regs[ARCH_NREGS];
|
||||
mcontext_t mctx;
|
||||
};
|
||||
|
||||
typedef struct arch_context arch_context_t;
|
||||
extern __thread arch_context_t base_context;
|
||||
|
||||
static void
|
||||
arch_mcontext_save(arch_context_t *ctx, mcontext_t *mc)
|
||||
{
|
||||
assert(ctx != &base_context);
|
||||
|
||||
ctx->regs[5] = 0;
|
||||
memset(ctx->regs, 0, ARCH_NREGS * sizeof(reg_t));
|
||||
memcpy(&ctx->mctx, mc, sizeof(mcontext_t));
|
||||
}
|
||||
|
||||
static void
|
||||
arch_mcontext_restore(mcontext_t *mc, arch_context_t *ctx)
|
||||
{
|
||||
assert(ctx != &base_context);
|
||||
|
||||
// if ctx->regs[5] is set, this was last in a user-level context switch state!
|
||||
// else restore mcontext..
|
||||
if (ctx->regs[5]) {
|
||||
mc->gregs[REG_RSP] = ctx->regs[5];
|
||||
mc->gregs[REG_RIP] = ctx->regs[16];
|
||||
ctx->regs[5] = 0;
|
||||
} else {
|
||||
memcpy(mc, &ctx->mctx, sizeof(mcontext_t));
|
||||
memset(&ctx->mctx, 0, sizeof(mcontext_t));
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
arch_context_init(arch_context_t *actx, reg_t ip, reg_t sp)
|
||||
{
|
||||
|
||||
memset(&actx->mctx, 0, sizeof(mcontext_t));
|
||||
memset((void *)actx->regs, 0, sizeof(reg_t) * ARCH_NREGS);
|
||||
|
||||
if (sp) {
|
||||
/*
|
||||
* context_switch conventions: bp is expected to be on top of the stack
|
||||
* when co-op context switching..
|
||||
*
|
||||
* so push sp on this new stack and use
|
||||
* that new sp as sp for switching to sandbox!
|
||||
*/
|
||||
asm volatile (
|
||||
"movq %%rsp, %%rbx\n\t" \
|
||||
"movq %%rax, %%rsp\n\t" \
|
||||
"pushq %%rax\n\t" \
|
||||
"movq %%rsp, %%rax\n\t" \
|
||||
"movq %%rbx, %%rsp\n\t" \
|
||||
: "=a" (sp)
|
||||
: "a" (sp)
|
||||
: "memory", "cc", "rbx"
|
||||
);
|
||||
}
|
||||
|
||||
*(actx->regs + 5) = sp;
|
||||
*(actx->regs + 16) = ip;
|
||||
}
|
||||
|
||||
static inline int
|
||||
arch_context_switch(arch_context_t *ca, arch_context_t *na)
|
||||
{
|
||||
if (!ca) {
|
||||
assert(na);
|
||||
// switching from "no sandbox to execute" state to "executing a sandbox"
|
||||
ca = &base_context;
|
||||
} else if (!na) {
|
||||
assert(ca);
|
||||
|
||||
// switching from "executing a sandbox" to "no execution" state.
|
||||
na = &base_context;
|
||||
} else {
|
||||
assert(na && ca);
|
||||
|
||||
// switching between sandboxes.
|
||||
}
|
||||
|
||||
reg_t *cr = ca->regs, *nr = na->regs;
|
||||
assert(cr && nr);
|
||||
|
||||
/* TODO: slowpath: signal the current pthread if resuming a preempted sandbox! */
|
||||
|
||||
asm volatile ( \
|
||||
"pushq %%rbp\n\t" \
|
||||
"movq %%rsp, %%rbp\n\t" \
|
||||
"movq $2f, 128(%%rax)\n\t" \
|
||||
"movq %%rsp, 40(%%rax)\n\t" \
|
||||
"cmpq $0, 40(%%rbx)\n\t" \
|
||||
"je 1f\n\t" \
|
||||
"movq 40(%%rbx), %%rsp\n\t" \
|
||||
"jmpq *128(%%rbx)\n\t" \
|
||||
"1:\n\t" \
|
||||
"call sandbox_switch_preempt\n\t" \
|
||||
".align 8\n\t" \
|
||||
"2:\n\t" \
|
||||
"movq $0, 40(%%rbx)\n\t" \
|
||||
".align 8\n\t" \
|
||||
"3:\n\t" \
|
||||
"popq %%rbp\n\t" \
|
||||
:
|
||||
: "a" (cr), "b" (nr)
|
||||
: "memory", "cc", "rcx", "rdx", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||
"xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15"
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* ARCH_X86_64_CONTEXT_H */
|
@ -0,0 +1,91 @@
|
||||
#ifndef SFRT_MODULE_H
|
||||
#define SFRT_MODULE_H
|
||||
|
||||
#include <uv.h>
|
||||
#include "types.h"
|
||||
|
||||
struct module {
|
||||
char name[MOD_NAME_MAX]; //not sure if i care for now.
|
||||
char path[MOD_PATH_MAX]; //to dlopen if it has not been opened already.
|
||||
|
||||
void *dl_handle;
|
||||
|
||||
mod_main_fn_t entry_fn;
|
||||
mod_glb_fn_t glb_init_fn;
|
||||
mod_mem_fn_t mem_init_fn;
|
||||
mod_tbl_fn_t tbl_init_fn;
|
||||
|
||||
i32 nargs; //as per the specification somewhere.
|
||||
/* i32 nrets; */
|
||||
|
||||
u32 stack_size; // a specification?
|
||||
u64 max_memory; //perhaps a specification of the module.
|
||||
u32 timeout; //again part of the module specification.
|
||||
|
||||
u32 refcnt; //ref count how many instances exist here.
|
||||
|
||||
u32 udpport;
|
||||
uv_udp_t udpsrv; // udp server to listen to requests.
|
||||
struct sockaddr_in srvaddr;
|
||||
|
||||
// FIXME: for now, per-sandbox. no reason to have it be per-sandbox.
|
||||
struct indirect_table_entry indirect_table[INDIRECT_TABLE_SIZE];
|
||||
// TODO: what else?
|
||||
};
|
||||
|
||||
// a runtime resource, perhaps use "malloc" on this?
|
||||
struct module *module_alloc(char *mod_name, char *mod_path, u32 udp_port, i32 nargs, i32 nrets, u32 stack_sz, u32 max_heap, u32 timeout/*, ...*/);
|
||||
// frees only if refcnt == 0
|
||||
void module_free(struct module *mod);
|
||||
struct module *module_find(char *name);
|
||||
|
||||
static inline int
|
||||
module_is_valid(struct module *mod)
|
||||
{
|
||||
if (mod && mod->dl_handle && mod->entry_fn) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
module_table_init(struct module *mod)
|
||||
{
|
||||
// called in a sandbox.
|
||||
mod->tbl_init_fn();
|
||||
}
|
||||
|
||||
static inline void
|
||||
module_memory_init(struct module *mod)
|
||||
{
|
||||
// called in a sandbox.
|
||||
mod->mem_init_fn();
|
||||
}
|
||||
|
||||
static inline i32
|
||||
module_entry(struct module *mod, i32 argc, i32 argv)
|
||||
{
|
||||
return mod->entry_fn(argc, argv);
|
||||
}
|
||||
|
||||
// instantiate this module.
|
||||
static inline void
|
||||
module_acquire(struct module *mod)
|
||||
{
|
||||
// FIXME: atomic.
|
||||
mod->refcnt++;
|
||||
}
|
||||
|
||||
static inline void
|
||||
module_release(struct module *mod)
|
||||
{
|
||||
// FIXME: atomic.
|
||||
mod->refcnt--;
|
||||
}
|
||||
|
||||
static inline i32
|
||||
module_nargs(struct module *mod)
|
||||
{
|
||||
return mod->nargs;
|
||||
}
|
||||
|
||||
#endif /* SFRT_MODULE_H */
|
@ -0,0 +1,189 @@
|
||||
/***
|
||||
* Copyright 2009-2017 by Gabriel Parmer. All rights reserved.
|
||||
* Redistribution of this file is permitted under the BSD 2 clause license.
|
||||
* Author: Gabriel Parmer, gparmer@gwu.edu, 2017
|
||||
*
|
||||
* History:
|
||||
* - Initial implementation, ~2009
|
||||
* - Adapted for parsec and relicensed, 2016
|
||||
*/
|
||||
|
||||
/*
|
||||
* API Conventions:
|
||||
* - obj, new, and tmp are pointers of type T where T is the struct containing the linked list
|
||||
* - head is a pointer to struct ps_list_head
|
||||
* - l is the list field name within T
|
||||
* - type is T without any ()
|
||||
* - all ps_list_head_* functions should be applied to struct ps_list_head pointers
|
||||
* - all ps_list_* functions should be passed items of type T, not struct ps_list_head
|
||||
* - as with most macro-based APIs, please avoid passing in functions that cannot be multiply evaluated;
|
||||
* generally passing only variables is a good move
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* struct ps_list_head h;
|
||||
* struct foo {
|
||||
* struct ps_list l;
|
||||
* void *d;
|
||||
* } node, *i, *tmp;
|
||||
*
|
||||
* ps_list_head_init(&h);
|
||||
* ps_list_init(&node);
|
||||
* ps_list_head_add(&h, &node, l);
|
||||
* ...
|
||||
* for (i = ps_list_head_first(&h, struct foo, l) ;
|
||||
* i != ps_list_head(&h, struct foo, l) ;
|
||||
* i = ps_list_next(i, l)) { ... }
|
||||
*
|
||||
* for (ps_list_iter_init(&h, i, l) ; !ps_list_iter_term(&h, i, l) ; i = ps_list_next(i, l)) { ... }
|
||||
*
|
||||
* ps_list_foreach(&h, i, l) { ... }
|
||||
*
|
||||
* ps_list_foreach_del(&h, i, tmp, l) {
|
||||
* ps_list_rem(i, l);
|
||||
* ps_free(i);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PS_LIST_H
|
||||
#define PS_LIST_H
|
||||
|
||||
struct ps_list {
|
||||
struct ps_list *n, *p;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a separate type to 1) provide guidance on how to use the
|
||||
* API, and 2) to prevent developers from comparing pointers that
|
||||
* should not be compared.
|
||||
*/
|
||||
struct ps_list_head {
|
||||
struct ps_list l;
|
||||
};
|
||||
|
||||
#define PS_LIST_DEF_NAME list
|
||||
|
||||
static inline void
|
||||
ps_list_ll_init(struct ps_list *l)
|
||||
{ l->n = l->p = l; }
|
||||
|
||||
static inline void
|
||||
ps_list_head_init(struct ps_list_head *lh)
|
||||
{ ps_list_ll_init(&lh->l); }
|
||||
|
||||
static inline int
|
||||
ps_list_ll_empty(struct ps_list *l)
|
||||
{ return l->n == l; }
|
||||
|
||||
static inline int
|
||||
ps_list_head_empty(struct ps_list_head *lh)
|
||||
{ return ps_list_ll_empty(&lh->l); }
|
||||
|
||||
static inline void
|
||||
ps_list_ll_add(struct ps_list *l, struct ps_list *new)
|
||||
{
|
||||
new->n = l->n;
|
||||
new->p = l;
|
||||
l->n = new;
|
||||
new->n->p = new;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ps_list_ll_rem(struct ps_list *l)
|
||||
{
|
||||
l->n->p = l->p;
|
||||
l->p->n = l->n;
|
||||
l->p = l->n = l;
|
||||
}
|
||||
|
||||
#define ps_offsetof(s, field) __builtin_offsetof(s, field)
|
||||
//#define ps_offsetof(s, field) ((unsigned long)&(((s *)0)->field))
|
||||
|
||||
#define ps_container(intern, type, field) \
|
||||
((type *)((char *)(intern) - ps_offsetof(type, field)))
|
||||
|
||||
/*
|
||||
* Get a pointer to the object containing *l, of a type shared with
|
||||
* *o. Importantly, "o" is not accessed here, and is _only_ used for
|
||||
* its type. It will typically be the iterator/cursor working through
|
||||
* a list. Do _not_ use this function. It is a utility used by the
|
||||
* following functions.
|
||||
*/
|
||||
#define ps_list_obj_get(l, o, lname) \
|
||||
ps_container(l, __typeof__(*(o)), lname)
|
||||
|
||||
//(typeof (*(o)) *)(((char*)(l)) - ps_offsetof(typeof(*(o)), lname))
|
||||
|
||||
/***
|
||||
* The object API. These functions are called with pointers to your
|
||||
* own (typed) structures.
|
||||
*/
|
||||
|
||||
#define ps_list_is_head(lh, o, lname) (ps_list_obj_get((lh), (o), lname) == (o))
|
||||
|
||||
/* functions for if we don't use the default name for the list field */
|
||||
#define ps_list_singleton(o, lname) ps_list_ll_empty(&(o)->lname)
|
||||
#define ps_list_init(o, lname) ps_list_ll_init(&(o)->lname)
|
||||
#define ps_list_next(o, lname) ps_list_obj_get((o)->lname.n, (o), lname)
|
||||
#define ps_list_prev(o, lname) ps_list_obj_get((o)->lname.p, (o), lname)
|
||||
#define ps_list_add(o, n, lname) ps_list_ll_add(&(o)->lname, &(n)->lname)
|
||||
#define ps_list_append(o, n, lname) ps_list_add(ps_list_prev((o), lname), n, lname)
|
||||
#define ps_list_rem(o, lname) ps_list_ll_rem(&(o)->lname)
|
||||
#define ps_list_head_add(lh, o, lname) ps_list_ll_add((&(lh)->l), &(o)->lname)
|
||||
#define ps_list_head_append(lh, o, lname) ps_list_ll_add(((&(lh)->l)->p), &(o)->lname)
|
||||
|
||||
/**
|
||||
* Explicit type API: Pass in the types of the nodes in the list, and
|
||||
* the name of the ps_list field in that type.
|
||||
*/
|
||||
|
||||
#define ps_list_head_first(lh, type, lname) \
|
||||
ps_container(((lh)->l.n), type, lname)
|
||||
#define ps_list_head_last(lh, type, lname) \
|
||||
ps_container(((lh)->l.p), type, lname)
|
||||
|
||||
/* If your struct named the list field "list" (as defined by PS_LIST_DEF_NAME */
|
||||
#define ps_list_is_head_d(lh, o) ps_list_is_head(lh, o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_singleton_d(o) ps_list_singleton(o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_init_d(o) ps_list_init(o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_next_d(o) ps_list_next(o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_prev_d(o) ps_list_prev(o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_add_d(o, n) ps_list_add(o, n, PS_LIST_DEF_NAME)
|
||||
#define ps_list_append_d(o, n) ps_list_append(o, n, PS_LIST_DEF_NAME)
|
||||
#define ps_list_rem_d(o) ps_list_rem(o, PS_LIST_DEF_NAME)
|
||||
|
||||
#define ps_list_head_last_d(lh, o) ps_list_head_last(lh, o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_head_first_d(lh, type) ps_list_head_first(lh, type, PS_LIST_DEF_NAME)
|
||||
#define ps_list_head_add_d(lh, o) ps_list_head_add(lh, o, PS_LIST_DEF_NAME)
|
||||
#define ps_list_head_append_d(lh, o) ps_list_head_append(lh, o, PS_LIST_DEF_NAME)
|
||||
|
||||
/**
|
||||
* Iteration API
|
||||
*/
|
||||
|
||||
/* Iteration without mutating the list */
|
||||
#define ps_list_foreach(head, iter, lname) \
|
||||
for (iter = ps_list_head_first((head), __typeof__(*iter), lname) ; \
|
||||
!ps_list_is_head((head), iter, lname) ; \
|
||||
(iter) = ps_list_next(iter, lname))
|
||||
|
||||
#define ps_list_foreach_d(head, iter) ps_list_foreach(head, iter, PS_LIST_DEF_NAME)
|
||||
|
||||
/*
|
||||
* Iteration where the current node can be ps_list_rem'ed.
|
||||
* Notes:
|
||||
* - typeof(iter) == typeof(tmp)
|
||||
* - ps_list_add can be used on iter, but the added node will not be iterated over
|
||||
*
|
||||
* TODO: Add SMR/parallel version of this macro
|
||||
*/
|
||||
#define ps_list_foreach_del(head, iter, tmp, lname) \
|
||||
for (iter = ps_list_head_first((head), __typeof__(*iter), lname), \
|
||||
(tmp) = ps_list_next((iter), lname) ; \
|
||||
!ps_list_is_head((head), iter, lname) ; \
|
||||
(iter) = (tmp), (tmp) = ps_list_next((tmp), lname))
|
||||
|
||||
#define ps_list_foreach_del_d(head, iter, tmp) ps_list_foreach_del(head, iter, tmp, PS_LIST_DEF_NAME)
|
||||
|
||||
#endif /* PS_LIST_H */
|
@ -0,0 +1,55 @@
|
||||
#ifndef SFRT_RUNTIME_H
|
||||
#define SFRT_RUNTIME_H
|
||||
#include "types.h"
|
||||
|
||||
#include <uv.h>
|
||||
#include "sandbox.h"
|
||||
|
||||
void alloc_linear_memory(void);
|
||||
void expand_memory(void);
|
||||
void free_linear_memory(void *base, u32 bound, u32 max);
|
||||
// Assumption: bounds_check < WASM_PAGE_SIZE
|
||||
INLINE char *get_memory_ptr_for_runtime(u32 offset, u32 bounds_check);
|
||||
|
||||
static inline void *
|
||||
get_memory_ptr_void(u32 offset, u32 bounds_check)
|
||||
{
|
||||
return (void*) get_memory_ptr_for_runtime(offset, bounds_check);
|
||||
}
|
||||
|
||||
static inline char *
|
||||
get_memory_string(u32 offset)
|
||||
{
|
||||
char *naive_ptr = get_memory_ptr_for_runtime(offset, 1);
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
// Keep bounds checking the waters over and over until we know it's safe (we find a terminating character)
|
||||
char ith_element = get_memory_ptr_for_runtime(offset, i + 1)[i];
|
||||
|
||||
if (ith_element == '\0') return naive_ptr;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE char *get_function_from_table(u32 idx, u32 type_id);
|
||||
|
||||
// libc/* might need to do some setup for the libc setup
|
||||
void stub_init(char *modulename, i32 offset, mod_init_libc_fn_t fn);
|
||||
|
||||
void runtime_init(void);
|
||||
|
||||
static inline void
|
||||
runtime_on_alloc(uv_handle_t *h, size_t suggested, uv_buf_t *buf)
|
||||
{
|
||||
buf->base = malloc(suggested);
|
||||
memset(buf->base, 0, suggested);
|
||||
buf->len = suggested;
|
||||
}
|
||||
|
||||
extern __thread uv_loop_t uvio;
|
||||
static inline uv_loop_t *
|
||||
runtime_uvio(void)
|
||||
{ return &uvio; }
|
||||
|
||||
#endif /* SFRT_RUNTIME_H */
|
@ -0,0 +1,211 @@
|
||||
#ifndef SFRT_SANDBOX_H
|
||||
#define SFRT_SANDBOX_H
|
||||
|
||||
#include "ps_list.h"
|
||||
#include "module.h"
|
||||
#include "arch/context.h"
|
||||
#include "softint.h"
|
||||
#include <ucontext.h>
|
||||
#include <uv.h>
|
||||
|
||||
struct io_handle {
|
||||
int fd;
|
||||
struct stat s_cache;
|
||||
union uv_any_handle uvh;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SANDBOX_FREE,
|
||||
SANDBOX_RUNNABLE,
|
||||
SANDBOX_BLOCKED,
|
||||
SANDBOX_WOKEN, //for race in block()/wakeup()
|
||||
SANDBOX_RETURNED, //waiting for parent to read status?
|
||||
} sandbox_state_t;
|
||||
|
||||
/*
|
||||
* This is the slowpath switch to a preempted sandbox!
|
||||
* SIGUSR1 on the current thread and restore mcontext there!
|
||||
*/
|
||||
extern void __attribute__((noreturn)) sandbox_switch_preempt(void);
|
||||
|
||||
struct sandbox {
|
||||
sandbox_state_t state;
|
||||
|
||||
void *linear_start;
|
||||
u32 linear_size;
|
||||
u32 linear_max_size;
|
||||
|
||||
void *stack_start; // guess we need a mechanism for stack allocation.
|
||||
u32 stack_size; // and to set the size of it.
|
||||
|
||||
arch_context_t ctxt; //register context for context switch.
|
||||
|
||||
// TODO: are all these necessary?
|
||||
u64 actual_deadline;
|
||||
u64 expected_deadline;
|
||||
u64 total_time;
|
||||
u64 remaining_time;
|
||||
u64 start_time;
|
||||
|
||||
struct module *mod; //which module is this an instance of?
|
||||
//struct indirect_table_entry indirect_table[INDIRECT_TABLE_SIZE];
|
||||
|
||||
i32 args_offset; //actual placement of args in the sandbox.
|
||||
/* i32 ret_offset; //placement of return value(s) in the sandbox. */
|
||||
void *args; // args from request, must be of module->nargs size.
|
||||
i32 retval;
|
||||
|
||||
struct io_handle handles[SBOX_MAX_OPEN];
|
||||
|
||||
char *read_buf;
|
||||
ssize_t read_len, read_size;
|
||||
|
||||
struct ps_list list;
|
||||
|
||||
// track I/O handles?
|
||||
};
|
||||
|
||||
// a runtime resource, malloc on this!
|
||||
struct sandbox *sandbox_alloc(struct module *mod, char *args);
|
||||
// should free stack and heap resources.. also any I/O handles.
|
||||
void sandbox_free(struct sandbox *sbox);
|
||||
|
||||
// next_sandbox only used in SIGUSR1
|
||||
extern __thread struct sandbox *current_sandbox;
|
||||
extern __thread arch_context_t *next_context;
|
||||
|
||||
typedef struct sandbox sandbox_t;
|
||||
|
||||
static inline struct sandbox *
|
||||
sandbox_current(void)
|
||||
{ return current_sandbox; }
|
||||
|
||||
static inline void
|
||||
sandbox_current_set(struct sandbox *sbox)
|
||||
{
|
||||
int dis = 0;
|
||||
|
||||
if (softint_enabled()) {
|
||||
dis = 1;
|
||||
softint_disable();
|
||||
}
|
||||
|
||||
// FIXME: critical-section.
|
||||
current_sandbox = sbox;
|
||||
if (sbox == NULL) return;
|
||||
|
||||
sandbox_lmbase = sbox->linear_start;
|
||||
sandbox_lmbound = sbox->linear_size;
|
||||
// TODO: module table or sandbox table?
|
||||
module_indirect_table = sbox->mod->indirect_table;
|
||||
|
||||
if (dis) softint_enable();
|
||||
}
|
||||
|
||||
static inline struct module *
|
||||
sandbox_module(struct sandbox *s)
|
||||
{
|
||||
if (!s) return NULL;
|
||||
|
||||
return s->mod;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sandbox_switch(struct sandbox *next)
|
||||
{
|
||||
arch_context_t *n = next == NULL ? NULL : &next->ctxt;
|
||||
|
||||
// disable interrupts (signals)
|
||||
softint_disable();
|
||||
|
||||
// switch sandbox (register context & base/bound/table)
|
||||
struct sandbox *curr = sandbox_current();
|
||||
arch_context_t *c = curr == NULL ? NULL : &curr->ctxt;
|
||||
sandbox_current_set(next);
|
||||
// save current's registers and restore next's registers.
|
||||
next_context = n;
|
||||
arch_context_switch(c, n);
|
||||
next_context = NULL;
|
||||
|
||||
// enable interrupts (signals)
|
||||
softint_enable();
|
||||
}
|
||||
|
||||
static inline char *
|
||||
sandbox_args(void)
|
||||
{
|
||||
struct sandbox *c = sandbox_current();
|
||||
|
||||
return (char *)c->args;
|
||||
}
|
||||
|
||||
void sandbox_run(struct sandbox *s);
|
||||
void *sandbox_run_func(void *data);
|
||||
struct sandbox *sandbox_schedule(void);
|
||||
void sandbox_block(void);
|
||||
void sandbox_wakeup(sandbox_t *sb);
|
||||
|
||||
// should be the entry-point for each sandbox so it can do per-sandbox mem/etc init.
|
||||
// should have been called with stack allocated and sandbox_current() set!
|
||||
void sandbox_entry(void);
|
||||
void sandbox_exit(void);
|
||||
|
||||
static inline int
|
||||
io_handle_preopen(void)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
int i;
|
||||
for (i = 0; i < SBOX_MAX_OPEN; i++) {
|
||||
if (s->handles[i].fd < 0) break;
|
||||
}
|
||||
if (i == SBOX_MAX_OPEN) return -1;
|
||||
s->handles[i].fd = SBOX_PREOPEN_MAGIC;
|
||||
memset(&s->handles[i].uvh, 0, sizeof(union uv_any_handle));
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_handle_open(int fd)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
if (fd < 0) return fd;
|
||||
int i = io_handle_preopen();
|
||||
s->handles[i].fd = fd; //well, per sandbox.. so synchronization necessary!
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_handle_preopen_set(int idx, int fd)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
if (idx >= SBOX_MAX_OPEN || idx < 0) return -1;
|
||||
if (fd < 0 || s->handles[idx].fd != SBOX_PREOPEN_MAGIC) return -1;
|
||||
s->handles[idx].fd = fd;
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_handle_fd(int idx)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
if (idx >= SBOX_MAX_OPEN || idx < 0) return -1;
|
||||
return s->handles[idx].fd;
|
||||
}
|
||||
|
||||
static inline void
|
||||
io_handle_close(int idx)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
if (idx >= SBOX_MAX_OPEN || idx < 0) return;
|
||||
s->handles[idx].fd = -1;
|
||||
}
|
||||
|
||||
static inline union uv_any_handle *
|
||||
io_handle_uv_get(int idx)
|
||||
{
|
||||
struct sandbox *s = sandbox_current();
|
||||
if (idx >= SBOX_MAX_OPEN || idx < 0) return NULL;
|
||||
return &s->handles[idx].uvh;
|
||||
}
|
||||
|
||||
#endif /* SFRT_SANDBOX_H */
|
@ -0,0 +1,73 @@
|
||||
#ifndef SFRT_SOFTINT_H
|
||||
#define SFRT_SOFTINT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
static inline int
|
||||
softint_mask(int sig)
|
||||
{
|
||||
sigset_t set;
|
||||
int ret;
|
||||
|
||||
assert(sig == SIGALRM || sig == SIGUSR1);
|
||||
/* all threads created by the calling thread will have sig blocked */
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, sig);
|
||||
ret = pthread_sigmask(SIG_BLOCK, &set, NULL);
|
||||
if (ret != 0) {
|
||||
errno = ret;
|
||||
perror("pthread_sigmask");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
softint_unmask(int sig)
|
||||
{
|
||||
sigset_t set;
|
||||
int ret;
|
||||
|
||||
assert(sig == SIGALRM || sig == SIGUSR1);
|
||||
/* all threads created by the calling thread will have sig unblocked */
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, sig);
|
||||
ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL);
|
||||
if (ret != 0) {
|
||||
errno = ret;
|
||||
perror("pthread_sigmask");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern __thread volatile sig_atomic_t softint_off;
|
||||
|
||||
static inline void
|
||||
softint_disable(void)
|
||||
{
|
||||
while (__sync_bool_compare_and_swap(&softint_off, 0, 1) == false) ;
|
||||
}
|
||||
|
||||
static inline void
|
||||
softint_enable(void)
|
||||
{
|
||||
if (__sync_bool_compare_and_swap(&softint_off, 1, 0) == false) assert(0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
softint_enabled(void)
|
||||
{
|
||||
return (softint_off == 0);
|
||||
}
|
||||
|
||||
void softint_init(void);
|
||||
|
||||
void softint_timer_arm(void);
|
||||
void softint_timer_disarm(void);
|
||||
|
||||
#endif /* SFRT_SOFTINT_H */
|
@ -0,0 +1,129 @@
|
||||
#ifndef SFRT_TYPES_H
|
||||
#define SFRT_TYPES_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <printf.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define EXPORT __attribute__ ((visibility ("default")))
|
||||
#define IMPORT __attribute__ ((visibility ("default")))
|
||||
|
||||
#define INLINE __attribute__((always_inline))
|
||||
#define WEAK __attribute__((weak))
|
||||
|
||||
// Type alias's so I don't have to write uint32_t a million times
|
||||
typedef signed char i8;
|
||||
typedef unsigned char u8;
|
||||
typedef int16_t i16;
|
||||
typedef uint16_t u16;
|
||||
typedef int32_t i32;
|
||||
typedef uint32_t u32;
|
||||
typedef int64_t i64;
|
||||
typedef uint64_t u64;
|
||||
|
||||
// FIXME: per-module configuration?
|
||||
#define WASM_PAGE_SIZE (1024 * 64) //64KB
|
||||
#define WASM_START_PAGES (1<<8) //16MB
|
||||
#define WASM_MAX_PAGES (1<<15) //4GB
|
||||
|
||||
#define WASM_STACK_SIZE (1<<15) // FIXME: fixed size for now.
|
||||
|
||||
// These are per module symbols and I'd need to dlsym for each module. instead just use global constants, see above macros.
|
||||
// The code generator compiles in the starting number of wasm pages, and the maximum number of pages
|
||||
// If we try and allocate more than max_pages, we should fault
|
||||
//extern u32 starting_pages;
|
||||
//extern u32 max_pages;
|
||||
|
||||
// The code generator also compiles in stubs that populate the linear memory and function table
|
||||
void populate_memory(void);
|
||||
void populate_table(void);
|
||||
|
||||
// memory/* also provides the table access functions
|
||||
// TODO: Change this to use a compiled in size
|
||||
#define INDIRECT_TABLE_SIZE 1024
|
||||
|
||||
struct indirect_table_entry {
|
||||
u32 type_id;
|
||||
void *func_pointer;
|
||||
};
|
||||
|
||||
extern __thread struct indirect_table_entry *module_indirect_table;
|
||||
|
||||
// for sandbox linear memory isolation
|
||||
extern __thread void *sandbox_lmbase;
|
||||
extern __thread u32 sandbox_lmbound;
|
||||
extern i32 logfd;
|
||||
|
||||
// functions in the module to lookup and call per sandbox.
|
||||
typedef i32 (*mod_main_fn_t)(i32 a, i32 b);
|
||||
typedef void (*mod_glb_fn_t)(void);
|
||||
typedef void (*mod_mem_fn_t)(void);
|
||||
typedef void (*mod_tbl_fn_t)(void);
|
||||
typedef void (*mod_init_libc_fn_t)(i32, i32);
|
||||
|
||||
typedef enum {
|
||||
MOD_ARG_MODPATH = 0,
|
||||
MOD_ARG_MODPORT,
|
||||
MOD_ARG_MODNAME,
|
||||
MOD_ARG_MODNARGS,
|
||||
MOD_ARG_MAX,
|
||||
} mod_argindex_t;
|
||||
|
||||
#define MOD_MAIN_FN "wasmf_main"
|
||||
#define MOD_GLB_FN "populate_globals"
|
||||
#define MOD_MEM_FN "populate_memory"
|
||||
#define MOD_TBL_FN "populate_table"
|
||||
#define MOD_INIT_LIBC_FN "wasmf___init_libc"
|
||||
|
||||
#define MOD_MAX_ARGS 16
|
||||
#define MOD_ARG_MAX_SZ 64
|
||||
#define MOD_MAX 1024
|
||||
|
||||
#define MOD_NAME_MAX 32
|
||||
#define MOD_PATH_MAX 256
|
||||
|
||||
#define JSON_ELE_MAX 16
|
||||
|
||||
// FIXME: some naive work-stealing here..
|
||||
#define SBOX_PULL_MAX 16
|
||||
|
||||
#define SBOX_MAX_OPEN 32
|
||||
#define SBOX_PREOPEN_MAGIC (707707707) // reads lol lol lol upside down
|
||||
|
||||
#define SOFTINT_TIMER_START_USEC (10*1000) //start timers 10 us from now.
|
||||
#define SOFTINT_TIMER_PERIOD_USEC (1000*100) // 100ms timer..
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef NOSTDIO
|
||||
#define debuglog(fmt,...) dprintf(logfd, "(%d,%lu) %s: " fmt, sched_getcpu(), pthread_self(), __func__, ## __VA_ARGS__)
|
||||
#else
|
||||
#define debuglog(fmt,...) printf("(%d,%lu) %s: " fmt, sched_getcpu(), pthread_self(), __func__, ## __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define debuglog(fmt,...)
|
||||
#endif
|
||||
|
||||
#define GLB_STDOUT "/dev/null"
|
||||
#define GLB_STDERR "/dev/null"
|
||||
#define GLB_STDIN "/dev/zero"
|
||||
|
||||
#define LOGFILE "awesome.log"
|
||||
|
||||
#define RDWR_VEC_MAX 16
|
||||
|
||||
#define MOD_REQ_CORE 0 // core dedicated to check module requests..
|
||||
#define SBOX_NCORES 2 // number of sandboxing threads/cores..
|
||||
|
||||
#endif /* SFRT_TYPES_H */
|
@ -0,0 +1,13 @@
|
||||
#ifndef SFRT_UTIL_H
|
||||
#define SFRT_UTIL_H
|
||||
|
||||
#include <sandbox.h>
|
||||
#include <module.h>
|
||||
|
||||
/* perhaps move it to module.h or sandbox.h? */
|
||||
struct sandbox *util_parse_sandbox_string_custom(struct module *m, char *str);
|
||||
struct sandbox *util_parse_sandbox_string_json(struct module *m, char *str);
|
||||
int util_parse_modules_file_json(char *filename);
|
||||
int util_parse_modules_file_custom(char *filename);
|
||||
|
||||
#endif /* SFRT_UTIL_H */
|
@ -0,0 +1 @@
|
||||
Subproject commit 85695f3d5903b1cd5b4030efe50db3b4f5f3c928
|
@ -0,0 +1,193 @@
|
||||
/* https://github.com/gwsystems/silverfish/blob/master/runtime/libc/libc_backing.c */
|
||||
#include <runtime.h>
|
||||
|
||||
extern i32 inner_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f);
|
||||
|
||||
i32
|
||||
env_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f)
|
||||
{
|
||||
i32 i = inner_syscall_handler(n, a, b, c, d, e, f);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
i32
|
||||
env___syscall(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f)
|
||||
{
|
||||
return env_syscall_handler(n, a, b, c, d, e, f);
|
||||
}
|
||||
|
||||
// Atomic functions, with definitions stolen from musl
|
||||
|
||||
i32
|
||||
env_a_ctz_64(u64 x)
|
||||
{
|
||||
__asm__( "bsf %1,%0" : "=r"(x) : "r"(x) );
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
//static inline int a_ctz_l(unsigned long x)
|
||||
//{
|
||||
// __asm__( "bsf %1,%0" : "=r"(x) : "r"(x) );
|
||||
// return x;
|
||||
//}
|
||||
|
||||
INLINE void
|
||||
env_a_and_64(i32 p_off, u64 v)
|
||||
{
|
||||
uint64_t* p = get_memory_ptr_void(p_off, sizeof(uint64_t));
|
||||
*p &= v;
|
||||
|
||||
// __asm__( "lock ; and %1, %0"
|
||||
// : "=m"(*p) : "r"(v) : "memory" );
|
||||
}
|
||||
|
||||
INLINE void
|
||||
env_a_or_64(i32 p_off, i64 v)
|
||||
{
|
||||
assert(sizeof(i64) == sizeof(uint64_t));
|
||||
uint64_t* p = get_memory_ptr_void(p_off, sizeof(i64));
|
||||
*p |= v;
|
||||
|
||||
// __asm__( "lock ; or %1, %0"
|
||||
// : "=m"(*p) : "r"(v) : "memory" );
|
||||
}
|
||||
|
||||
//static inline void a_or_l(volatile void *p, long v)
|
||||
//{
|
||||
// __asm__( "lock ; or %1, %0"
|
||||
// : "=m"(*(long *)p) : "r"(v) : "memory" );
|
||||
//}
|
||||
//
|
||||
//static inline void *a_cas_p(volatile void *p, void *t, void *s)
|
||||
//{
|
||||
// __asm__( "lock ; cmpxchg %3, %1"
|
||||
// : "=a"(t), "=m"(*(long *)p) : "a"(t), "r"(s) : "memory" );
|
||||
// return t;
|
||||
//}
|
||||
//
|
||||
i32
|
||||
env_a_cas(i32 p_off, i32 t, i32 s)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* p = get_memory_ptr_void(p_off, sizeof(i32));
|
||||
|
||||
__asm__( "lock ; cmpxchg %3, %1"
|
||||
: "=a"(t), "=m"(*p) : "a"(t), "r"(s) : "memory" );
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
env_a_or(i32 p_off, i32 v)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* p = get_memory_ptr_void(p_off, sizeof(i32));
|
||||
__asm__( "lock ; or %1, %0"
|
||||
: "=m"(*p) : "r"(v) : "memory" );
|
||||
}
|
||||
|
||||
//static inline void a_and(volatile int *p, int v)
|
||||
//{
|
||||
// __asm__( "lock ; and %1, %0"
|
||||
// : "=m"(*p) : "r"(v) : "memory" );
|
||||
//}
|
||||
|
||||
i32
|
||||
env_a_swap(i32 x_off, i32 v)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* x = get_memory_ptr_void(x_off, sizeof(i32));
|
||||
|
||||
__asm__( "xchg %0, %1" : "=r"(v), "=m"(*x) : "0"(v) : "memory" );
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
i32
|
||||
env_a_fetch_add(i32 x_off, i32 v)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* x = get_memory_ptr_void(x_off, sizeof(i32));
|
||||
|
||||
__asm__( "lock ; xadd %0, %1" : "=r"(v), "=m"(*x) : "0"(v) : "memory" );
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
env_a_inc(i32 x_off)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* x = get_memory_ptr_void(x_off, sizeof(i32));
|
||||
|
||||
__asm__( "lock ; incl %0" : "=m"(*x) : "m"(*x) : "memory" );
|
||||
}
|
||||
|
||||
void
|
||||
env_a_dec(i32 x_off)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* x = get_memory_ptr_void(x_off, sizeof(i32));
|
||||
|
||||
__asm__( "lock ; decl %0" : "=m"(*x) : "m"(*x) : "memory" );
|
||||
}
|
||||
|
||||
void
|
||||
env_a_store(i32 p_off, i32 x)
|
||||
{
|
||||
assert(sizeof(i32) == sizeof(volatile int));
|
||||
volatile int* p = get_memory_ptr_void(p_off, sizeof(i32));
|
||||
__asm__ __volatile__(
|
||||
"mov %1, %0 ; lock ; orl $0,(%%esp)" : "=m"(*p) : "r"(x) : "memory" );
|
||||
}
|
||||
|
||||
void
|
||||
env_do_spin(i32 i)
|
||||
{
|
||||
__asm__ __volatile__( "pause" : : : "memory" );
|
||||
}
|
||||
|
||||
void
|
||||
env_do_crash(i32 i)
|
||||
{
|
||||
printf("crashing: %d\n", i);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
int
|
||||
env_a_ctz_32(i32 x)
|
||||
{
|
||||
__asm__( "bsf %1,%0" : "=r"(x) : "r"(x) );
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void
|
||||
env_do_barrier(i32 x)
|
||||
{
|
||||
__asm__ __volatile__( "" : : : "memory" );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
env___unmapself(u32 base, u32 size)
|
||||
{
|
||||
// Just do some no op
|
||||
}
|
||||
|
||||
|
||||
// Floating point routines
|
||||
// TODO: Do a fair comparison between musl and wasm-musl
|
||||
INLINE double
|
||||
env_sin(double d)
|
||||
{
|
||||
return sin(d);
|
||||
}
|
||||
|
||||
INLINE double
|
||||
env_cos(double d)
|
||||
{
|
||||
return cos(d);
|
||||
}
|
@ -0,0 +1,666 @@
|
||||
#ifdef USE_SYSCALL
|
||||
|
||||
/* code from https://github.com/gwsystems/silverfish/blob/master/runtime/libc/libc_backing.c */
|
||||
#include <runtime.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
// What should we tell the child program its UID and GID are?
|
||||
#define UID 0xFF
|
||||
#define GID 0xFE
|
||||
|
||||
// Elf auxilary vector values (see google for what those are)
|
||||
#define AT_NULL 0
|
||||
#define AT_IGNORE 1
|
||||
#define AT_EXECFD 2
|
||||
#define AT_PHDR 3
|
||||
#define AT_PHENT 4
|
||||
#define AT_PHNUM 5
|
||||
#define AT_PAGESZ 6
|
||||
#define AT_BASE 7
|
||||
#define AT_FLAGS 8
|
||||
#define AT_ENTRY 9
|
||||
#define AT_NOTELF 10
|
||||
#define AT_UID 11
|
||||
#define AT_EUID 12
|
||||
#define AT_GID 13
|
||||
#define AT_EGID 14
|
||||
#define AT_CLKTCK 17
|
||||
#define AT_SECURE 23
|
||||
#define AT_BASE_PLATFORM 24
|
||||
#define AT_RANDOM 25
|
||||
|
||||
// offset = a WASM ptr to memory the runtime can use
|
||||
void
|
||||
stub_init(char *program_name, i32 offset, mod_init_libc_fn_t libcfn)
|
||||
{
|
||||
printf("Don't think we should reinit libc! so ignore for now!\n");
|
||||
}
|
||||
|
||||
// Emulated syscall implementations
|
||||
|
||||
// We define our own syscall numbers, because WASM uses x86_64 values even on systems that are not x86_64
|
||||
#define SYS_READ 0
|
||||
u32
|
||||
wasm_read(i32 filedes, i32 buf_offset, i32 nbyte)
|
||||
{
|
||||
char* buf = get_memory_ptr_void(buf_offset, nbyte);
|
||||
i32 res = (i32) read(filedes, buf, nbyte);
|
||||
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_WRITE 1
|
||||
i32
|
||||
wasm_write(i32 fd, i32 buf_offset, i32 buf_size)
|
||||
{
|
||||
char* buf = get_memory_ptr_void(buf_offset, buf_size);
|
||||
i32 res = (i32) write(fd, buf, buf_size);
|
||||
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define WO_RDONLY 00
|
||||
#define WO_WRONLY 01
|
||||
#define WO_RDWR 02
|
||||
#define WO_CREAT 0100
|
||||
#define WO_EXCL 0200
|
||||
#define WO_NOCTTY 0400
|
||||
#define WO_TRUNC 01000
|
||||
#define WO_APPEND 02000
|
||||
#define WO_NONBLOCK 04000
|
||||
#define WO_DSYNC 010000
|
||||
#define WO_SYNC 04010000
|
||||
#define WO_RSYNC 04010000
|
||||
#define WO_DIRECTORY 0200000
|
||||
#define WO_NOFOLLOW 0400000
|
||||
#define WO_CLOEXEC 02000000
|
||||
|
||||
|
||||
#define SYS_OPEN 2
|
||||
i32
|
||||
wasm_open(i32 path_off, i32 flags, i32 mode)
|
||||
{
|
||||
char* path = get_memory_string(path_off);
|
||||
|
||||
i32 modified_flags = 0;
|
||||
|
||||
if (flags & WO_RDONLY) {
|
||||
modified_flags |= O_RDONLY;
|
||||
flags ^= WO_RDONLY;
|
||||
}
|
||||
|
||||
if (flags & WO_WRONLY) {
|
||||
modified_flags |= O_WRONLY;
|
||||
flags ^= WO_WRONLY;
|
||||
}
|
||||
|
||||
if (flags & WO_RDWR) {
|
||||
modified_flags |= O_RDWR;
|
||||
flags ^= WO_RDWR;
|
||||
}
|
||||
|
||||
if (flags & WO_APPEND) {
|
||||
modified_flags |= O_APPEND;
|
||||
flags ^= WO_APPEND;
|
||||
}
|
||||
|
||||
if (flags & WO_CREAT) {
|
||||
modified_flags |= O_CREAT;
|
||||
flags ^= WO_CREAT;
|
||||
}
|
||||
|
||||
if (flags & WO_EXCL) {
|
||||
modified_flags |= O_EXCL;
|
||||
flags ^= WO_EXCL;
|
||||
}
|
||||
|
||||
i32 res = (i32) open(path, modified_flags, mode);
|
||||
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
#define SYS_CLOSE 3
|
||||
i32
|
||||
wasm_close(i32 fd)
|
||||
{
|
||||
i32 res = (i32) close(fd);
|
||||
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// What the wasm stat structure looks like
|
||||
struct wasm_stat {
|
||||
i64 st_dev;
|
||||
u64 st_ino;
|
||||
u32 st_nlink;
|
||||
|
||||
u32 st_mode;
|
||||
u32 st_uid;
|
||||
u32 st_gid;
|
||||
u32 __pad0;
|
||||
u64 st_rdev;
|
||||
u64 st_size;
|
||||
i32 st_blksize;
|
||||
i64 st_blocks;
|
||||
|
||||
struct { i32 tv_sec; i32 tv_nsec; } st_atim;
|
||||
struct { i32 tv_sec; i32 tv_nsec; } st_mtim;
|
||||
struct { i32 tv_sec; i32 tv_nsec; } st_ctim;
|
||||
i32 __pad1[3];
|
||||
};
|
||||
|
||||
#define SYS_STAT 4
|
||||
// What the OSX stat structure looks like:
|
||||
// struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */
|
||||
// dev_t st_dev; /* device inode resides on */
|
||||
// ino_t st_ino; /* inode's number */
|
||||
// mode_t st_mode; /* inode protection mode */
|
||||
// nlink_t st_nlink; /* number of hard links to the file */
|
||||
// uid_t st_uid; /* user-id of owner */
|
||||
// gid_t st_gid; /* group-id of owner */
|
||||
// dev_t st_rdev; /* device type, for special file inode */
|
||||
// struct timespec st_atimespec; /* time of last access */
|
||||
// struct timespec st_mtimespec; /* time of last data modification */
|
||||
// struct timespec st_ctimespec; /* time of last file status change */
|
||||
// off_t st_size; /* file size, in bytes */
|
||||
// quad_t st_blocks; /* blocks allocated for file */
|
||||
// u_long st_blksize;/* optimal file sys I/O ops blocksize */
|
||||
// u_long st_flags; /* user defined flags for file */
|
||||
// u_long st_gen; /* file generation number */
|
||||
// };
|
||||
|
||||
i32
|
||||
wasm_stat(u32 path_str_offset, i32 stat_offset)
|
||||
{
|
||||
char *path = get_memory_string(path_str_offset);
|
||||
struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat));
|
||||
|
||||
struct stat stat;
|
||||
i32 res = lstat(path, &stat);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
*stat_ptr = (struct wasm_stat) {
|
||||
.st_dev = stat.st_dev,
|
||||
.st_ino = stat.st_ino,
|
||||
.st_nlink = stat.st_nlink,
|
||||
.st_mode = stat.st_mode,
|
||||
.st_uid = stat.st_uid,
|
||||
.st_gid = stat.st_gid,
|
||||
.st_rdev = stat.st_rdev,
|
||||
.st_size = stat.st_size,
|
||||
.st_blksize = stat.st_blksize,
|
||||
.st_blocks = stat.st_blocks,
|
||||
};
|
||||
#ifdef __APPLE__
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec;
|
||||
#else
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_FSTAT 5
|
||||
i32
|
||||
wasm_fstat(i32 filedes, i32 stat_offset)
|
||||
{
|
||||
struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat));
|
||||
|
||||
struct stat stat;
|
||||
i32 res = fstat(filedes, &stat);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
*stat_ptr = (struct wasm_stat) {
|
||||
.st_dev = stat.st_dev,
|
||||
.st_ino = stat.st_ino,
|
||||
.st_nlink = stat.st_nlink,
|
||||
.st_mode = stat.st_mode,
|
||||
.st_uid = stat.st_uid,
|
||||
.st_gid = stat.st_gid,
|
||||
.st_rdev = stat.st_rdev,
|
||||
.st_size = stat.st_size,
|
||||
.st_blksize = stat.st_blksize,
|
||||
.st_blocks = stat.st_blocks,
|
||||
};
|
||||
#ifdef __APPLE__
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec;
|
||||
#else
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_LSTAT 6
|
||||
i32
|
||||
wasm_lstat(i32 path_str_offset, i32 stat_offset)
|
||||
{
|
||||
char *path = get_memory_string(path_str_offset);
|
||||
struct wasm_stat* stat_ptr = get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat));
|
||||
|
||||
struct stat stat;
|
||||
i32 res = lstat(path, &stat);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
*stat_ptr = (struct wasm_stat) {
|
||||
.st_dev = stat.st_dev,
|
||||
.st_ino = stat.st_ino,
|
||||
.st_nlink = stat.st_nlink,
|
||||
.st_mode = stat.st_mode,
|
||||
.st_uid = stat.st_uid,
|
||||
.st_gid = stat.st_gid,
|
||||
.st_rdev = stat.st_rdev,
|
||||
.st_size = stat.st_size,
|
||||
.st_blksize = stat.st_blksize,
|
||||
.st_blocks = stat.st_blocks,
|
||||
};
|
||||
#ifdef __APPLE__
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec;
|
||||
#else
|
||||
stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec;
|
||||
stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec;
|
||||
|
||||
stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec;
|
||||
stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec;
|
||||
|
||||
stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec;
|
||||
stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
#define SYS_LSEEK 8
|
||||
i32
|
||||
wasm_lseek(i32 filedes, i32 file_offset, i32 whence)
|
||||
{
|
||||
i32 res = (i32) lseek(filedes, file_offset, whence);
|
||||
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_MMAP 9
|
||||
u32
|
||||
wasm_mmap(i32 addr, i32 len, i32 prot, i32 flags, i32 fd, i32 offset)
|
||||
{
|
||||
if (addr != 0) {
|
||||
printf("parameter void *addr is not supported!\n");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
printf("file mapping is not supported!\n");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
assert(len % WASM_PAGE_SIZE == 0);
|
||||
|
||||
i32 result = sandbox_lmbound;
|
||||
for (int i = 0; i < len / WASM_PAGE_SIZE; i++) {
|
||||
expand_memory();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define SYS_MUNMAP 11
|
||||
|
||||
#define SYS_BRK 12
|
||||
|
||||
#define SYS_RT_SIGACTION 13
|
||||
|
||||
#define SYS_RT_SIGPROGMASK 14
|
||||
|
||||
#define SYS_IOCTL 16
|
||||
i32
|
||||
wasm_ioctl(i32 fd, i32 request, i32 data_offet)
|
||||
{
|
||||
// musl libc does some ioctls to stdout, so just allow these to silently go through
|
||||
// FIXME: The above is idiotic
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SYS_READV 19
|
||||
struct wasm_iovec {
|
||||
i32 base_offset;
|
||||
i32 len;
|
||||
};
|
||||
|
||||
i32
|
||||
wasm_readv(i32 fd, i32 iov_offset, i32 iovcnt)
|
||||
{
|
||||
i32 read = 0;
|
||||
struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec));
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
read += wasm_read(fd, iov[i].base_offset, iov[i].len);
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
#define SYS_WRITEV 20
|
||||
i32
|
||||
wasm_writev(i32 fd, i32 iov_offset, i32 iovcnt)
|
||||
{
|
||||
struct wasm_iovec *iov = get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec));
|
||||
|
||||
// If we aren't on MUSL, pass writev to printf if possible
|
||||
#if defined(__APPLE__) || defined(__GLIBC__)
|
||||
if (fd == 1) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
i32 len = iov[i].len;
|
||||
void* ptr = get_memory_ptr_void(iov[i].base_offset, len);
|
||||
|
||||
printf("%.*s", len, ptr);
|
||||
sum += len;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct iovec vecs[iovcnt];
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
i32 len = iov[i].len;
|
||||
void* ptr = get_memory_ptr_void(iov[i].base_offset, len);
|
||||
vecs[i] = (struct iovec) {ptr, len};
|
||||
}
|
||||
|
||||
i32 res = (i32) writev(fd, vecs, iovcnt);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_MADVISE 28
|
||||
|
||||
#define SYS_GETPID 39
|
||||
u32
|
||||
wasm_getpid()
|
||||
{
|
||||
return (u32) getpid();
|
||||
}
|
||||
|
||||
|
||||
#define WF_DUPFD 0
|
||||
#define WF_GETFD 1
|
||||
#define WF_SETFD 2
|
||||
#define WF_GETFL 3
|
||||
#define WF_SETFL 4
|
||||
|
||||
#define WF_SETOWN 8
|
||||
#define WF_GETOWN 9
|
||||
#define WF_SETSIG 10
|
||||
#define WF_GETSIG 11
|
||||
|
||||
#define WF_GETLK 5
|
||||
#define WF_SETLK 6
|
||||
#define WF_SETLKW 7
|
||||
|
||||
#define SYS_FCNTL 72
|
||||
u32
|
||||
wasm_fcntl(u32 fd, u32 cmd, u32 arg_or_lock_ptr)
|
||||
{
|
||||
switch (cmd) {
|
||||
case WF_SETFD:
|
||||
// return fcntl(fd, F_SETFD, arg_or_lock_ptr);
|
||||
return 0;
|
||||
case WF_SETLK:
|
||||
return 0;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define SYS_FSYNC 74
|
||||
u32
|
||||
wasm_fsync(u32 filedes)
|
||||
{
|
||||
u32 res = fsync(filedes);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SYS_GETCWD 79
|
||||
u32
|
||||
wasm_getcwd(u32 buf_offset, u32 buf_size)
|
||||
{
|
||||
char* buf = get_memory_ptr_void(buf_offset, buf_size);
|
||||
char* res = getcwd(buf, buf_size);
|
||||
|
||||
if (!res) return 0;
|
||||
return buf_offset;
|
||||
}
|
||||
|
||||
#define SYS_UNLINK 87
|
||||
u32
|
||||
wasm_unlink(u32 path_str_offset)
|
||||
{
|
||||
char* str = get_memory_string(path_str_offset);
|
||||
u32 res = unlink(str);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SYS_GETEUID 107
|
||||
u32
|
||||
wasm_geteuid()
|
||||
{
|
||||
return (u32) geteuid();
|
||||
}
|
||||
|
||||
#define SYS_SET_THREAD_AREA 205
|
||||
|
||||
#define SYS_SET_TID_ADDRESS 218
|
||||
|
||||
#define SYS_GET_TIME 228
|
||||
struct wasm_time_spec {
|
||||
u32 sec;
|
||||
u32 nanosec;
|
||||
};
|
||||
|
||||
i32
|
||||
wasm_get_time(i32 clock_id, i32 timespec_off)
|
||||
{
|
||||
clockid_t real_clock;
|
||||
switch (clock_id) {
|
||||
case 0:
|
||||
real_clock = CLOCK_REALTIME;
|
||||
break;
|
||||
case 1:
|
||||
real_clock = CLOCK_MONOTONIC;
|
||||
break;
|
||||
case 2:
|
||||
real_clock = CLOCK_PROCESS_CPUTIME_ID;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
struct wasm_time_spec* timespec = get_memory_ptr_void(timespec_off, sizeof(struct wasm_time_spec));
|
||||
|
||||
struct timespec native_timespec = { 0, 0 };
|
||||
int res = clock_gettime(real_clock, &native_timespec);
|
||||
if (res == -1) return -errno;
|
||||
|
||||
timespec->sec = native_timespec.tv_sec;
|
||||
timespec->nanosec = native_timespec.tv_nsec;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SYS_EXIT_GROUP 231
|
||||
i32
|
||||
wasm_exit_group(i32 status)
|
||||
{
|
||||
exit(status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SYS_FCHOWN 93
|
||||
i32
|
||||
wasm_fchown(i32 fd, u32 owner, u32 group)
|
||||
{
|
||||
return fchown(fd, owner, group);
|
||||
}
|
||||
|
||||
// networking syscalls
|
||||
#define SYS_SOCKET 41
|
||||
#define SYS_CONNECT 42
|
||||
#define SYS_ACCEPT 43
|
||||
#define SYS_BIND 49
|
||||
#define SYS_LISTEN 50
|
||||
i32
|
||||
wasm_socket(i32 domain, i32 type, i32 protocol)
|
||||
{
|
||||
return socket(domain, type, protocol);
|
||||
}
|
||||
|
||||
i32
|
||||
wasm_connect(i32 sockfd, i32 sockaddr_offset, i32 addrlen)
|
||||
{
|
||||
return connect(sockfd, get_memory_ptr_void(sockaddr_offset, addrlen), addrlen);
|
||||
}
|
||||
|
||||
i32
|
||||
wasm_accept(i32 sockfd, i32 sockaddr_offset, i32 addrlen_offset)
|
||||
{
|
||||
socklen_t *addrlen = get_memory_ptr_void(addrlen_offset, sizeof(socklen_t));
|
||||
|
||||
return accept(sockfd, get_memory_ptr_void(sockaddr_offset, *addrlen), addrlen);
|
||||
}
|
||||
|
||||
i32
|
||||
wasm_bind(i32 sockfd, i32 sockaddr_offset, i32 addrlen)
|
||||
{
|
||||
return bind(sockfd, get_memory_ptr_void(sockaddr_offset, addrlen), addrlen);
|
||||
}
|
||||
|
||||
i32
|
||||
wasm_listen(i32 sockfd, i32 backlog)
|
||||
{
|
||||
return listen(sockfd, backlog);
|
||||
}
|
||||
|
||||
#define SYS_SENDTO 44
|
||||
#define SYS_RECVFROM 45
|
||||
|
||||
i32
|
||||
wasm_sendto(i32 fd, i32 buff_offset, i32 len, i32 flags, i32 sockaddr_offset, i32 sockaddr_len)
|
||||
{
|
||||
char *buf = get_memory_ptr_void(buff_offset, len);
|
||||
struct sockaddr *addr = sockaddr_len ? get_memory_ptr_void(sockaddr_offset, sockaddr_len) : NULL;
|
||||
|
||||
return sendto(fd, buf, len, flags, addr, sockaddr_len);
|
||||
}
|
||||
|
||||
i32
|
||||
wasm_recvfrom(i32 fd, i32 buff_offset, i32 size, i32 flags, i32 sockaddr_offset, i32 socklen_offset)
|
||||
{
|
||||
char *buf = get_memory_ptr_void(buff_offset, size);
|
||||
socklen_t *len = get_memory_ptr_void(socklen_offset, sizeof(socklen_t));
|
||||
struct sockaddr *addr = *len ? get_memory_ptr_void(sockaddr_offset, *len) : NULL;
|
||||
|
||||
return recvfrom(fd, buf, size, flags, addr, addr ? len : NULL);
|
||||
}
|
||||
|
||||
i32
|
||||
inner_syscall_handler(i32 n, i32 a, i32 b, i32 c, i32 d, i32 e, i32 f)
|
||||
{
|
||||
i32 res;
|
||||
switch(n) {
|
||||
case SYS_READ: return wasm_read(a, b, c);
|
||||
case SYS_WRITE: return wasm_write(a, b, c);
|
||||
case SYS_OPEN: return wasm_open(a, b, c);
|
||||
case SYS_CLOSE: return wasm_close(a);
|
||||
case SYS_STAT: return wasm_stat(a, b);
|
||||
case SYS_FSTAT: return wasm_fstat(a, b);
|
||||
case SYS_LSTAT: return wasm_lstat(a, b);
|
||||
case SYS_LSEEK: return wasm_lseek(a, b, c);
|
||||
case SYS_MMAP: return wasm_mmap(a, b, c, d, e, f);
|
||||
case SYS_MUNMAP: return 0;
|
||||
case SYS_BRK: return 0;
|
||||
case SYS_RT_SIGACTION: return 0;
|
||||
case SYS_RT_SIGPROGMASK: return 0;
|
||||
case SYS_IOCTL: return wasm_ioctl(a, b, c);
|
||||
case SYS_READV: return wasm_readv(a, b, c);
|
||||
case SYS_WRITEV: return wasm_writev(a, b, c);
|
||||
case SYS_MADVISE: return 0;
|
||||
case SYS_GETPID: return wasm_getpid();
|
||||
case SYS_FCNTL: return wasm_fcntl(a, b, c);
|
||||
case SYS_FSYNC: return wasm_fsync(a);
|
||||
case SYS_UNLINK: return wasm_unlink(a);
|
||||
case SYS_GETCWD: return wasm_getcwd(a, b);
|
||||
case SYS_GETEUID: return wasm_geteuid();
|
||||
case SYS_SET_THREAD_AREA: return 0;
|
||||
case SYS_SET_TID_ADDRESS: return 0;
|
||||
case SYS_GET_TIME: return wasm_get_time(a, b);
|
||||
case SYS_EXIT_GROUP: return wasm_exit_group(a);
|
||||
case SYS_FCHOWN: return wasm_fchown(a, b, c);
|
||||
|
||||
case SYS_SOCKET: return wasm_socket(a, b, c);
|
||||
case SYS_CONNECT: return wasm_connect(a, b, c);
|
||||
case SYS_ACCEPT: return wasm_accept(a, b, c);
|
||||
case SYS_BIND: return wasm_bind(a, b, c);
|
||||
case SYS_LISTEN: return wasm_listen(a, b);
|
||||
case SYS_SENDTO: return wasm_sendto(a, b, c, d, e, f);
|
||||
case SYS_RECVFROM: return wasm_recvfrom(a, b, c, d, e, f);
|
||||
}
|
||||
printf("syscall %d (%d, %d, %d, %d, %d, %d)\n", n, a, b, c, d, e, f);
|
||||
assert(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,100 @@
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <runtime.h>
|
||||
#include <sandbox.h>
|
||||
#include <softint.h>
|
||||
#include <util.h>
|
||||
|
||||
#define MOD_LINE_MAX 1024
|
||||
|
||||
i32 logfd = -1;
|
||||
|
||||
u32 ncores = 0, sbox_ncores = 0, sbox_core_st = 0;
|
||||
|
||||
static void
|
||||
usage(char *cmd)
|
||||
{
|
||||
printf("%s <modules_file>\n", cmd);
|
||||
debuglog("%s <modules_file>\n", cmd);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int i = 0, rtthd_ret[SBOX_NCORES] = { 0 };
|
||||
pthread_t rtthd[SBOX_NCORES];
|
||||
|
||||
if (argc != 2) {
|
||||
usage(argv[0]);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ncores = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
if (ncores > 1) {
|
||||
u32 x = ncores - 1;
|
||||
sbox_ncores = SBOX_NCORES;
|
||||
if (x < SBOX_NCORES) sbox_ncores = x;
|
||||
sbox_core_st = 1;
|
||||
} else {
|
||||
sbox_ncores = 1;
|
||||
}
|
||||
debuglog("Number of cores %u, sandboxing cores %u (start: %u) and module reqs %u\n", ncores, sbox_ncores, sbox_core_st, MOD_REQ_CORE);
|
||||
|
||||
#ifdef NOSTDIO
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
fclose(stdin);
|
||||
logfd = open(LOGFILE, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU | S_IRWXG);
|
||||
if (logfd < 0) {
|
||||
perror("open");
|
||||
exit(-1);
|
||||
}
|
||||
#else
|
||||
logfd = 1;
|
||||
#endif
|
||||
|
||||
runtime_init();
|
||||
debuglog("Parsing modules file [%s]\n", argv[1]);
|
||||
if (util_parse_modules_file_json(argv[1])) {
|
||||
// if (util_parse_modules_file_custom(argv[1])) {
|
||||
printf("failed to parse modules file[%s]\n", argv[1]);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < sbox_ncores; i++) {
|
||||
int ret = pthread_create(&rtthd[i], NULL, sandbox_run_func, (void *)&rtthd_ret[i]);
|
||||
if (ret) {
|
||||
errno = ret;
|
||||
perror("pthread_create");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
cpu_set_t cs;
|
||||
CPU_ZERO(&cs);
|
||||
CPU_SET(sbox_core_st + i, &cs);
|
||||
ret = pthread_setaffinity_np(rtthd[i], sizeof(cs), &cs);
|
||||
assert(ret == 0);
|
||||
}
|
||||
debuglog("Sandboxing environment ready!\n");
|
||||
|
||||
for (i = 0; i < sbox_ncores; i++) {
|
||||
int ret = pthread_join(rtthd[i], NULL);
|
||||
if (ret) {
|
||||
errno = ret;
|
||||
perror("pthread_join");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// runtime threads run forever!! so join should not return!!
|
||||
printf("\nOh no..!! This can't be happening..!!\n");
|
||||
exit(-1);
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/* Code from https://github.com/gwsystems/silverfish/blob/master/runtime/memory/64bit_nix.c */
|
||||
#include <runtime.h>
|
||||
#include <sandbox.h>
|
||||
|
||||
#ifdef USE_MEM_VM
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define MAX_LINEAR_MEM ((1LL << 32) + WASM_PAGE_SIZE)
|
||||
|
||||
void
|
||||
alloc_linear_memory(void)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
|
||||
// Map 4gb + PAGE_SIZE of memory that will fault when accessed
|
||||
// We allocate the extra page so that reads off the end will also fail
|
||||
sandbox_lmbase = mmap(NULL, MAX_LINEAR_MEM, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (sandbox_lmbase == MAP_FAILED) {
|
||||
perror("Mapping of initial unusable region failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *map_result = mmap(sandbox_lmbase, WASM_PAGE_SIZE * WASM_START_PAGES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if (map_result == MAP_FAILED) {
|
||||
perror("Mapping of initial memory failed");
|
||||
exit(1);
|
||||
}
|
||||
sandbox_lmbound = WASM_PAGE_SIZE * WASM_START_PAGES;
|
||||
|
||||
curr->linear_start = sandbox_lmbase;
|
||||
curr->linear_size = sandbox_lmbound;
|
||||
}
|
||||
|
||||
void
|
||||
free_linear_memory(void *base, u32 bound, u32 max)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
|
||||
assert(base && bound);
|
||||
// cannot free currently executing sandbox's memory
|
||||
assert(base != curr->linear_start || base != sandbox_lmbase);
|
||||
|
||||
int ret = munmap(base, MAX_LINEAR_MEM);
|
||||
if (ret) perror("munmap");
|
||||
}
|
||||
|
||||
void
|
||||
expand_memory(void)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
|
||||
// max_pages = 0 => no limit: FIXME
|
||||
assert(sandbox_lmbound / WASM_PAGE_SIZE < WASM_MAX_PAGES);
|
||||
// Remap the relevant wasm page to readable
|
||||
char *mem_as_chars = sandbox_lmbase;
|
||||
char *page_address = &mem_as_chars[sandbox_lmbound];
|
||||
|
||||
void *map_result = mmap(page_address, WASM_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if (map_result == MAP_FAILED) {
|
||||
perror("Mapping of new memory failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// TODO: check curr->linear_max_size
|
||||
sandbox_lmbound += WASM_PAGE_SIZE;
|
||||
curr->linear_size = sandbox_lmbound;
|
||||
}
|
||||
|
||||
INLINE char *
|
||||
get_memory_ptr_for_runtime(u32 offset, u32 bounds_check)
|
||||
{
|
||||
// Due to how we setup memory for x86, the virtual memory mechanism will catch the error, if bounds < WASM_PAGE_SIZE
|
||||
assert(bounds_check < WASM_PAGE_SIZE ||
|
||||
(sandbox_lmbound > bounds_check &&
|
||||
offset <= sandbox_lmbound - bounds_check));
|
||||
|
||||
char *mem_as_chars = (char *)sandbox_lmbase;
|
||||
char *address = &mem_as_chars[offset];
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
#else
|
||||
#error "Incorrect runtime memory module!"
|
||||
#endif
|
@ -0,0 +1,35 @@
|
||||
#include <runtime.h>
|
||||
|
||||
__thread struct indirect_table_entry *module_indirect_table = NULL;
|
||||
__thread void *sandbox_lmbase = NULL;
|
||||
__thread u32 sandbox_lmbound = 0;
|
||||
|
||||
// Region initialization helper function
|
||||
EXPORT void
|
||||
initialize_region(u32 offset, u32 data_count, char* data)
|
||||
{
|
||||
assert(sandbox_lmbound >= data_count);
|
||||
assert(offset < sandbox_lmbound - data_count);
|
||||
|
||||
// FIXME: Hack around segmented and unsegmented access
|
||||
memcpy(get_memory_ptr_for_runtime(offset, data_count), data, data_count);
|
||||
}
|
||||
|
||||
void
|
||||
add_function_to_table(u32 idx, u32 type_id, char* pointer)
|
||||
{
|
||||
assert(idx < INDIRECT_TABLE_SIZE);
|
||||
|
||||
// TODO: atomic for multiple concurrent invocations?
|
||||
if (module_indirect_table[idx].type_id == type_id && module_indirect_table[idx].func_pointer == pointer) return;
|
||||
|
||||
module_indirect_table[idx] = (struct indirect_table_entry) { .type_id = type_id, .func_pointer = pointer };
|
||||
}
|
||||
|
||||
// If we are using runtime globals, we need to populate them
|
||||
WEAK void
|
||||
populate_globals()
|
||||
{
|
||||
assert(0); // FIXME: is this used in WASM as dynamic modules?
|
||||
}
|
||||
|
@ -0,0 +1,137 @@
|
||||
#include <runtime.h>
|
||||
#include <module.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <uv.h>
|
||||
#include <util.h>
|
||||
|
||||
static struct module *__mod_db[MOD_MAX] = { NULL };
|
||||
static int __mod_free_off = 0;
|
||||
|
||||
// todo: optimize this.. do we care? plus not atomic!!
|
||||
struct module *
|
||||
module_find(char *name)
|
||||
{
|
||||
int f = __mod_free_off;
|
||||
for (int i = 0; i < f; i++) {
|
||||
assert(__mod_db[i]);
|
||||
if (strcmp(__mod_db[i]->name, name) == 0) return __mod_db[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
module_add(struct module *m)
|
||||
{
|
||||
assert(module_find(m->name) == NULL);
|
||||
|
||||
int f = __sync_fetch_and_add(&__mod_free_off, 1);
|
||||
assert(f < MOD_MAX);
|
||||
__mod_db[f] = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
module_on_recv(uv_udp_t *h, ssize_t nr, const uv_buf_t *rcvbuf, const struct sockaddr *addr, unsigned flags)
|
||||
{
|
||||
if (nr <= 0) goto done;
|
||||
|
||||
debuglog("MC:%s, %s\n", h->data, rcvbuf->base);
|
||||
// invoke a function!
|
||||
struct sandbox *s = util_parse_sandbox_string_json((struct module *)(h->data), rcvbuf->base);
|
||||
//struct sandbox *s = util_parse_sandbox_string_custom((struct module *)(h->data), rcvbuf->base);
|
||||
assert(s);
|
||||
|
||||
done:
|
||||
free(rcvbuf->base);
|
||||
}
|
||||
|
||||
static void
|
||||
module_io_init(struct module *m)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = uv_udp_init(uv_default_loop(), &m->udpsrv);
|
||||
assert(status >= 0);
|
||||
|
||||
debuglog("MIO:%s,%u\n", m->name, m->udpport);
|
||||
uv_ip4_addr("127.0.0.1", m->udpport, &m->srvaddr);
|
||||
status = uv_udp_bind(&m->udpsrv, (const struct sockaddr *)&m->srvaddr, 0);
|
||||
assert(status >= 0);
|
||||
m->udpsrv.data = (void *)m;
|
||||
|
||||
status = uv_udp_recv_start(&m->udpsrv, runtime_on_alloc, module_on_recv);
|
||||
assert(status >= 0);
|
||||
}
|
||||
|
||||
struct module *
|
||||
module_alloc(char *modname, char *modpath, u32 udp_port, i32 nargs, i32 nrets, u32 stacksz, u32 maxheap, u32 timeout)
|
||||
{
|
||||
// FIXME: cannot do this at runtime, we may be interfering with a sandbox's heap!
|
||||
struct module *mod = (struct module *)malloc(sizeof(struct module));
|
||||
|
||||
if (!mod) return NULL;
|
||||
|
||||
memset(mod, 0, sizeof(struct module));
|
||||
mod->dl_handle = dlopen(modpath, RTLD_LAZY | RTLD_DEEPBIND);
|
||||
if (mod->dl_handle == NULL) goto dl_open_error;
|
||||
|
||||
mod->entry_fn = (mod_main_fn_t)dlsym(mod->dl_handle, MOD_MAIN_FN);
|
||||
if (mod->entry_fn == NULL) goto dl_error;
|
||||
|
||||
// TODO: don't think this is necessary or implemented.
|
||||
//mod->glb_init_fn = (mod_glb_fn_t)dlsym(mod->dl_handle, MOD_GLB_FN);
|
||||
//if (mod->glb_init_fn == NULL) goto dl_error;
|
||||
|
||||
mod->mem_init_fn = (mod_mem_fn_t)dlsym(mod->dl_handle, MOD_MEM_FN);
|
||||
if (mod->mem_init_fn == NULL) goto dl_error;
|
||||
|
||||
mod->tbl_init_fn = (mod_tbl_fn_t)dlsym(mod->dl_handle, MOD_TBL_FN);
|
||||
if (mod->tbl_init_fn == NULL) goto dl_error;
|
||||
|
||||
strncpy(mod->name, modname, MOD_NAME_MAX);
|
||||
strncpy(mod->path, modpath, MOD_PATH_MAX);
|
||||
|
||||
mod->nargs = nargs;
|
||||
/* mod->nrets = nrets; */
|
||||
mod->stack_size = stacksz == 0 ? WASM_STACK_SIZE : stacksz;
|
||||
mod->max_memory = maxheap == 0 ? ((u64)WASM_PAGE_SIZE * WASM_MAX_PAGES) : maxheap;
|
||||
mod->timeout = timeout;
|
||||
|
||||
struct indirect_table_entry *cache_tbl = module_indirect_table;
|
||||
// assumption: modules are created before enabling preemption and before running runtime-sandboxing threads..
|
||||
// if this isn't a good assumption, just let the first invocation do table init..!!
|
||||
assert(cache_tbl == NULL);
|
||||
module_indirect_table = mod->indirect_table;
|
||||
module_table_init(mod);
|
||||
module_indirect_table = cache_tbl;
|
||||
mod->udpport = udp_port;
|
||||
module_add(mod);
|
||||
module_io_init(mod);
|
||||
|
||||
return mod;
|
||||
|
||||
dl_error:
|
||||
dlclose(mod->dl_handle);
|
||||
|
||||
dl_open_error:
|
||||
free(mod);
|
||||
debuglog("%s\n", dlerror());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
module_free(struct module *mod)
|
||||
{
|
||||
if (mod == NULL) return;
|
||||
if (mod->dl_handle == NULL) return;
|
||||
if (mod->refcnt) return;
|
||||
|
||||
dlclose(mod->dl_handle);
|
||||
memset(mod, 0, sizeof(struct module));
|
||||
|
||||
// FIXME: use global/static memory. cannot interfere with some sandbox's heap!
|
||||
free(mod);
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
#include <runtime.h>
|
||||
#include <types.h>
|
||||
#include <sandbox.h>
|
||||
#include <arch/context.h>
|
||||
#include <module.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <softint.h>
|
||||
#include <uv.h>
|
||||
|
||||
// global queue for stealing! TODO: work-stealing-deque
|
||||
static struct ps_list_head glbq;
|
||||
static pthread_mutex_t glbq_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// per-thread (per-core) run and wait queues.. (using doubly-linked-lists)
|
||||
__thread static struct ps_list_head runq;
|
||||
__thread static struct ps_list_head waitq;
|
||||
|
||||
// current sandbox that is active..
|
||||
__thread sandbox_t *current_sandbox = NULL;
|
||||
|
||||
// context pointer to switch to when this thread gets a SIGUSR1
|
||||
__thread arch_context_t *next_context = NULL;
|
||||
|
||||
// context of the runtime thread before running sandboxes or to resume its "main".
|
||||
__thread arch_context_t base_context;
|
||||
|
||||
// libuv i/o loop handle per sandboxing thread!
|
||||
__thread uv_loop_t uvio;
|
||||
|
||||
static inline void
|
||||
sandbox_local_run(struct sandbox *s)
|
||||
{
|
||||
assert(ps_list_singleton_d(s));
|
||||
|
||||
ps_list_head_append_d(&runq, s);
|
||||
}
|
||||
|
||||
static inline int
|
||||
sandbox_pull(void)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (n < SBOX_PULL_MAX) {
|
||||
pthread_mutex_lock(&glbq_mtx);
|
||||
if (ps_list_head_empty(&glbq)) {
|
||||
pthread_mutex_unlock(&glbq_mtx);
|
||||
break;
|
||||
}
|
||||
struct sandbox *g = ps_list_head_first_d(&glbq, struct sandbox);
|
||||
|
||||
assert(g);
|
||||
ps_list_rem_d(g);
|
||||
pthread_mutex_unlock(&glbq_mtx);
|
||||
debuglog("[%p: %s]\n", g, g->mod->name);
|
||||
assert(g->state == SANDBOX_RUNNABLE);
|
||||
sandbox_local_run(g);
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static __thread unsigned int in_callback;
|
||||
|
||||
void
|
||||
sandbox_io_nowait(void)
|
||||
{
|
||||
#ifdef USE_UVIO
|
||||
// non-zero if more callbacks are expected
|
||||
in_callback = 1;
|
||||
int n = uv_run(runtime_uvio(), UV_RUN_NOWAIT), i = 0;
|
||||
while (n > 0) {
|
||||
n--;
|
||||
uv_run(runtime_uvio(), UV_RUN_NOWAIT);
|
||||
}
|
||||
in_callback = 0;
|
||||
#endif
|
||||
// zero, so there is nothing (don't block!)
|
||||
}
|
||||
|
||||
struct sandbox *
|
||||
sandbox_schedule(void)
|
||||
{
|
||||
if (!in_callback) sandbox_io_nowait();
|
||||
|
||||
struct sandbox *s = NULL;
|
||||
if (ps_list_head_empty(&runq)) {
|
||||
if (sandbox_pull() == 0) {
|
||||
//debuglog("[null: null]\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
s = ps_list_head_first_d(&runq, struct sandbox);
|
||||
|
||||
// round-robin
|
||||
ps_list_rem_d(s);
|
||||
ps_list_head_append_d(&runq, s);
|
||||
debuglog("[%p: %s]\n", s, s->mod->name);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
sandbox_wakeup(sandbox_t *s)
|
||||
{
|
||||
debuglog("[%p: %s]\n", s, s->mod->name);
|
||||
// perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist..
|
||||
assert(ps_list_singleton_d(s));
|
||||
s->state = SANDBOX_RUNNABLE;
|
||||
ps_list_head_append_d(&runq, s);
|
||||
}
|
||||
|
||||
void
|
||||
sandbox_block(void)
|
||||
{
|
||||
// perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist..
|
||||
softint_disable();
|
||||
struct sandbox *c = sandbox_current();
|
||||
ps_list_rem_d(c);
|
||||
softint_enable();
|
||||
debuglog("[%p: %s]\n", c, c->mod->name);
|
||||
c->state = SANDBOX_BLOCKED;
|
||||
struct sandbox *s = sandbox_schedule();
|
||||
sandbox_switch(s);
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) __attribute__((noreturn))
|
||||
sandbox_switch_preempt(void)
|
||||
{
|
||||
pthread_kill(pthread_self(), SIGUSR1);
|
||||
|
||||
assert(0); // should not get here..
|
||||
while (1) ;
|
||||
}
|
||||
static inline void
|
||||
sandbox_local_stop(struct sandbox *s)
|
||||
{
|
||||
ps_list_rem_d(s);
|
||||
}
|
||||
|
||||
void *
|
||||
sandbox_run_func(void *data)
|
||||
{
|
||||
arch_context_init(&base_context, 0, 0);
|
||||
|
||||
ps_list_head_init(&runq);
|
||||
ps_list_head_init(&waitq);
|
||||
softint_off = 0;
|
||||
next_context = NULL;
|
||||
#ifndef PREEMPT_DISABLE
|
||||
softint_unmask(SIGALRM);
|
||||
softint_unmask(SIGUSR1);
|
||||
#endif
|
||||
uv_loop_init(&uvio);
|
||||
in_callback = 0;
|
||||
|
||||
while (1) {
|
||||
struct sandbox *s = sandbox_schedule();
|
||||
while (s) {
|
||||
sandbox_switch(s);
|
||||
s = sandbox_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
*(int *)data = -1;
|
||||
pthread_exit(data);
|
||||
}
|
||||
|
||||
void
|
||||
sandbox_run(struct sandbox *s)
|
||||
{
|
||||
// for now, a pull model...
|
||||
// sandbox_run adds to the global ready queue..
|
||||
// each sandboxing thread pulls off of that global ready queue..
|
||||
debuglog("[%p: %s]\n", s, s->mod->name);
|
||||
s->state = SANDBOX_RUNNABLE;
|
||||
pthread_mutex_lock(&glbq_mtx);
|
||||
ps_list_head_append_d(&glbq, s);
|
||||
pthread_mutex_unlock(&glbq_mtx);
|
||||
}
|
||||
|
||||
// perhaps respond to request
|
||||
void
|
||||
sandbox_exit(void)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
|
||||
assert(curr);
|
||||
|
||||
debuglog("[%p: %s]\n", curr, curr->mod->name);
|
||||
sandbox_local_stop(curr);
|
||||
curr->state = SANDBOX_RETURNED;
|
||||
// TODO: free resources here? or only from main?
|
||||
sandbox_switch(sandbox_schedule());
|
||||
}
|
||||
|
||||
void *
|
||||
runtime_uvio_thdfn(void *d)
|
||||
{
|
||||
assert(d == (void *)uv_default_loop());
|
||||
while (1) {
|
||||
// runs until there are no events..
|
||||
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
pthread_yield();
|
||||
}
|
||||
|
||||
assert(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
runtime_init(void)
|
||||
{
|
||||
ps_list_head_init(&glbq);
|
||||
|
||||
softint_mask(SIGUSR1);
|
||||
softint_mask(SIGALRM);
|
||||
cpu_set_t cs;
|
||||
|
||||
CPU_ZERO(&cs);
|
||||
CPU_SET(MOD_REQ_CORE, &cs);
|
||||
|
||||
pthread_t iothd;
|
||||
int ret = pthread_create(&iothd, NULL, runtime_uvio_thdfn, (void *)uv_default_loop());
|
||||
assert(ret == 0);
|
||||
ret = pthread_setaffinity_np(iothd, sizeof(cpu_set_t), &cs);
|
||||
assert(ret == 0);
|
||||
ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs);
|
||||
assert(ret == 0);
|
||||
|
||||
softint_init();
|
||||
softint_timer_arm();
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
#include <assert.h>
|
||||
#include <runtime.h>
|
||||
#include <sandbox.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
static inline void
|
||||
sandbox_args_setup(i32 argc)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
char *args = sandbox_args();
|
||||
|
||||
// whatever gregor has, to be able to pass args to a module!
|
||||
curr->args_offset = sandbox_lmbound;
|
||||
assert(sandbox_lmbase == curr->linear_start);
|
||||
expand_memory();
|
||||
|
||||
i32 *array_ptr = get_memory_ptr_void(curr->args_offset, argc * sizeof(i32));
|
||||
i32 string_off = curr->args_offset + (argc * sizeof(i32));
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
char *arg = args + (i * MOD_ARG_MAX_SZ);
|
||||
size_t str_sz = strlen(arg) + 1;
|
||||
|
||||
array_ptr[i] = string_off;
|
||||
// why get_memory_ptr_for_runtime??
|
||||
strncpy(get_memory_ptr_for_runtime(string_off, strlen(arg) + 1), arg, strlen(arg));
|
||||
|
||||
string_off += str_sz;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sandbox_entry(void)
|
||||
{
|
||||
struct sandbox *curr = sandbox_current();
|
||||
// FIXME: is this right? this is the first time this sandbox is running.. so it wont
|
||||
// return to sandbox_switch() api..
|
||||
// we'd potentially do what we'd in sandbox_switch() api here for cleanup..
|
||||
if (!softint_enabled()) {
|
||||
arch_context_init(&curr->ctxt, 0, 0);
|
||||
next_context = NULL;
|
||||
softint_enable();
|
||||
}
|
||||
struct module *curr_mod = sandbox_module(curr);
|
||||
int argc = module_nargs(curr_mod);
|
||||
|
||||
// for stdio
|
||||
int f = io_handle_open(0);
|
||||
assert(f == 0);
|
||||
f = io_handle_open(1);
|
||||
assert(f == 1);
|
||||
f = io_handle_open(2);
|
||||
assert(f == 2);
|
||||
|
||||
alloc_linear_memory();
|
||||
|
||||
// perhaps only initialized for the first instance? or TODO!
|
||||
//module_table_init(curr_mod);
|
||||
module_memory_init(curr_mod);
|
||||
|
||||
sandbox_args_setup(argc);
|
||||
|
||||
curr->retval = module_entry(curr_mod, argc, curr->args_offset);
|
||||
|
||||
sandbox_exit();
|
||||
}
|
||||
|
||||
struct sandbox *
|
||||
sandbox_alloc(struct module *mod, char *args)
|
||||
{
|
||||
if (!module_is_valid(mod)) return NULL;
|
||||
|
||||
// FIXME: don't use malloc. huge security problem!
|
||||
// perhaps, main should be in its own sandbox, when it is not running any sandbox.
|
||||
struct sandbox *sb = (struct sandbox *)malloc(sizeof(struct sandbox));
|
||||
|
||||
if (!sb) return NULL;
|
||||
|
||||
memset(sb, 0, sizeof(struct sandbox));
|
||||
//actual module instantiation!
|
||||
sb->mod = mod;
|
||||
module_acquire(mod);
|
||||
sb->args = (void *)args;
|
||||
sb->stack_size = mod->stack_size;
|
||||
sb->stack_start = mmap(NULL, sb->stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
|
||||
if (sb->stack_start == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
assert(0);
|
||||
}
|
||||
for (int i = 0; i < SBOX_MAX_OPEN; i++) sb->handles[i].fd = -1;
|
||||
ps_list_init_d(sb);
|
||||
|
||||
arch_context_init(&sb->ctxt, (reg_t)sandbox_entry, (reg_t)(sb->stack_start + sb->stack_size));
|
||||
sandbox_run(sb);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
void
|
||||
sandbox_free(struct sandbox *sb)
|
||||
{
|
||||
// you have to context switch away to free a sandbox.
|
||||
if (!sb || sb == sandbox_current()) return;
|
||||
|
||||
// again sandbox should be done and waiting for the parent.
|
||||
// TODO: this needs to be enhanced. you may be killing a sandbox when its in any other execution states.
|
||||
if (sb->state != SANDBOX_RETURNED) return;
|
||||
module_release(sb->mod);
|
||||
|
||||
free(sb->args);
|
||||
// remove stack! and also heap!
|
||||
int ret = munmap(sb->stack_start, sb->stack_size);
|
||||
if (ret) perror("munmap");
|
||||
|
||||
// depending on the memory type
|
||||
free_linear_memory(sb->linear_start, sb->linear_size, sb->linear_max_size);
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <types.h>
|
||||
#include <sandbox.h>
|
||||
#include <module.h>
|
||||
#include <arch/context.h>
|
||||
#include <softint.h>
|
||||
|
||||
__thread static volatile sig_atomic_t alarm_cnt = 0, usr1_cnt = 0;
|
||||
|
||||
__thread volatile sig_atomic_t softint_off = 0;
|
||||
|
||||
static const int softints[] = {
|
||||
SIGALRM,
|
||||
SIGUSR1
|
||||
};
|
||||
|
||||
void
|
||||
softint_timer_arm(void)
|
||||
{
|
||||
#ifndef PREEMPT_DISABLE
|
||||
struct itimerval it;
|
||||
|
||||
memset(&it, 0, sizeof(struct itimerval));
|
||||
it.it_value.tv_usec = SOFTINT_TIMER_START_USEC;
|
||||
it.it_interval.tv_usec = SOFTINT_TIMER_PERIOD_USEC;
|
||||
|
||||
int ret = setitimer(ITIMER_REAL, &it, NULL);
|
||||
if (ret) {
|
||||
perror("setitimer");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
softint_timer_disarm(void)
|
||||
{
|
||||
struct itimerval it;
|
||||
|
||||
memset(&it, 0, sizeof(struct itimerval));
|
||||
it.it_value.tv_sec = 0;
|
||||
it.it_interval.tv_usec = 0;
|
||||
|
||||
int ret = setitimer(ITIMER_REAL, &it, NULL);
|
||||
if (ret) {
|
||||
perror("setitimer");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
softint_handler(int sig, siginfo_t *si, void *u)
|
||||
{
|
||||
#ifdef PREEMPT_DISABLE
|
||||
assert(0);
|
||||
#else
|
||||
struct sandbox *curr = sandbox_current();
|
||||
ucontext_t *uc = (ucontext_t *)u;
|
||||
|
||||
switch(sig) {
|
||||
case SIGALRM:
|
||||
{
|
||||
// if interrupts are disabled.. increment a per_thread counter and return
|
||||
if (si->si_code == SI_KERNEL) {
|
||||
// TODO: deliver signal to all other runtime threads..
|
||||
} else {
|
||||
assert(si->si_code == SI_TKILL);
|
||||
}
|
||||
//debuglog("alrm:%d\n", alarm_cnt);
|
||||
|
||||
alarm_cnt++;
|
||||
// softints per-core..
|
||||
if (next_context) return;
|
||||
if (!softint_enabled()) return;
|
||||
|
||||
// no sandboxes running..so nothing to preempt..let the "main" scheduler run its course.
|
||||
if (curr == NULL) return;
|
||||
|
||||
// find a next sandbox to run..
|
||||
struct sandbox *next = sandbox_schedule();
|
||||
if (next == NULL) return;
|
||||
if (next == curr) return; // only this sandbox to schedule.. return to it!
|
||||
// save the current sandbox, state from uc!
|
||||
arch_mcontext_save(&curr->ctxt, &uc->uc_mcontext);
|
||||
|
||||
// sandbox_current_set on it. restore through *uc..
|
||||
sandbox_current_set(next);
|
||||
|
||||
arch_mcontext_restore(&uc->uc_mcontext, &next->ctxt);
|
||||
// reset if SIGALRM happens before SIGUSR1 and if don't preempt..OR
|
||||
// perhaps switch here for SIGUSR1 and see if we can clear that signal
|
||||
// so it doesn't get called on SIGALRM return..
|
||||
// next_context = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
case SIGUSR1:
|
||||
{
|
||||
/* we set current before calling pthread_kill! */
|
||||
assert(next_context && (&curr->ctxt == next_context));
|
||||
assert(si->si_code == SI_TKILL);
|
||||
//debuglog("usr1:%d\n", usr1_cnt);
|
||||
|
||||
usr1_cnt++;
|
||||
// sigalrm happened.. such a waste of time..!!
|
||||
if (next_context == NULL) return;
|
||||
// make sure sigalrm doesn't mess this up if nested..
|
||||
assert(!softint_enabled());
|
||||
|
||||
// do not save current sandbox.. it is in co-operative switch..
|
||||
// pick the next from "next_context"..
|
||||
// assert its "sp" to be zero in regs..
|
||||
// memcpy from next context..
|
||||
arch_mcontext_restore(&uc->uc_mcontext, &curr->ctxt);
|
||||
next_context = NULL;
|
||||
softint_enable();
|
||||
break;
|
||||
}
|
||||
case SIGPIPE:
|
||||
case SIGILL:
|
||||
case SIGFPE:
|
||||
case SIGSEGV:
|
||||
{
|
||||
// determine if the crash was in the sandbox..
|
||||
// if (pthread_self() == one_of_the_runtime_threads), a sandbox crashed.. kill it.
|
||||
// another check there could be if it is in linear memory or outside, if outside it could repeat with other sandboxes.. so perhaps restart that thread or start a fresh thread??.
|
||||
// else, shoot yourself in the head!..
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
softint_init(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_sigaction = softint_handler;
|
||||
sa.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
|
||||
for (int i = 0; i < (sizeof(softints) / sizeof(softints[0])); i++) {
|
||||
int ret = sigaction(softints[i], &sa, NULL);
|
||||
if (ret) {
|
||||
perror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <types.h>
|
||||
#include <sandbox.h>
|
||||
#include <module.h>
|
||||
#include <util.h>
|
||||
#include <jsmn.h>
|
||||
|
||||
#define UTIL_MOD_LINE_MAX 1024
|
||||
|
||||
static char *
|
||||
util_remove_spaces(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (isspace(*str)) str++;
|
||||
i = strlen(str);
|
||||
while (isspace(str[i-1])) str[i-1]='\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
util_parse_modules_file_json(char *filename)
|
||||
{
|
||||
struct stat sb;
|
||||
memset(&sb, 0, sizeof(struct stat));
|
||||
if (stat(filename, &sb) < 0) {
|
||||
perror("stat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *mf = fopen(filename, "r");
|
||||
if (!mf) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *filebuf = malloc(sb.st_size);
|
||||
memset(filebuf, 0, sb.st_size);
|
||||
int ret = fread(filebuf, sizeof(char), sb.st_size, mf);
|
||||
debuglog("size read: %d content: %s\n", ret, filebuf);
|
||||
if (ret != sb.st_size) {
|
||||
perror("fread");
|
||||
return -1;
|
||||
}
|
||||
fclose(mf);
|
||||
|
||||
jsmn_parser modp;
|
||||
jsmn_init(&modp);
|
||||
jsmntok_t toks[MOD_MAX * JSON_ELE_MAX];
|
||||
|
||||
int r = jsmn_parse(&modp, filebuf, strlen(filebuf), toks, sizeof(toks) / sizeof(toks[0]));
|
||||
if (r < 0) {
|
||||
debuglog("jsmn_parse: invalid JSON?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int nmods = 0;
|
||||
for (int i = 0; i < r; i++) {
|
||||
assert(toks[i].type == JSMN_OBJECT);
|
||||
|
||||
char mname[MOD_NAME_MAX] = { 0 };
|
||||
char mpath[MOD_PATH_MAX] = { 0 };
|
||||
i32 nargs = 0;
|
||||
u32 udp_port = 0;
|
||||
i32 isactive = 0;
|
||||
for (int j = 1; j < (toks[i].size * 2); j+=2) {
|
||||
char val[256] = { 0 }, key[32] = { 0 };
|
||||
|
||||
sprintf(val, "%.*s", toks[j + i + 1].end - toks[j + i + 1].start, filebuf + toks[j + i + 1].start);
|
||||
sprintf(key, "%.*s", toks[j + i].end - toks[j + i].start, filebuf + toks[j + i].start);
|
||||
if (strcmp(key, "name") == 0) {
|
||||
strcpy(mname, val);
|
||||
} else if (strcmp(key, "path") == 0) {
|
||||
strcpy(mpath, val);
|
||||
} else if (strcmp(key, "port") == 0) {
|
||||
udp_port = atoi(val);
|
||||
} else if (strcmp(key, "argsize") == 0) {
|
||||
nargs = atoi(val);
|
||||
} else if (strcmp(key, "active") == 0) {
|
||||
isactive = (strcmp(val, "yes") == 0);
|
||||
} else {
|
||||
debuglog("Invalid (%s,%s)\n", key, val);
|
||||
}
|
||||
}
|
||||
i += (toks[i].size * 2);
|
||||
// do not load if it is not active
|
||||
if (isactive == 0) continue;
|
||||
|
||||
struct module *m = module_alloc(mname, mpath, udp_port, nargs, 0, 0, 0, 0);
|
||||
assert(m);
|
||||
nmods++;
|
||||
}
|
||||
|
||||
free(filebuf);
|
||||
assert(nmods);
|
||||
debuglog("Loaded %d module%s!\n", nmods, nmods > 1 ? "s" : "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST data file should contain:
|
||||
* module_name:<arg1,arg2,arg3...>
|
||||
* and if your arg has to contain a ',', woops i can't deal with that for now!
|
||||
* if the first character in a line is ";", then the line is ignored!
|
||||
*/
|
||||
int
|
||||
parse_sandbox_file_custom(char *filename)
|
||||
{
|
||||
FILE *mf = fopen(filename, "r");
|
||||
char buff[UTIL_MOD_LINE_MAX] = { 0 };
|
||||
int total_boxes = 0;
|
||||
|
||||
if (!mf) {
|
||||
perror("fopen");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buff, UTIL_MOD_LINE_MAX, mf) != NULL) {
|
||||
char mname[MOD_NAME_MAX] = { 0 };
|
||||
char *tok = NULL, *src = buff;
|
||||
struct module *mod = NULL;
|
||||
struct sandbox *sb = NULL;
|
||||
char *args = NULL;
|
||||
|
||||
src = util_remove_spaces(src);
|
||||
if (src[0] == ';') goto next;
|
||||
|
||||
if ((tok = strtok_r(src, ":", &src))) {
|
||||
int ntoks = 0;
|
||||
strncpy(mname, tok, MOD_NAME_MAX);
|
||||
|
||||
mod = module_find(mname);
|
||||
assert(mod);
|
||||
if (mod->nargs > 0) {
|
||||
args = (char *)malloc(mod->nargs * MOD_ARG_MAX_SZ);
|
||||
assert(args);
|
||||
|
||||
while ((tok = strtok_r(src, ",", &src))) {
|
||||
strncpy(args + (ntoks * MOD_ARG_MAX_SZ), tok, MOD_ARG_MAX_SZ);
|
||||
ntoks++;
|
||||
|
||||
assert(ntoks < MOD_MAX_ARGS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
sb = sandbox_alloc(mod, args);
|
||||
assert(sb);
|
||||
total_boxes++;
|
||||
|
||||
next:
|
||||
memset(buff, 0, UTIL_MOD_LINE_MAX);
|
||||
}
|
||||
|
||||
assert(total_boxes);
|
||||
debuglog("Instantiated %d sandbox%s!\n", total_boxes, total_boxes > 1 ? "es" : "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct sandbox *
|
||||
util_parse_sandbox_string_json(struct module *mod, char *str)
|
||||
{
|
||||
jsmn_parser sp;
|
||||
jsmntok_t tk[JSON_ELE_MAX];
|
||||
jsmn_init(&sp);
|
||||
|
||||
int r = jsmn_parse(&sp, str, strlen(str), tk, sizeof(tk)/sizeof(tk[0]));
|
||||
if (r < 1) {
|
||||
debuglog("Failed to parse string:%s\n", str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tk[0].type != JSMN_OBJECT) return NULL;
|
||||
|
||||
for (int j = 1; j < r; j++) {
|
||||
char key[32] = { 0 };
|
||||
sprintf(key, "%.*s", tk[j].end - tk[j].start, str + tk[j].start);
|
||||
if (strcmp(key, "module") == 0) {
|
||||
char name[32] = { 0 };
|
||||
sprintf(name, "%.*s", tk[j + 1].end - tk[j + 1].start, str + tk[j + 1].start);
|
||||
if (strcmp(name, mod->name) != 0) return NULL;
|
||||
j++;
|
||||
} else if (strcmp(key, "args") == 0) {
|
||||
if (tk[j + 1].type != JSMN_ARRAY) return NULL;
|
||||
|
||||
char *args = malloc(tk[j + 1].size * MOD_ARG_MAX_SZ);
|
||||
assert(args);
|
||||
|
||||
for (int k = 1; k <= tk[j + 1].size; k++) {
|
||||
jsmntok_t *g = &tk[j + k + 1];
|
||||
strncpy(args + ((k - 1) * MOD_ARG_MAX_SZ), str + g->start, g->end - g->start);
|
||||
*(args + ((k - 1) * MOD_ARG_MAX_SZ) + g->end - g->start) = '\0';
|
||||
}
|
||||
|
||||
struct sandbox *sb = sandbox_alloc(mod, args);
|
||||
assert(sb);
|
||||
|
||||
return sb;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sandbox *
|
||||
util_parse_sandbox_string_custom(struct module *mod, char *str)
|
||||
{
|
||||
char *tok = NULL, *src = str;
|
||||
|
||||
src = util_remove_spaces(src);
|
||||
if (src[0] == ';') return NULL;
|
||||
|
||||
if (!(tok = strtok_r(src, ":", &src))) return NULL;
|
||||
|
||||
if (strcmp(mod->name, tok)) return NULL;
|
||||
// struct module *mod = module_find(tok);
|
||||
// if (!mod) return NULL;
|
||||
assert(mod->nargs >= 0 && mod->nargs < MOD_MAX_ARGS);
|
||||
|
||||
char *args = (char *)malloc(mod->nargs * MOD_ARG_MAX_SZ);
|
||||
assert(args);
|
||||
int ntoks = 0;
|
||||
while ((tok = strtok_r(src, ",", &src))) {
|
||||
strncpy(args + (ntoks * MOD_ARG_MAX_SZ), tok, MOD_ARG_MAX_SZ);
|
||||
ntoks++;
|
||||
assert(ntoks < MOD_MAX_ARGS);
|
||||
}
|
||||
|
||||
struct sandbox *sb = sandbox_alloc(mod, args);
|
||||
assert(sb);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each line in the file should be like:
|
||||
*
|
||||
* module_path:module_name:module_nargs:module_stack_size:module_max_heap_size[:moreargs::argn\n]
|
||||
* if the first character in a line is ";", then the line is ignored!
|
||||
*/
|
||||
int
|
||||
util_parse_modules_file_custom(char *filename)
|
||||
{
|
||||
FILE *mf = fopen(filename, "r");
|
||||
char buff[UTIL_MOD_LINE_MAX] = { 0 };
|
||||
int nmods = 0;
|
||||
|
||||
if (!mf) {
|
||||
perror("fopen");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buff, UTIL_MOD_LINE_MAX, mf) != NULL) {
|
||||
char mname[MOD_NAME_MAX] = { 0 };
|
||||
char mpath[MOD_PATH_MAX] = { 0 };
|
||||
i32 nargs = 0;
|
||||
u32 stack_sz = 0;
|
||||
u32 max_heap = 0;
|
||||
u32 timeout = 0;
|
||||
char *tok = NULL, *src = buff;
|
||||
u32 udp_port = 0;
|
||||
i32 ntoks = 0;
|
||||
|
||||
src = util_remove_spaces(src);
|
||||
if (src[0] == ';') goto next;
|
||||
while ((tok = strtok_r(src, ":", &src))) {
|
||||
switch(ntoks) {
|
||||
case MOD_ARG_MODPATH: strncpy(mpath, tok, MOD_PATH_MAX); break;
|
||||
case MOD_ARG_MODPORT: udp_port = atoi(tok);
|
||||
case MOD_ARG_MODNAME: strncpy(mname, tok, MOD_NAME_MAX); break;
|
||||
case MOD_ARG_MODNARGS: nargs = atoi(tok); break;
|
||||
default: break;
|
||||
}
|
||||
ntoks++;
|
||||
}
|
||||
assert(ntoks >= MOD_ARG_MAX);
|
||||
|
||||
struct module *m = module_alloc(mname, mpath, udp_port, nargs, 0, 0, 0, 0);
|
||||
assert(m);
|
||||
nmods++;
|
||||
|
||||
next:
|
||||
memset(buff, 0, UTIL_MOD_LINE_MAX);
|
||||
}
|
||||
|
||||
assert(nmods);
|
||||
debuglog("Loaded %d module%s!\n", nmods, nmods > 1 ? "s" : "");
|
||||
fclose(mf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
include Makefile.inc
|
||||
|
||||
BENCH_DIR=../../silverfish/code_benches/
|
||||
|
||||
TESTS=forever filesys sockserver sockclient
|
||||
TESTSRT=$(TESTS:%=%_rt)
|
||||
BENCHES=adpcm basic_math binarytrees bitcount blowfish crc dijkstra fft function_pointers \
|
||||
gsm libjpeg mandelbrot patricia pgp qsort rsynth sha sqlite stringsearch susan
|
||||
BENCHESSF=$(BENCHES:%=%_sf)
|
||||
|
||||
adpcm_CFLAGS=-Wno-implicit-int -Wno-implicit-function-declaration
|
||||
crc_CFLAGS=-Wno-implicit-int -Wno-format
|
||||
dijkstra_CFLAGS=-Wno-return-type
|
||||
gsm_CFLAGS=-DSASR -Wno-everything
|
||||
libjpeg_CFLAGS=-Wno-incompatible-library-redeclaration -Wno-implicit-function-declaration -Wno-shift-negative-value
|
||||
pgp_CFLAGS=-DUNIX -D_BSD -DPORTABLE -DUSE_NBIO -DMPORTABLE -I${BENCH_DIR}/pgp/ -Wno-everything
|
||||
rsynth_CFLAGS=-I${BENCH_DIR}/rsynth -Wno-everything -I/usr/local/include
|
||||
sha_CLFAGS=-Wno-everything
|
||||
susan_CFLAGS=-Wno-everything
|
||||
|
||||
.PHONY: all clean rttests sftests
|
||||
|
||||
sftests: $(BENCHESSF)
|
||||
|
||||
rttests: $(TESTSRT)
|
||||
|
||||
all: rttests sftests
|
||||
@echo "Compilation done!"
|
||||
|
||||
clean:
|
||||
@rm -rf ${TMP_DIR}
|
||||
@rm -f ${BIN_DIR}/*_wasm.so
|
||||
|
||||
%_sf:
|
||||
@mkdir -p ${TMP_DIR}
|
||||
@echo "Compiling $(@:%_sf=%)"
|
||||
${WASMCC} ${$(@:%_sf=%)_CFLAGS} ${WASMCFLAGS} ${OPTFLAGS} ${BENCH_DIR}/$(@:%_sf=%)/*.c -o ${TMP_DIR}/$(@:%_sf=%).wasm
|
||||
${SFCC} ${TMP_DIR}/$(@:%_sf=%).wasm -o ${TMP_DIR}/$(@:%_sf=%).bc
|
||||
${CC} --shared -fPIC ${OPTFLAGS} -I${RT_INC} -D${USE_MEM} ${TMP_DIR}/$(@:%_sf=%).bc ${MEMC} ${WASMISA} -o ${TMP_DIR}/$(@:%_sf=%)_wasm.so
|
||||
@cp ${TMP_DIR}/$(@:%_sf=%)_wasm.so ${BIN_DIR}
|
||||
@rm -rf ${TMP_DIR}
|
||||
|
||||
%_rt:
|
||||
@mkdir -p ${TMP_DIR}
|
||||
@echo "Compiling $(@:%_rt=%)"
|
||||
${WASMCC} ${$(@:%_rt=%)_CFLAGS} ${WASMCFLAGS} ${OPTFLAGS} $(@:%_rt=%)/*.c -o ${TMP_DIR}/$(@:%_rt=%).wasm
|
||||
${SFCC} ${TMP_DIR}/$(@:%_rt=%).wasm -o ${TMP_DIR}/$(@:%_rt=%).bc
|
||||
${CC} --shared -fPIC ${OPTFLAGS} -I${RT_INC} -D${USE_MEM} ${TMP_DIR}/$(@:%_rt=%).bc ${MEMC} ${WASMISA} -o ${TMP_DIR}/$(@:%_rt=%)_wasm.so
|
||||
@cp ${TMP_DIR}/$(@:%_rt=%)_wasm.so ${BIN_DIR}
|
||||
@rm -rf ${TMP_DIR}
|
||||
|
@ -0,0 +1,22 @@
|
||||
BASE=awsm
|
||||
SFCC=silverfish
|
||||
CC=clang
|
||||
WASMCC=wasm32-unknown-unknown-wasm-clang
|
||||
|
||||
OPTFLAGS=-O3 -flto
|
||||
#same stack-size for all
|
||||
WASMLINKFLAGS=-Wl,-z,stack-size=524288,--allow-undefined,--no-threads,--stack-first,--no-entry,--export-all,--export=main,--export=dummy
|
||||
WASMCFLAGS=${WASMLINKFLAGS} -nostartfiles
|
||||
|
||||
BASE_DIR=../../
|
||||
RT_DIR=${BASE_DIR}/runtime/
|
||||
RT_INC=${RT_DIR}/include/
|
||||
|
||||
USE_MEM=USE_MEM_VM
|
||||
|
||||
ifeq ($(USE_MEM),USE_MEM_VM)
|
||||
MEMC=${RT_DIR}/compiletime/memory/64bit_nix.c
|
||||
endif
|
||||
|
||||
BIN_DIR=${RT_DIR}/bin/
|
||||
TMP_DIR=tmp/
|
@ -0,0 +1,118 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define BUF_MAX 44
|
||||
#define RDWR_MAX 1
|
||||
#if RDWR_MAX > 1
|
||||
int main(int argc, char **argv) __attribute__ ((optnone))
|
||||
{
|
||||
char buf[RDWR_MAX][BUF_MAX] = { 0 };
|
||||
|
||||
printf("%s enter [in:%s, out:%s]\n", argv[0], argv[1], argv[2]);
|
||||
int fdr = open(argv[1], O_RDONLY, S_IRUSR | S_IRGRP);
|
||||
if (fdr < 0) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
int fdw = creat(argv[2], S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
|
||||
if (fdw < 0) {
|
||||
perror("creat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
struct iovec iov[RDWR_MAX] = { 0 };
|
||||
for (int i = 0; i < RDWR_MAX; i++) {
|
||||
iov[i].iov_base = buf[i];
|
||||
iov[i].iov_len = BUF_MAX;
|
||||
}
|
||||
while ((n = readv(fdr, iov, RDWR_MAX)) > 0) {
|
||||
int wvcnt = n / BUF_MAX;
|
||||
if (n % BUF_MAX) {
|
||||
iov[wvcnt].iov_len = n % BUF_MAX;
|
||||
wvcnt++;
|
||||
}
|
||||
if (writev(fdw, iov, wvcnt) != n) perror("writev");
|
||||
|
||||
memset(buf, 0, RDWR_MAX * BUF_MAX);
|
||||
for (int i = 0; i < RDWR_MAX; i++) {
|
||||
iov[i].iov_base = buf[i];
|
||||
iov[i].iov_len = BUF_MAX;
|
||||
}
|
||||
n = 0;
|
||||
}
|
||||
|
||||
close(fdr);
|
||||
close(fdw);
|
||||
|
||||
printf("%s done\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int
|
||||
main(int argc, char **argv) __attribute__ ((optnone))
|
||||
{
|
||||
char buf[BUF_MAX] = { 0 };
|
||||
|
||||
printf("%s enter [in:%s, out:%s]\n", argv[0], argv[1], argv[2]);
|
||||
#ifdef USE_OPEN
|
||||
int fdr = open(argv[1], O_RDONLY, S_IRUSR | S_IRGRP);
|
||||
if (fdr < 0) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
int fdw = creat(argv[2], S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
|
||||
if (fdw < 0) {
|
||||
perror("creat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
while ((n = read(fdr, buf, BUF_MAX)) > 0) {
|
||||
if (write(fdw, buf, n) != n) perror("write");
|
||||
memset(buf, 0, BUF_MAX);
|
||||
n = 0;
|
||||
}
|
||||
if (n < 0) perror("read");
|
||||
|
||||
close(fdr);
|
||||
close(fdw);
|
||||
|
||||
#else
|
||||
FILE *fpr = fopen(argv[1], "r");
|
||||
if (!fpr) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
FILE *fpw = fopen(argv[2], "w");
|
||||
if (!fpw) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!feof(fpr)) {
|
||||
char *p = NULL;
|
||||
if ((p = fgets(buf, BUF_MAX, fpr)) == NULL) perror("fgets");
|
||||
else {
|
||||
if (fputs(p, fpw) < 0) perror("fputs");
|
||||
p = NULL;
|
||||
}
|
||||
memset(buf, 0, BUF_MAX);
|
||||
}
|
||||
|
||||
fclose(fpr);
|
||||
fclose(fpw);
|
||||
#endif
|
||||
printf("%s done\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -0,0 +1,27 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ITERS 50000000
|
||||
|
||||
int
|
||||
main(int argc, char **argv) __attribute__ ((optnone))
|
||||
{
|
||||
printf("%s enter\n", argv[0]);
|
||||
int n = 0, e = 1;
|
||||
if (argc == 2) {
|
||||
n = atoi(argv[1]);
|
||||
if (n > 0) e = 0;
|
||||
}
|
||||
|
||||
while (e || n > 0) {
|
||||
int i = ITERS;
|
||||
n--;
|
||||
while (i-- > 0) {
|
||||
int j = ITERS;
|
||||
while (j-- > 0) __asm__ __volatile__("nop": : :"memory");
|
||||
}
|
||||
printf("%s\n", argv[0]);
|
||||
}
|
||||
printf("%s done\n", argv[0]);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* code from http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html */
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
|
||||
void error(char *msg)
|
||||
{
|
||||
perror(msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int sockfd, portno, n;
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
struct hostent *server;
|
||||
|
||||
char buffer[256] = "The quick brown fox jumps over the lazy dog";
|
||||
if (argc < 3) {
|
||||
fprintf(stderr,"usage %s hostname port\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
portno = atoi(argv[2]);
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
error("ERROR opening socket");
|
||||
server = gethostbyname(argv[1]);
|
||||
if (server == NULL) {
|
||||
fprintf(stderr,"ERROR, no such host\n");
|
||||
exit(0);
|
||||
}
|
||||
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
bcopy((char *)server->h_addr,
|
||||
(char *)&serv_addr.sin_addr.s_addr,
|
||||
server->h_length);
|
||||
serv_addr.sin_port = htons(portno);
|
||||
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
|
||||
error("ERROR connecting");
|
||||
printf("Sending message %s\n", buffer);
|
||||
n = send(sockfd,buffer,strlen(buffer), 0);
|
||||
if (n < 0)
|
||||
error("ERROR writing to socket");
|
||||
bzero(buffer,256);
|
||||
n = recv(sockfd,buffer,255, 0);
|
||||
if (n < 0)
|
||||
error("ERROR reading from socket");
|
||||
printf("%s\n",buffer);
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/* A simple server in the internet domain using TCP
|
||||
The port number is passed as an argument */
|
||||
/* code from: http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html */
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
void error(char *msg)
|
||||
{
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int sockfd, newsockfd, portno, clilen;
|
||||
char buffer[256];
|
||||
struct sockaddr_in serv_addr, cli_addr;
|
||||
int n;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr,"ERROR, no port provided\n");
|
||||
exit(1);
|
||||
}
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
error("ERROR opening socket");
|
||||
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||
portno = atoi(argv[1]);
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
serv_addr.sin_port = htons(portno);
|
||||
if (bind(sockfd, (struct sockaddr *) &serv_addr,
|
||||
sizeof(serv_addr)) < 0)
|
||||
error("ERROR on binding");
|
||||
listen(sockfd,5);
|
||||
clilen = sizeof(cli_addr);
|
||||
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||
if (newsockfd < 0)
|
||||
error("ERROR on accept");
|
||||
bzero(buffer,256);
|
||||
n = recv(newsockfd,buffer,255, 0);
|
||||
if (n < 0) error("ERROR reading from socket");
|
||||
printf("Here is the message: %s\n",buffer);
|
||||
n = send(newsockfd,"I got your message",18, 0);
|
||||
if (n < 0) error("ERROR writing to socket");
|
||||
close(newsockfd);
|
||||
close(sockfd);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "adpcm",
|
||||
"path" : "adpcm_wasm.so",
|
||||
"port" : 10000,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "bitcount",
|
||||
"path" : "bitcount_wasm.so",
|
||||
"port" : 10002,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "basic_math",
|
||||
"path" : "basic_math_wasm.so",
|
||||
"port" : 10004,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "binarytrees",
|
||||
"path" : "binarytrees_wasm.so",
|
||||
"port" : 10006,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "crc",
|
||||
"path" : "crc_wasm.so",
|
||||
"port" : 10008,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "dijkstra",
|
||||
"path" : "dijkstra_wasm.so",
|
||||
"port" : 10010,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "forever",
|
||||
"path" : "forever_wasm.so",
|
||||
"port" : 10012,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "fornever",
|
||||
"path" : "forever_wasm.so",
|
||||
"port" : 10014,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "fft",
|
||||
"path" : "fft_wasm.so",
|
||||
"port" : 10016,
|
||||
"argsize" : 3
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "function_pointers",
|
||||
"path" : "function_pointers_wasm.so",
|
||||
"port" : 10018,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "gsm",
|
||||
"path" : "gsm_wasm.so",
|
||||
"port" : 10020,
|
||||
"argsize" : 4
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "libjpeg",
|
||||
"path" : "libjpeg_wasm.so",
|
||||
"port" : 10022,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "mandelbrot",
|
||||
"path" : "mandelbrot_wasm.so",
|
||||
"port" : 10024,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "matrix_multiply",
|
||||
"path" : "matrix_multiply_wasm.so",
|
||||
"port" : 10026,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "particia",
|
||||
"path" : "partricia_wasm.so",
|
||||
"port" : 10028,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "sqlite",
|
||||
"path" : "sqlite_wasm.so",
|
||||
"port" : 10030,
|
||||
"argsize" : 1
|
||||
},
|
||||
{
|
||||
"active" : "no",
|
||||
"name" : "filesys",
|
||||
"path" : "filesys_wasm.so",
|
||||
"port" : 10032,
|
||||
"argsize" : 3
|
||||
},
|
||||
{
|
||||
"active" : "yes",
|
||||
"name" : "sockserver",
|
||||
"path" : "sockserver_wasm.so",
|
||||
"port" : 10034,
|
||||
"argsize" : 2
|
||||
},
|
||||
{
|
||||
"active" : "yes",
|
||||
"name" : "sockclient",
|
||||
"path" : "sockclient_wasm.so",
|
||||
"port" : 10036,
|
||||
"argsize" : 3
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
adpcm_wasm.so:10000:adpcm:2:0:0:0:0
|
||||
bitcount_wasm.so:10002:bitcount:2:0:0:0:0
|
||||
basic_math_wasm.so:10004:basic_math:1:0:0:0:0
|
||||
binarytrees_wasm.so:10006:binarytrees:2:0:0:0:0
|
||||
crc_wasm.so:10008:crc:2:0:0:0:0
|
||||
;dijkstra_wasm.so:10010:dijkstra:2:0:0:0:0
|
||||
forever_wasm.so:10012:forever:1:0:0:0:0
|
||||
forever_wasm.so:10014:fornever:2:0:0:0:0
|
||||
;fft_wasm.so:10016:fft:3:0:0:0:0
|
||||
function_pointers_wasm.so:10018:function_pointers:1:0:0:0:0
|
||||
gsm_wasm.so:10020:gsm:4:0:0:0:0
|
||||
;libjpeg_wasm.so:10022:libjpeg:1:0:0:0:0
|
||||
mandelbrot_wasm.so:10024:mandelbrot:2:0:0:0:0
|
||||
;matrix_multiply_wasm.so:10026:matrix_multiply:1:0:0:0:0
|
||||
patricia_wasm.so:10028:patricia:2:0:0:0:0
|
||||
sqlite_wasm.so:10030:sqlite:1:0:0:0:0
|
@ -0,0 +1,34 @@
|
||||
;127.0.0.1:10002${ "module" : "bitcount", "args" : [ "bitcount1" , "16777216" ] }
|
||||
;127.0.0.1:10002${ "module" : "bitcount", "args" : [ "bitcount2" , "16777216" ] }
|
||||
;127.0.0.1:10004${ "module" : "basic_math", "args" : [ "basic_math1" ] }
|
||||
;127.0.0.1:10004${ "module" : "basic_math", "args" : [ "basic_math2" ] }
|
||||
;127.0.0.1:10006${ "module" : "binarytrees", "args" : [ "binarytrees1", "16" ] }
|
||||
;127.0.0.1:10006${ "module" : "binarytrees", "args" : [ "binarytrees2", "16" ] }
|
||||
;127.0.0.1:10008${ "module" : "crc", "args" : [ "crc1", "crc/large.pcm" ] }
|
||||
;127.0.0.1:10008${ "module" : "crc", "args" : [ "crc2", "crc/large.pcm" ] }
|
||||
;127.0.0.1:10010${ "module" : "dijkstra", "args" : [ "dijkstra1", "dijkstra/input.dat" ] }
|
||||
;127.0.0.1:10010${ "module" : "dijkstra", "args" : [ "dijkstra2", "dijkstra/input.dat" ] }
|
||||
;127.0.0.1:10012${ "module" : "forever", "args" : [ "forever01" ] }
|
||||
;127.0.0.1:10012${ "module" : "forever", "args" : [ "forever02" ] }
|
||||
;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever01", "10" ] }
|
||||
;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever02", "20" ] }
|
||||
;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever03", "30" ] }
|
||||
;127.0.0.1:10014${ "module" : "fornever", "args" : [ "fornever04", "40" ] }
|
||||
;127.0.0.1:10016${ "module" : "fft", "args" : [ "fft1" , "8", "32768" ] }
|
||||
;127.0.0.1:10016${ "module" : "fft", "args" : [ "fft2" , "8", "32768" ] }
|
||||
;127.0.0.1:10018${ "module" : "function_pointers", "args" : [ "function_pointers1" ] }
|
||||
;127.0.0.1:10018${ "module" : "function_pointers", "args" : [ "function_pointers2" ] }
|
||||
;127.0.0.1:10020${ "module" : "gsm", "args" : [ "gsm1" , "-fps" , "-c", "gsm/large.au" ] }
|
||||
;127.0.0.1:10020${ "module" : "gsm", "args" : [ "gsm2" , "-fps" , "-c", "gsm/large.au" ] }
|
||||
;127.0.0.1:10022${ "module" : "libjpeg", "args" : [ "libjpeg1" ] }
|
||||
;127.0.0.1:10022${ "module" : "libjpeg", "args" : [ "libjpeg2" ] }
|
||||
;127.0.0.1:10024${ "module" : "mandelbrot", "args" : [ "mandelbrot1", "5000" ] }
|
||||
;127.0.0.1:10024${ "module" : "mandelbrot", "args" : [ "mandelbrot2", "5000" ] }
|
||||
;127.0.0.1:10026${ "module" : "matrix_multiply", "args" : [ "matrix_multiply1" ] }
|
||||
;127.0.0.1:10026${ "module" : "matrix_multiply", "args" : [ "matrix_multiply2" ] }
|
||||
;127.0.0.1:10028${ "module" : "patricia", "args" : [ "patricia1" , "large.udp" ] }
|
||||
;127.0.0.1:10030${ "module" : "sqlite", "args" : [ "sqlite1" ] }
|
||||
;127.0.0.1:10030${ "module" : "sqlite", "args" : [ "sqlite2" ] }
|
||||
;127.0.0.1:10032${ "module" : "filesys", "args" : [ "filesys1", "fs_in.txt", "fs_out.txt" ] }
|
||||
127.0.0.1:10034${ "module" : "sockserver", "args" : [ "sockserv1", "20000" ] }
|
||||
127.0.0.1:10036${ "module" : "sockclient", "args" : [ "sockcli1", "localhost", "20000" ] }
|
@ -0,0 +1,33 @@
|
||||
10002$bitcount:bitcount1,16777216
|
||||
10002$bitcount:bitcount2,16777216
|
||||
10004$basic_math:basic_math1
|
||||
10004$basic_math:basic_math2
|
||||
10006$binarytrees:binarytrees1,16
|
||||
10006$binarytrees:binarytrees2,16
|
||||
10008$crc:crc1,crc/large.pcm
|
||||
10008$crc:crc2,crc/large.pcm
|
||||
;10010$dijkstra:dijkstra1,dijkstra/input.dat
|
||||
;10010$dijkstra:dijkstra2,dijkstra/input.dat
|
||||
;10012$forever:forever01
|
||||
;10012$forever:forever02
|
||||
10014$fornever:fornever01,10
|
||||
10014$fornever:fornever02,20
|
||||
;10012$forever:forever03
|
||||
;10012$forever:forever04
|
||||
10014$fornever:fornever03,30
|
||||
10014$fornever:fornever04,40
|
||||
;10016$fft:fft1,8,32768
|
||||
;10016$fft:fft2,8,32768
|
||||
;10018$function_pointers:function_pointers1
|
||||
;10018$function_pointers:function_pointers2
|
||||
;10020$gsm:gsm1,-fps,-c,gsm/large.au
|
||||
;10020$gsm:gsm2,-fps,-c,gsm/large.au
|
||||
;10022$libjpeg:libjpeg1
|
||||
;10022$libjpeg:libjpeg2
|
||||
;10024$mandelbrot:mandelbrot1,5000
|
||||
;10024$mandelbrot:mandelbrot2,5000
|
||||
;10026$matrix_multiply:matrix_multiply1
|
||||
;10026$matrix_multiply:matrix_multiply2
|
||||
;10028$patricia:patricia1,large.udp
|
||||
;10030$sqlite:sqlite1
|
||||
;10030$sqlite:sqlite2
|
@ -0,0 +1,13 @@
|
||||
SUBDIRS=$(wildcard */)
|
||||
|
||||
dirs:
|
||||
@for dir in $(SUBDIRS) ; do \
|
||||
$(MAKE) -C $$dir ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
@for dir in $(SUBDIRS) ; do \
|
||||
$(MAKE) -C $$dir clean ; \
|
||||
done
|
||||
|
||||
all: clean dirs
|
@ -0,0 +1,9 @@
|
||||
udp: udpclient.c
|
||||
@echo "Compiling udpclient"
|
||||
@gcc udpclient.c -o ../../bin/udpclient
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up udpclient"
|
||||
@rm -f ../../bin/udpclient
|
||||
|
||||
all: clean udp
|
@ -0,0 +1,102 @@
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MSG_MAX 1024
|
||||
|
||||
static char *
|
||||
remove_spaces(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (isspace(*str)) str++;
|
||||
i = strlen(str);
|
||||
while (isspace(str[i-1])) str[i-1]='\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <sandbox_file>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
if (!f) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char line[MSG_MAX] = { 0 };
|
||||
while (fgets(line, MSG_MAX, f) != NULL) {
|
||||
int fd = -1;
|
||||
struct sockaddr_in sa;
|
||||
char *msg = NULL, *tok, *src = line;
|
||||
char ip[32] = { 0 }, port[32] = { 0 };
|
||||
src = remove_spaces(src);
|
||||
|
||||
if (src[0] == ';') goto next;
|
||||
tok = strtok_r(src, ":", &src);
|
||||
strcpy(ip, tok);
|
||||
tok = strtok_r(src, "$", &src);
|
||||
strcpy(port, tok);
|
||||
msg = src;
|
||||
|
||||
int i = 0;
|
||||
printf("\nRequest [%s] to [%s:%d]\n (1:Proceed 2:Skip ANY:Exit) ", msg, ip, atoi(port));
|
||||
scanf("%d", &i);
|
||||
if (i <= 0 || i > 2) {
|
||||
printf("Exiting!\n");
|
||||
exit(0);
|
||||
} else if (i == 1) {
|
||||
printf("Proceeding!\n");
|
||||
} else {
|
||||
printf("Skipping!\n");
|
||||
goto next;
|
||||
}
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(atoi(port));
|
||||
sa.sin_addr.s_addr = inet_addr(ip);
|
||||
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
perror("Establishing socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sendto(fd, msg, strlen(msg), 0, (struct sockaddr*)&sa, sizeof(sa)) < 0 &&
|
||||
errno != EINTR) {
|
||||
perror("sendto");
|
||||
return -1;
|
||||
}
|
||||
printf("Done!\n");
|
||||
next:
|
||||
memset(line, 0, MSG_MAX);
|
||||
fflush(stdin);
|
||||
fflush(stdout);
|
||||
|
||||
if (fd >= 0) close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1 @@
|
||||
Subproject commit 015e2bb638d47a793d0da96bb4b697be9d010209
|
Loading…
Reference in new issue