]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: "xref" identifier infrastructure
authorDavid Lamparter <equinox@opensourcerouting.org>
Thu, 9 Aug 2018 20:50:19 +0000 (22:50 +0200)
committerDavid Lamparter <equinox@diac24.net>
Mon, 1 Feb 2021 16:18:02 +0000 (17:18 +0100)
This adds the machinery for cross reference points (hence "xref") for
things to be annotated with source code location or other metadata
and/or to be uniquely identified and found at runtime or by dissecting
executable files.

The extraction tool to walk down an ELF file is done and working but
needs some more cleanup and will be added in a separate commit.

Signed-off-by: David Lamparter <equinox@diac24.net>
configure.ac
lib/module.c
lib/module.h
lib/subdir.am
lib/xref.c [new file with mode: 0644]
lib/xref.h [new file with mode: 0644]

index f82aa7a2a4eabe6d33b516adcdda5fc277d47780..09ec23ab7670186cd8f1c0b0a02350359bc60a71 100755 (executable)
@@ -411,6 +411,21 @@ else
   ])
 fi
 
+AC_MSG_CHECKING([whether linker supports __start/stop_section symbols])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <stdio.h>
+int __attribute__((section("secttest"))) var = 1;
+extern int __start_secttest, __stop_secttest;
+]], [[
+  void *a = &var, *b = &__start_secttest, *c = &__stop_secttest;
+  printf("%p %p %p\n", a, b, c);
+]])], [
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_SECTION_SYMS, 1, [have __start/stop_section symbols])
+], [
+  AC_MSG_RESULT(no)
+])
+
 dnl ----------
 dnl Essentials
 dnl ----------
index 14d5cfd44ff0d73f08fd72ce1e7ad94652f19b3f..3d299a6a2e0d682bbd819a155fbd3992bf98432e 100644 (file)
@@ -43,6 +43,8 @@ union _frrmod_runtime_u frrmod_default = {
                },
 };
 
+XREF_SETUP()
+
 // if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE)
 // union _frrmod_runtime_u _frrmod_this_module
 //     __attribute__((weak, alias("frrmod_default")));
index 79cf52d75aa6a74529427152578debc300a0f757..5d8d9cfbcc2126625555f7c2815cca095ca41742 100644 (file)
@@ -20,6 +20,9 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#include "compiler.h"
+#include "xref.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -75,7 +78,10 @@ extern union _frrmod_runtime_u _frrmod_this_module;
        DSO_LOCAL union _frrmod_runtime_u _frrmod_this_module = {{             \
                NULL,                                                          \
                &_frrmod_info,                                                 \
-       }};
+       }};                                                                    \
+       XREF_SETUP()                                                           \
+       /* end */
+
 #define FRR_MODULE_SETUP(...)                                                  \
        FRR_COREMOD_SETUP(__VA_ARGS__)                                         \
        DSO_SELF struct frrmod_runtime *frr_module = &_frrmod_this_module.r;
index 570e0c3d287ea64af8b0e974477cd5838d5486a0..d5ffa08546074ed39c4640563b5f322179587833 100644 (file)
@@ -103,6 +103,7 @@ lib_libfrr_la_SOURCES = \
        lib/vty.c \
        lib/wheel.c \
        lib/workqueue.c \
+       lib/xref.c \
        lib/yang.c \
        lib/yang_translator.c \
        lib/yang_wrappers.c \
@@ -268,6 +269,7 @@ pkginclude_HEADERS += \
        lib/vxlan.h \
        lib/wheel.h \
        lib/workqueue.h \
+       lib/xref.h \
        lib/yang.h \
        lib/yang_translator.h \
        lib/yang_wrappers.h \
@@ -411,6 +413,7 @@ lib_clippy_CFLAGS = $(PYTHON_CFLAGS)
 lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS)
 lib_clippy_LDFLAGS = -export-dynamic
 lib_clippy_SOURCES = \
+       lib/jhash.c \
        lib/clippy.c \
        lib/command_graph.c \
        lib/command_lex.l \
