Skip to content

Commit 0f91a44

Browse files
committed
Fully support aarch64 ABI's va_list struct
aarch64 ABI (section 7.1.4) mandates that va_list is a struct with particular members. The C library model was fixed in #8366, but we didn't yet implement the struct type support in goto conversion.
1 parent ce11a71 commit 0f91a44

File tree

9 files changed

+108
-29
lines changed

9 files changed

+108
-29
lines changed

regression/cbmc/va_list2/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#ifdef __GNUC__
1+
#if defined(__GNUC__) && (!defined(__aarch64__) || defined(__APPLE__))
22

33
# include <assert.h>
44

regression/cbmc/va_list4/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#ifdef __GNUC__
1+
#if defined(__GNUC__) && (!defined(__aarch64__) || defined(__APPLE__))
22

33
struct __va_list_tag;
44
typedef struct __va_list_tag __va_list_tag;

src/ansi-c/ansi_c_internal_additions.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,23 @@ void ansi_c_internal_additions(std::string &code, bool support_float16_type)
286286
code+="typedef signed __int128 __int128_t;\n"
287287
"typedef unsigned __int128 __uint128_t;\n";
288288
}
289+
290+
if(
291+
config.ansi_c.arch == "arm64" &&
292+
config.ansi_c.os != configt::ansi_ct::ost::OS_MACOS)
293+
{
294+
code += "typedef struct __va_list {";
295+
code += "void *__stack;";
296+
code += "void *__gr_top;";
297+
code += "void *__vr_top;";
298+
code += "int __gr_offs;";
299+
code += "int __vr_offs;";
300+
code += " } __builtin_va_list;\n";
301+
}
302+
else
303+
{
304+
code += "typedef void ** __builtin_va_list;\n";
305+
}
289306
}
290307

291308
// this is Visual C/C++ only

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,16 +543,32 @@ void c_typecheck_baset::typecheck_expr_builtin_va_arg(exprt &expr)
543543
// The first parameter is the va_list, and the second
544544
// is the type, which will need to be fixed and checked.
545545
// The type is given by the parser as type of the expression.
546-
547-
typet arg_type=expr.type();
548-
typecheck_type(arg_type);
549-
550-
const code_typet new_type(
551-
{code_typet::parametert(pointer_type(void_type()))}, std::move(arg_type));
546+
auto type_not_permitted = [this](const exprt &expr)
547+
{
548+
const exprt &arg = to_unary_expr(expr).op();
549+
error().source_location = expr.source_location();
550+
error() << "argument of type '" << to_string(arg.type())
551+
<< "' not permitted for va_arg" << eom;
552+
throw 0;
553+
};
552554

553555
exprt arg = to_unary_expr(expr).op();
556+
if(auto struct_tag_type = type_try_dynamic_cast<struct_tag_typet>(arg.type()))
557+
{
558+
// aarch64 ABI mandates that va_list has struct type with member names as
559+
// specified
560+
const auto &components = follow_tag(*struct_tag_type).components();
561+
if(components.size() != 5)
562+
type_not_permitted(expr);
563+
}
564+
else if(arg.type().id() != ID_pointer && arg.type().id() != ID_array)
565+
type_not_permitted(expr);
566+
567+
typet arg_type = expr.type();
568+
typecheck_type(arg_type);
554569

555-
implicit_typecast(arg, pointer_type(void_type()));
570+
const code_typet new_type{
571+
{code_typet::parametert{arg.type()}}, std::move(arg_type)};
556572

557573
symbol_exprt function(ID_gcc_builtin_va_arg, new_type);
558574
function.add_source_location() = expr.source_location();

src/ansi-c/compiler_headers/gcc_builtin_headers_generic.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
// stdarg
33
void* __builtin_apply_args();
44
void* __builtin_apply(void (*)(), void*, __CPROVER_size_t);
5-
void __builtin_ms_va_end(void *ap);
6-
void __builtin_ms_va_start(void *ap, ...);
5+
void __builtin_ms_va_end(__builtin_ms_va_list ap);
6+
void __builtin_ms_va_start(__builtin_ms_va_list ap, ...);
77
void* __builtin_next_arg();
88
int __builtin_va_arg_pack();
99
int __builtin_va_arg_pack_len();
1010
void __builtin_va_copy(__builtin_va_list dest, __builtin_va_list src);
11-
void __builtin_va_end(void *ap);
12-
void __builtin_va_start(void *ap, ...);
11+
void __builtin_va_end(__builtin_va_list ap);
12+
void __builtin_va_start(__builtin_va_list ap, ...);
1313

1414
// stdlib
1515
void __builtin__Exit(int);

src/ansi-c/compiler_headers/gcc_builtin_headers_types.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// clang-format off
2-
typedef void ** __builtin_va_list;
32
typedef void ** __builtin_ms_va_list;
43

54
typedef int __gcc_m64 __attribute__ ((__vector_size__ (8), __may_alias__));

src/ansi-c/goto-conversion/builtin_functions.cpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,20 @@ void goto_convertt::do_array_op(
593593
copy(array_op_statement, OTHER, dest);
594594
}
595595

