Skip to content

Commit b9d1482

Browse files
committed
C front-end: support constructor/destructor attribute with priority argument
GCC supports priorities to prescribe the order in which constructors (and destructors) are to be run: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html. Fixes: #8688
1 parent 1505c36 commit b9d1482

File tree

8 files changed

+178
-6
lines changed

8 files changed

+178
-6
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <assert.h>
2+
3+
#ifdef __GNUC__
4+
int x = 0, y = 0, w = 0;
5+
6+
// Constructor with priority argument; name chosen to make sure lexicographic
7+
// ordering does not incidentally produce the correct sequence.
8+
__attribute__((constructor(101))) void z_init_priority(void)
9+
{
10+
x = 1;
11+
}
12+
13+
// Constructor with priority argument
14+
__attribute__((constructor(102))) void y_init_priority2(void)
15+
{
16+
assert(x == 1);
17+
x = 2;
18+
}
19+
20+
// Destructor with priority argument
21+
__attribute__((destructor(201))) void fini_priority(void)
22+
{
23+
assert(w == 4);
24+
y = 2;
25+
}
26+
27+
// Constructor without priority (should still work, have lowest priority)
28+
__attribute__((constructor)) void x_init_no_priority(void)
29+
{
30+
assert(x == 2);
31+
x = 3;
32+
}
33+
34+
// Destructor without priority (should still work, has highest priority)
35+
__attribute__((destructor)) void fini_no_priority(void)
36+
{
37+
w = 4;
38+
}
39+
#endif
40+
41+
int main()
42+
{
43+
#ifdef __GNUC__
44+
// All constructors should have run before main
45+
assert(x == 3);
46+
// Destructors haven't run yet
47+
assert(y == 0);
48+
assert(w == 0);
49+
#endif
50+
return 0;
51+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/ansi_c_convert_type.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,23 @@ void ansi_c_convert_typet::read_rec(const typet &type)
228228
else if(type.id()==ID_noreturn)
229229
c_qualifiers.is_noreturn=true;
230230
else if(type.id()==ID_constructor)
231+
{
231232
constructor=true;
233+
234+
// may come with priority
235+
const irept &priority_opt = type.find(ID_constructor_priority);
236+
if(priority_opt.is_not_nil())
237+
constructor_priority = static_cast<const exprt &>(priority_opt);
238+
}
232239
else if(type.id()==ID_destructor)
240+
{
233241
destructor=true;
242+
243+
// may come with priority
244+
const irept &priority_opt = type.find(ID_destructor_priority);
245+
if(priority_opt.is_not_nil())
246+
destructor_priority = static_cast<const exprt &>(priority_opt);
247+
}
234248
else if(
235249
type.id() == ID_alias && type.has_subtype() &&
236250
to_type_with_subtype(type).subtype().id() == ID_string_constant)
@@ -363,7 +377,18 @@ void ansi_c_convert_typet::write(typet &type)
363377
throw 0;
364378
}
365379

366-
type_p->id(constructor ? ID_constructor : ID_destructor);
380+
if(constructor)
381+
{
382+
type_p->id(ID_constructor);
383+
if(constructor_priority.is_not_nil())
384+
type_p->add(ID_constructor_priority, constructor_priority);
385+
}
386+
else
387+
{
388+
type_p->id(ID_destructor);
389+
if(destructor_priority.is_not_nil())
390+
type_p->add(ID_destructor_priority, destructor_priority);
391+
}
367392
}
368393
}
369394
else if(constructor || destructor)

src/ansi-c/ansi_c_convert_type.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ansi_c_convert_typet
4444
exprt vector_size, alignment, bv_width, fraction_width;
4545
exprt msc_based; // this is Visual Studio
4646
bool constructor, destructor;
47+
exprt constructor_priority, destructor_priority;
4748

4849
// contracts
4950
exprt::operandst c_assigns, c_frees, c_ensures, c_requires;
@@ -113,6 +114,8 @@ class ansi_c_convert_typet
113114
msc_based(nil_exprt{}),
114115
constructor(false),
115116
destructor(false),
117+
constructor_priority(nil_exprt{}),
118+
destructor_priority(nil_exprt{}),
116119
message_handler(_message_handler)
117120
{
118121
}