diff --git a/lib/xref.c b/lib/xref.c
new file mode 100644 (file)
index 0000000..eb5e8ed
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017-20  David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include <inttypes.h>
+
+#include "xref.h"
+#include "vty.h"
+#include "jhash.h"
+#include "sha256.h"
+#include "memory.h"
+#include "hash.h"
+
+struct xref_block *xref_blocks;
+static struct xref_block **xref_block_last = &xref_blocks;
+
+static void base32(uint8_t **inpos, int *bitpos,
+                  char *out, size_t n_chars)
+{
+       static const char base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
+
+       char *opos = out;
+       uint8_t *in = *inpos;
+       int bp = *bitpos;
+
+       while (opos < out + n_chars) {
+               uint32_t bits = in[0] | (in[1] << 8);
+
+               if (bp == -1)
+                       bits |= 0x10;
+               else
+                       bits >>= bp;
+
+               *opos++ = base32ch[bits & 0x1f];
+
+               bp += 5;
+               if (bp >= 8)
+                       in++, bp -= 8;
+       }
+       *opos = '\0';
+       *inpos = in;
+       *bitpos = bp;
+}
+
+void xref_block_add(struct xref_block *block)
+{
+       const struct xref * const *xrefp;
+       SHA256_CTX sha;
+
+       *xref_block_last = block;
+       xref_block_last = &block->next;
+
+       for (xrefp = block->start; xrefp < block->stop; xrefp++) {
+               const struct xref *xref = *xrefp;
+               struct xrefdata *xrefdata;
+
+               const char *filename, *p, *q;
+               uint8_t hash[32], *h = hash;
+               uint32_t be_val;
+               int bitpos;
+
+               if (!xref || !xref->xrefdata)
+                       continue;
+
+               xrefdata = xref->xrefdata;
+               xrefdata->xref = xref;
+
+               if (!xrefdata->hashstr)
+                       continue;
+
+               /* as far as the unique ID is concerned, only use the last
+                * directory name + filename, e.g. "bgpd/bgp_route.c".  This
+                * gives a little leeway in moving things and avoids IDs being
+                * screwed up by out of tree builds or absolute pathnames.
+                */
+               filename = xref->file;
+               p = strrchr(filename, '/');
+               if (p) {
+                       q = memrchr(filename, '/', p - filename);
+                       if (q)
+                               filename = q + 1;
+                       else
+                               filename = p + 1;
+               }
+
+               SHA256_Init(&sha);
+               SHA256_Update(&sha, filename, strlen(filename));
+               SHA256_Update(&sha, xrefdata->hashstr,
+                             strlen(xrefdata->hashstr));
+               be_val = htonl(xrefdata->hashu32[0]);
+               SHA256_Update(&sha, &be_val, sizeof(be_val));
+               be_val = htonl(xrefdata->hashu32[1]);
+               SHA256_Update(&sha, &be_val, sizeof(be_val));
+               SHA256_Final(hash, &sha);
+
+               bitpos = -1;
+               base32(&h, &bitpos, &xrefdata->uid[0], 5);
+               xrefdata->uid[5] = '-';
+               base32(&h, &bitpos, &xrefdata->uid[6], 5);
+       }
+}
diff --git a/lib/xref.h b/lib/xref.h
new file mode 100644 (file)
index 0000000..8cfe6a3
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2017-20  David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_XREF_H
+#define _FRR_XREF_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include "compiler.h"
+
+enum xref_type {
+       XREFT_NONE = 0,
+};
+
+/* 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 */
+};
+
+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) -- */
+};
+
+/* 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);
+
+#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;
+
+/* 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 = {                               \
+                       .file = __FILE__, .line = __LINE__, .func = "dummy",   \
+                       .type = XREFT_NONE,                                    \
+       };                                                                     \
+       static const struct xref * const _dummy_xref_p                         \
+                       __attribute__((used, section("xref_array")))           \
+                       = &_dummy_xref;                                        \
+       static void __attribute__((used, _CONSTRUCTOR(1100)))                  \
+                       _xref_init(void) {                                     \
+               static struct xref_block _xref_block = {                       \
+                       .start = __start_xref_array,                           \
+                       .stop = __stop_xref_array,                             \
+               };                                                             \
+               xref_block_add(&_xref_block);                                  \
+       }                                                                      \
+       asm(XREF_NOTE);                                                        \
+       /* 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.
+ */
+#ifdef FRR_XREF_NO_NOTE
+#define XREF_NOTE ""
+#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 */
+#define XREF_LINK(dst)                                                         \
+       static const struct xref * const NAMECTR(xref_p_)                      \
+                       __attribute__((used, section("xref_array")))           \
+               = &(dst)                                                       \
+       /* end */
+
+/* initializer for a "struct xref" */
+#define XREF_INIT(type_, xrefdata_, func_)                                     \
+       {                                                                      \
+               .type = (type_), .xrefdata = (xrefdata_),                      \
+               .file = __FILE__, .line = __LINE__, .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 */