Skip to content

Commit 40a5b94

Browse files
committed
Rearchitect var args support (making it actually work)
Previously all the logic to access values of variable argument lists was put into our va_arg() implementation. This required guessing what the parameters could have been, and did not take into account the initial value passed to va_start. Now va_start triggers constructing an array of pointers to parameters, the first of which is the original argument to va_start. The va_list is then just a pointer to the first element of the array. va_arg just increments the pointer. goto_program conversion places a side_effect_exprt where va_start was, and goto-symex populates the array with the actual values applicable for a particular invocation of the function.
1 parent f4dedec commit 40a5b94

File tree

11 files changed

+94
-110
lines changed

11 files changed

+94
-110
lines changed

regression/cbmc/Variadic1/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FUTURE
1+
CORE
22
main.c
33

44
^EXIT=0$

regression/cbmc/va_list3/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33

44
^EXIT=0$

src/ansi-c/expr2c.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -3668,8 +3668,8 @@ std::string expr2ct::convert_with_precedence(
36683668
return convert_prob_uniform(src, precedence=16);
36693669
else if(statement==ID_statement_expression)
36703670
return convert_statement_expression(src, precedence=15);
3671-
else if(statement==ID_gcc_builtin_va_arg_next)
3672-
return convert_function(src, "gcc_builtin_va_arg_next", precedence=16);
3671+
else if(statement == ID_gcc_builtin_va_start)
3672+
return convert_function(src, "gcc_builtin_va_start", precedence = 16);
36733673
else
36743674
return convert_norep(src, precedence);
36753675
}

src/goto-instrument/goto_program2code.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ void goto_program2codet::scan_for_varargs()
105105
const exprt &l = target->get_assign().lhs();
106106
const exprt &r = target->get_assign().rhs();
107107