src/ansi-c/ansi_c_entry_point.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,10 @@ bool generate_ansi_c_start_function(
511511

512512
record_function_outputs(symbol, init_code, symbol_table);
513513

514-
// now call destructor functions (a GCC extension)
514+
// now call destructor functions (a GCC extension); compute required order
515+
// first
516+
std::map<mp_integer, std::list<std::reference_wrapper<const symbolt>>>
517+
destructors_by_priority;
515518

516519
for(const auto &symbol_table_entry : symbol_table.symbols)
517520
{
@@ -525,8 +528,39 @@ bool generate_ansi_c_start_function(
525528
code_type.return_type().id() == ID_destructor &&
526529
code_type.parameters().empty())
527530
{
528-
code_function_callt destructor_call(symbol.symbol_expr());
529-
destructor_call.add_source_location() = symbol.location;
531+
const exprt &priority = static_cast<const exprt &>(
532+
code_type.return_type().find(ID_destructor_priority));
533+
if(priority.is_nil())
534+
destructors_by_priority[1].push_back(symbol);
535+
else
536+
{
537+
std::optional<mp_integer> priority_int_opt =
538+
numeric_cast<mp_integer>(priority);
539+
DATA_INVARIANT(
540+
priority_int_opt.has_value() && *priority_int_opt >= 0,
541+
"destructor priority expected to be non-negative integer");
542+
destructors_by_priority[-*priority_int_opt].push_back(symbol);
543+
}
544+
}
545+
}
546+
547+
// append all destructors with priority "default" to the otherwise
548+
// lowest-priority destructor list
549+
if(
550+
destructors_by_priority.size() > 1 &&
551+
std::prev(destructors_by_priority.end())->first == 1)
552+
{
553+
auto &dest_list = destructors_by_priority.begin()->second;
554+
dest_list.splice(
555+
dest_list.begin(), std::prev(destructors_by_priority.end())->second);
556+
}
557+
558+
for(const auto &priority_pair : destructors_by_priority)
559+
{
560+
for(const auto &sym_ref : priority_pair.second)
561+
{
562+
code_function_callt destructor_call{sym_ref.get().symbol_expr()};
563+
destructor_call.add_source_location() = sym_ref.get().location;
530564
init_code.add(std::move(destructor_call));
531565
}
532566
}

src/ansi-c/parser.y

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,8 +1699,20 @@ gcc_type_attribute:
16991699
{ $$=$1; set($$, ID_noreturn); }
17001700
| TOK_GCC_ATTRIBUTE_CONSTRUCTOR
17011701
{ $$=$1; set($$, ID_constructor); }
1702+
| TOK_GCC_ATTRIBUTE_CONSTRUCTOR '(' TOK_INTEGER ')'
1703+
{
1704+
$$=$1;
1705+
set($$, ID_constructor);
1706+
parser_stack($$).set(ID_constructor_priority, parser_stack($3));
1707+
}
17021708
| TOK_GCC_ATTRIBUTE_DESTRUCTOR
17031709
{ $$=$1; set($$, ID_destructor); }
1710+
| TOK_GCC_ATTRIBUTE_DESTRUCTOR '(' TOK_INTEGER ')'
1711+
{
1712+
$$=$1;
1713+
set($$, ID_destructor);
1714+
parser_stack($$).set(ID_destructor_priority, parser_stack($3));
1715+
}
17041716
| TOK_GCC_ATTRIBUTE_USED
17051717
{ $$=$1; set($$, ID_used); }
17061718
;

src/linking/static_lifetime_init.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ void static_lifetime_init(
142142
dest.add(std::move(*code));
143143
}
144144

145-
// now call designated "initialization" functions
145+
// now call designated "initialization" functions; compute required order
146+
// first
147+
std::map<mp_integer, std::list<std::reference_wrapper<const symbolt>>>
148+
constructors_by_priority;
146149

147150
for(const std::string &id : symbols)
148151
{
@@ -156,8 +159,42 @@ void static_lifetime_init(
156159
code_type.return_type().id() == ID_constructor &&
157160
code_type.parameters().empty())
158161
{
162+
const exprt &priority = static_cast<const exprt &>(
163+
code_type.return_type().find(ID_constructor_priority));
164+
if(priority.is_nil())
165+
constructors_by_priority[-1].push_back(symbol);
166+
else
167+
{
168+
std::optional<mp_integer> priority_int_opt =
169+
numeric_cast<mp_integer>(priority);
170+
DATA_INVARIANT(
171+
priority_int_opt.has_value() && *priority_int_opt >= 0,
172+
"constructor priority expected to be non-negative integer");
173+
constructors_by_priority[*priority_int_opt].push_back(symbol);
174+
}
175+
}
176+
}
177+
178+
// append all constructors with priority "default" to the otherwise
179+
// lowest-priority constructor list
180+
if(
181+
constructors_by_priority.size() > 1 &&
182+
constructors_by_priority.begin()->first == -1)
183+
{
184+
auto &dest_list = std::prev(constructors_by_priority.end())->second;
185+
dest_list.splice(dest_list.end(), constructors_by_priority.begin()->second);
186+
}
187+
188+
for(const auto &priority_pair : constructors_by_priority)
189+
{
190+
for(const auto &sym_ref : priority_pair.second)
191+
{
192+
const code_typet &code_type = to_code_type(sym_ref.get().type);
159193
dest.add(code_expressiont{side_effect_expr_function_callt{
160-
symbol.symbol_expr(), {}, code_type.return_type(), source_location}});
194+
sym_ref.get().symbol_expr(),
195+
{},
196+
code_type.return_type(),
197+
source_location}});
161198
}
162199
}
163200
}

src/util/irep_ids.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,8 @@ IREP_ID_ONE(methods)
422422
IREP_ID_ONE(static_members)
423423
IREP_ID_ONE(constructor)
424424
IREP_ID_ONE(destructor)
425+
IREP_ID_ONE(constructor_priority)
426+
IREP_ID_ONE(destructor_priority)
425427
IREP_ID_ONE(bases)
426428
IREP_ID_ONE(base)
427429
IREP_ID_ONE(from_base)

0 commit comments

Comments
 (0)