]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
4368277c | 2 | |
67e16c31 LP |
3 | #pragma once |
4 | ||
e30f9c97 | 5 | #include "alloc-util.h" |
67e16c31 | 6 | #include "macro.h" |
9695b0c0 | 7 | #include "memory-util.h" |
67e16c31 LP |
8 | |
9 | /* A framework for registering static variables that shall be freed on shutdown of a process. It's a bit like gcc's | |
10 | * destructor attribute, but allows us to precisely schedule when we want to free the variables. This is supposed to | |
11 | * feel a bit like the gcc cleanup attribute, but for static variables. Note that this does not work for static | |
12 | * variables declared in .so's, as the list is private to the same linking unit. But maybe that's a good thing. */ | |
13 | ||
555ead89 YW |
14 | #define _common_static_destruct_attrs_ \ |
15 | /* Older compilers don't know "retain" attribute. */ \ | |
16 | _Pragma("GCC diagnostic ignored \"-Wattributes\"") \ | |
17 | /* The actual destructor structure we place in a special section to find it. */ \ | |
18 | _section_("SYSTEMD_STATIC_DESTRUCT") \ | |
19 | /* Use pointer alignment, since that is apparently what gcc does for static variables. */ \ | |
20 | _alignptr_ \ | |
21 | /* Make sure this is not dropped from the image despite not being explicitly referenced. */ \ | |
22 | _used_ \ | |
23 | /* Prevent garbage collection by the linker. */ \ | |
24 | _retain_ \ | |
25 | /* Make sure that AddressSanitizer doesn't pad this variable: we want everything in this section | |
26 | * packed next to each other so that we can enumerate it. */ \ | |
27 | _variable_no_sanitize_address_ | |
28 | ||
9695b0c0 YW |
29 | typedef enum StaticDestructorType { |
30 | STATIC_DESTRUCTOR_SIMPLE, | |
31 | STATIC_DESTRUCTOR_ARRAY, | |
32 | _STATIC_DESTRUCTOR_TYPE_MAX, | |
33 | _STATIC_DESTRUCTOR_INVALID = -EINVAL, | |
34 | } StaticDestructorType; | |
35 | ||
36 | typedef struct SimpleCleanup { | |
67e16c31 | 37 | void *data; |
e30f9c97 | 38 | free_func_t destroy; |
9695b0c0 YW |
39 | } SimpleCleanup; |
40 | ||
41 | typedef struct StaticDestructor { | |
42 | StaticDestructorType type; | |
43 | union { | |
44 | SimpleCleanup simple; | |
45 | ArrayCleanup array; | |
46 | }; | |
67e16c31 LP |
47 | } StaticDestructor; |
48 | ||
49 | #define STATIC_DESTRUCTOR_REGISTER(variable, func) \ | |
50 | _STATIC_DESTRUCTOR_REGISTER(UNIQ, variable, func) | |
51 | ||
52 | #define _STATIC_DESTRUCTOR_REGISTER(uq, variable, func) \ | |
53 | /* Type-safe destructor */ \ | |
54 | static void UNIQ_T(static_destructor_wrapper, uq)(void *p) { \ | |
55 | typeof(variable) *q = p; \ | |
56 | func(q); \ | |
57 | } \ | |
555ead89 | 58 | _common_static_destruct_attrs_ \ |
67e16c31 | 59 | static const StaticDestructor UNIQ_T(static_destructor_entry, uq) = { \ |
9695b0c0 YW |
60 | .type = STATIC_DESTRUCTOR_SIMPLE, \ |
61 | .simple.data = &(variable), \ | |
62 | .simple.destroy = UNIQ_T(static_destructor_wrapper, uq), \ | |
67e16c31 LP |
63 | } |
64 | ||
9695b0c0 YW |
65 | #define STATIC_ARRAY_DESTRUCTOR_REGISTER(a, n, func) \ |
66 | _STATIC_ARRAY_DESTRUCTOR_REGISTER(UNIQ, a, n, func) | |
67 | ||
68 | #define _STATIC_ARRAY_DESTRUCTOR_REGISTER(uq, a, n, func) \ | |
69 | /* Type-safety check */ \ | |
70 | _unused_ static void (* UNIQ_T(static_destructor_wrapper, uq))(typeof(a[0]) *x, size_t y) = (func); \ | |
71 | _common_static_destruct_attrs_ \ | |
72 | static const StaticDestructor UNIQ_T(static_destructor_entry, uq) = { \ | |
73 | .type = STATIC_DESTRUCTOR_ARRAY, \ | |
74 | .array.parray = (void**) &(a), \ | |
75 | .array.pn = &(n), \ | |
76 | .array.pfunc = (free_array_func_t) (func), \ | |
77 | }; | |
78 | ||
1f568ba1 ZJS |
79 | /* Beginning and end of our section listing the destructors. We define these as weak as we want this to work |
80 | * even if no destructors are defined and the section is missing. */ | |
555ead89 YW |
81 | extern const StaticDestructor _weak_ __start_SYSTEMD_STATIC_DESTRUCT[]; |
82 | extern const StaticDestructor _weak_ __stop_SYSTEMD_STATIC_DESTRUCT[]; | |
67e16c31 | 83 | |
dfaf16eb LP |
84 | /* The function to destroy everything. (Note that this must be static inline, as it's key that it remains in |
85 | * the same linking unit as the variables we want to destroy.) */ | |
67e16c31 | 86 | static inline void static_destruct(void) { |
67e16c31 LP |
87 | if (!__start_SYSTEMD_STATIC_DESTRUCT) |
88 | return; | |
89 | ||
555ead89 YW |
90 | for (const StaticDestructor *d = ALIGN_PTR(__start_SYSTEMD_STATIC_DESTRUCT); |
91 | d < __stop_SYSTEMD_STATIC_DESTRUCT; | |
92 | d = ALIGN_PTR(d + 1)) | |
9695b0c0 YW |
93 | switch (d->type) { |
94 | case STATIC_DESTRUCTOR_SIMPLE: | |
95 | d->simple.destroy(d->simple.data); | |
96 | break; | |
97 | ||
98 | case STATIC_DESTRUCTOR_ARRAY: | |
99 | array_cleanup(&d->array); | |
100 | break; | |
101 | ||
102 | default: | |
103 | assert_not_reached(); | |
104 | } | |
67e16c31 | 105 | } |