108-
// find va_arg_next
109-
if(r.id()==ID_side_effect &&
110-
to_side_effect_expr(r).get_statement()==ID_gcc_builtin_va_arg_next)
108+
// find va_start
109+
if(
110+
r.id() == ID_side_effect &&
111+
to_side_effect_expr(r).get_statement() == ID_gcc_builtin_va_start)
111112
{
112113
assert(r.has_operands());
113114
va_list_expr.insert(r.op0());
@@ -327,17 +328,18 @@ goto_programt::const_targett goto_program2codet::convert_assign_varargs(
327328

328329
dest.add(std::move(f));
329330
}
330-
else if(r.id()==ID_address_of)
331+
else if(
332+
r.id() == ID_side_effect &&
333+
to_side_effect_expr(r).get_statement() == ID_gcc_builtin_va_start)
331334
{
332335
code_function_callt f(
333336
symbol_exprt("va_start", code_typet({}, empty_typet())),
334-
{this_va_list_expr, to_address_of_expr(r).object()});
337+
{this_va_list_expr, to_address_of_expr(skip_typecast(r.op0())).object()});
335338
f.arguments().front().type().id(ID_gcc_builtin_va_list);
336339

337340
dest.add(std::move(f));
338341
}
339-
else if(r.id()==ID_side_effect &&
340-
to_side_effect_expr(r).get_statement()==ID_gcc_builtin_va_arg_next)
342+
else if(r.id() == ID_plus)
341343
{
342344
code_function_callt f(
343345
symbol_exprt("va_arg", code_typet({}, empty_typet())),

src/goto-programs/builtin_functions.cpp

+16-20
Original file line numberDiff line numberDiff line change
@@ -1100,26 +1100,20 @@ void goto_convertt::do_function_call_symbol(
11001100

11011101
exprt list_arg=make_va_list(arguments[0]);
11021102

1103-
{
1104-
side_effect_exprt rhs(
1105-
ID_gcc_builtin_va_arg_next,
1106-
list_arg.type(),
1107-
function.source_location());
1108-
rhs.copy_to_operands(list_arg);
1109-
rhs.set(ID_C_va_arg_type, to_code_type(function.type()).return_type());
1110-
goto_programt::targett t1=dest.add_instruction(ASSIGN);
1111-
t1->source_location=function.source_location();
1112-
t1->code=code_assignt(list_arg, rhs);
1113-
}
1103+
code_assignt assign{
1104+
list_arg, plus_exprt{list_arg, from_integer(1, pointer_diff_type())}};
1105+
assign.rhs().set(
1106+
ID_C_va_arg_type, to_code_type(function.type()).return_type());
1107+
dest.add(goto_programt::make_assignment(
1108+
std::move(assign), function.source_location()));
11141109

11151110
if(lhs.is_not_nil())
11161111
{
11171112
typet t=pointer_type(lhs.type());
1118-
dereference_exprt rhs(typecast_exprt(list_arg, t), lhs.type());
1113+
dereference_exprt rhs{typecast_exprt(dereference_exprt{list_arg}, t)};
11191114
rhs.add_source_location()=function.source_location();
1120-
goto_programt::targett t2=dest.add_instruction(ASSIGN);
1121-
t2->source_location=function.source_location();
1122-
t2->code=code_assignt(lhs, rhs);
1115+
dest.add(goto_programt::make_assignment(
1116+
lhs, std::move(rhs), function.source_location()));
11231117
}
11241118
}
11251119
else if(identifier=="__builtin_va_copy")
@@ -1159,8 +1153,6 @@ void goto_convertt::do_function_call_symbol(
11591153
}
11601154

11611155
exprt dest_expr=make_va_list(arguments[0]);
1162-
const typecast_exprt src_expr(
1163-
address_of_exprt(arguments[1]), dest_expr.type());
11641156

11651157
if(!is_lvalue(dest_expr))
11661158
{
@@ -1169,9 +1161,13 @@ void goto_convertt::do_function_call_symbol(
11691161
throw 0;
11701162
}
11711163

1172-
goto_programt::targett t=dest.add_instruction(ASSIGN);
1173-
t->source_location=function.source_location();
1174-
t->code=code_assignt(dest_expr, src_expr);
1164+
side_effect_exprt rhs{
1165+
ID_gcc_builtin_va_start, dest_expr.type(), function.source_location()};
1166+
rhs.add_to_operands(
1167+
typecast_exprt{address_of_exprt{arguments[1]}, dest_expr.type()});
1168+
1169+
dest.add(goto_programt::make_assignment(
1170+
std::move(dest_expr), std::move(rhs), function.source_location()));
11751171
}
11761172
else if(identifier=="__builtin_va_end")
11771173
{

src/goto-symex/goto_symex.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,10 @@ class goto_symext
374374
/// \return The resulting expression
375375
static exprt add_to_lhs(const exprt &lhs, const exprt &what);
376376

377-
virtual void symex_gcc_builtin_va_arg_next(
378-
statet &, const exprt &lhs, const side_effect_exprt &);
377+
virtual void symex_gcc_builtin_va_start(
378+
statet &,
379+
const exprt &lhs,
380+
const side_effect_exprt &);
379381
virtual void symex_allocate(
380382
statet &, const exprt &lhs, const side_effect_exprt &);
381383
virtual void symex_cpp_delete(statet &, const codet &);

src/goto-symex/goto_symex_state.h

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct framet
9090
irep_idt function_identifier;
9191
std::map<goto_programt::const_targett, goto_state_listt> goto_state_map;
9292
symex_targett::sourcet calling_location;
93+
std::vector<irep_idt> parameter_names;
9394

9495
goto_programt::const_targett end_of_function;
9596
exprt return_value = nil_exprt();

src/goto-symex/symex_assign.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ void goto_symext::symex_assign(
5151
PRECONDITION(lhs.is_nil());
5252
symex_printf(state, side_effect_expr);
5353
}
54-
else if(statement==ID_gcc_builtin_va_arg_next)
55-
symex_gcc_builtin_va_arg_next(state, lhs, side_effect_expr);
54+
else if(statement == ID_gcc_builtin_va_start)
55+
symex_gcc_builtin_va_start(state, lhs, side_effect_expr);
5656
else
5757
UNREACHABLE;
5858
}

src/goto-symex/symex_builtin_functions.cpp

+42-51
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Author: Daniel Kroening, [email protected]
1414
#include <util/arith_tools.h>
1515
#include <util/c_types.h>
1616
#include <util/expr_initializer.h>
17+
#include <util/expr_util.h>
18+
#include <util/fresh_symbol.h>
1719
#include <util/invariant_utils.h>
1820
#include <util/optional.h>
1921
#include <util/pointer_offset_size.h>
@@ -203,24 +205,7 @@ void goto_symext::symex_allocate(
203205
code_assignt(lhs, typecast_exprt::conditional_cast(rhs, lhs.type())));
204206
}
205207

206-
irep_idt get_symbol(const exprt &src)
207-
{
208-
if(src.id()==ID_typecast)
209-
return get_symbol(to_typecast_expr(src).op());
210-
else if(src.id()==ID_address_of)
211-
{
212-
exprt op=to_address_of_expr(src).object();
213-
if(op.id()==ID_symbol &&
214-
op.get_bool(ID_C_SSA_symbol))
215-
return to_ssa_expr(op).get_object_name();
216-
else
217-
return irep_idt();
218-
}
219-
else
220-
return irep_idt();
221-
}
222-
223-
void goto_symext::symex_gcc_builtin_va_arg_next(
208+
void goto_symext::symex_gcc_builtin_va_start(
224209
statet &state,
225210
const exprt &lhs,
226211
const side_effect_exprt &code)
@@ -230,42 +215,48 @@ void goto_symext::symex_gcc_builtin_va_arg_next(
230215
if(lhs.is_nil())
231216
return; // ignore
232217

233-
exprt tmp=code.op0();
234-
state.rename(tmp, ns); // to allow constant propagation
235-
do_simplify(tmp);
236-
irep_idt id=get_symbol(tmp);
237-
238-
const auto zero = zero_initializer(lhs.type(), code.source_location(), ns);
239-
CHECK_RETURN(zero.has_value());
240-
exprt rhs(*zero);
218+
// create an array holding pointers to the parameters, starting with the
219+
// parameter that the operand points to
220+
const exprt &op = skip_typecast(code.op0());
221+
// this must be the address of a symbol
222+
const irep_idt &start_parameter =
223+
to_symbol_expr(to_address_of_expr(op).object()).get_identifier();
241224

242-
if(!id.empty())
225+
exprt::operandst va_arg_operands;
226+
bool parameter_id_found = false;
227+
for(auto &parameter : state.top().parameter_names)
243228
{
244-
// strip last name off id to get function name
245-
std::size_t pos=id2string(id).rfind("::");
246-
if(pos!=std::string::npos)
247-
{
248-
irep_idt function_identifier=std::string(id2string(id), 0, pos);
249-
std::string base=id2string(function_identifier)+"::va_arg";
250-
251-
if(has_prefix(id2string(id), base))
252-
id=base+std::to_string(
253-
safe_string2unsigned(
254-
std::string(id2string(id), base.size(), std::string::npos))+1);
255-
else
256-
id=base+"0";
257-
258-
const symbolt *symbol;
259-
if(!ns.lookup(id, symbol))
260-
{
261-
const symbol_exprt symbol_expr(symbol->name, symbol->type);
262-
rhs = typecast_exprt::conditional_cast(
263-
address_of_exprt(symbol_expr), lhs.type());
264-
}
265-
}
266-
}
229+
if(!parameter_id_found && parameter == start_parameter)
230+
parameter_id_found = true;
267231

268-
symex_assign(state, code_assignt(lhs, rhs));
232+
va_arg_operands.push_back(typecast_exprt::conditional_cast(
233+
address_of_exprt{ns.lookup(parameter).symbol_expr()},
234+
pointer_type(empty_typet{})));
235+
}
236+
const std::size_t va_arg_size = va_arg_operands.size();
237+
array_exprt array{std::move(va_arg_operands),
238+
array_typet{pointer_type(empty_typet{}),
239+
from_integer(va_arg_size, size_type())}};
240+
241+
symbolt &va_array = get_fresh_aux_symbol(
242+
array.type(),
243+
id2string(state.source.function_id),
244+
"va_args",
245+
state.source.pc->source_location,
246+
ns.lookup(state.source.function_id).mode,
247+
state.symbol_table);
248+
va_array.value = array;
249+
250+
state.rename(array, ns);
251+
do_simplify(array);
252+
symex_assign(state, code_assignt{va_array.symbol_expr(), std::move(array)});
253+
254+
address_of_exprt rhs{
255+
index_exprt{va_array.symbol_expr(), from_integer(0, index_type())}};
256+
state.rename(rhs, ns);
257+
symex_assign(
258+
state,
259+
code_assignt{lhs, typecast_exprt::conditional_cast(rhs, lhs.type())});
269260
}
270261

271262
irep_idt get_string_argument_rec(const exprt &src)

src/goto-symex/symex_function_call.cpp

+15-23
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include <util/byte_operators.h>
1717
#include <util/c_types.h>
1818
#include <util/exception_utils.h>
19+
#include <util/fresh_symbol.h>
1920
#include <util/invariant.h>
2021

2122
static void locality(
@@ -60,6 +61,7 @@ void goto_symext::parameter_assignments(
6061
INVARIANT(
6162
!identifier.empty(),
6263
"function pointer parameter must have an identifier");
64+
state.top().parameter_names.push_back(identifier);
6365

6466
const symbolt &symbol=ns.lookup(identifier);
6567
symbol_exprt lhs=symbol.symbol_expr();
@@ -156,30 +158,20 @@ void goto_symext::parameter_assignments(
156158
if(function_type.has_ellipsis())
157159
{
158160
// These are va_arg arguments; their types may differ from call to call
159-
std::size_t va_count=0;
160-
const symbolt *va_sym=nullptr;
161-
while(!ns.lookup(
162-
id2string(function_identifier)+"::va_arg"+std::to_string(va_count),
163-
va_sym))
164-
++va_count;
165-
166-
for( ; it1!=arguments.end(); it1++, va_count++)
161+
for(; it1 != arguments.end(); it1++)
167162
{
168-
irep_idt id=
169-
id2string(function_identifier)+"::va_arg"+std::to_string(va_count);
170-
171-
// add to symbol table
172-
symbolt symbol;
173-
symbol.name=id;
174-
symbol.base_name="va_arg"+std::to_string(va_count);
175-
symbol.mode=ID_C;
176-
symbol.type=it1->type();
177-
178-
state.symbol_table.insert(std::move(symbol));
179-
180-
symbol_exprt lhs=symbol_exprt(id, it1->type());
181-
182-
symex_assign(state, code_assignt(lhs, *it1));
163+
symbolt &va_arg = get_fresh_aux_symbol(
164+
it1->type(),
165+
id2string(function_identifier),
166+
"va_arg",
167+
state.source.pc->source_location,
168+
ns.lookup(function_identifier).mode,
169+
state.symbol_table);
170+
va_arg.is_parameter = true;
171+
172+
state.top().parameter_names.push_back(va_arg.name);
173+
174+
symex_assign(state, code_assignt{va_arg.symbol_expr(), *it1});
183175
}
184176
}
185177
else if(it1!=arguments.end())

src/util/irep_ids.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ IREP_ID_ONE(alignof)
304304
IREP_ID_ONE(clang_builtin_convertvector)
305305
IREP_ID_ONE(gcc_builtin_va_arg)
306306
IREP_ID_ONE(gcc_builtin_types_compatible_p)
307-
IREP_ID_ONE(gcc_builtin_va_arg_next)
307+
IREP_ID_ONE(gcc_builtin_va_start)
308308
IREP_ID_ONE(gcc_builtin_va_list)
309309
IREP_ID_ONE(gcc_float16)
310310
IREP_ID_ONE(gcc_float32)

0 commit comments

Comments
 (0)