11f5207b7SJohn Levon /*
21f5207b7SJohn Levon * Copyright (C) 2017 Oracle.
31f5207b7SJohn Levon *
41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon * of the License, or (at your option) any later version.
81f5207b7SJohn Levon *
91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121f5207b7SJohn Levon * GNU General Public License for more details.
131f5207b7SJohn Levon *
141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon */
171f5207b7SJohn Levon
181f5207b7SJohn Levon #include "smatch.h"
191f5207b7SJohn Levon #include "smatch_slist.h"
201f5207b7SJohn Levon #include "smatch_extra.h"
211f5207b7SJohn Levon
221f5207b7SJohn Levon static int my_id;
231f5207b7SJohn Levon static int param_id;
241f5207b7SJohn Levon
get_param_from_container_of(struct expression * expr)251f5207b7SJohn Levon int get_param_from_container_of(struct expression *expr)
261f5207b7SJohn Levon {
271f5207b7SJohn Levon struct expression *param_expr;
281f5207b7SJohn Levon struct symbol *type;
291f5207b7SJohn Levon sval_t sval;
301f5207b7SJohn Levon int param;
311f5207b7SJohn Levon
321f5207b7SJohn Levon
331f5207b7SJohn Levon type = get_type(expr);
341f5207b7SJohn Levon if (!type || type->type != SYM_PTR)
351f5207b7SJohn Levon return -1;
361f5207b7SJohn Levon
371f5207b7SJohn Levon expr = strip_expr(expr);
381f5207b7SJohn Levon if (expr->type != EXPR_BINOP || expr->op != '-')
391f5207b7SJohn Levon return -1;
401f5207b7SJohn Levon
411f5207b7SJohn Levon if (!get_value(expr->right, &sval))
421f5207b7SJohn Levon return -1;
431f5207b7SJohn Levon if (sval.value < 0 || sval.value > 4096)
441f5207b7SJohn Levon return -1;
451f5207b7SJohn Levon
461f5207b7SJohn Levon param_expr = get_assigned_expr(expr->left);
471f5207b7SJohn Levon if (!param_expr)
481f5207b7SJohn Levon return -1;
491f5207b7SJohn Levon param = get_param_num(param_expr);
501f5207b7SJohn Levon if (param < 0)
511f5207b7SJohn Levon return -1;
521f5207b7SJohn Levon
531f5207b7SJohn Levon return param;
541f5207b7SJohn Levon }
551f5207b7SJohn Levon
get_offset_from_container_of(struct expression * expr)561f5207b7SJohn Levon int get_offset_from_container_of(struct expression *expr)
571f5207b7SJohn Levon {
581f5207b7SJohn Levon struct expression *param_expr;
591f5207b7SJohn Levon struct symbol *type;
601f5207b7SJohn Levon sval_t sval;
611f5207b7SJohn Levon
621f5207b7SJohn Levon type = get_type(expr);
631f5207b7SJohn Levon if (!type || type->type != SYM_PTR)
641f5207b7SJohn Levon return -1;
651f5207b7SJohn Levon
661f5207b7SJohn Levon expr = strip_expr(expr);
671f5207b7SJohn Levon if (expr->type != EXPR_BINOP || expr->op != '-')
681f5207b7SJohn Levon return -1;
691f5207b7SJohn Levon
701f5207b7SJohn Levon if (!get_value(expr->right, &sval))
711f5207b7SJohn Levon return -1;
721f5207b7SJohn Levon if (sval.value < 0 || sval.value > 4096)
731f5207b7SJohn Levon return -1;
741f5207b7SJohn Levon
751f5207b7SJohn Levon param_expr = get_assigned_expr(expr->left);
761f5207b7SJohn Levon if (!param_expr)
771f5207b7SJohn Levon return -1;
781f5207b7SJohn Levon
791f5207b7SJohn Levon return sval.value;
801f5207b7SJohn Levon }
811f5207b7SJohn Levon
print_returns_container_of(int return_id,char * return_ranges,struct expression * expr)821f5207b7SJohn Levon static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
831f5207b7SJohn Levon {
841f5207b7SJohn Levon int offset;
851f5207b7SJohn Levon int param;
861f5207b7SJohn Levon char key[64];
871f5207b7SJohn Levon char value[64];
881f5207b7SJohn Levon
891f5207b7SJohn Levon param = get_param_from_container_of(expr);
901f5207b7SJohn Levon if (param < 0)
911f5207b7SJohn Levon return;
921f5207b7SJohn Levon offset = get_offset_from_container_of(expr);
931f5207b7SJohn Levon if (offset < 0)
941f5207b7SJohn Levon return;
951f5207b7SJohn Levon
961f5207b7SJohn Levon snprintf(key, sizeof(key), "%d", param);
971f5207b7SJohn Levon snprintf(value, sizeof(value), "-%d", offset);
981f5207b7SJohn Levon
991f5207b7SJohn Levon /* no need to add it to return_implies because it's not really param_used */
1001f5207b7SJohn Levon sql_insert_return_states(return_id, return_ranges, CONTAINER, -1,
1011f5207b7SJohn Levon key, value);
1021f5207b7SJohn Levon }
1031f5207b7SJohn Levon
get_deref_count(struct expression * expr)104efe51d0cSJohn Levon static int get_deref_count(struct expression *expr)
1051f5207b7SJohn Levon {
106efe51d0cSJohn Levon int cnt = 0;
107efe51d0cSJohn Levon
108efe51d0cSJohn Levon while (expr && expr->type == EXPR_DEREF) {
109efe51d0cSJohn Levon expr = expr->deref;
110efe51d0cSJohn Levon if (expr->type == EXPR_PREOP && expr->op == '*')
111efe51d0cSJohn Levon expr = expr->unop;
112efe51d0cSJohn Levon cnt++;
113efe51d0cSJohn Levon if (cnt > 100)
114efe51d0cSJohn Levon return -1;
115efe51d0cSJohn Levon }
116efe51d0cSJohn Levon return cnt;
117efe51d0cSJohn Levon }
1181f5207b7SJohn Levon
get_partial_deref(struct expression * expr,int cnt)119efe51d0cSJohn Levon static struct expression *get_partial_deref(struct expression *expr, int cnt)
120efe51d0cSJohn Levon {
121efe51d0cSJohn Levon while (--cnt >= 0) {
122efe51d0cSJohn Levon if (!expr || expr->type != EXPR_DEREF)
123efe51d0cSJohn Levon return expr;
124efe51d0cSJohn Levon expr = expr->deref;
125efe51d0cSJohn Levon if (expr->type == EXPR_PREOP && expr->op == '*')
126efe51d0cSJohn Levon expr = expr->unop;
1271f5207b7SJohn Levon }
128efe51d0cSJohn Levon return expr;
129efe51d0cSJohn Levon }
130efe51d0cSJohn Levon
partial_deref_to_offset_str(struct expression * expr,int cnt,char op,char * buf,int size)131efe51d0cSJohn Levon static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
132efe51d0cSJohn Levon {
133efe51d0cSJohn Levon int n, offset;
134efe51d0cSJohn Levon
135efe51d0cSJohn Levon if (cnt == 0)
136efe51d0cSJohn Levon return snprintf(buf, size, "%c0", op);
137efe51d0cSJohn Levon
138efe51d0cSJohn Levon n = 0;
139efe51d0cSJohn Levon while (--cnt >= 0) {
140efe51d0cSJohn Levon offset = get_member_offset_from_deref(expr);
141efe51d0cSJohn Levon if (offset < 0)
142efe51d0cSJohn Levon return -1;
143efe51d0cSJohn Levon n += snprintf(buf + n, size - n, "%c%d", op, offset);
144efe51d0cSJohn Levon if (expr->type != EXPR_DEREF)
145efe51d0cSJohn Levon return -1;
146efe51d0cSJohn Levon expr = expr->deref;
147efe51d0cSJohn Levon if (expr->type == EXPR_PREOP && expr->op == '*')
148efe51d0cSJohn Levon expr = expr->unop;
1491f5207b7SJohn Levon }
1501f5207b7SJohn Levon
151efe51d0cSJohn Levon return n;
1521f5207b7SJohn Levon }
1531f5207b7SJohn Levon
get_shared_str(struct expression * expr,struct expression * container)1545a0e240fSJohn Levon static char *get_shared_str(struct expression *expr, struct expression *container)
1551f5207b7SJohn Levon {
156efe51d0cSJohn Levon struct expression *one, *two;
1575a0e240fSJohn Levon int exp, cont, min, ret, n;
158efe51d0cSJohn Levon static char buf[48];
1591f5207b7SJohn Levon
160efe51d0cSJohn Levon exp = get_deref_count(expr);
1615a0e240fSJohn Levon cont = get_deref_count(container);
1625a0e240fSJohn Levon if (exp < 0 || cont < 0)
163efe51d0cSJohn Levon return NULL;
164efe51d0cSJohn Levon
1655a0e240fSJohn Levon min = (exp < cont) ? exp : cont;
166efe51d0cSJohn Levon while (min >= 0) {
1675a0e240fSJohn Levon one = get_partial_deref(expr, exp - min);
1685a0e240fSJohn Levon two = get_partial_deref(container, cont - min);
169efe51d0cSJohn Levon if (expr_equiv(one, two))
170efe51d0cSJohn Levon goto found;
171efe51d0cSJohn Levon min--;
1721f5207b7SJohn Levon }
1731f5207b7SJohn Levon
174efe51d0cSJohn Levon return NULL;
1751f5207b7SJohn Levon
176efe51d0cSJohn Levon found:
1775a0e240fSJohn Levon ret = partial_deref_to_offset_str(expr, exp - min, '-', buf, sizeof(buf));
178efe51d0cSJohn Levon if (ret < 0)
179efe51d0cSJohn Levon return NULL;
180efe51d0cSJohn Levon n = ret;
1815a0e240fSJohn Levon ret = partial_deref_to_offset_str(container, cont - min, '+', buf + ret, sizeof(buf) - ret);
182efe51d0cSJohn Levon if (ret < 0)
183efe51d0cSJohn Levon return NULL;
184efe51d0cSJohn Levon n += ret;
185efe51d0cSJohn Levon if (n >= sizeof(buf))
186efe51d0cSJohn Levon return NULL;
187efe51d0cSJohn Levon
188efe51d0cSJohn Levon return buf;
189efe51d0cSJohn Levon }
190efe51d0cSJohn Levon
get_stored_container_name(struct expression * container,struct expression * expr)1915a0e240fSJohn Levon static char *get_stored_container_name(struct expression *container,
1925a0e240fSJohn Levon struct expression *expr)
1935a0e240fSJohn Levon {
1945a0e240fSJohn Levon struct smatch_state *state;
1955a0e240fSJohn Levon static char buf[64];
1965a0e240fSJohn Levon char *p;
1975a0e240fSJohn Levon int param;
1985a0e240fSJohn Levon
1995a0e240fSJohn Levon if (!container || container->type != EXPR_SYMBOL)
2005a0e240fSJohn Levon return NULL;
2015a0e240fSJohn Levon if (!expr || expr->type != EXPR_SYMBOL)
2025a0e240fSJohn Levon return NULL;
2035a0e240fSJohn Levon state = get_state_expr(param_id, expr);
2045a0e240fSJohn Levon if (!state)
2055a0e240fSJohn Levon return NULL;
2065a0e240fSJohn Levon
2075a0e240fSJohn Levon snprintf(buf, sizeof(buf), "%s", state->name);
2085a0e240fSJohn Levon p = strchr(buf, '|');
2095a0e240fSJohn Levon if (!p)
2105a0e240fSJohn Levon return NULL;
2115a0e240fSJohn Levon *p = '\0';
2125a0e240fSJohn Levon param = atoi(p + 2);
2135a0e240fSJohn Levon if (get_param_sym_from_num(param) == container->symbol)
2145a0e240fSJohn Levon return buf;
2155a0e240fSJohn Levon return NULL;
2165a0e240fSJohn Levon }
2175a0e240fSJohn Levon
get_container_name_helper(struct expression * container,struct expression * expr)21831ad075eSJohn Levon static char *get_container_name_helper(struct expression *container, struct expression *expr)
219efe51d0cSJohn Levon {
220efe51d0cSJohn Levon struct symbol *container_sym, *sym;
221efe51d0cSJohn Levon static char buf[64];
2225a0e240fSJohn Levon char *ret, *shared;
223efe51d0cSJohn Levon bool star;
224efe51d0cSJohn Levon
2255a0e240fSJohn Levon expr = strip_expr(expr);
2265a0e240fSJohn Levon container = strip_expr(container);
22731ad075eSJohn Levon if (!expr || !container)
22831ad075eSJohn Levon return NULL;
2295a0e240fSJohn Levon
2305a0e240fSJohn Levon ret = get_stored_container_name(container, expr);
2315a0e240fSJohn Levon if (ret)
2325a0e240fSJohn Levon return ret;
2335a0e240fSJohn Levon
234efe51d0cSJohn Levon sym = expr_to_sym(expr);
2355a0e240fSJohn Levon container_sym = expr_to_sym(container);
23631ad075eSJohn Levon if (!sym || !container_sym)
23731ad075eSJohn Levon return NULL;
23831ad075eSJohn Levon if (sym != container_sym)
23931ad075eSJohn Levon return NULL;
2405a0e240fSJohn Levon
2415a0e240fSJohn Levon if (container->type == EXPR_DEREF)
2425a0e240fSJohn Levon star = true;
2435a0e240fSJohn Levon else
244efe51d0cSJohn Levon star = false;
245efe51d0cSJohn Levon
2465a0e240fSJohn Levon if (container->type == EXPR_PREOP && container->op == '&')
2475a0e240fSJohn Levon container = strip_expr(container->unop);
2485a0e240fSJohn Levon if (expr->type == EXPR_PREOP && expr->op == '&')
2495a0e240fSJohn Levon expr = strip_expr(expr->unop);
2505a0e240fSJohn Levon
2515a0e240fSJohn Levon shared = get_shared_str(expr, container);
2525a0e240fSJohn Levon if (!shared)
2535a0e240fSJohn Levon return NULL;
254efe51d0cSJohn Levon if (star)
255efe51d0cSJohn Levon snprintf(buf, sizeof(buf), "*(%s)", shared);
256efe51d0cSJohn Levon else
257efe51d0cSJohn Levon snprintf(buf, sizeof(buf), "%s", shared);
258efe51d0cSJohn Levon
259efe51d0cSJohn Levon return buf;
2601f5207b7SJohn Levon }
2611f5207b7SJohn Levon
get_container_name(struct expression * container,struct expression * expr)26231ad075eSJohn Levon char *get_container_name(struct expression *container, struct expression *expr)
26331ad075eSJohn Levon {
26431ad075eSJohn Levon char *ret;
26531ad075eSJohn Levon
26631ad075eSJohn Levon ret = get_container_name_helper(container, expr);
26731ad075eSJohn Levon if (ret)
26831ad075eSJohn Levon return ret;
26931ad075eSJohn Levon
27031ad075eSJohn Levon ret = get_container_name_helper(get_assigned_expr(container), expr);
27131ad075eSJohn Levon if (ret)
27231ad075eSJohn Levon return ret;
27331ad075eSJohn Levon
27431ad075eSJohn Levon ret = get_container_name_helper(container, get_assigned_expr(expr));
27531ad075eSJohn Levon if (ret)
27631ad075eSJohn Levon return ret;
27731ad075eSJohn Levon
27831ad075eSJohn Levon ret = get_container_name_helper(get_assigned_expr(container),
27931ad075eSJohn Levon get_assigned_expr(expr));
28031ad075eSJohn Levon if (ret)
28131ad075eSJohn Levon return ret;
28231ad075eSJohn Levon
28331ad075eSJohn Levon return NULL;
28431ad075eSJohn Levon }
28531ad075eSJohn Levon
is_fn_ptr(struct expression * expr)2865a0e240fSJohn Levon static bool is_fn_ptr(struct expression *expr)
2875a0e240fSJohn Levon {
2885a0e240fSJohn Levon struct symbol *type;
2895a0e240fSJohn Levon
2905a0e240fSJohn Levon if (!expr)
2915a0e240fSJohn Levon return false;
2925a0e240fSJohn Levon if (expr->type != EXPR_SYMBOL && expr->type != EXPR_DEREF)
2935a0e240fSJohn Levon return false;
2945a0e240fSJohn Levon
2955a0e240fSJohn Levon type = get_type(expr);
2965a0e240fSJohn Levon if (!type || type->type != SYM_PTR)
2975a0e240fSJohn Levon return false;
2985a0e240fSJohn Levon type = get_real_base_type(type);
2995a0e240fSJohn Levon if (!type || type->type != SYM_FN)
3005a0e240fSJohn Levon return false;
3015a0e240fSJohn Levon return true;
3025a0e240fSJohn Levon }
3035a0e240fSJohn Levon
match_call(struct expression * call)3041f5207b7SJohn Levon static void match_call(struct expression *call)
3051f5207b7SJohn Levon {
3065a0e240fSJohn Levon struct expression *fn, *arg, *tmp;
3075a0e240fSJohn Levon bool found = false;
3085a0e240fSJohn Levon int fn_param, param;
3095a0e240fSJohn Levon char buf[32];
310efe51d0cSJohn Levon char *name;
3111f5207b7SJohn Levon
3121f5207b7SJohn Levon /*
3131f5207b7SJohn Levon * We're trying to link the function with the parameter. There are a
3141f5207b7SJohn Levon * couple ways this can be passed:
3151f5207b7SJohn Levon * foo->func(foo, ...);
3161f5207b7SJohn Levon * foo->func(foo->x, ...);
3171f5207b7SJohn Levon * foo->bar.func(&foo->bar, ...);
3181f5207b7SJohn Levon * foo->bar->baz->func(foo, ...);
3191f5207b7SJohn Levon *
3201f5207b7SJohn Levon * So the method is basically to subtract the offsets until we get to
3211f5207b7SJohn Levon * the common bit, then add the member offsets to get the parameter.
3221f5207b7SJohn Levon *
3231f5207b7SJohn Levon * If we're taking an address then the offset math is not stared,
3241f5207b7SJohn Levon * otherwise it is. Starred means dereferenced.
3251f5207b7SJohn Levon */
3261f5207b7SJohn Levon fn = strip_expr(call->fn);
3271f5207b7SJohn Levon
3281f5207b7SJohn Levon param = -1;
3291f5207b7SJohn Levon FOR_EACH_PTR(call->args, arg) {
3301f5207b7SJohn Levon param++;
3311f5207b7SJohn Levon
3325a0e240fSJohn Levon name = get_container_name(arg, fn);
333efe51d0cSJohn Levon if (!name)
3341f5207b7SJohn Levon continue;
3351f5207b7SJohn Levon
3365a0e240fSJohn Levon found = true;
337efe51d0cSJohn Levon sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
338efe51d0cSJohn Levon } END_FOR_EACH_PTR(arg);
3395a0e240fSJohn Levon
3405a0e240fSJohn Levon if (found)
3415a0e240fSJohn Levon return;
3425a0e240fSJohn Levon
3435a0e240fSJohn Levon fn_param = -1;
3445a0e240fSJohn Levon FOR_EACH_PTR(call->args, arg) {
3455a0e240fSJohn Levon fn_param++;
3465a0e240fSJohn Levon if (!is_fn_ptr(arg))
3475a0e240fSJohn Levon continue;
3485a0e240fSJohn Levon param = -1;
3495a0e240fSJohn Levon FOR_EACH_PTR(call->args, tmp) {
3505a0e240fSJohn Levon param++;
3515a0e240fSJohn Levon
3525a0e240fSJohn Levon /* the function isn't it's own container */
3535a0e240fSJohn Levon if (arg == tmp)
3545a0e240fSJohn Levon continue;
3555a0e240fSJohn Levon
3565a0e240fSJohn Levon name = get_container_name(tmp, arg);
3575a0e240fSJohn Levon if (!name)
3585a0e240fSJohn Levon continue;
3595a0e240fSJohn Levon
3605a0e240fSJohn Levon snprintf(buf, sizeof(buf), "$%d", param);
3615a0e240fSJohn Levon sql_insert_caller_info(call, CONTAINER, fn_param, name, buf);
3625a0e240fSJohn Levon return;
3635a0e240fSJohn Levon } END_FOR_EACH_PTR(tmp);
3645a0e240fSJohn Levon } END_FOR_EACH_PTR(arg);
3651f5207b7SJohn Levon }
3661f5207b7SJohn Levon
db_passed_container(const char * name,struct symbol * sym,char * key,char * value)3671f5207b7SJohn Levon static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
3681f5207b7SJohn Levon {
3695a0e240fSJohn Levon char buf[64];
3705a0e240fSJohn Levon
3715a0e240fSJohn Levon snprintf(buf, sizeof(buf), "%s|%s", key, value);
3725a0e240fSJohn Levon set_state(param_id, name, sym, alloc_state_str(buf));
3731f5207b7SJohn Levon }
3741f5207b7SJohn Levon
3751f5207b7SJohn Levon struct db_info {
3761f5207b7SJohn Levon struct symbol *arg;
3771f5207b7SJohn Levon int prev_offset;
3781f5207b7SJohn Levon struct range_list *rl;
3791f5207b7SJohn Levon int star;
3801f5207b7SJohn Levon struct stree *stree;
3811f5207b7SJohn Levon };
3821f5207b7SJohn Levon
get_member_from_offset(struct symbol * sym,int offset)3831f5207b7SJohn Levon static struct symbol *get_member_from_offset(struct symbol *sym, int offset)
3841f5207b7SJohn Levon {
3851f5207b7SJohn Levon struct symbol *type, *tmp;
3861f5207b7SJohn Levon int cur;
3871f5207b7SJohn Levon
3881f5207b7SJohn Levon type = get_real_base_type(sym);
3891f5207b7SJohn Levon if (!type || type->type != SYM_PTR)
3901f5207b7SJohn Levon return NULL;
3911f5207b7SJohn Levon type = get_real_base_type(type);
3921f5207b7SJohn Levon if (!type || type->type != SYM_STRUCT)
3931f5207b7SJohn Levon return NULL;
3941f5207b7SJohn Levon
3951f5207b7SJohn Levon cur = 0;
3961f5207b7SJohn Levon FOR_EACH_PTR(type->symbol_list, tmp) {
3971f5207b7SJohn Levon cur = ALIGN(cur, tmp->ctype.alignment);
3981f5207b7SJohn Levon if (offset == cur)
3991f5207b7SJohn Levon return tmp;
4001f5207b7SJohn Levon cur += type_bytes(tmp);
4011f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
4021f5207b7SJohn Levon return NULL;
4031f5207b7SJohn Levon }
4041f5207b7SJohn Levon
get_member_type_from_offset(struct symbol * sym,int offset)4051f5207b7SJohn Levon static struct symbol *get_member_type_from_offset(struct symbol *sym, int offset)
4061f5207b7SJohn Levon {
4071f5207b7SJohn Levon struct symbol *base_type;
4081f5207b7SJohn Levon struct symbol *member;
4091f5207b7SJohn Levon
4101f5207b7SJohn Levon base_type = get_real_base_type(sym);
4111f5207b7SJohn Levon if (base_type && base_type->type == SYM_PTR)
4121f5207b7SJohn Levon base_type = get_real_base_type(base_type);
4131f5207b7SJohn Levon if (offset == 0 && base_type && base_type->type == SYM_BASETYPE)
4141f5207b7SJohn Levon return base_type;
4151f5207b7SJohn Levon
4161f5207b7SJohn Levon member = get_member_from_offset(sym, offset);
4171f5207b7SJohn Levon if (!member)
4181f5207b7SJohn Levon return NULL;
4191f5207b7SJohn Levon return get_real_base_type(member);
4201f5207b7SJohn Levon }
4211f5207b7SJohn Levon
get_name_from_offset(struct symbol * arg,int offset)4221f5207b7SJohn Levon static const char *get_name_from_offset(struct symbol *arg, int offset)
4231f5207b7SJohn Levon {
4241f5207b7SJohn Levon struct symbol *member, *type;
4251f5207b7SJohn Levon const char *name;
4261f5207b7SJohn Levon static char fullname[256];
4271f5207b7SJohn Levon
4281f5207b7SJohn Levon name = arg->ident->name;
4291f5207b7SJohn Levon
4301f5207b7SJohn Levon type = get_real_base_type(arg);
4311f5207b7SJohn Levon if (!type || type->type != SYM_PTR)
4321f5207b7SJohn Levon return name;
4331f5207b7SJohn Levon
4341f5207b7SJohn Levon type = get_real_base_type(type);
4351f5207b7SJohn Levon if (!type)
4361f5207b7SJohn Levon return NULL;
4371f5207b7SJohn Levon if (type->type != SYM_STRUCT) {
4381f5207b7SJohn Levon snprintf(fullname, sizeof(fullname), "*%s", name);
4391f5207b7SJohn Levon return fullname;
4401f5207b7SJohn Levon }
4411f5207b7SJohn Levon
4421f5207b7SJohn Levon member = get_member_from_offset(arg, offset);
4435a0e240fSJohn Levon if (!member || !member->ident)
4441f5207b7SJohn Levon return NULL;
4451f5207b7SJohn Levon
4461f5207b7SJohn Levon snprintf(fullname, sizeof(fullname), "%s->%s", name, member->ident->name);
4471f5207b7SJohn Levon return fullname;
4481f5207b7SJohn Levon }
4491f5207b7SJohn Levon
set_param_value(struct stree ** stree,struct symbol * arg,int offset,struct range_list * rl)4501f5207b7SJohn Levon static void set_param_value(struct stree **stree, struct symbol *arg, int offset, struct range_list *rl)
4511f5207b7SJohn Levon {
4521f5207b7SJohn Levon const char *name;
4531f5207b7SJohn Levon
4541f5207b7SJohn Levon name = get_name_from_offset(arg, offset);
4551f5207b7SJohn Levon if (!name)
4561f5207b7SJohn Levon return;
4571f5207b7SJohn Levon set_state_stree(stree, SMATCH_EXTRA, name, arg, alloc_estate_rl(rl));
4581f5207b7SJohn Levon }
4591f5207b7SJohn Levon
save_vals(void * _db_info,int argc,char ** argv,char ** azColName)4601f5207b7SJohn Levon static int save_vals(void *_db_info, int argc, char **argv, char **azColName)
4611f5207b7SJohn Levon {
4621f5207b7SJohn Levon struct db_info *db_info = _db_info;
4631f5207b7SJohn Levon struct symbol *type;
4641f5207b7SJohn Levon struct range_list *rl;
4651f5207b7SJohn Levon int offset = 0;
4661f5207b7SJohn Levon const char *value;
4671f5207b7SJohn Levon
4681f5207b7SJohn Levon if (argc == 2) {
4691f5207b7SJohn Levon offset = atoi(argv[0]);
4701f5207b7SJohn Levon value = argv[1];
4711f5207b7SJohn Levon } else {
4721f5207b7SJohn Levon value = argv[0];
4731f5207b7SJohn Levon }
4741f5207b7SJohn Levon
4751f5207b7SJohn Levon if (db_info->prev_offset != -1 &&
4761f5207b7SJohn Levon db_info->prev_offset != offset) {
4771f5207b7SJohn Levon set_param_value(&db_info->stree, db_info->arg, db_info->prev_offset, db_info->rl);
4781f5207b7SJohn Levon db_info->rl = NULL;
4791f5207b7SJohn Levon }
4801f5207b7SJohn Levon
4811f5207b7SJohn Levon db_info->prev_offset = offset;
4821f5207b7SJohn Levon
4831f5207b7SJohn Levon type = get_real_base_type(db_info->arg);
4841f5207b7SJohn Levon if (db_info->star)
4851f5207b7SJohn Levon goto found_type;
4861f5207b7SJohn Levon if (type->type != SYM_PTR)
4871f5207b7SJohn Levon return 0;
4881f5207b7SJohn Levon type = get_real_base_type(type);
4891f5207b7SJohn Levon if (type->type == SYM_BASETYPE)
4901f5207b7SJohn Levon goto found_type;
4911f5207b7SJohn Levon type = get_member_type_from_offset(db_info->arg, offset);
4921f5207b7SJohn Levon found_type:
4931f5207b7SJohn Levon str_to_rl(type, (char *)value, &rl);
4941f5207b7SJohn Levon if (db_info->rl)
4951f5207b7SJohn Levon db_info->rl = rl_union(db_info->rl, rl);
4961f5207b7SJohn Levon else
4971f5207b7SJohn Levon db_info->rl = rl;
4981f5207b7SJohn Levon
4991f5207b7SJohn Levon return 0;
5001f5207b7SJohn Levon }
5011f5207b7SJohn Levon
load_tag_info_sym(mtag_t tag,struct symbol * arg,int arg_offset,int star)5021f5207b7SJohn Levon static struct stree *load_tag_info_sym(mtag_t tag, struct symbol *arg, int arg_offset, int star)
5031f5207b7SJohn Levon {
5041f5207b7SJohn Levon struct db_info db_info = {
5051f5207b7SJohn Levon .arg = arg,
5061f5207b7SJohn Levon .prev_offset = -1,
5071f5207b7SJohn Levon .star = star,
5081f5207b7SJohn Levon };
5091f5207b7SJohn Levon struct symbol *type;
5101f5207b7SJohn Levon
5111f5207b7SJohn Levon if (!tag || !arg->ident)
5121f5207b7SJohn Levon return NULL;
5131f5207b7SJohn Levon
5141f5207b7SJohn Levon type = get_real_base_type(arg);
5151f5207b7SJohn Levon if (!type)
5161f5207b7SJohn Levon return NULL;
5171f5207b7SJohn Levon if (!star) {
5181f5207b7SJohn Levon if (type->type != SYM_PTR)
5191f5207b7SJohn Levon return NULL;
5201f5207b7SJohn Levon type = get_real_base_type(type);
5211f5207b7SJohn Levon if (!type)
5221f5207b7SJohn Levon return NULL;
5231f5207b7SJohn Levon }
5241f5207b7SJohn Levon
5251f5207b7SJohn Levon if (star || type->type == SYM_BASETYPE) {
5261f5207b7SJohn Levon run_sql(save_vals, &db_info,
5271f5207b7SJohn Levon "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
5281f5207b7SJohn Levon tag, arg_offset, DATA_VALUE);
5291f5207b7SJohn Levon } else { /* presumably the parameter is a struct pointer */
5301f5207b7SJohn Levon run_sql(save_vals, &db_info,
531*6523a3aaSJohn Levon "select offset, value from mtag_data where tag = %lld and type = %d order by offset;",
5321f5207b7SJohn Levon tag, DATA_VALUE);
5331f5207b7SJohn Levon }
5341f5207b7SJohn Levon
5351f5207b7SJohn Levon if (db_info.prev_offset != -1)
5361f5207b7SJohn Levon set_param_value(&db_info.stree, arg, db_info.prev_offset, db_info.rl);
5371f5207b7SJohn Levon
5381f5207b7SJohn Levon // FIXME: handle an offset correctly
5391f5207b7SJohn Levon if (!star && !arg_offset) {
5401f5207b7SJohn Levon sval_t sval;
5411f5207b7SJohn Levon
5421f5207b7SJohn Levon sval.type = get_real_base_type(arg);
5431f5207b7SJohn Levon sval.uvalue = tag;
5441f5207b7SJohn Levon set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
5451f5207b7SJohn Levon }
5461f5207b7SJohn Levon return db_info.stree;
5471f5207b7SJohn Levon }
5481f5207b7SJohn Levon
load_container_data(struct symbol * arg,const char * info)549efe51d0cSJohn Levon static void load_container_data(struct symbol *arg, const char *info)
5501f5207b7SJohn Levon {
551efe51d0cSJohn Levon mtag_t cur_tag, container_tag, arg_tag;
552efe51d0cSJohn Levon int container_offset, arg_offset;
5531f5207b7SJohn Levon struct sm_state *sm;
5541f5207b7SJohn Levon struct stree *stree;
5555a0e240fSJohn Levon char *p, *cont;
5565a0e240fSJohn Levon char copy[64];
557efe51d0cSJohn Levon bool star = 0;
5581f5207b7SJohn Levon
5595a0e240fSJohn Levon snprintf(copy, sizeof(copy), "%s", info);
5605a0e240fSJohn Levon p = strchr(copy, '|');
5615a0e240fSJohn Levon if (!p)
5625a0e240fSJohn Levon return;
5635a0e240fSJohn Levon *p = '\0';
5645a0e240fSJohn Levon cont = p + 1;
5655a0e240fSJohn Levon p = copy;
566efe51d0cSJohn Levon if (p[0] == '*') {
567efe51d0cSJohn Levon star = 1;
568efe51d0cSJohn Levon p += 2;
569efe51d0cSJohn Levon }
5701f5207b7SJohn Levon
5715a0e240fSJohn Levon if (strcmp(cont, "$(-1)") != 0)
5725a0e240fSJohn Levon return;
5735a0e240fSJohn Levon
574efe51d0cSJohn Levon if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
5751f5207b7SJohn Levon return;
5761f5207b7SJohn Levon
577efe51d0cSJohn Levon while (true) {
578efe51d0cSJohn Levon container_offset = strtoul(p, &p, 0);
579efe51d0cSJohn Levon if (local_debug)
580efe51d0cSJohn Levon sm_msg("%s: cur_tag = %llu container_offset = %d",
581efe51d0cSJohn Levon __func__, cur_tag, container_offset);
582*6523a3aaSJohn Levon if (!mtag_map_select_container(cur_tag, -container_offset, &container_tag))
583efe51d0cSJohn Levon return;
584efe51d0cSJohn Levon cur_tag = container_tag;
585efe51d0cSJohn Levon if (local_debug)
586efe51d0cSJohn Levon sm_msg("%s: container_tag = %llu p = '%s'",
587efe51d0cSJohn Levon __func__, container_tag, p);
588efe51d0cSJohn Levon if (!p)
589efe51d0cSJohn Levon return;
590efe51d0cSJohn Levon if (p[0] != '-')
591efe51d0cSJohn Levon break;
592efe51d0cSJohn Levon p++;
593efe51d0cSJohn Levon }
594efe51d0cSJohn Levon
595efe51d0cSJohn Levon if (p[0] != '+')
5961f5207b7SJohn Levon return;
597efe51d0cSJohn Levon
598efe51d0cSJohn Levon p++;
599efe51d0cSJohn Levon arg_offset = strtoul(p, &p, 0);
600efe51d0cSJohn Levon if (p && *p && *p != ')')
6011f5207b7SJohn Levon return;
602efe51d0cSJohn Levon
6031f5207b7SJohn Levon if (!arg_offset || star) {
6041f5207b7SJohn Levon arg_tag = container_tag;
6051f5207b7SJohn Levon } else {
606*6523a3aaSJohn Levon if (!mtag_map_select_tag(container_tag, arg_offset, &arg_tag))
6071f5207b7SJohn Levon return;
6081f5207b7SJohn Levon }
6091f5207b7SJohn Levon
6101f5207b7SJohn Levon stree = load_tag_info_sym(arg_tag, arg, arg_offset, star);
6111f5207b7SJohn Levon FOR_EACH_SM(stree, sm) {
6121f5207b7SJohn Levon set_state(sm->owner, sm->name, sm->sym, sm->state);
6131f5207b7SJohn Levon } END_FOR_EACH_SM(sm);
6141f5207b7SJohn Levon free_stree(&stree);
6151f5207b7SJohn Levon }
6161f5207b7SJohn Levon
handle_passed_container(struct symbol * sym)617efe51d0cSJohn Levon static void handle_passed_container(struct symbol *sym)
618efe51d0cSJohn Levon {
619efe51d0cSJohn Levon struct symbol *arg;
620efe51d0cSJohn Levon struct smatch_state *state;
621efe51d0cSJohn Levon
622efe51d0cSJohn Levon FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
623efe51d0cSJohn Levon state = get_state(param_id, arg->ident->name, arg);
624efe51d0cSJohn Levon if (!state || state == &merged)
625efe51d0cSJohn Levon continue;
626efe51d0cSJohn Levon load_container_data(arg, state->name);
627efe51d0cSJohn Levon } END_FOR_EACH_PTR(arg);
628efe51d0cSJohn Levon }
629efe51d0cSJohn Levon
register_container_of(int id)6301f5207b7SJohn Levon void register_container_of(int id)
6311f5207b7SJohn Levon {
6321f5207b7SJohn Levon my_id = id;
6331f5207b7SJohn Levon
6341f5207b7SJohn Levon add_split_return_callback(&print_returns_container_of);
6351f5207b7SJohn Levon add_hook(&match_call, FUNCTION_CALL_HOOK);
6361f5207b7SJohn Levon }
6371f5207b7SJohn Levon
register_container_of2(int id)6381f5207b7SJohn Levon void register_container_of2(int id)
6391f5207b7SJohn Levon {
6401f5207b7SJohn Levon param_id = id;
6411f5207b7SJohn Levon
642efe51d0cSJohn Levon set_dynamic_states(param_id);
6431f5207b7SJohn Levon select_caller_info_hook(db_passed_container, CONTAINER);
644efe51d0cSJohn Levon add_merge_hook(param_id, &merge_str_state);
6451f5207b7SJohn Levon add_hook(&handle_passed_container, AFTER_DEF_HOOK);
6461f5207b7SJohn Levon }
6471f5207b7SJohn Levon
648