/* * 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; }