502 lines
10 KiB
C
502 lines
10 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#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; i<tokens->tokens_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; i<op_sz; i++)
|
|
{
|
|
if(buff_sz - offset < 21)
|
|
{
|
|
buff_sz += 40;
|
|
cur = realloc(buff, sizeof(char) * buff_sz);
|
|
if(!cur)
|
|
{
|
|
err=errno;
|
|
perror("Error allocating random expression");
|
|
errno=err;
|
|
return NULL;
|
|
}
|
|
buff=cur;
|
|
}
|
|
cur = buff + offset;
|
|
*cur = '\0';
|
|
op_n = drand48() / step;
|
|
if(op_n < rpn_op_sz())
|
|
{
|
|
cur[0] = rpn_ops[op_n].chr;
|
|
cur[1] = ' ';
|
|
cur[2] = '\0';
|
|
offset += 2;
|
|
}
|
|
else if(op_n == rpn_op_sz())
|
|
{
|
|
|
|
if(getrandom(&rnd_val, sizeof(long int), 0) < 0)
|
|
{
|
|
err=errno;
|
|
perror("Fails to get a random number for value");
|
|
errno=err;
|
|
return NULL;
|
|
}
|
|
// values
|
|
if((nchr = sprintf(cur, "0x%lX ", rnd_val)) < 0)
|
|
{
|
|
err=errno;
|
|
perror("Error while sprintf arguments in random generator");
|
|
errno=err;
|
|
return NULL;
|
|
}
|
|
offset += nchr;
|
|
}
|
|
else
|
|
{
|
|
rnd = drand48() / (1.0 / args_count);
|
|
// arguments
|
|
if((nchr = sprintf(cur, "A%ld ", rnd)) < 0)
|
|
{
|
|
err=errno;
|
|
perror("Error while sprintf arguments in random generator");
|
|
errno=err;
|
|
return NULL;
|
|
}
|
|
offset += nchr;
|
|
}
|
|
}
|
|
buff[offset] = '\0';
|
|
return buff;
|
|
}
|
|
|
|
int _rpn_expr_compile_expr(rpn_expr_t* expr)
|
|
{
|
|
rpn_tokenizer_t tokenizer;
|
|
rpn_token_t *token;
|
|
|
|
|
|
if(expr->state == 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; i<expr->toks.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;
|
|
}
|
|
|