diff options
| -rwxr-xr-x | configure.ac | 15 | ||||
| -rw-r--r-- | lib/module.c | 2 | ||||
| -rw-r--r-- | lib/module.h | 8 | ||||
| -rw-r--r-- | lib/subdir.am | 3 | ||||
| -rw-r--r-- | lib/xref.c | 122 | ||||
| -rw-r--r-- | lib/xref.h | 231 | 
6 files changed, 380 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index f82aa7a2a4..09ec23ab76 100755 --- a/configure.ac +++ b/configure.ac @@ -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 ---------- diff --git a/lib/module.c b/lib/module.c index 14d5cfd44f..3d299a6a2e 100644 --- a/lib/module.c +++ b/lib/module.c @@ -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"))); diff --git a/lib/module.h b/lib/module.h index 79cf52d75a..5d8d9cfbcc 100644 --- a/lib/module.h +++ b/lib/module.h @@ -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; diff --git a/lib/subdir.am b/lib/subdir.am index 570e0c3d28..d5ffa08546 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -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 index 0000000000..eb5e8ed2c2 --- /dev/null +++ b/lib/xref.c @@ -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 index 0000000000..8cfe6a3b38 --- /dev/null +++ b/lib/xref.h @@ -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 */  | 
