Skip to content

Commit c0a3df4

Browse files
committed
Linking: actually replace code type information in expressions
The linker already accepted a number of type mismatches for function declarations, but did not actually modify the goto functions to reflect these type mismatches, just the type information in the symbol table was updated. These changes enable support for conflicts on function return types, which are now resolved by introducing type casts.
1 parent 8cbd33c commit c0a3df4

File tree

15 files changed

+173
-71
lines changed

15 files changed

+173
-71
lines changed

regression/ansi-c/undeclared_function/fileB.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
#include <stdlib.h>
1+
void *malloc(__CPROVER_size_t s)
2+
{
3+
return (void *)0 + s;
4+
}
25

36
int f()
47
{

regression/ansi-c/undeclared_function/undeclared_function1.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
fileA.c
33
fileB.c --validate-goto-model
44
^EXIT=0$

regression/ansi-c/incomplete-structs/test.desc renamed to regression/cbmc/incomplete-structs/test.desc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
CORE
22
typesmain.c
3-
types1.c types2.c types3.c --verbosity 2
3+
types1.c types2.c types3.c
44
reason for conflict at \*#this.u: number of members is different \(3/0\)
55
reason for conflict at \*#this.u: number of members is different \(0/3\)
66
struct U \(incomplete\)
77
warning: pointer parameter types differ between declaration and definition "bar"
88
warning: pointer parameter types differ between declaration and definition "foo"
9+
^VERIFICATION SUCCESSFUL$
910
^EXIT=0$
1011
^SIGNAL=0$
1112
--

regression/cbmc/return6/test.desc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
CORE
22
main.c
33
f_def.c
4-
^EXIT=6$
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
56
^SIGNAL=0$
6-
CONVERSION ERROR
77
--
88
^warning: ignoring
9-
^VERIFICATION SUCCESSFUL$

regression/linking-goto-binaries/chain.sh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ is_windows=$3
88

99
main=${*:$#}
1010
main=${main%.c}
11+
main_local=$(basename ${main})
1112
args=${*:4:$#-4}
1213
next=${args%.c}
14+
next_local=$(basename ${next})
1315

1416
if [[ "${is_windows}" == "true" ]]; then
15-
$goto_cc /c "${main}.c" "/Fo${main}.gb"
16-
$goto_cc /c "${next}.c" "/Fo${next}.gb"
17-
$goto_cc "${main}.gb" "${next}.gb" "/Fefinal.gb"
17+
$goto_cc /c "${main}.c" "/Fo${main_local}.gb"
18+
$goto_cc /c "${next}.c" "/Fo${next_local}.gb"
19+
$goto_cc "${main_local}.gb" "${next_local}.gb" "/Fefinal.gb"
1820
else
19-
$goto_cc -c -o "${main}.gb" "${main}.c"
20-
$goto_cc -c -o "${next}.gb" "${next}.c"
21-
$goto_cc "${main}.gb" "${next}.gb" -o "final.gb"
21+
$goto_cc -c -o "${main_local}.gb" "${main}.c"
22+
$goto_cc -c -o "${next_local}.gb" "${next}.c"
23+
$goto_cc "${main_local}.gb" "${next_local}.gb" -o "final.gb"
2224
fi
2325

24-
$cbmc "final.gb"
26+
$cbmc --validate-goto-model "final.gb"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
../../cbmc/Linking7/main.c
3+
../../cbmc/Linking7/module2.c
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
line 21 assertion \*g\.a == 42: SUCCESS
8+
line 22 assertion \*g\.c == 41: FAILURE
9+
^\*\* 1 of 2 failed
10+
--
11+
^warning: ignoring
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
../../cbmc/Linking7/return_type1.c
3+
../../cbmc/Linking7/return_type2.c
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
Note issue #3081
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
../../cbmc/Linking7/main.c
3+
../../cbmc/Linking7/module.c
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\*\* 1 of 2 failed
8+
line 21 assertion \*g\.a == 42: SUCCESS
9+
line 22 assertion \*g\.b == 41: FAILURE
10+
--
11+
^warning: ignoring

src/goto-programs/link_goto_model.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ void finalize_linking(
152152
goto_modelt &dest,
153153
const replace_symbolt::expr_mapt &type_updates)
154154
{
155-
unchecked_replace_symbolt object_type_updates;
155+
casting_replace_symbolt object_type_updates;
156156
object_type_updates.get_expr_map().insert(
157157
type_updates.begin(), type_updates.end());
158158

@@ -200,10 +200,30 @@ void finalize_linking(
200200
{
201201
for(auto &instruction : gf_entry.second.body.instructions)
202202
{
203-
instruction.transform([&object_type_updates](exprt expr) {
204-
object_type_updates(expr);
205-
return expr;
206-
});
203+
if(instruction.is_function_call())
204+
{
205+
const bool changed =
206+
!object_type_updates.replace(instruction.call_function());
207+
if(changed && instruction.call_lhs().is_not_nil())
208+
{
209+
object_type_updates(instruction.call_lhs());
210+
if(
211+
instruction.call_lhs().type() !=
212+
to_code_type(instruction.call_function().type()).return_type())
213+
{
214+
instruction.call_lhs() = typecast_exprt{
215+
instruction.call_lhs(),
216+
to_code_type(instruction.call_function().type()).return_type()};
217+
}
218+
}
219+
}
220+
else
221+
{
222+
instruction.transform([&object_type_updates](exprt expr) {
223+
const bool changed = !object_type_updates.replace(expr);
224+
return changed ? optionalt<exprt>{expr} : nullopt;
225+
});
226+
}
207227
}
208228
}
209229
}

src/linking/linking.cpp

Lines changed: 93 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ Author: Daniel Kroening, [email protected]
1010
/// ANSI-C Linking
1111

1212
#include "linking.h"
13-
14-
#include <deque>
15-
#include <unordered_set>
13+
#include "linking_class.h"
1614

1715
#include <util/base_type.h>
1816
#include <util/c_types.h>
@@ -21,11 +19,76 @@ Author: Daniel Kroening, [email protected]
2119
#include <util/pointer_expr.h>
2220
#include <util/pointer_offset_size.h>
2321
#include <util/simplify_expr.h>
22+
#include <util/std_code.h>
2423
#include <util/symbol_table.h>
2524

2625
#include <langapi/language_util.h>
2726

28-
#include "linking_class.h"
27+
#include <deque>
28+
29+
bool casting_replace_symbolt::replace(exprt &dest) const
30+
{
31+
bool result = true; // unchanged
32+
33+
// first look at type
34+
35+
const exprt &const_dest(dest);
36+
if(have_to_replace(const_dest.type()))
37+
if(!replace_symbolt::replace(dest.type()))
38+
result = false;
39+
40+
// now do expression itself
41+
42+
if(!have_to_replace(dest))
43+
return result;
44+
45+
if(dest.id() == ID_side_effect)
46+
{
47+
if(auto call = expr_try_dynamic_cast<side_effect_expr_function_callt>(dest))
48+
{
49+
if(!have_to_replace(call->function()))
50+
return replace_symbolt::replace(dest);
51+
52+
exprt before = dest;
53+
code_typet type = to_code_type(call->function().type());
54+
55+
result &= replace_symbolt::replace(call->function());
56+
57+
// maybe add type casts here?
58+
for(auto &arg : call->arguments())
59+
result &= replace_symbolt::replace(arg);
60+
61+
if(
62+
type.return_type() !=
63+
to_code_type(call->function().type()).return_type())
64+
{
65+
call->type() = to_code_type(call->function().type()).return_type();
66+
dest = typecast_exprt(*call, type.return_type());
67+
result = true;
68+
}
69+
70+
return result;
71+
}
72+
}
73+
else if(dest.id() == ID_address_of)
74+
{
75+
pointer_typet ptr_type = to_pointer_type(dest.type());
76+
77+
result &= replace_symbolt::replace(dest);
78+
79+
address_of_exprt address_of = to_address_of_expr(dest);
80+
if(address_of.object().type() != ptr_type.subtype())
81+
{
82+
to_pointer_type(address_of.type()).subtype() = address_of.object().type();
83+
dest = typecast_exprt(address_of, ptr_type);
84+
result = true;
85+
}
86+
87+
return result;
88+
}
89+
90+
return replace_symbolt::replace(dest);
91+
}
2992

3093
bool casting_replace_symbolt::replace_symbol_expr(symbol_exprt &s) const
3194
{
@@ -36,7 +99,7 @@ bool casting_replace_symbolt::replace_symbol_expr(symbol_exprt &s) const
3699

37100
const exprt &e = it->second;
38101

39-
if(e.type().id() != ID_array)
102+
if(e.type().id() != ID_array && e.type().id() != ID_code)
40103
{
41104
typet type = s.type();
42105
static_cast<exprt &>(s) = typecast_exprt::conditional_cast(e, type);
@@ -491,22 +554,9 @@ void linkingt::duplicate_code_symbol(
491554
const code_typet &old_t=to_code_type(old_symbol.type);
492555
const code_typet &new_t=to_code_type(new_symbol.type);
493556

494-
// if one of them was an implicit declaration then only conflicts on the
495-
// return type are an error as we would end up with assignments with
496-
// mismatching types; as we currently do not patch these by inserting type
497-
// casts we need to fail hard
498557
if(old_symbol.type.get_bool(ID_C_incomplete) && old_symbol.value.is_nil())
499558
{
500-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
501-
link_warning(
502-
old_symbol,
503-
new_symbol,
504-
"implicit function declaration");
505-
else
506-
link_error(
507-
old_symbol,
508-
new_symbol,
509-
"implicit function declaration");
559+
link_warning(old_symbol, new_symbol, "implicit function declaration");
510560

511561
old_symbol.type=new_symbol.type;
512562
old_symbol.location=new_symbol.location;
@@ -515,24 +565,15 @@ void linkingt::duplicate_code_symbol(
515565
else if(
516566
new_symbol.type.get_bool(ID_C_incomplete) && new_symbol.value.is_nil())
517567
{
518-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
519-
link_warning(
520-
old_symbol,
521-
new_symbol,
522-
"ignoring conflicting implicit function declaration");
523-
else
524-
link_error(
525-
old_symbol,
526-
new_symbol,
527-
"implicit function declaration");
568+
link_warning(
569+
old_symbol,
570+
new_symbol,
571+
"ignoring conflicting implicit function declaration");
528572
}
529573
// handle (incomplete) function prototypes
530-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
531-
((old_t.parameters().empty() &&
532-
old_t.has_ellipsis() &&
574+
else if(((old_t.parameters().empty() && old_t.has_ellipsis() &&
533575
old_symbol.value.is_nil()) ||
534-
(new_t.parameters().empty() &&
535-
new_t.has_ellipsis() &&
576+
(new_t.parameters().empty() && new_t.has_ellipsis() &&
536577
new_symbol.value.is_nil())))
537578
{
538579
if(old_t.parameters().empty() &&
@@ -583,9 +624,7 @@ void linkingt::duplicate_code_symbol(
583624
}
584625
// conflicting declarations without a definition, matching return
585626
// types
586-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
587-
old_symbol.value.is_nil() &&
588-
new_symbol.value.is_nil())
627+
else if(old_symbol.value.is_nil() && new_symbol.value.is_nil())
589628
{
590629
link_warning(
591630
old_symbol,
@@ -625,8 +664,11 @@ void linkingt::duplicate_code_symbol(
625664
conflictst conflicts;
626665

627666
if(!base_type_eq(old_t.return_type(), new_t.return_type(), ns))
628-
conflicts.push_back(
629-
std::make_pair(old_t.return_type(), new_t.return_type()));
667+
{
668+
link_warning(old_symbol, new_symbol, "conflicting return types");
669+
670+
conflicts.emplace_back(old_t.return_type(), new_t.return_type());
671+
}
630672

631673
code_typet::parameterst::const_iterator
632674
n_it=new_t.parameters().begin(),
@@ -676,21 +718,11 @@ void linkingt::duplicate_code_symbol(
676718
const typet &t1=follow_tags_symbols(ns, conflicts.front().first);
677719
const typet &t2=follow_tags_symbols(ns, conflicts.front().second);
678720

679-
// void vs. non-void return type may be acceptable if the
680-
// return value is never used
681-
if((t1.id()==ID_empty || t2.id()==ID_empty) &&
682-
(old_symbol.value.is_nil() || new_symbol.value.is_nil()))
683-
{
684-
if(warn_msg.empty())
685-
warn_msg="void/non-void return type conflict on function";
686-
replace=
687-
new_symbol.value.is_not_nil() ||
688-
(old_symbol.value.is_nil() && t2.id()==ID_empty);
689-
}
690721
// different pointer arguments without implementation may work
691-
else if((t1.id()==ID_pointer || t2.id()==ID_pointer) &&
692-
pointer_offset_bits(t1, ns)==pointer_offset_bits(t2, ns) &&
693-
old_symbol.value.is_nil() && new_symbol.value.is_nil())
722+
if(
723+
(t1.id() == ID_pointer || t2.id() == ID_pointer) &&
724+
pointer_offset_bits(t1, ns) == pointer_offset_bits(t2, ns) &&
725+
old_symbol.value.is_nil() && new_symbol.value.is_nil())
694726
{
695727
if(warn_msg.empty())
696728
warn_msg="different pointer types in extern function";
@@ -793,6 +825,9 @@ void linkingt::duplicate_code_symbol(
793825
}
794826
}
795827
}
828+
829+
object_type_updates.insert(
830+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
796831
}
797832

798833
if(!new_symbol.value.is_nil())
@@ -807,6 +842,11 @@ void linkingt::duplicate_code_symbol(
807842
old_symbol.is_weak=new_symbol.is_weak;
808843
old_symbol.location=new_symbol.location;
809844
old_symbol.is_macro=new_symbol.is_macro;
845+
846+
// replace any previous update
847+
object_type_updates.erase(old_symbol.name);
848+
object_type_updates.insert(
849+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
810850
}
811851
else if(to_code_type(old_symbol.type).get_inlined())
812852
{

src/linking/linking_class.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ Author: Daniel Kroening, [email protected]
1919
#include <util/symbol.h>
2020
#include <util/typecheck.h>
2121

22+
#include <unordered_set>
23+
2224
class casting_replace_symbolt : public replace_symbolt
2325
{
26+
public:
27+
bool replace(exprt &dest) const override;
28+
2429
private:
2530
bool replace_symbol_expr(symbol_exprt &dest) const override;
2631
};

0 commit comments

Comments
 (0)