From 08a73c422de98eb12a5f3a86112e7a4829eb3fd6 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 2 Feb 2021 19:38:38 +0100 Subject: [PATCH] lib/xref: work around GCC bug 41091 gcc fucks up global variables with section attributes when they're used in templated C++ code. The template instantiation "magic" kinda breaks down (it's implemented through COMDAT in the linker, which clashes with the section attribute.) The workaround provides full runtime functionality, but the xref extraction tool (xrelfo.py) won't work on C++ code compiled by GCC. FWIW, clang gets this right. Signed-off-by: David Lamparter --- doc/developer/xrefs.rst | 7 +++ lib/xref.c | 106 +++++++++++++++++++++------------------- lib/xref.h | 24 +++++++++ 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/doc/developer/xrefs.rst b/doc/developer/xrefs.rst index 689a21cc1f..6a0794d41b 100644 --- a/doc/developer/xrefs.rst +++ b/doc/developer/xrefs.rst @@ -161,3 +161,10 @@ the xref array is in the file. Also note the owner is clearly marked as For SystemTap's use of ELF notes, refer to https://libstapsdt.readthedocs.io/en/latest/how-it-works/internals.html as an entry point. + +.. note:: + + Due to GCC bug 41091, the "xref_array" section is not correctly generated + for C++ code when compiled by GCC. A workaround is present for runtime + functionality, but to extract the xrefs from a C++ source file, it needs + to be built with clang (or a future fixed version of GCC) instead. diff --git a/lib/xref.c b/lib/xref.c index eb5e8ed2c2..40efe51363 100644 --- a/lib/xref.c +++ b/lib/xref.c @@ -63,60 +63,68 @@ static void base32(uint8_t **inpos, int *bitpos, *bitpos = bp; } +static void xref_add_one(const struct xref *xref) +{ + SHA256_CTX sha; + struct xrefdata *xrefdata; + + const char *filename, *p, *q; + uint8_t hash[32], *h = hash; + uint32_t be_val; + int bitpos; + + if (!xref || !xref->xrefdata) + return; + + xrefdata = xref->xrefdata; + xrefdata->xref = xref; + + if (!xrefdata->hashstr) + return; + + /* 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); +} + +void xref_gcc_workaround(const struct xref *xref) +{ + xref_add_one(xref); +} + 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); - } + for (xrefp = block->start; xrefp < block->stop; xrefp++) + xref_add_one(*xrefp); } diff --git a/lib/xref.h b/lib/xref.h index e464da4131..426ffad705 100644 --- a/lib/xref.h +++ b/lib/xref.h @@ -119,6 +119,7 @@ struct xref_block { 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 @@ -218,12 +219,35 @@ extern const struct xref * const __stop_xref_array[1] DSO_LOCAL; #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__((used, section("xref_array"))) \ = &(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_) \ { \ -- 2.39.5