596-
exprt make_va_list(const exprt &expr)
596+
static exprt make_va_list(const exprt &expr, const namespacet &ns)
597597
{
598+
if(
599+
auto struct_tag_type = type_try_dynamic_cast<struct_tag_typet>(expr.type()))
600+
{
601+
// aarch64 ABI mandates that va_list has struct type with member names as
602+
// specified
603+
const auto &components = ns.follow_tag(*struct_tag_type).components();
604+
DATA_INVARIANT(
605+
components.size() == 5,
606+
"va_list struct type expected to have 5 components");
607+
return member_exprt{expr, components.front()};
608+
}
609+
598610
exprt result = skip_typecast(expr);
599611

600612
// if it's an address of an lvalue, we take that
@@ -1296,14 +1308,15 @@ void goto_convertt::do_function_call_symbol(
12961308
throw 0;
12971309
}
12981310

1299-
exprt list_arg = make_va_list(arguments[0]);
1311+
exprt list_arg = make_va_list(arguments[0], ns);
1312+
const bool va_list_is_void_ptr =
1313+
list_arg.type().id() == ID_pointer &&
1314+
to_pointer_type(list_arg.type()).base_type().id() == ID_empty;
13001315

13011316
if(lhs.is_not_nil())
13021317
{
13031318
exprt list_arg_cast = list_arg;
1304-
if(
1305-
list_arg.type().id() == ID_pointer &&
1306-
to_pointer_type(list_arg.type()).base_type().id() == ID_empty)
1319+
if(va_list_is_void_ptr)
13071320
{
13081321
list_arg_cast =
13091322
typecast_exprt{list_arg, pointer_type(pointer_type(empty_typet{}))};
@@ -1317,8 +1330,14 @@ void goto_convertt::do_function_call_symbol(
13171330
goto_programt::make_assignment(lhs, rhs, function.source_location()));
13181331
}
13191332

1320-
code_assignt assign{
1321-
list_arg, plus_exprt{list_arg, from_integer(1, pointer_diff_type())}};
1333+
exprt list_arg_ptr_arithmetic = typecast_exprt::conditional_cast(
1334+
plus_exprt{
1335+
(va_list_is_void_ptr
1336+
? typecast_exprt{list_arg, pointer_type(pointer_type(empty_typet{}))}
1337+
: list_arg),
1338+
from_integer(1, pointer_diff_type())},
1339+
list_arg.type());
1340+
code_assignt assign{list_arg, std::move(list_arg_ptr_arithmetic)};
13221341
assign.rhs().set(
13231342
ID_C_va_arg_type, to_code_type(function.type()).return_type());
13241343
dest.add(goto_programt::make_assignment(
@@ -1333,7 +1352,7 @@ void goto_convertt::do_function_call_symbol(
13331352
throw 0;
13341353
}
13351354

1336-
exprt dest_expr = make_va_list(arguments[0]);
1355+
exprt dest_expr = make_va_list(arguments[0], ns);
13371356
const typecast_exprt src_expr(arguments[1], dest_expr.type());
13381357

13391358
if(!is_assignable(dest_expr))
@@ -1357,7 +1376,7 @@ void goto_convertt::do_function_call_symbol(
13571376
throw 0;
13581377
}
13591378

1360-
exprt dest_expr = make_va_list(arguments[0]);
1379+
exprt dest_expr = make_va_list(arguments[0], ns);
13611380

13621381
if(!is_assignable(dest_expr))
13631382
{
@@ -1392,7 +1411,7 @@ void goto_convertt::do_function_call_symbol(
13921411
throw 0;
13931412
}
13941413

1395-
exprt dest_expr = make_va_list(arguments[0]);
1414+
exprt dest_expr = make_va_list(arguments[0], ns);
13961415

13971416
if(!is_assignable(dest_expr))
13981417
{

src/cpp/cpp_internal_additions.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,23 @@ void cpp_internal_additions(std::ostream &out)
155155
out << "typedef signed __int128 __int128_t;" << '\n';
156156
out << "typedef unsigned __int128 __uint128_t;" << '\n';
157157
}
158+
159+
if(
160+
config.ansi_c.arch == "arm64" &&
161+
config.ansi_c.os != configt::ansi_ct::ost::OS_MACOS)
162+
{
163+
out << "typedef struct __va_list {";
164+
out << "void *__stack;";
165+
out << "void *__gr_top;";
166+
out << "void *__vr_top;";
167+
out << "int __gr_offs;";
168+
out << "int __vr_offs;";
169+
out << " } __builtin_va_list;" << '\n';
170+
}
171+
else
172+
{
173+
out << "typedef void ** __builtin_va_list;" << '\n';
174+
}
158175
}
159176

160177
// this is Visual C/C++ only

src/goto-symex/symex_builtin_functions.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,17 @@ static std::optional<exprt> get_va_args(const exprt::operandst &operands)
351351
if(operands.size() != 2)
352352
return {};
353353

354-
const exprt &second_op = skip_typecast(operands.back());
354+
exprt second_op = skip_typecast(operands.back());
355+
if(second_op.id() == ID_struct)
356+
{
357+
// aarch64 ABI mandates that va_list has struct type with member names as
358+
// specified
359+
if(second_op.operands().size() != 5)
360+
return {};
361+
362+
second_op = skip_typecast(second_op.operands().front());
363+
}
364+
355365
if(second_op.id() != ID_address_of)
356366
return {};
357367

@@ -402,11 +412,12 @@ void goto_symext::symex_printf(
402412
return;
403413
}
404414

405-
// Visual Studio has va_list == char*, else we have va_list == void** and
406-
// need to add dereferencing
415+
// Visual Studio has va_list == char*, else we have va_list == void** or a
416+
// struct containing void** (aarch64) and need to add dereferencing
407417
const bool need_deref =
408-
operands.back().type().id() == ID_pointer &&
409-
to_pointer_type(operands.back().type()).base_type().id() == ID_pointer;
418+
operands.back().type().id() == ID_struct_tag ||
419+
(operands.back().type().id() == ID_pointer &&
420+
to_pointer_type(operands.back().type()).base_type().id() == ID_pointer);
410421

411422
for(const auto &op : va_args->operands())
412423
{

0 commit comments

Comments
 (0)