Skip to content

Commit

Permalink
log: Remove linker 'magic' and just use static callsites.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrissie-c committed Sep 10, 2018
1 parent 3af6104 commit 4a414f4
Show file tree
Hide file tree
Showing 6 changed files with 1 addition and 421 deletions.
139 changes: 0 additions & 139 deletions configure.ac
Expand Up @@ -658,145 +658,6 @@ fi
AM_CONDITIONAL(HAVE_SLOW_TESTS, [test "x${enable_slow_tests}" = xyes])
AC_SUBST(HAVE_SLOW_TESTS)

# --- callsite sections ---
if test "x${GCC}" = xyes; then
AX_SAVE_FLAGS
AC_MSG_CHECKING([whether GCC supports __attribute__((section()) + ld supports orphan sections])
if test "x${ac_cv_link_attribute_section}" = x ; then
LDFLAGS="${LDFLAGS_save} -shared -fPIC" # we are compiling shared lib
AC_LINK_IFELSE(
[AC_LANG_SOURCE(
[[#include <assert.h>
extern int __start___verbose[], __stop___verbose[];
int test(void) {
static int my_var __attribute__((section("__verbose"))) = 3;
assert("L:non-empty data section"
&& __start___verbose != __stop___verbose);
assert("L:no data section value loss"
&& my_var == 3 /* for 2.29.1+ */);
return *((int *) __start___verbose); }]]
)],
[gcc_has_attribute_section=yes; cp "conftest${ac_exeext}" "conftest.so"],
[gcc_has_attribute_section=no]
)
AX_RESTORE_FLAGS
else
gcc_has_attribute_section=${ac_cv_link_attribute_section}
fi
AC_MSG_RESULT($gcc_has_attribute_section)

# in the failing case (e.g. with ld from binutils 2.29), it's likely the
# following will fail readily in linkage (hidden symbol `__stop___verbose'
# in conftest is referenced by DSO), but keep the sensible test
# (in-executable symbol is expected to be propagated into the library,
# and to draw the full circle back to the executable through standard
# return value passing (respectively no-exec probing to spot the issue);
# -rpath passed because LD_LIBRARY_PATH exporting is unwieldy here);
# moreover, "my_var" == 3 assertion above (respectively checking if the
# boundary symbol visibility differs from DEFAULT in readelf output) is
# necessary so that binutils 2.29.1+ will not slip other parts of the
# overall is-workaround-needed harness, as it restored some (but not
# all) of the original behaviour, but the workaround is still provably
# needed
if test "x${gcc_has_attribute_section}" = xyes; then
AC_MSG_CHECKING([whether linker emits global boundary symbols for orphan sections])
LIBS="${LIBS} -L. -l:conftest${shrext_cmds} -Wl,-rpath=$(pwd)"
dnl could be turned to AC_TRY_RUN (first assertion is equivalent to
dnl the further check in action-if-true), but that would prevent
dnl cross-building
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[[#include <assert.h>
extern int __start___verbose[], __stop___verbose[];
int test(void);]],
[[static int my_var __attribute__((section("__verbose"))) = 5;
assert("E:non-empty data section"
&& __start___verbose != __stop___verbose);
assert("E:no data section value loss"
&& my_var == test() /*5?*/);]]
)],
[# alternatively something like (but requires number parsing):
# readelf -SW "conftest${ac_exeext}" \
# | sed -n '/__verbose/s/^[[:space:]]*//p' | tr -s ' ' | cut -d" " -f6
verbose_start_addr=$(${NM} -g --portability -- "conftest${ac_exeext}" \
| grep __start___verbose | cut -d" " -f 3)
verbose_stop_addr=$(${NM} -g --portability -- "conftest${ac_exeext}" \
| grep __stop___verbose | cut -d" " -f 3)
test "${verbose_start_addr}" = "${verbose_stop_addr}" \
&& gcc_has_attribute_section_visible=no \
|| { verbose_start_type=$(${READELF} -s "conftest${shrext_cmds}" \
| sed -n \
'/__start___verbose/{s/^[[[[:space:]]]]*//p;q;}' \
| tr -s ' ' \
| cut -d" " -f6)
test "${verbose_start_type}" = DEFAULT \
&& gcc_has_attribute_section_visible=yes \
|| gcc_has_attribute_section_visible=no; }],
[gcc_has_attribute_section_visible=no]
)
AX_RESTORE_FLAGS
AC_MSG_RESULT($gcc_has_attribute_section_visible)
rm -f "conftest${shrext_cmds}"

