Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binutils 2.29 followup #288

Closed
wants to merge 9 commits into from
125 changes: 87 additions & 38 deletions include/qb/qblog.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017 Red Hat, Inc.
* Copyright 2018 Red Hat, Inc.
*
* All rights reserved.
*
Expand Down Expand Up @@ -43,6 +43,14 @@ extern "C" {
#undef QB_HAVE_ATTRIBUTE_SECTION
#endif /* defined(QB_KILL_ATTRIBUTE_SECTION) || defined(S_SPLINT_S) */

#if defined(QB_HAVE_ATTRIBUTE_SECTION) && !defined(__GNUC__)
_Pragma(QB_PP_STRINGIFY(message (QB_PP_STRINGIFY( \
without GNU-compatible compiler (defining "__GNUC__") callsite \
section cannot be used despite being supported by the platform \
and not disabled; also QB_LOG_INIT_DATA is no-op, therefore))));
#undef QB_HAVE_ATTRIBUTE_SECTION
#endif

#ifdef QB_HAVE_ATTRIBUTE_SECTION
#include <assert.h> /* possibly needed for QB_LOG_INIT_DATA */
#include <dlfcn.h> /* dynamic linking: dlopen, dlsym, dladdr, ... */
Expand Down Expand Up @@ -83,6 +91,8 @@ extern "C" {
* discern misuse of @c QB_LOG_INIT_DATA() macro in no-logging context from
* broken callsite section handling assumptions owing to overboard fancy
* linker -- situation that the self-check aims to detect in the first place.
* See also @ref qb_log_init_data_note_linker
* "important note on consideration when combined with binutils 2.29 linker".
*
* @par Configuring log targets.
* A log target can be syslog, stderr, the blackbox, stdout, or a text file.
Expand Down Expand Up @@ -286,35 +296,83 @@ 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

/* with proper toolchain support, linker magic backs the invisible counterpart:
https://sourceware.org/binutils/docs/ld/Orphan-Sections.html
+ see also qb_logt below */

#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[];
extern struct qb_log_callsite QB_ATTR_SECTION_START[]
#if !defined(QB_NOAPI_LOG_INT) && !defined(_GNU_SOURCE) && !defined(QB_LD_2_29)
__attribute__((visibility("protected")))
#endif
;
extern struct qb_log_callsite QB_ATTR_SECTION_STOP[]
#if !defined(QB_NOAPI_LOG_INT) && !defined(_GNU_SOURCE) && !defined(QB_LD_2_29)
__attribute__((visibility("protected")))
#endif
;

/* Related to the next macro that is -- unlike this one -- a public API */
#ifndef _GNU_SOURCE
#if !defined(_GNU_SOURCE) && !defined(QB_LD_2_29)
#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)))
{ _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 \
and only superficially of the target site originating \
this check alone. \
NOT COMPATIBLE WITH BINUTILS 2.29 (2.29.1 OK)))); \
/* original, straightforward check */ \
assert("target's own callsite section is populated, otherwise \
target's build is at fault, preventing reliable logging" \
&& QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); }
#elif !defined(_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; \
{ Dl_info work_dli; dlerror(); \
/* sanity of the target site originating this check alone */ \
if (dladdr(&(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 is 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 is populated, 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); \
} else if (work_s1 != QB_ATTR_SECTION_START \
&& work_s2 != QB_ATTR_SECTION_STOP) { \
/* skip "cannot dynamically load executable" \
https://sourceware.org/bugzilla/show_bug.cgi?id=11754 */ \
assert((fprintf(stderr, "%s\n", dlerror()), \
!"can detect/dlopen/dlsym target")); \
} else { \
assert("executable's own callsite section is populated, \
otherwise target's build is at fault, preventing reliable logging" \
&& QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } \
/* 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) */ \
dlerror(); \
if ((dladdr(dlsym(RTLD_DEFAULT, "qb_ver_str"), &work_dli) \
|| dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli)) \
&& (work_handle = dlopen(work_dli.dli_fname, \
Expand All @@ -329,24 +387,9 @@ libqb's build is at fault, preventing reliable logging" \
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); } }
dlclose(work_handle); \
} else { assert((fprintf(stderr, "%s\n", dlerror()), \
!"can detect/dlopen/dlsym libqb")); } }
#endif /* _GNU_SOURCE */

/**
Expand All @@ -373,6 +416,13 @@ target's own linkage at fault and logging would not work reliably \
* @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.
*
* @anchor qb_log_init_data_note_linker
* @note When this macro is leveraged in the end executable that is moreover
* linked with ld.bfd from binutils 2.29 (2.29.1 is not of concern,
* though), make sure the respective code is compiled with at least one
* of @c _GNU_SOURCE and @c QB_LD_2_29 macros defined so as to avoid
* run-time misbehaviour of the pertaining checks.
*/
#define QB_LOG_INIT_DATA(name) \
void name(void); \
Expand All @@ -381,21 +431,20 @@ target's own linkage at fault and logging would not work reliably \
/* 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 */ \
dlerror(); \
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" \
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);
dlclose(work_handle); /* perhaps overly eager thing to do */ \
} else { assert((fprintf(stderr, "%s\n", dlerror()), \
!"can dlopen/dlsym main program")); } \
QB_NONAPI_LOG_INIT_DATA_EXTRA_(name); } \
void __attribute__((constructor)) name(void);
#else
#define QB_LOG_INIT_DATA(name)
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
Expand Down
10 changes: 8 additions & 2 deletions lib/libqb.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ Libs: -L${libdir} -lqb @client_dlopen_LIBS@
# 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
# NOTE: Conversely, when QB_LOG_INIT_DATA macro is leveraged in the end
# executable relying on this library that is moreover linked with
# ld.bfd from binutils 2.29 (2.29.1 is not of concern, though), make
# sure the respective code is compiled with at least one of _GNU_SOURCE
# and QB_LD_2_29 macros defined so as to avoid run-time misbehaviour
# of the pertaining checks.
# 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
# and the build finishes OK, take it merely as a harmless side-effect.
Libs.private: @LIBS@
Cflags: -I${includedir}
2 changes: 2 additions & 0 deletions lib/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#define QB_NOAPI_LOG_INT 1 /* preventing explicit protected visibility */

#include "os_base.h"
#include <ctype.h>
#ifdef HAVE_LINK_H
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/log.am
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2017 Red Hat, Inc.
# Copyright 2018 Red Hat, Inc.
#
# Author: Jan Pokorny <[email protected]>
#
Expand All @@ -21,7 +21,7 @@ MAINTAINERCLEANFILES = Makefile.in
CLEANFILES = log_test_client.err.real log_test_interlib_client.err.real \
../log_callsite_bench.c

AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include

noinst_PROGRAMS = log_client log_interlib_client
# cannot use {check,noinst}_LTLIBRARIES because it leads to solely static lib
Expand Down
24 changes: 19 additions & 5 deletions tests/functional/log_client.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017 Red Hat, Inc.
* Copyright 2018 Red Hat, Inc.
*
* All rights reserved.
*
Expand All @@ -20,13 +20,27 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#ifndef POSIXONLY
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#include <qb/qblog.h>

#ifndef NSELFCHECK
QB_LOG_INIT_DATA(linker_contra_log);
#endif

#ifndef NLOG
#define do_perror(msg) qb_perror(LOG_ERR, msg)
#else
#define do_perror(msg) perror(msg)
#endif

static const char *
my_tags_stringify(uint32_t tags)
{
Expand All @@ -51,7 +65,7 @@ main(int32_t argc, char *argv[])
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);

qb_log_tags_stringify_fn_set(my_tags_stringify);
qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%l:%b");
qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%b");

#if 0
printf("\n==%s consists of: %d, %d, %d, %s==\n\n", qb_ver_str,
Expand All @@ -76,14 +90,14 @@ main(int32_t argc, char *argv[])
blackbox file. */
tmpfile_fd = mkstemp(tmpfile_buf);
if (tmpfile_fd == -1) {
qb_perror(LOG_ERR, "creating temporary file");
do_perror("creating temporary file");
exit(EXIT_FAILURE);
}
unlink(tmpfile_buf);
close(tmpfile_fd);
#if 0
if (stat(tmpfile_buf, &tmpfile_stat) == -1) {
qb_perror(LOG_ERR, "stat'ing nonexistent temporary file");
do_perror("stat'ing nonexistent temporary file");
exit(EXIT_FAILURE);
}
#endif
Expand Down
24 changes: 19 additions & 5 deletions tests/functional/log_interlib.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017 Red Hat, Inc.
* Copyright 2018 Red Hat, Inc.
*
* All rights reserved.
*
Expand All @@ -20,13 +20,27 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#ifndef POSIXONLY
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#include <qb/qblog.h>

#ifndef NSELFCHECK
#ifndef NLIBSELFCHECK
QB_LOG_INIT_DATA(linker_contra_log_lib);
#endif

#ifndef NLIBLOG
#define do_perror(msg) qb_perror(LOG_ERR, msg)
#else
#define do_perror(msg) perror(msg)
#endif

void foo(void);

void
Expand Down Expand Up @@ -54,14 +68,14 @@ foo(void)
blackbox file. */
tmpfile_fd = mkstemp(tmpfile_buf);
if (tmpfile_fd == -1) {
qb_perror(LOG_ERR, "creating temporary file");
do_perror("creating temporary file");
exit(EXIT_FAILURE);
}
unlink(tmpfile_buf);
close(tmpfile_fd);
#if 0
if (stat(tmpfile_buf, &tmpfile_stat) == -1) {
qb_perror(LOG_ERR, "stat'ing nonexistent temporary file");
do_perror("stat'ing nonexistent temporary file");
exit(EXIT_FAILURE);
}
#endif
Expand Down
11 changes: 8 additions & 3 deletions tests/functional/log_interlib_client.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017 Red Hat, Inc.
* Copyright 2018 Red Hat, Inc.
*
* All rights reserved.
*
Expand All @@ -20,7 +20,12 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#ifndef POSIXONLY
#define _GNU_SOURCE
#endif

#include <stdio.h>

#include <qb/qblog.h>

#ifndef NSELFCHECK
Expand Down Expand Up @@ -49,7 +54,7 @@ main(int argc, char *argv[])
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);

qb_log_tags_stringify_fn_set(my_tags_stringify);
qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%l:%b");
qb_log_format_set(QB_LOG_STDERR, "[%5g|%p] %f:%b");

#if 0
printf("--\n");
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/log_test_client.err
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[MAIN |debug] ../log_client.c:69:hello
[libqb|error] log_blackbox.c:196:qb_log_blackbox_print_from_file:
[MAIN |debug] ../log_client.c:hello
[libqb|error] log_blackbox.c:qb_log_blackbox_print_from_file:
8 changes: 4 additions & 4 deletions tests/functional/log_test_interlib_client.err
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[MAIN |info] ../log_interlib_client.c:61:BEFORE
[MAIN |info] ../log_interlib.c:47:aloha
[libqb|error] log_blackbox.c:196:qb_log_blackbox_print_from_file:
[MAIN |info] ../log_interlib_client.c:65:AFTER
[MAIN |info] ../log_interlib_client.c:BEFORE
[MAIN |info] ../log_interlib.c:aloha
[libqb|error] log_blackbox.c:qb_log_blackbox_print_from_file:
[MAIN |info] ../log_interlib_client.c:AFTER
Loading