/*
* Copyright (C) 2020 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see .
*/
#include "rpn_jit.h"
int rpn_expr_init(rpn_expr_t* expr, const unsigned char stack_sz,
const size_t args_count)
{
#ifdef DEBUG
if(!expr)
{
dprintf(2, "Error, NULL ptr given as expression to rpn_expr_init");
errno = EINVAL;
return -1;
}
#endif
bzero(expr, sizeof(rpn_expr_t));
expr->stack_sz = stack_sz;
expr->args_count = args_count;
expr->state = RPN_SOURCE;
memset(expr->err_reason, (int)'\0', 128);
expr->stack = malloc(sizeof(unsigned long) * stack_sz);
if(!expr->stack)
{
snprintf(expr->err_reason, 128,
"Unable to malloc stack : %s", strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
bzero(expr->stack, sizeof(unsigned long) * stack_sz);
if(_rpn_expr_init_map(expr) < 0)
{
snprintf(expr->err_reason, 128,
"Unable to init code map : %s", strerror(errno));
free(expr->expr);
expr->state = RPN_ERROR;
return -1;
}
return 0;
}
int rpn_expr_reinit(rpn_expr_t* expr)
{
#ifdef DEBUG
if(!expr)
{
dprintf(2, "Error, NULL ptr given as expression to rpn_expr_compile");
errno = EINVAL;
return -1;
}
#endif
bzero(expr->code_map, expr->code_map_sz);
bzero(expr->stack, sizeof(unsigned long) * expr->stack_sz);
if(_rpn_expr_init_map(expr) < 0)
{
snprintf(expr->err_reason, 128,
"Unable to re-init code map : %s", strerror(errno));
free(expr->expr);
expr->state = RPN_ERROR;
return -1;
}
return 0;
}
int rpn_expr_compile(rpn_expr_t *expr, const char *code)
{
#ifdef DEBUG
if(!expr)
{
dprintf(2, "Error, NULL ptr given as expression to rpn_expr_compile");
errno = EINVAL;
return -1;
}
#endif
expr->expr = strdup(code);
if(!expr->expr)
{
snprintf(expr->err_reason, 128,
"Unable to strdup expression : %s", strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
return _rpn_expr_compile_expr(expr);
}
int rpn_expr_untokenize(rpn_expr_t *expr, rpn_tokenized_t *tokens, char long_op)
{
int err;
size_t i;
errno = 0;
#ifdef DEBUG
if(!expr)
{
dprintf(2, "Error, NULL ptr given as expression to rpn_expr_untokenize");
err = EINVAL;
goto ret_err;
}
if(tokens->argc != expr->args_count)
{
/* even if it should work with tokens->argc < expr->args_count */
snprintf(expr->err_reason, 128,
"Expression argc differ from tokenized version");
err = EINVAL;
goto ret_err;
}
#endif
if(!(expr->expr = rpn_tokenized_expr(tokens, long_op)))
{
err = errno;
snprintf(expr->err_reason, 128,
"Error reading tokenized expression : %s",
strerror(err));
goto ret_err;
}
for(i=0; itokens_sz; i++)
{
if(_rpn_expr_token_copy(expr, &(tokens->tokens[i])) < 0)
{
err = errno;
if(errno == EINVAL)
{
dprintf(2,
"Fatal error, unknown token type : %d.\nMemory corruption ?\n",
tokens->tokens[i].type);
exit(1);
}
snprintf(expr->err_reason, 128,
"Untokenize error : %s",
strerror(err));
goto ret_err;
}
}
if(_rpn_expr_end_map(expr))
{
snprintf(expr->err_reason, 128,
"Error ending code map : %s",
strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
expr->state = RPN_READY;
return 0;
ret_err:
expr->state = RPN_ERROR;
errno = err;
return -1;
}
char* rpn_random(size_t op_sz, size_t args_count)
{
double step;
size_t i, buff_sz, offset, rnd;
char *buff, *cur;
unsigned char op_n;
unsigned long int seed, rnd_val;
int nchr, err;
buff_sz = offset = 0;
buff = NULL;
step = 1.0 / (rpn_op_sz() + (args_count>0?2:1)); // + args and values
if(getrandom(&seed, sizeof(long int), 0) < 0)
{
err=errno;
perror("Fails to get a random number from kernel");
errno=err;
return NULL;
}
srand48(seed);
for(i=0; istate == RPN_ERROR)
{
goto err;
}
if(rpn_tokenizer_start(&tokenizer, &(expr->toks), expr->expr,
expr->args_count) < 0)
{
snprintf(expr->err_reason, 128,
"Error starting tokenizer : %s",
tokenizer.err_reason);
goto err;
}
while((token = rpn_tok(&tokenizer)))
{
if(_rpn_expr_token_copy(expr, token) < 0)
{
if(errno == EINVAL)
{
dprintf(2,
"Fatal error, unknown token type : %d chr %ld.\nMemory corruption ?\n",
token->type, tokenizer.chr_no);
exit(1);
}
snprintf(expr->err_reason, 128,
"Compilation error on chr %ld, unable to copy code part : %s",
tokenizer.chr_no, strerror(errno));
goto err;
}
}
if(rpn_tokenizer_error(&tokenizer))
{
snprintf(expr->err_reason, 128,
"Compilation error, chr %ld : %s",
tokenizer.chr_no, tokenizer.err_reason);
goto err;
}
if(_rpn_expr_end_map(expr))
{
snprintf(expr->err_reason, 128,
"Error ending code map : %s",
strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
expr->state = RPN_READY;
return 0;
err:
expr->state = RPN_ERROR;
return -1;
}
int _rpn_expr_compile_tokens(rpn_expr_t* expr)
{
size_t i;
rpn_token_t *token;
for(i=0; itoks.tokens_sz; i++)
{
token = &(expr->toks.tokens[i]);
if(_rpn_expr_token_copy(expr, token) < 0)
{
if(errno == EINVAL)
{
dprintf(2,
"Fatal error, unknown token type : %d\nMemory corruption ?\n",
token->type);
exit(1);
}
snprintf(expr->err_reason, 128,
"Compilation error, unable to copy code part : %s",
strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
}
if(_rpn_expr_end_map(expr))
{
snprintf(expr->err_reason, 128,
"Error ending code map : %s",
strerror(errno));
expr->state = RPN_ERROR;
return -1;
}
expr->state = RPN_READY;
return 0;
}
unsigned long rpn_expr_eval(rpn_expr_t *expr, unsigned long *args)
{
rpn_run_f expr_run;
unsigned long int res;
if(expr->state == RPN_ERROR)
{
return 0;
}
expr_run = expr->code_map;
res = expr_run(expr->stack_sz, args, expr->stack);
return res;
}
void rpn_expr_close(rpn_expr_t* expr)
{
if(expr->expr)
{
free(expr->expr);
expr->expr = NULL;
}
if(expr->stack)
{
free(expr->stack);
expr->stack = NULL;
}
if(expr->code_map)
{
if(munmap(expr->code_map, expr->code_map_sz))
{
perror("Unable to unmap code_map");
}
expr->code_map = NULL;
}
}
void rpn_expr_reset_stack(rpn_expr_t *expr)
{
bzero(expr->stack, sizeof(unsigned long) * expr->stack_sz);
}
int _rpn_expr_token_copy(rpn_expr_t *expr, rpn_token_t *token)
{
unsigned long int *value;
rpn_op_t local_op;
value = NULL;
switch(token->type)
{
case RPN_op:
local_op = *(token->op);
value = NULL;
break;
case RPN_arg:
local_op.fun = &rpn_arg;
local_op.fun_sz = &(CODE_SZ(rpn_arg));
value = &(token->arg_n);
break;
case RPN_val:
local_op.fun = &rpn_value;
local_op.fun_sz = &(CODE_SZ(rpn_value));
value = &(token->value);
break;
default:
errno = EINVAL;
return -1;
}
if(_rpn_code_part_cpy(expr, local_op.fun, *(local_op.fun_sz),
value))
{
return -1;
}
return 0;
}
int _rpn_code_part_cpy(rpn_expr_t *expr, const void *code_part,
unsigned long code_part_sz, const unsigned long *value)
{
size_t old_sz, code_sz;
void *new_ptr;
//printf("DEBUG _copy : %p %ld %p:%ld\n", code_part, code_part_sz, value, value?*value:0);
code_sz = expr->code_map_ptr - expr->code_map;
if(!expr->code_map_sz)
{
errno = EINVAL;
return -1;
}
if(code_sz + code_part_sz >= expr->code_map_sz)
{
old_sz = expr->code_map_sz;
expr->code_map_sz = (((code_sz + code_part_sz)>>9)+1)<<9;
new_ptr = mremap(expr->code_map, old_sz, expr->code_map_sz,
MREMAP_MAYMOVE);
if(new_ptr == (void*)-1)
{
expr->code_map_sz = 0;
return -1;
}
expr->code_map = new_ptr;
expr->code_map_ptr = expr->code_map + code_sz;
}
memcpy(expr->code_map_ptr, code_part, code_part_sz);
if(value)
{
// set 1st instruction argument
*(unsigned long*)(expr->code_map_ptr + 2) = *value;
}
expr->code_map_ptr += code_part_sz;
return 0;
}
int _rpn_expr_init_map(rpn_expr_t* expr)
{
if(!expr->code_map_sz)
{
expr->code_map_sz = RPN_MAP_CHUNK;
expr->code_map = mmap(NULL, expr->code_map_sz, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(!expr->code_map)
{
return -1;
}
}
expr->code_map_ptr = expr->code_map;
if(CODE_PART_CPY(expr, rpn_exec))
{
return -1;
}
return 0;
}
int _rpn_expr_end_map(rpn_expr_t *expr)
{
if(CODE_PART_CPY(expr, rpn_exec_ret))
{
return -1;
}
if(mprotect(expr->code_map, expr->code_map_ptr - expr->code_map,
PROT_EXEC))
{
return -1;
}
return 0;
}