if test "x${gcc_has_attribute_section_visible}" = xno; then
# check if the linker script based workaround is
# feasible at all, otherwise fallback to using no
# section attribute while making some noise about it
# as combining with-without accustomed logging
# participants is currently uncharted waters
AC_MSG_CHECKING([whether linker workaround for orphan sections usable])
>conftest.ld cat <<-EOF
SECTIONS {
__verbose : {
__start___verbose = .;
*(__verbose);
__stop___verbose = .;
}
}
EOF
LDFLAGS="${LDFLAGS} -Wl,conftest.ld"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[[#include <assert.h>
extern int __start___verbose[], __stop___verbose[];
int test(void);]],
[[static int my_var __attribute__((section("__verbose"))) = 5;
assert("E:non-empty data section"
&& __start___verbose != __stop___verbose);
assert("E:no data section value loss"
&& my_var == 5);]]
)],
[],
[gcc_has_attribute_section=no]
)
AX_RESTORE_FLAGS
AC_MSG_RESULT([$gcc_has_attribute_section])
rm -f "conftest.ld"
fi

if test "x${gcc_has_attribute_section}" = xyes; then
AC_DEFINE([QB_HAVE_ATTRIBUTE_SECTION], 1,
[Enabling code using __attribute__((section))])
AC_SUBST([client_dlopen_LIBS],[$dlopen_LIBS])
if test "x${gcc_has_attribute_section_visible}" = xyes; then
PACKAGE_FEATURES="$PACKAGE_FEATURES attribute-section"
else
AC_DEFINE([QB_NEED_ATTRIBUTE_SECTION_WORKAROUND], 1,
[Enabling code using __attribute__((section))])
PACKAGE_FEATURES="$PACKAGE_FEATURES attribute-section-workaround"
fi
elif test "x${enable_nosection_fallback}" = xyes; then
AC_MSG_NOTICE([Falling back to not using orphan section])
else
AC_MSG_ERROR([Would use section attribute, cannot; see --enable-nosection-fallback])
fi
fi
fi
AM_CONDITIONAL(HAVE_GCC_ATTRIBUTE_SECTION, [test "x${gcc_has_attribute_section}" = xyes])
AM_CONDITIONAL(NEED_GCC_ATTRIBUTE_SECTION_WORKAROUND,
[test "x${gcc_has_attribute_section}" = xyes \
&& test "x${gcc_has_attribute_section_visible}" != xyes])

# --- ansi ---
if test "x${enable_ansi}" = xyes && \
cc_supports_flag -std=iso9899:199409 ; then
Expand Down
3 changes: 0 additions & 3 deletions include/qb/qbconfig.h.in
Expand Up @@ -27,9 +27,6 @@
/* need atomic memory barrier */
#undef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED

/* Enabling code using __attribute__((section)) */
#undef QB_HAVE_ATTRIBUTE_SECTION

/* versioning info: MAJOR, MINOR, MICRO, and REST components;
note that static compile-time info is not that useful as consulting
the respectively named members of qb_version struct constant under
Expand Down
130 changes: 0 additions & 130 deletions include/qb/qblog.h
Expand Up @@ -39,15 +39,6 @@ extern "C" {
#include <qb/qbutil.h>
#include <qb/qbconfig.h>

#if defined(QB_KILL_ATTRIBUTE_SECTION) || defined(S_SPLINT_S)
#undef QB_HAVE_ATTRIBUTE_SECTION
#endif /* defined(QB_KILL_ATTRIBUTE_SECTION) || defined(S_SPLINT_S) */

