#include "scope.h"
#include "smatch.h"
#include "smatch_slist.h"
static int my_id;
static char *get_from__symbol_get(struct expression *expr)
{
struct expression *arg;
expr = strip_expr(expr);
if (expr->type != EXPR_CALL)
return NULL;
if (!sym_name_is("__symbol_get", expr->fn))
return NULL;
arg = get_argument_from_call_expr(expr->args, 0);
if (!arg || arg->type != EXPR_STRING)
return NULL;
return alloc_string(arg->string->data);
}
static int xxx_is_array(struct expression *expr)
{
struct symbol *type;
expr = strip_expr(expr);
if (!expr)
return 0;
if (expr->type == EXPR_PREOP && expr->op == '*') {
expr = strip_expr(expr->unop);
if (!expr)
return 0;
if (expr->type == EXPR_BINOP && expr->op == '+')
return 1;
}
if (expr->type != EXPR_BINOP || expr->op != '+')
return 0;
type = get_type(expr->left);
if (!type)
return 0;
if (type->type != SYM_ARRAY && type->type != SYM_PTR)
return 0;
return 1;
}
static struct expression *xxx_get_array_base(struct expression *expr)
{
if (!xxx_is_array(expr))
return NULL;
expr = strip_expr(expr);
if (expr->type == EXPR_PREOP && expr->op == '*')
expr = strip_expr(expr->unop);
if (expr->type != EXPR_BINOP || expr->op != '+')
return NULL;
return strip_parens(expr->left);
}
static char *get_array_ptr(struct expression *expr)
{
struct expression *array;
struct symbol *type;
char *name;
char buf[256];
array = xxx_get_array_base(expr);
if (array) {
name = get_member_name(array);
if (name)
return name;
}
type = get_type(expr);
if (!array && type && type->type == SYM_ARRAY)
array = expr;
if (array) {
name = expr_to_var(array);
if (!name)
return NULL;
snprintf(buf, sizeof(buf), "%s[]", name);
return alloc_string(buf);
}
expr = get_assigned_expr(expr);
array = xxx_get_array_base(expr);
if (!array)
return NULL;
name = expr_to_var(array);
if (!name)
return NULL;
snprintf(buf, sizeof(buf), "%s[]", name);
free_string(name);
return alloc_string(buf);
}
static int is_local_symbol(struct symbol *sym)
{
if (!sym ||
!(sym->ctype.modifiers & MOD_TOPLEVEL))
return 1;
return 0;
}
static char *ptr_prefix(struct symbol *sym)
{
static char buf[128];
if (is_local_symbol(sym))
snprintf(buf, sizeof(buf), "%s ptr", get_function());
else if (sym && toplevel(sym->scope))
snprintf(buf, sizeof(buf), "%s ptr", get_base_file());
else
snprintf(buf, sizeof(buf), "ptr");
return buf;
}
char *get_returned_ptr(struct expression *expr)
{
struct symbol *type;
char *name;
char buf[256];
if (expr->type != EXPR_CALL)
return NULL;
if (!expr->fn || expr->fn->type != EXPR_SYMBOL)
return NULL;
type = get_type(expr);
if (type && type->type == SYM_PTR)
type = get_real_base_type(type);
if (!type || type->type != SYM_FN)
return NULL;
name = expr_to_var(expr->fn);
if (!name)
return NULL;
snprintf(buf, sizeof(buf), "r %s()", name);
free_string(name);
return alloc_string(buf);
}
char *get_fnptr_name(struct expression *expr)
{
char *name;
if (expr_is_zero(expr))
return NULL;
expr = strip_expr(expr);
if (expr->type == EXPR_PREOP && expr->op == '*')
expr = strip_expr(expr->unop);
name = get_from__symbol_get(expr);
if (name)
return name;
name = get_array_ptr(expr);
if (name)
return name;
name = get_returned_ptr(expr);
if (name)
return name;
name = get_member_name(expr);
if (name)
return name;
if (expr->type == EXPR_SYMBOL) {
int param;
char buf[256];
struct symbol *sym;
struct symbol *type;
param = get_param_num_from_sym(expr->symbol);
if (param >= 0) {
snprintf(buf, sizeof(buf), "%s param %d", get_function(), param);
return alloc_string(buf);
}
name = expr_to_var_sym(expr, &sym);
if (!name)
return NULL;
type = get_type(expr);
if (type && type->type == SYM_PTR) {
snprintf(buf, sizeof(buf), "%s %s", ptr_prefix(sym), name);
free_string(name);
return alloc_string(buf);
}
return name;
}
return expr_to_var(expr);
}
static void match_passes_function_pointer(struct expression *expr)
{
struct expression *arg, *tmp;
struct symbol *type;
char *called_name;
char *fn_name;
char ptr_name[256];
int i;
i = -1;
FOR_EACH_PTR(expr->args, arg) {
i++;
tmp = strip_expr(arg);
if (tmp->type == EXPR_PREOP && tmp->op == '&')
tmp = strip_expr(tmp->unop);
type = get_type(tmp);
if (type && type->type == SYM_PTR)
type = get_real_base_type(type);
if (!type || type->type != SYM_FN)
continue;
called_name = expr_to_var(expr->fn);
if (!called_name)
return;
fn_name = get_fnptr_name(tmp);
if (!fn_name)
goto free;
snprintf(ptr_name, sizeof(ptr_name), "%s param %d", called_name, i);
sql_insert_function_ptr(fn_name, ptr_name);
free:
free_string(fn_name);
free_string(called_name);
} END_FOR_EACH_PTR(arg);
}
static int get_row_count(void *_row_count, int argc, char **argv, char **azColName)
{
int *row_count = _row_count;
*row_count = 0;
if (argc != 1)
return 0;
*row_count = atoi(argv[0]);
return 0;
}
static int can_hold_function_ptr(struct expression *expr)
{
struct symbol *type;
type = get_type(expr);
if (!type)
return 0;
if (type->type == SYM_PTR || type->type == SYM_ARRAY) {
type = get_real_base_type(type);
if (!type)
return 0;
}
if (type->type == SYM_PTR || type->type == SYM_ARRAY) {
type = get_real_base_type(type);
if (!type)
return 0;
}
if (type->type == SYM_FN)
return 1;
if (type == &ulong_ctype && expr->type == EXPR_DEREF)
return 1;
if (type == &void_ctype)
return 1;
return 0;
}
static void match_function_assign(struct expression *expr)
{
struct expression *right;
struct symbol *type;
char *fn_name;
char *ptr_name;
if (__in_fake_assign)
return;
right = strip_expr(expr->right);
if (right->type == EXPR_PREOP && right->op == '&')
right = strip_expr(right->unop);
if (right->type != EXPR_SYMBOL &&
right->type != EXPR_DEREF &&
right->type != EXPR_CALL)
return;
if (!can_hold_function_ptr(right) ||
!can_hold_function_ptr(expr->left))
return;
fn_name = get_fnptr_name(right);
ptr_name = get_fnptr_name(expr->left);
if (!fn_name || !ptr_name)
goto free;
if (strcmp(fn_name, ptr_name) == 0)
goto free;
type = get_type(right);
if (!type)
return;
if (type->type == SYM_PTR || type->type == SYM_ARRAY) {
type = get_real_base_type(type);
if (!type)
return;
}
if (type->type != SYM_FN) {
int count = 0;
run_sql(get_row_count, &count,
"select count(*) from function_ptr where ptr = '%s'",
fn_name);
if (count == 0)
goto free;
}
sql_insert_function_ptr(fn_name, ptr_name);
free:
free_string(fn_name);
free_string(ptr_name);
}
static void match_returns_function_pointer(struct expression *expr)
{
struct symbol *type;
char *fn_name;
char ptr_name[256];
if (__inline_fn)
return;
type = get_real_base_type(cur_func_sym);
if (!type || type->type != SYM_FN)
return;
type = get_real_base_type(type);
if (!type || type->type != SYM_PTR)
return;
type = get_real_base_type(type);
if (!type || type->type != SYM_FN)
return;
if (expr->type == EXPR_PREOP && expr->op == '&')
expr = strip_expr(expr->unop);
fn_name = get_fnptr_name(expr);
if (!fn_name)
return;
snprintf(ptr_name, sizeof(ptr_name), "r %s()", get_function());
sql_insert_function_ptr(fn_name, ptr_name);
}
static void print_initializer_list(struct expression_list *expr_list,
struct symbol *struct_type)
{
struct expression *expr;
struct symbol *base_type;
char struct_name[256];
FOR_EACH_PTR(expr_list, expr) {
if (expr->type == EXPR_INDEX && expr->idx_expression && expr->idx_expression->type == EXPR_INITIALIZER) {
print_initializer_list(expr->idx_expression->expr_list, struct_type);
continue;
}
if (expr->type != EXPR_IDENTIFIER)
continue;
if (!expr->expr_ident)
continue;
if (!expr->ident_expression ||
expr->ident_expression->type != EXPR_SYMBOL ||
!expr->ident_expression->symbol_name)
continue;
base_type = get_type(expr->ident_expression);
if (!base_type || base_type->type != SYM_FN)
continue;
snprintf(struct_name, sizeof(struct_name), "(struct %s)->%s",
struct_type->ident->name, expr->expr_ident->name);
sql_insert_function_ptr(expr->ident_expression->symbol_name->name,
struct_name);
} END_FOR_EACH_PTR(expr);
}
static void global_variable(struct symbol *sym)
{
struct symbol *struct_type;
if (!sym->ident)
return;
if (!sym->initializer || sym->initializer->type != EXPR_INITIALIZER)
return;
struct_type = get_base_type(sym);
if (!struct_type)
return;
if (struct_type->type == SYM_ARRAY) {
struct_type = get_base_type(struct_type);
if (!struct_type)
return;
}
if (struct_type->type != SYM_STRUCT || !struct_type->ident)
return;
print_initializer_list(sym->initializer->expr_list, struct_type);
}
void register_function_ptrs(int id)
{
my_id = id;
if (!option_info)
return;
add_hook(&global_variable, BASE_HOOK);
add_hook(&global_variable, DECLARATION_HOOK);
add_hook(&match_passes_function_pointer, FUNCTION_CALL_HOOK);
add_hook(&match_returns_function_pointer, RETURN_HOOK);
add_hook(&match_function_assign, ASSIGNMENT_HOOK);
add_hook(&match_function_assign, GLOBAL_ASSIGNMENT_HOOK);
}