use crate::value::Value;
use crate::state::State;
use crate::sims::syscall;
const MAX_LEN: u64 = 8192;
pub fn puts(state: &mut State, args: &[Value]) -> Value {
let addr = &args[0];
let length = strlen(state, &args[0..1]);
let mut data = state.memory_read(addr, &length);
data.push(Value::Concrete('\n' as u64, 0)); state.filesystem.write(1, data);
length
}
pub fn putchar(state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
state.filesystem.write(1, vec!(c.clone()));
c
}
pub fn getchar(state: &mut State, _args: &[Value]) -> Value {
state.filesystem.read(0, 1).get(0).unwrap_or(&vc(-1i64 as u64)).to_owned()
}
pub fn fprintf(state: &mut State, args: &[Value]) -> Value {
puts(state, &[args[1].to_owned()])
}
pub fn printf(state: &mut State, args: &[Value]) -> Value {
puts(state, args)
}
pub fn memmove(state: &mut State, args: &[Value]) -> Value {
state.memory_move(&args[0], &args[1], &args[2]);
args[0].to_owned()
}
pub fn memcpy(state: &mut State, args: &[Value]) -> Value {
state.memory_move(&args[0], &args[1], &args[2]);
args[0].to_owned()
}
pub fn bcopy(state: &mut State, args: &[Value]) -> Value {
state.memory_move(&args[0], &args[1], &args[2]);
Value::Concrete(0, 0)
}
pub fn bzero(state: &mut State, args: &[Value]) -> Value {
memset(state, &[args[0].to_owned(),
Value::Concrete(0, 0), args[1].to_owned()]);
Value::Concrete(0, 0)
}
pub fn mempcpy(state: &mut State, args: &[Value]) -> Value {
memcpy(state, args).add(&args[2])
}
pub fn memccpy(state: &mut State, args: &[Value]) -> Value {
memcpy(state, args)
}
pub fn memfrob(state: &mut State, args: &[Value]) -> Value {
let addr = &args[0];
let num = &args[1];
let x = Value::Concrete(0x2a, 0);
let data = state.memory_read(&addr, &num);
let mut new_data = vec!();
for d in data {
new_data.push(d.to_owned() ^ x.to_owned());
}
state.memory_write(addr, &new_data, &num);
Value::Concrete(0, 0)
}
pub fn strlen(state: &mut State, args: &[Value]) -> Value {
state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0))
}
pub fn strnlen(state: &mut State, args: &[Value]) -> Value {
state.memory_strlen(&args[0], &args[1])
}
pub fn gets(state: &mut State, args: &[Value]) -> Value {
syscall::read(state, &[Value::Concrete(0,0),
args[0].to_owned(),
Value::Concrete(1024, 0)]);
args[0].to_owned()
}
pub fn fgets(state: &mut State, args: &[Value]) -> Value {
let fd = fileno(state, &args[2..3]);
syscall::read(state, &[fd, args[0].to_owned(),
args[1].to_owned()-Value::Concrete(1,0)]);
args[0].to_owned()
}
pub fn strcpy(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[1], &Value::Concrete(MAX_LEN, 0))
+Value::Concrete(1, 0);
state.memory_move(&args[0], &args[1], &length);
args[0].to_owned()
}
pub fn stpcpy(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[1], &Value::Concrete(MAX_LEN, 0));
strcpy(state, args) + length
}
pub fn strdup(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0))
+Value::Concrete(1, 0);
let new_addr = Value::Concrete(state.memory.alloc(&length), 0);
state.memory_move(&new_addr, &args[0], &length);
new_addr
}
pub fn strdupa(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0))
+Value::Concrete(1, 0);
strdup(state, args) + length
}
pub fn strndup(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[0], &args[1]);
let new_addr = Value::Concrete(state.memory.alloc(&length), 0);
state.memory_move(&new_addr, &args[0], &length);
new_addr
}
pub fn strndupa(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[0], &args[1]);
strndup(state, args) + length
}
pub fn strfry(_state: &mut State, args: &[Value]) -> Value {
args[0].to_owned()
}
pub fn strncpy(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[1], &args[2]);
state.memory_move(&args[0], &args[1], &length);
args[0].to_owned()
}
pub fn strcat(state: &mut State, args: &[Value]) -> Value {
let length1 = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0));
let length2 = state.memory_strlen(&args[1], &Value::Concrete(MAX_LEN, 0))+Value::Concrete(1, 0);
state.memory_move(&args[0].add(&length1), &args[1], &length2);
args[0].to_owned()
}
pub fn strncat(state: &mut State, args: &[Value]) -> Value {
let length1 = state.memory_strlen(&args[0], &args[2]);
let length2 = state.memory_strlen(&args[1], &args[2])+Value::Concrete(1, 0);
state.memory_move(&args[0].add(&length1), &args[1], &length2);
args[0].to_owned()
}
pub fn memset(state: &mut State, args: &[Value]) -> Value {
let mut data = vec!();
let length = state.solver.max_value(&args[2]);
for _ in 0..length {
data.push(args[1].to_owned());
}
state.memory_write(&args[0], &data, &args[2]);
args[0].to_owned()
}
pub fn memchr_helper(state: &mut State, args: &[Value], reverse: bool) -> Value {
state.memory_search(&args[0], &args[1], &args[2], reverse)
}
pub fn memchr(state: &mut State, args: &[Value]) -> Value {
memchr_helper(state, args, false)
}
pub fn memrchr(state: &mut State, args: &[Value]) -> Value {
memchr_helper(state, args, true)
}
pub fn strchr_helper(state: &mut State, args: &[Value], reverse: bool) -> Value {
let length = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0));
memchr_helper(state, &[args[0].to_owned(), args[1].to_owned(), length], reverse)
}
pub fn strchr(state: &mut State, args: &[Value]) -> Value {
strchr_helper(state, args, false)
}
pub fn strrchr(state: &mut State, args: &[Value]) -> Value {
strchr_helper(state, args, true)
}
pub fn memcmp(state: &mut State, args: &[Value]) -> Value {
state.memory_compare(&args[0], &args[1], &args[2])
}
pub fn strcmp(state: &mut State, args: &[Value]) -> Value {
let len1 = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0));
let len2 = state.memory_strlen(&args[1], &Value::Concrete(MAX_LEN, 0));
let length = state.solver.conditional(&(len1.ult(&len2)),
&len1, &len2)+Value::Concrete(1, 0);
state.memory_compare(&args[0], &args[1], &length)
}
pub fn strncmp(state: &mut State, args: &[Value]) -> Value {
let len1 = state.memory_strlen(&args[0], &args[2]);
let len2 = state.memory_strlen(&args[1], &args[2]);
let length = state.solver.conditional(&(len1.ult(&len2)),
&len1, &len2)+Value::Concrete(1, 0);
state.memory_compare(&args[0], &args[1], &length)
}
pub fn memmem(state: &mut State, args: &[Value]) -> Value {
let len = state.solver.evalcon_to_u64(&args[3]).unwrap() as usize;
let mut needle_val = state.memory_read_value(&args[2], len);
needle_val = Value::Symbolic(state.solver.to_bv(&needle_val, 8*len as u32), needle_val.get_taint());
memchr_helper(state, &[args[0].to_owned(), needle_val, args[1].to_owned()], false)
}
pub fn strstr(state: &mut State, args: &[Value]) -> Value {
let dlen = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0));
let slen = state.memory_strlen(&args[1], &Value::Concrete(MAX_LEN, 0));
let len = state.solver.evalcon_to_u64(&slen).unwrap() as usize;
let needle_val = state.memory_read_value(&args[0], len);
memchr_helper(state, &[args[0].to_owned(), needle_val, dlen], false)
}
pub fn malloc(state: &mut State, args: &[Value]) -> Value {
state.memory_alloc(&args[0])
}
pub fn calloc(state: &mut State, args: &[Value]) -> Value {
state.memory_alloc(&args[0].mul(&args[1]))
}
pub fn free(state: &mut State, args: &[Value]) -> Value {
state.memory_free(&args[0]);
Value::Concrete(0, 0)
}
pub fn mmap(state: &mut State, args: &[Value]) -> Value {
syscall::mmap(state, args)
}
pub fn munmap(state: &mut State, args: &[Value]) -> Value {
syscall::munmap(state, args)
}
pub fn c_syscall(state: &mut State, args: &[Value]) -> Value {
syscall::syscall("indirect_syscall", state, args)
}
pub fn __libc_start_main(state: &mut State, args: &[Value]) -> Value {
let main = args[0].to_owned();
let argc = args[1].to_owned();
let argv = args[2].to_owned();
state.registers.set_with_alias("PC", main);
state.registers.set_with_alias("A0", argc);
state.registers.set_with_alias("A1", argv);
state.registers.set_with_alias("A2", Value::Concrete(0, 0));
args[1].to_owned()
}
pub const LINUX_FILENO_OFFSET: u64 = 112;
pub const MACOS_FILENO_OFFSET: u64 = 18;
pub fn fileno(state: &mut State, args: &[Value]) -> Value {
let fd_addr = if state.info.bin.os == "darwin" {
args[0].add(&Value::Concrete(MACOS_FILENO_OFFSET, 0))
} else {
args[0].add(&Value::Concrete(LINUX_FILENO_OFFSET, 0))
};
state.memory_read_value(&(fd_addr), 4)
}
pub fn fopen(state: &mut State, args: &[Value]) -> Value {
let fd = syscall::open(state, args.clone());
let file_struct = state.memory.alloc(&Value::Concrete(216, 0));
let fd_addr = if state.info.bin.os == "darwin" {
vc(file_struct).add(&Value::Concrete(MACOS_FILENO_OFFSET, 0))
} else {
vc(file_struct).add(&Value::Concrete(LINUX_FILENO_OFFSET, 0))
};
state.memory_write_value(&fd_addr, &fd, 4);
Value::Concrete(file_struct, 0)
}
pub fn fclose(state: &mut State, args: &[Value]) -> Value {
let fd = fileno(state, &[args[0].to_owned()]);
syscall::close(state, &[fd])
}
pub fn fread(state: &mut State, args: &[Value]) -> Value {
let fd = fileno(state, &[args[3].to_owned()]);
syscall::read(state, &[fd, args[1].to_owned(), args[2].to_owned()])
}
pub fn fwrite(state: &mut State, args: &[Value]) -> Value {
let fd = fileno(state, &[args[3].to_owned()]);
syscall::write(state, &[fd, args[1].to_owned(), args[2].to_owned()])
}
pub fn fseek(state: &mut State, args: &[Value]) -> Value {
let fd = fileno(state, &[args[0].to_owned()]);
syscall::lseek(state, &[fd, args[1].to_owned(), args[2].to_owned()])
}
fn vc(n: u64) -> Value {
Value::Concrete(n, 0)
}
fn isdig(c: &Value) -> Value {
c.ult(&Value::Concrete(0x3a, 0)) & !c.ult(&Value::Concrete(0x30, 0))
}
fn _isws(c: &Value) -> Value {
c.eq(&Value::Concrete(0x09, 0)) |
c.eq(&Value::Concrete(0x20, 0)) |
c.eq(&Value::Concrete(0x0d, 0)) |
c.eq(&Value::Concrete(0x0a, 0))
}
fn bv_pow(bv: &Value, exp: u32) -> Value {
let mut result = vc(1);
for _ in 0..exp {
result = result * bv.clone();
}
result
}
fn isbasedigit(state: &State, c: &Value, base: &Value) -> Value {
state.solver.conditional(
&base.ult(&vc(11)),
&(c.ult(&(vc('0' as u64)+base.clone())) & !c.ult(&vc('0' as u64))),
&(isdig(c) | (c.ult(&(vc('a' as u64)+base.sub(&vc(10)))) & !c.ult(&vc('a' as u64))) |
(c.ult(&(vc('A' as u64)+base.sub(&vc(10)))) & !c.ult(&vc('A' as u64)))))
}
fn tonum(state: &State, c: &Value) -> Value {
let alpha = state.solver.conditional(
&c.ulte(&vc('Z' as u64)),
&c.sub(&vc('A' as u64 - 10)),
&c.sub(&vc('a' as u64 - 10))
);
state.solver.conditional(
&c.ulte(&vc('9' as u64)),
&c.sub(&vc('0' as u64)),
&alpha
)
}
fn atoi_concrete(state: &mut State, addr: &Value, base: &Value, len: usize) -> Value {
let numstr = state.memory_read_string(addr.as_u64().unwrap(), len);
let numstr = numstr.trim_start();
let start = if &numstr[0..2] == "0x" { 2 } else { 0 }; let end = if let Some(n) = numstr[start+1..].chars().position(|c| isbasedigit(
state, &vc(c as u64), base).as_u64().unwrap() != 1) { start+n+1 } else { len };
let numopt = u64::from_str_radix(&numstr[start..end], base.as_u64().unwrap() as u32);
return if let Ok(n) = numopt { vc(n) } else { vc(0) };
}
pub fn atoi_helper(state: &mut State, addr: &Value, base: &Value, size: u64) -> Value {
let length = state.memory_strlen(&addr, &Value::Concrete(64, 0));
let data = state.memory_read(&addr, &length);
let len = data.len();
state.assert_value(&length.eq(&vc(len as u64)));
if len == 0 {
return Value::Concrete(0, 0);
}
if addr.is_concrete() && base.is_concrete()
&& data.iter().all(|x| x.is_concrete()) {
return atoi_concrete(state, addr, base, len);
}
let mut result = Value::Concrete(0, 0);
let neg_mul = state.solver.conditional(
&data[0].eq(&vc('-' as u64)),
&Value::Concrete(-1i64 as u64, 0),
&Value::Concrete(1, 0));
for (i, d) in data.iter().enumerate() {
let dx = d.uext(&vc(8));
let exp = (len-i-1) as u32;
let cond = if i == 0 {
isbasedigit(state, &dx, base) |
dx.eq(&vc('-' as u64)) | dx.eq(&vc('+' as u64))
} else {
isbasedigit(state, &dx, base)
};
state.assert_value(&cond);
result = result + state.solver.conditional(
&!isbasedigit(state, &dx, base), &vc(0),
&(bv_pow(base, exp) * tonum(state, &dx))
);
}
let mask = (1i64.wrapping_shl(size as u32)-1) as u64;
state.assert_value(&result.ulte(&Value::Concrete(mask, 0)));
result * neg_mul
}
pub fn atoi(state: &mut State, args: &[Value]) -> Value {
atoi_helper(state, &args[0], &vc(10), 32)
}
pub fn atol(state: &mut State, args: &[Value]) -> Value {
let bits = state.memory.bits;
atoi_helper(state, &args[0], &vc(10), bits)
}
pub fn atoll(state: &mut State, args: &[Value]) -> Value {
atoi_helper(state, &args[0], &vc(10), 64)
}
pub fn strtoll(state: &mut State, args: &[Value]) -> Value {
if let Value::Concrete(addr, _) = args[1] {
if addr != 0 {
let length = state.memory_strlen(&args[0], &Value::Concrete(64, 0));
state.memory_write_value(&args[1], &args[0].add(&length),
state.memory.bits as usize / 8);
}
}
atoi_helper(state, &args[0], &args[2], 64)
}
pub fn strtod(state: &mut State, args: &[Value]) -> Value {
strtoll(state, args).slice(31, 0)
}
pub fn strtol(state: &mut State, args: &[Value]) -> Value {
let bits = state.memory.bits;
strtoll(state, args).slice(bits-1, 0)
}
pub fn itoa_helper(state: &mut State, value: &Value,
string: &Value, base: &Value, sign: bool, size: usize) -> Value {
let mut data = vec!();
let neg_cond = &(value.slt(&vc(0)) & base.eq(&vc(10)) & vc(sign as u64));
let uval = state.solver.conditional(
&neg_cond, &value.mul(&vc(-1i64 as u64)), &value);
let uval = Value::Symbolic(state.solver.to_bv(&uval, 128), 0);
let ubase = Value::Symbolic(state.solver.to_bv(&base, 128), 0);
let mut shift = Value::Symbolic(state.solver.bvv(0, 64), 0);
for i in 0..size as u32 {
let dx = uval.rem(&bv_pow(&ubase, i+1)).div(&bv_pow(&ubase, i));
shift = state.solver.conditional(
&!dx.clone(), &shift.add(&vc(8)), &vc(0));
data.push(state.solver.conditional(
&dx.ult(&vc(10)),
&dx.add(&vc('0' as u64)),
&dx.sub(&vc(10)).add(&vc('a' as u64))
));
}
data.reverse();
let bv = state.memory.pack(&data).as_bv().unwrap();
let shift_bits = 31 - bv.get_width().leading_zeros(); let bv = bv.srl(&shift.as_bv().unwrap().slice(shift_bits-1, 0));
let mut new_addr = string.clone();
if sign {
let b = state.solver.conditional(
&neg_cond, &vc('-' as u64), &vc('+' as u64));
state.memory_write_value(&string, &b, 1);
new_addr = state.solver.conditional(
&neg_cond, &(new_addr.clone()+vc(1)), &new_addr);
}
state.memory_write_value(&new_addr, &Value::Symbolic(bv,0), data.len());
string.to_owned()
}
pub fn itoa(state: &mut State, args: &[Value]) -> Value {
itoa_helper(state, &args[0], &args[1], &args[2], true, 32)
}
pub fn islower(_state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
c.ult(&Value::Concrete(0x7b, 0)) & !c.ult(&Value::Concrete(0x61, 0))
}
pub fn isupper(_state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
c.ult(&Value::Concrete(0x5b, 0)) & !c.ult(&Value::Concrete(0x41, 0))
}
pub fn isalpha(state: &mut State, args: &[Value]) -> Value {
isupper(state, args.clone()) | islower(state, args)
}
pub fn isdigit(_state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
c.ult(&Value::Concrete(0x3a, 0)) & !c.ult(&Value::Concrete(0x30, 0))
}
pub fn isalnum(state: &mut State, args: &[Value]) -> Value {
isalpha(state, args.clone()) | isdigit(state, args)
}
pub fn isblank(_state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
c.eq(&Value::Concrete(0x20, 0)) | c.eq(&Value::Concrete(0x09, 0))
}
pub fn iscntrl(_state: &mut State, args: &[Value]) -> Value {
let c = args[0].slice(7, 0);
(c.ugte(&Value::Concrete(0, 0)) & c.ulte(&Value::Concrete(0x1f, 0)))
| c.eq(&Value::Concrete(0x7f, 0))
}
pub fn toupper(state: &mut State, args: &[Value]) -> Value {
let islo = islower(state, args.clone());
state.solver.conditional(&islo,
&(args[0].to_owned()-Value::Concrete(0x20, 0)), &args[0])
}
pub fn tolower(state: &mut State, args: &[Value]) -> Value {
let isup = isupper(state, args.clone());
state.solver.conditional(&isup,
&(args[0].to_owned()+Value::Concrete(0x20, 0)), &args[0])
}
pub fn zero(_state: &mut State, _args: &[Value]) -> Value {
Value::Concrete(0, 0)
}
pub fn rand(state: &mut State, _args: &[Value]) -> Value {
state.symbolic_value("rand", 32)
}
pub fn srand(_state: &mut State, _args: &[Value]) -> Value {
Value::Concrete(0, 0)
}
pub fn fflush(_state: &mut State, _args: &[Value]) -> Value {
Value::Concrete(0, 0)
}
pub fn getpid(state: &mut State, args: &[Value]) -> Value {
syscall::getpid(state, args)
}
pub fn getuid(state: &mut State, args: &[Value]) -> Value {
syscall::getuid(state, args)
}
pub fn getgid(state: &mut State, args: &[Value]) -> Value {
syscall::getuid(state, args)
}
pub fn geteuid(state: &mut State, args: &[Value]) -> Value {
syscall::getuid(state, args)
}
pub fn getegid(state: &mut State, args: &[Value]) -> Value {
syscall::getuid(state, args)
}
pub fn fork(state: &mut State, args: &[Value]) -> Value {
syscall::fork(state, args)
}
pub fn brk(state: &mut State, args: &[Value]) -> Value {
let addr = state.solver.evalcon_to_u64(&args[0]).unwrap();
let current = syscall::sbrk(state, &[vc(0)]);
let new = syscall::brk(state, args);
if current.as_u64().unwrap() == addr || new.as_u64().unwrap() != addr {
vc(0)
} else {
vc(-1i64 as u64)
}
}
pub fn sbrk(state: &mut State, args: &[Value]) -> Value {
syscall::sbrk(state, args)
}
pub fn getpagesize(_state: &mut State, _args: &[Value]) -> Value {
Value::Concrete(0x1000, 0)
}
pub fn gethostname(state: &mut State, args: &[Value]) -> Value {
let addr = state.solver.evalcon_to_u64(&args[0]).unwrap();
state.memory_write_string(addr, "radius");
Value::Concrete(0, 0)
}
pub fn getenv(state: &mut State, args: &[Value]) -> Value {
if state.context.get("env").is_none() {
return Value::Concrete(0, 0);
}
let arg_ptr = args[0].to_owned();
let arg_length = state.memory_strlen(&arg_ptr, &vc(MAX_LEN));
let bits = state.memory.bits as usize;
let mut env_ptr = state.context["env"][0].clone();
let mut result = vc(0);
loop {
let var_ptr = state.memory_read_value(&env_ptr, bits/8);
if state.solver.check_sat(&var_ptr.eq(&vc(0))) {
state.assert_value(&var_ptr.eq(&vc(0)));
break;
}
let full_length = state.memory_strlen(&var_ptr, &vc(MAX_LEN));
let name_end = state.memory_search(
&var_ptr, &vc('=' as u64), &full_length, false);
let name_length = state.solver.conditional(
&name_end.eq(&vc(0)), &full_length, &name_end.sub(&var_ptr));
let value_ptr = var_ptr.add(&name_length).add(&vc(1));
let long_len = state.solver.conditional(
&arg_length.ugte(&name_length), &arg_length, &name_length);
let cmp = state.memory_compare(&arg_ptr, &var_ptr, &long_len);
result = state.solver.conditional(&cmp.eq(&vc(0)), &value_ptr, &result);
env_ptr = env_ptr + vc(bits as u64/8);
}
result
}
pub fn realpath(state: &mut State, args: &[Value]) -> Value {
let length = state.memory_strlen(&args[0], &Value::Concrete(MAX_LEN, 0));
state.memory_move(&args[1], &args[0], &length);
args[1].to_owned()
}
pub fn sleep(_state: &mut State, _args: &[Value]) -> Value {
Value::Concrete(0, 0)
}
pub fn open(state: &mut State, args: &[Value]) -> Value {
syscall::open(state, args)
}
pub fn close(state: &mut State, args: &[Value]) -> Value {
syscall::close(state, args)
}
pub fn read(state: &mut State, args: &[Value]) -> Value {
syscall::read(state, args)
}
pub fn write(state: &mut State, args: &[Value]) -> Value {
syscall::write(state, args)
}
pub fn lseek(state: &mut State, args: &[Value]) -> Value {
syscall::lseek(state, args)
}
pub fn access(state: &mut State, args: &[Value]) -> Value {
syscall::access(state, args)
}
pub fn stat(state: &mut State, args: &[Value]) -> Value {
syscall::stat(state, args)
}
pub fn fstat(state: &mut State, args: &[Value]) -> Value {
syscall::fstat(state, args)
}
pub fn lstat(state: &mut State, args: &[Value]) -> Value {
syscall::lstat(state, args)
}
pub fn ptrace(state: &mut State, args: &[Value]) -> Value {
syscall::ptrace(state, args)
}
pub fn exit(state: &mut State, args: &[Value]) -> Value {
syscall::exit(state, args)
}