#ifdef QB_HAVE_ATTRIBUTE_SECTION
#include <assert.h> /* possibly needed for QB_LOG_INIT_DATA */
#include <dlfcn.h> /* dynamic linking: dlopen, dlsym, dladdr, ... */
#endif

/**
* @file qblog.h
* The logging API provides four main parts (basics, filtering, threading & blackbox).
Expand Down Expand Up @@ -287,119 +278,7 @@ struct qb_log_callsite {

typedef void (*qb_log_filter_fn)(struct qb_log_callsite * cs);

/* will be assigned by linker magic (assuming linker supports that):
* https://sourceware.org/binutils/docs/ld/Orphan-Sections.html
*/
#ifdef QB_HAVE_ATTRIBUTE_SECTION

#define QB_ATTR_SECTION __verbose /* conforms to C ident. */
#define QB_ATTR_SECTION_STR QB_PP_STRINGIFY(QB_ATTR_SECTION)
#define QB_ATTR_SECTION_START QB_PP_JOIN(__start_, QB_ATTR_SECTION)
#define QB_ATTR_SECTION_STOP QB_PP_JOIN(__stop_, QB_ATTR_SECTION)
#define QB_ATTR_SECTION_START_STR QB_PP_STRINGIFY(QB_ATTR_SECTION_START)
#define QB_ATTR_SECTION_STOP_STR QB_PP_STRINGIFY(QB_ATTR_SECTION_STOP)

extern struct qb_log_callsite QB_ATTR_SECTION_START[];
extern struct qb_log_callsite QB_ATTR_SECTION_STOP[];

