summaryrefslogtreecommitdiffstats
path: root/lib/xref.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /lib/xref.h
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/xref.h')
-rw-r--r--lib/xref.h299
1 files changed, 299 insertions, 0 deletions
diff --git a/lib/xref.h b/lib/xref.h
new file mode 100644
index 0000000..f06d65b
--- /dev/null
+++ b/lib/xref.h
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc.
+ */
+
+#ifndef _FRR_XREF_H
+#define _FRR_XREF_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include "compiler.h"
+#include "typesafe.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum xref_type {
+ XREFT_NONE = 0,
+
+ XREFT_EVENTSCHED = 0x100,
+
+ XREFT_LOGMSG = 0x200,
+ XREFT_ASSERT = 0x280,
+
+ XREFT_DEFUN = 0x300,
+ XREFT_INSTALL_ELEMENT = 0x301,
+};
+
+/* struct xref is the "const" part; struct xrefdata is the writable part. */
+struct xref;
+struct xrefdata;
+
+struct xref {
+ /* this may be NULL, depending on the type of the xref.
+ * if it is NULL, the xref has no unique ID and cannot be accessed
+ * through that mechanism.
+ */
+ struct xrefdata *xrefdata;
+
+ /* type isn't generally needed at runtime */
+ enum xref_type type;
+
+ /* code location */
+ int line;
+ const char *file;
+ const char *func;
+
+ /* -- 32 bytes (on 64bit) -- */
+
+ /* type-specific bits appended by embedding this struct */
+};
+
+PREDECL_RBTREE_UNIQ(xrefdata_uid);
+
+struct xrefdata {
+ /* pointer back to the const part; this will be initialized at
+ * program startup by xref_block_add(). (Creating structs with
+ * cyclic pointers to each other is not easily possible for
+ * function-scoped static variables.)
+ *
+ * There is no xrefdata w/o xref, but there are xref w/o xrefdata.
+ */
+ const struct xref *xref;
+
+ /* base32(crockford) of unique ID. not all bytes are used, but
+ * let's pad to 16 for simplicity
+ */
+ char uid[16];
+
+ /* hash/uid input
+ * if hashstr is NULL, no UID is assigned/calculated. Use macro
+ * string concatenation if multiple values need to be fed in.
+ * (This is here to not make the UID calculation independent of
+ * xref type.)
+ */
+ const char *hashstr;
+ uint32_t hashu32[2];
+
+ /* -- 32 bytes (on 64bit) -- */
+ struct xrefdata_uid_item xui;
+};
+
+static inline int xrefdata_uid_cmp(const struct xrefdata *a,
+ const struct xrefdata *b)
+{
+ return strcmp(a->uid, b->uid);
+}
+
+DECLARE_RBTREE_UNIQ(xrefdata_uid, struct xrefdata, xui, xrefdata_uid_cmp);
+extern struct xrefdata_uid_head xrefdata_uid;
+
+/* linker "magic" is used to create an array of pointers to struct xref.
+ * the result is a contiguous block of pointers, each pointing to an xref
+ * somewhere in the code. The linker gives us start and end pointers, we
+ * stuff those into the struct below and hook up a constructor to run at
+ * program startup with the struct passed.
+ *
+ * Placing the xrefs themselves into an array doesn't work because they'd
+ * need to be constant size, but we're embedding struct xref into other
+ * container structs with extra data. Also this means that external code
+ * (like the python xref dumper) can safely ignore extra data at the end of
+ * xrefs without needing to account for size in iterating the array.
+ *
+ * If you're curious, this is also how __attribute__((constructor)) (and
+ * destructor) are implemented - there are 2 arrays, ".init_array" and
+ * ".fini_array", containing function pointers. The magic turns out to be
+ * quite mundane, actually ;)
+ *
+ * The slightly tricky bit is that this is a per-object (i.e. per shared
+ * library & daemon) thing and we need a bit of help (in XREF_SETUP) to
+ * initialize correctly.
+ */
+
+struct xref_block {
+ struct xref_block *next;
+ const struct xref * const *start;
+ const struct xref * const *stop;
+};
+
+extern struct xref_block *xref_blocks;
+extern void xref_block_add(struct xref_block *block);
+extern void xref_gcc_workaround(const struct xref *xref);
+
+#ifndef HAVE_SECTION_SYMS
+/* we have a build system patch to use GNU ld on Solaris; if that doesn't
+ * work we end up on Solaris ld which doesn't support the section start/end
+ * symbols.
+ */
+#define XREF_SETUP() \
+ CPP_NOTICE("Missing linker support for section arrays. Solaris ld?")
+#else
+/* the actual symbols that the linker provides for us. Note these are
+ * _symbols_ referring to the actual section start/end, i.e. they are very
+ * much NOT _pointers_, rather the symbol *value* is the pointer. Declaring
+ * them as size-1 arrays is the "best" / "right" thing.
+ */
+extern const struct xref * const __start_xref_array[1] DSO_LOCAL;
+extern const struct xref * const __stop_xref_array[1] DSO_LOCAL;
+
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+/* no redzone around each of the xref_p please, we're building an array out
+ * of variables here. kinda breaks things if there's redzones between each
+ * array item.
+ */
+#define xref_array_attr used, section("xref_array"), no_sanitize("address")
+#endif
+#endif
+#ifndef xref_array_attr
+#define xref_array_attr used, section("xref_array")
+#endif
+
+/* this macro is invoked once for each standalone DSO through
+ * FRR_MODULE_SETUP \
+ * }-> FRR_COREMOD_SETUP -> XREF_SETUP
+ * FRR_DAEMON_INFO /
+ */
+#define XREF_SETUP() \
+ static const struct xref _dummy_xref = { \
+ /* .xrefdata = */ NULL, \
+ /* .type = */ XREFT_NONE, \
+ /* .line = */ __LINE__, \
+ /* .file = */ __FILE__, \
+ /* .func = */ "dummy", \
+ }; \
+ static const struct xref * const _dummy_xref_p \
+ __attribute__((xref_array_attr)) = &_dummy_xref; \
+ static void __attribute__((used, _CONSTRUCTOR(1100))) \
+ _xref_init(void) { \
+ static struct xref_block _xref_block = { \
+ .next = NULL, \
+ .start = __start_xref_array, \
+ .stop = __stop_xref_array, \
+ }; \
+ xref_block_add(&_xref_block); \
+ } \
+ asm(XREF_NOTE); \
+ MACRO_REQUIRE_SEMICOLON() /* end */
+
+/* the following blurb emits an ELF note indicating start and end of the xref
+ * array in the binary. This is technically the "correct" entry point for
+ * external tools reading xrefs out of an ELF shared library or executable.
+ *
+ * right now, the extraction tools use the section header for "xref_array"
+ * instead; however, section headers are technically not necessarily preserved
+ * for fully linked libraries or executables. (In practice they are only
+ * stripped by obfuscation tools.)
+ *
+ * conversely, for reading xrefs out of a single relocatable object file (e.g.
+ * bar.o), section headers are the right thing to look at since the note is
+ * only emitted for the final binary once.
+ *
+ * FRR itself does not need this note to operate correctly, so if you have
+ * some build issue with it just add -DFRR_XREF_NO_NOTE to your build flags
+ * to disable it.
+ */
+#if defined(FRR_XREF_NO_NOTE) || defined(__mips64)
+#define XREF_NOTE ""
+
+/* mips64 note: MIPS64 (regardless of endianness, both mips64 & mips64el)
+ * does not have a 64-bit PC-relative relocation type. Unfortunately, a
+ * 64-bit PC-relative relocation is exactly what the below asm magic emits.
+ * Therefore, the xref ELF note is permanently disabled on MIPS64.
+ *
+ * For some context, refer to https://reviews.llvm.org/D80390
+ *
+ * As noted above, xref extraction still works through the section header
+ * path, so no functionality is lost.
+ */
+#else
+
+#if __SIZEOF_POINTER__ == 4
+#define _NOTE_2PTRSIZE "8"
+#define _NOTE_PTR ".long"
+#elif __SIZEOF_POINTER__ == 8
+#define _NOTE_2PTRSIZE "16"
+#define _NOTE_PTR ".quad"
+#else
+#error unsupported pointer size
+#endif
+
+#ifdef __arm__
+# define asmspecial "%"
+#else
+# define asmspecial "@"
+#endif
+
+#define XREF_NOTE \
+ "" "\n"\
+ " .type _frr_xref_note," asmspecial "object" "\n"\
+ " .pushsection .note.FRR,\"a\"," asmspecial "note" "\n"\
+ " .p2align 2" "\n"\
+ "_frr_xref_note:" "\n"\
+ " .long 9" "\n"\
+ " .long " _NOTE_2PTRSIZE "\n"\
+ " .ascii \"XREF\"" "\n"\
+ " .ascii \"FRRouting\\0\\0\\0\"" "\n"\
+ " " _NOTE_PTR " __start_xref_array-." "\n"\
+ " " _NOTE_PTR " __stop_xref_array-." "\n"\
+ " .size _frr_xref_note, .-_frr_xref_note" "\n"\
+ " .popsection" "\n"\
+ "" "\n"\
+ /* end */
+#endif
+
+#endif /* HAVE_SECTION_SYMS */
+
+/* emit the array entry / pointer to xref */
+#if defined(__clang__) || !defined(__cplusplus)
+#define XREF_LINK(dst) \
+ static const struct xref * const NAMECTR(xref_p_) \
+ __attribute__((xref_array_attr)) \
+ = &(dst) \
+ /* end */
+
+#else /* GCC && C++ */
+/* workaround for GCC bug 41091 (dated 2009), added in 2021...
+ *
+ * this breaks extraction of xrefs with xrelfo.py (because the xref_array
+ * entry will be missing), but provides full runtime functionality. To get
+ * the proper list of xrefs from C++ code, build with clang...
+ */
+struct _xref_p {
+ const struct xref * const ptr;
+
+ _xref_p(const struct xref *_ptr) : ptr(_ptr)
+ {
+ xref_gcc_workaround(_ptr);
+ }
+};
+
+#define XREF_LINK(dst) \
+ static const struct _xref_p __attribute__((used)) \
+ NAMECTR(xref_p_)(&(dst)) \
+ /* end */
+#endif
+
+/* initializer for a "struct xref" */
+#define XREF_INIT(type_, xrefdata_, func_) \
+ { \
+ /* .xrefdata = */ (xrefdata_), \
+ /* .type = */ (type_), \
+ /* .line = */ __LINE__, \
+ /* .file = */ __FILE__, \
+ /* .func = */ func_, \
+ } \
+ /* end */
+
+/* use with XREF_INIT when outside of a function, i.e. no __func__ */
+#define XREF_NO_FUNC "<global>"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_XREF_H */