]>
Commit | Line | Data |
---|---|---|
8e427c29 DL |
1 | /* |
2 | * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc. | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #ifndef _FRR_XREF_H | |
18 | #define _FRR_XREF_H | |
19 | ||
20 | #include <stdint.h> | |
21 | #include <stdlib.h> | |
22 | #include <limits.h> | |
23 | #include <errno.h> | |
24 | #include "compiler.h" | |
4894e9c1 | 25 | #include "typesafe.h" |
8e427c29 | 26 | |
96a70614 DL |
27 | #ifdef __cplusplus |
28 | extern "C" { | |
29 | #endif | |
30 | ||
8e427c29 DL |
31 | enum xref_type { |
32 | XREFT_NONE = 0, | |
60a3efec DL |
33 | |
34 | XREFT_THREADSCHED = 0x100, | |
131879fb DL |
35 | |
36 | XREFT_LOGMSG = 0x200, | |
64dd7736 | 37 | XREFT_ASSERT = 0x280, |
feb06e7a DL |
38 | |
39 | XREFT_DEFUN = 0x300, | |
01485adb | 40 | XREFT_INSTALL_ELEMENT = 0x301, |
8e427c29 DL |
41 | }; |
42 | ||
43 | /* struct xref is the "const" part; struct xrefdata is the writable part. */ | |
44 | struct xref; | |
45 | struct xrefdata; | |
46 | ||
47 | struct xref { | |
48 | /* this may be NULL, depending on the type of the xref. | |
49 | * if it is NULL, the xref has no unique ID and cannot be accessed | |
50 | * through that mechanism. | |
51 | */ | |
52 | struct xrefdata *xrefdata; | |
53 | ||
54 | /* type isn't generally needed at runtime */ | |
55 | enum xref_type type; | |
56 | ||
57 | /* code location */ | |
58 | int line; | |
59 | const char *file; | |
60 | const char *func; | |
61 | ||
62 | /* -- 32 bytes (on 64bit) -- */ | |
63 | ||
64 | /* type-specific bits appended by embedding this struct */ | |
65 | }; | |
66 | ||
4894e9c1 DL |
67 | PREDECL_RBTREE_UNIQ(xrefdata_uid); |
68 | ||
8e427c29 DL |
69 | struct xrefdata { |
70 | /* pointer back to the const part; this will be initialized at | |
71 | * program startup by xref_block_add(). (Creating structs with | |
72 | * cyclic pointers to each other is not easily possible for | |
73 | * function-scoped static variables.) | |
74 | * | |
75 | * There is no xrefdata w/o xref, but there are xref w/o xrefdata. | |
76 | */ | |
77 | const struct xref *xref; | |
78 | ||
79 | /* base32(crockford) of unique ID. not all bytes are used, but | |
80 | * let's pad to 16 for simplicity | |
81 | */ | |
82 | char uid[16]; | |
83 | ||
84 | /* hash/uid input | |
85 | * if hashstr is NULL, no UID is assigned/calculated. Use macro | |
86 | * string concatenation if multiple values need to be fed in. | |
87 | * (This is here to not make the UID calculation independent of | |
88 | * xref type.) | |
89 | */ | |
90 | const char *hashstr; | |
91 | uint32_t hashu32[2]; | |
92 | ||
93 | /* -- 32 bytes (on 64bit) -- */ | |
4894e9c1 | 94 | struct xrefdata_uid_item xui; |
8e427c29 DL |
95 | }; |
96 | ||
4894e9c1 DL |
97 | static inline int xrefdata_uid_cmp(const struct xrefdata *a, |
98 | const struct xrefdata *b) | |
99 | { | |
100 | return strcmp(a->uid, b->uid); | |
101 | } | |
102 | ||
103 | DECLARE_RBTREE_UNIQ(xrefdata_uid, struct xrefdata, xui, xrefdata_uid_cmp); | |
104 | extern struct xrefdata_uid_head xrefdata_uid; | |
105 | ||
8e427c29 DL |
106 | /* linker "magic" is used to create an array of pointers to struct xref. |
107 | * the result is a contiguous block of pointers, each pointing to an xref | |
108 | * somewhere in the code. The linker gives us start and end pointers, we | |
109 | * stuff those into the struct below and hook up a constructor to run at | |
110 | * program startup with the struct passed. | |
111 | * | |
112 | * Placing the xrefs themselves into an array doesn't work because they'd | |
113 | * need to be constant size, but we're embedding struct xref into other | |
114 | * container structs with extra data. Also this means that external code | |
115 | * (like the python xref dumper) can safely ignore extra data at the end of | |
116 | * xrefs without needing to account for size in iterating the array. | |
117 | * | |
118 | * If you're curious, this is also how __attribute__((constructor)) (and | |
119 | * destructor) are implemented - there are 2 arrays, ".init_array" and | |
120 | * ".fini_array", containing function pointers. The magic turns out to be | |
121 | * quite mundane, actually ;) | |
122 | * | |
123 | * The slightly tricky bit is that this is a per-object (i.e. per shared | |
124 | * library & daemon) thing and we need a bit of help (in XREF_SETUP) to | |
125 | * initialize correctly. | |
126 | */ | |
127 | ||
128 | struct xref_block { | |
129 | struct xref_block *next; | |
130 | const struct xref * const *start; | |
131 | const struct xref * const *stop; | |
132 | }; | |
133 | ||
134 | extern struct xref_block *xref_blocks; | |
135 | extern void xref_block_add(struct xref_block *block); | |
08a73c42 | 136 | extern void xref_gcc_workaround(const struct xref *xref); |
8e427c29 DL |
137 | |
138 | #ifndef HAVE_SECTION_SYMS | |
139 | /* we have a build system patch to use GNU ld on Solaris; if that doesn't | |
140 | * work we end up on Solaris ld which doesn't support the section start/end | |
141 | * symbols. | |
142 | */ | |
143 | #define XREF_SETUP() \ | |
144 | CPP_NOTICE("Missing linker support for section arrays. Solaris ld?") | |
145 | #else | |
146 | /* the actual symbols that the linker provides for us. Note these are | |
147 | * _symbols_ referring to the actual section start/end, i.e. they are very | |
148 | * much NOT _pointers_, rather the symbol *value* is the pointer. Declaring | |
149 | * them as size-1 arrays is the "best" / "right" thing. | |
150 | */ | |
151 | extern const struct xref * const __start_xref_array[1] DSO_LOCAL; | |
152 | extern const struct xref * const __stop_xref_array[1] DSO_LOCAL; | |
153 | ||
1b958b2e DL |
154 | #if defined(__has_feature) |
155 | #if __has_feature(address_sanitizer) | |
156 | /* no redzone around each of the xref_p please, we're building an array out | |
157 | * of variables here. kinda breaks things if there's redzones between each | |
158 | * array item. | |
159 | */ | |
160 | #define xref_array_attr used, section("xref_array"), no_sanitize("address") | |
161 | #endif | |
162 | #endif | |
163 | #ifndef xref_array_attr | |
164 | #define xref_array_attr used, section("xref_array") | |
165 | #endif | |
166 | ||
8e427c29 DL |
167 | /* this macro is invoked once for each standalone DSO through |
168 | * FRR_MODULE_SETUP \ | |
169 | * }-> FRR_COREMOD_SETUP -> XREF_SETUP | |
170 | * FRR_DAEMON_INFO / | |
171 | */ | |
172 | #define XREF_SETUP() \ | |
173 | static const struct xref _dummy_xref = { \ | |
cbb1337a DL |
174 | /* .xrefdata = */ NULL, \ |
175 | /* .type = */ XREFT_NONE, \ | |
176 | /* .line = */ __LINE__, \ | |
177 | /* .file = */ __FILE__, \ | |
178 | /* .func = */ "dummy", \ | |
8e427c29 DL |
179 | }; \ |
180 | static const struct xref * const _dummy_xref_p \ | |
1b958b2e | 181 | __attribute__((xref_array_attr)) = &_dummy_xref; \ |
8e427c29 DL |
182 | static void __attribute__((used, _CONSTRUCTOR(1100))) \ |
183 | _xref_init(void) { \ | |
184 | static struct xref_block _xref_block = { \ | |
af1b88e9 | 185 | .next = NULL, \ |
8e427c29 DL |
186 | .start = __start_xref_array, \ |
187 | .stop = __stop_xref_array, \ | |
188 | }; \ | |
189 | xref_block_add(&_xref_block); \ | |
190 | } \ | |
191 | asm(XREF_NOTE); \ | |
80413c20 | 192 | MACRO_REQUIRE_SEMICOLON() /* end */ |
8e427c29 DL |
193 | |
194 | /* the following blurb emits an ELF note indicating start and end of the xref | |
195 | * array in the binary. This is technically the "correct" entry point for | |
196 | * external tools reading xrefs out of an ELF shared library or executable. | |
197 | * | |
198 | * right now, the extraction tools use the section header for "xref_array" | |
199 | * instead; however, section headers are technically not necessarily preserved | |
200 | * for fully linked libraries or executables. (In practice they are only | |
201 | * stripped by obfuscation tools.) | |
202 | * | |
203 | * conversely, for reading xrefs out of a single relocatable object file (e.g. | |
204 | * bar.o), section headers are the right thing to look at since the note is | |
205 | * only emitted for the final binary once. | |
206 | * | |
207 | * FRR itself does not need this note to operate correctly, so if you have | |
208 | * some build issue with it just add -DFRR_XREF_NO_NOTE to your build flags | |
209 | * to disable it. | |
210 | */ | |
ee4a6b9f | 211 | #if defined(FRR_XREF_NO_NOTE) || defined(__mips64) |
8e427c29 | 212 | #define XREF_NOTE "" |
ee4a6b9f DL |
213 | |
214 | /* mips64 note: MIPS64 (regardless of endianness, both mips64 & mips64el) | |
215 | * does not have a 64-bit PC-relative relocation type. Unfortunately, a | |
216 | * 64-bit PC-relative relocation is exactly what the below asm magic emits. | |
217 | * Therefore, the xref ELF note is permanently disabled on MIPS64. | |
218 | * | |
219 | * For some context, refer to https://reviews.llvm.org/D80390 | |
220 | * | |
221 | * As noted above, xref extraction still works through the section header | |
222 | * path, so no functionality is lost. | |
223 | */ | |
8e427c29 DL |
224 | #else |
225 | ||
226 | #if __SIZEOF_POINTER__ == 4 | |
227 | #define _NOTE_2PTRSIZE "8" | |
228 | #define _NOTE_PTR ".long" | |
229 | #elif __SIZEOF_POINTER__ == 8 | |
230 | #define _NOTE_2PTRSIZE "16" | |
231 | #define _NOTE_PTR ".quad" | |
232 | #else | |
233 | #error unsupported pointer size | |
234 | #endif | |
235 | ||
236 | #ifdef __arm__ | |
237 | # define asmspecial "%" | |
238 | #else | |
239 | # define asmspecial "@" | |
240 | #endif | |
241 | ||
242 | #define XREF_NOTE \ | |
243 | "" "\n"\ | |
244 | " .type _frr_xref_note," asmspecial "object" "\n"\ | |
245 | " .pushsection .note.FRR,\"a\"," asmspecial "note" "\n"\ | |
246 | " .p2align 2" "\n"\ | |
247 | "_frr_xref_note:" "\n"\ | |
248 | " .long 9" "\n"\ | |
249 | " .long " _NOTE_2PTRSIZE "\n"\ | |
250 | " .ascii \"XREF\"" "\n"\ | |
251 | " .ascii \"FRRouting\\0\\0\\0\"" "\n"\ | |
252 | " " _NOTE_PTR " __start_xref_array-." "\n"\ | |
253 | " " _NOTE_PTR " __stop_xref_array-." "\n"\ | |
254 | " .size _frr_xref_note, .-_frr_xref_note" "\n"\ | |
255 | " .popsection" "\n"\ | |
256 | "" "\n"\ | |
257 | /* end */ | |
258 | #endif | |
259 | ||
260 | #endif /* HAVE_SECTION_SYMS */ | |
261 | ||
262 | /* emit the array entry / pointer to xref */ | |
08a73c42 | 263 | #if defined(__clang__) || !defined(__cplusplus) |
8e427c29 DL |
264 | #define XREF_LINK(dst) \ |
265 | static const struct xref * const NAMECTR(xref_p_) \ | |
1b958b2e | 266 | __attribute__((xref_array_attr)) \ |
8e427c29 DL |
267 | = &(dst) \ |
268 | /* end */ | |
269 | ||
08a73c42 DL |
270 | #else /* GCC && C++ */ |
271 | /* workaround for GCC bug 41091 (dated 2009), added in 2021... | |
272 | * | |
273 | * this breaks extraction of xrefs with xrelfo.py (because the xref_array | |
274 | * entry will be missing), but provides full runtime functionality. To get | |
275 | * the proper list of xrefs from C++ code, build with clang... | |
276 | */ | |
277 | struct _xref_p { | |
278 | const struct xref * const ptr; | |
279 | ||
280 | _xref_p(const struct xref *_ptr) : ptr(_ptr) | |
281 | { | |
282 | xref_gcc_workaround(_ptr); | |
283 | } | |
284 | }; | |
285 | ||
286 | #define XREF_LINK(dst) \ | |
287 | static const struct _xref_p __attribute__((used)) \ | |
288 | NAMECTR(xref_p_)(&(dst)) \ | |
289 | /* end */ | |
290 | #endif | |
291 | ||
8e427c29 DL |
292 | /* initializer for a "struct xref" */ |
293 | #define XREF_INIT(type_, xrefdata_, func_) \ | |
294 | { \ | |
cbb1337a DL |
295 | /* .xrefdata = */ (xrefdata_), \ |
296 | /* .type = */ (type_), \ | |
297 | /* .line = */ __LINE__, \ | |
298 | /* .file = */ __FILE__, \ | |
299 | /* .func = */ func_, \ | |
8e427c29 DL |
300 | } \ |
301 | /* end */ | |
302 | ||
303 | /* use with XREF_INIT when outside of a function, i.e. no __func__ */ | |
304 | #define XREF_NO_FUNC "<global>" | |
305 | ||
306 | #ifdef __cplusplus | |
307 | } | |
308 | #endif | |
309 | ||
310 | #endif /* _FRR_XREF_H */ |