/* Related to the next macro that is -- unlike this one -- a public API */
#ifndef _GNU_SOURCE
#define QB_NONAPI_LOG_INIT_DATA_EXTRA_(name) \
_Pragma(QB_PP_STRINGIFY(GCC warning QB_PP_STRINGIFY( \
without "_GNU_SOURCE" defined (directly or not) \
QB_LOG_INIT_DATA cannot check sanity of libqb proper \
nor of the target site originating this check alone)))
#else
#define QB_NONAPI_LOG_INIT_DATA_EXTRA_(name) \
{ Dl_info work_dli; \
/* libqb sanity (locating libqb by it's relatively unique \
non-functional symbols -- the two are mutually exclusive, the \
ordinarily latter was introduced by accident, the former is \
intentional -- due to possible confusion otherwise) */ \
if ((dladdr(dlsym(RTLD_DEFAULT, "qb_ver_str"), &work_dli) \
|| dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli)) \
&& (work_handle = dlopen(work_dli.dli_fname, \
RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
work_s1 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
work_s2 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
assert("libqb's callsite section is observable, otherwise \
libqb's build is at fault, preventing reliable logging" \
&& work_s1 != NULL && work_s2 != NULL); \
assert("libqb's callsite section is populated, otherwise \
libqb's build is at fault, preventing reliable logging" \
&& work_s1 != work_s2); \
dlclose(work_handle); } \
/* sanity of the target site originating this check alone */ \
if (dladdr(dlsym(RTLD_DEFAULT, QB_PP_STRINGIFY(name)), &work_dli) \
&& (work_handle = dlopen(work_dli.dli_fname, \
RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
work_s1 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
work_s2 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
assert("target's own callsite section observable, otherwise \
target's own linkage at fault and logging would not work reliably \
(unless QB_LOG_INIT_DATA macro used unexpectedly in no-logging context)"\
&& work_s1 != NULL && work_s2 != NULL); \
assert("target's own callsite section non-empty, otherwise \
target's own linkage at fault and logging would not work reliably \
(unless QB_LOG_INIT_DATA macro used unexpectedly in no-logging context)"\
&& work_s1 != work_s2); \
dlclose(work_handle); } }
#endif /* _GNU_SOURCE */

/**
* Optional on-demand self-check of 1/ toolchain sanity (prerequisite for
* the logging subsystem to work properly) and 2/ non-void active use of
* logging (satisfied with a justifying existence of a logging callsite as
* defined with a @c qb_logt invocation) at the target (but see below), which
* is supposedly assured by it's author(!) as of relying on this very macro
* [technically, the symbols that happen to be resolved under the respective
* identifiers do not necessarily originate in the same compilation unit as
* when it's not the end executable (or by induction, a library positioned
* earlier in the symbol lookup order) but a shared library, the former takes
* a precedence unless that site comes short of exercising the logging,
* making its callsite section empty and, in turn, without such boundary
* symbols, hence making the resolution continue further in the lookup order
* -- despite fuzzily targeted attestation, the check remains reasonable];
* only effective when link-time ("run-time amortizing") callsite collection
* is; as a side effect, it can ensure the boundary-denoting symbols for the
* target collection area are kept alive with some otherwise unkind linkers.
*
* Applying this macro in the target program/library is strongly recommended
* whenever the logging as framed by this header file is in use.
* Moreover, it's important to state that using this check while not ensuring
* @c _GNU_SOURCE macro definition is present at compile-time means only half
* of the available sanity checking will be performed, possibly resulting
* in libqb's own internally logged messages being lost without warning.
*/
#define QB_LOG_INIT_DATA(name) \
void name(void); \
void name(void) { \
void *work_handle; struct qb_log_callsite *work_s1, *work_s2; \
/* our own (target's) sanity, or possibly that of higher priority \
symbol resolution site (unless target equals end executable) \
or even the lower one if no such predecessor defines these */ \
if ((work_handle = dlopen(NULL, RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
work_s1 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
work_s2 = (struct qb_log_callsite *) \
dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
assert("implicit callsite section is observable, otherwise \
target's and/or libqb's build is at fault, preventing reliable logging" \
&& work_s1 != NULL && work_s2 != NULL); \
dlclose(work_handle); /* perhaps overly eager thing to do */ } \
QB_NONAPI_LOG_INIT_DATA_EXTRA_(name); \
/* finally, original, straightforward check */ \
assert("implicit callsite section is populated, otherwise \
target's build is at fault, preventing reliable logging" \
&& QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } \
void __attribute__ ((constructor)) name(void);
#else
#define QB_LOG_INIT_DATA(name)
#endif /* QB_HAVE_ATTRIBUTE_SECTION */

/**
* Internal function: use qb_log() or qb_logt()
Expand Down Expand Up @@ -477,21 +356,12 @@ void qb_log_from_external_source_va(const char *function,
* @param fmt usual printf style format specifiers
* @param args usual printf style args
*/
#ifdef QB_HAVE_ATTRIBUTE_SECTION
#define qb_logt(priority, tags, fmt, args...) do { \
static struct qb_log_callsite descriptor \
__attribute__((section(QB_ATTR_SECTION_STR), aligned(8))) = \
{ __func__, __FILE__, fmt, priority, __LINE__, 0, tags }; \
qb_log_real_(&descriptor, ##args); \
} while(0)
#else
#define qb_logt(priority, tags, fmt, args...) do { \
struct qb_log_callsite* descriptor_pt = \
qb_log_callsite_get(__func__, __FILE__, fmt, \
priority, __LINE__, tags); \
qb_log_real_(descriptor_pt, ##args); \
} while(0)
#endif /* QB_HAVE_ATTRIBUTE_SECTION */


/**
Expand Down
32 changes: 0 additions & 32 deletions lib/Makefile.am
Expand Up @@ -79,9 +79,6 @@ EXTRA_DIST = qblog_script.ld.in qblog_script.la.in qblog_script_noop.ld
libqb_la_SOURCES = $(source_to_lint) unix.c
libqb_la_CFLAGS = $(PTHREAD_CFLAGS)
libqb_la_LIBADD = $(LTLIBOBJS) $(dlopen_LIBS) $(PTHREAD_LIBS) $(socket_LIBS)
if NEED_GCC_ATTRIBUTE_SECTION_WORKAROUND
libqb_la_LIBADD += qblog_script.la
endif

AM_LDFLAGS = $(LDFLAGS_COPY:-Bsymbolic-functions=)

Expand Down Expand Up @@ -112,35 +109,6 @@ qblog_script.ld: %.ld: %.ld.in
pkgconfigexecdir = $(libdir)/pkgconfig
pkgconfigexec_DATA = libqb.pc

# find the libqb.so symlink's target, if so, try to find out, iteratively,
# its gradually shorter forms that likewise symlinks the same target as the
# original libqb.so path, point to that file from the linker script using
# qblog_script.ld as a template, storing result in place of original libqb.so
# (e.g., libqb.so := "INPUT(libqb.so.0) " [...] "SECTIONS { " [...] "}")
# NOTE: readlink nor realpath are POSIX; not chained links ready
# NOTE: conservative check, i.e., not per NEED_GCC_ATTRIBUTE_SECTION_WORKAROUND
if HAVE_GCC_ATTRIBUTE_SECTION
install-exec-hook: qblog_script.ld
target=$$(ls -l "$(DESTDIR)$(libdir)/libqb.so" || :); \
target=$${target#* -> }; t1_bn=$$(basename "$${target}" || :); \
while test -n "$${t1_bn}"; do t2_bn=$${t1_bn%.*[0-9]*}; \
test "$${t2_bn}" != libqb.so || break; \
test -L "$${t2_bn}" || { t1_bn=$${t2_bn}; continue; }; \
t2_target=$$(ls -l "$${t2_bn}" || break); t2_target=$${t2_target#* -> }; \
test "$${t2_target}" = "$${target}" || break; \
t1_bn=$${t2_bn}; done; test -n "$${t1_bn}" || \
{ echo "only applicable to SO symlink scheme"; exit 1; }; \
echo "$${t1_bn}" > "$(DESTDIR)$(libdir)/libqb.so-t"
so_ver=$$(cat "$(DESTDIR)$(libdir)/libqb.so-t"); \
echo "INPUT($${so_ver})" > "$(DESTDIR)$(libdir)/libqb.so-t"; \
cat $< >> "$(DESTDIR)$(libdir)/libqb.so-t"; \
sed -i -- "s/libqb.so.<digit>/$${so_ver}/" \
"$(DESTDIR)$(libdir)/libqb.so-t" "$(DESTDIR)$(pkgconfigexecdir)/libqb.pc"
mv -f "$(DESTDIR)$(libdir)/libqb.so-t" "$(DESTDIR)$(libdir)/libqb.so"
rm -f "$(DESTDIR)$(pkgconfigexecdir)/libqb.pc--"
rm -f "$(DESTDIR)$(libdir)/libqb.so-t--"
endif

if HAVE_SPLINT
check_SCRIPTS = run_splint.sh
TESTS = $(check_SCRIPTS)
Expand Down
13 changes: 1 addition & 12 deletions lib/libqb.pc.in
Expand Up @@ -7,17 +7,6 @@ Name: libqb
Version: @PACKAGE_VERSION@
Description: libqb
Requires:
Libs: -L${libdir} -lqb @client_dlopen_LIBS@
# NOTE: If -lqb not usable for linking (e.g. linker not compatible with
# linker scripts ad-hoc modifying output sections), try recent
# ld.bfd/binutils linker first when available, otherwise you can
# try "-l:libqb.so.<digit>" link switch that bypasses said linker
# script -- but beware, logging may be less efficient and may lack
# possible future optimizations and extra features. Consequently,
# logging issues (typically bound to QB_LOG_INIT_DATA macro) can be
# mitigated with QB_KILL_ATTRIBUTE_SECTION macro defined for a build.
# NOTE: when concerned about a warning coming from the build process like
# warning: [...]libqb.so contains output sections; did you forget -T?
# and the build finishes OK, take it merely as a harmless side-effect
Libs: -L${libdir} -lqb
Libs.private: @LIBS@
Cflags: -I${includedir}

0 comments on commit 4a414f4

Please sign in to comment.