mirror of
https://github.com/openharmony/third_party_elfutils.git
synced 2026-07-20 23:45:11 -04:00
debuginfod 1/2: client side
Introduce the debuginfod/ subdirectory, containing the client for a new debuginfo-over-http service, in shared-library and command-line forms. Two functions in libdwfl make calls into the client library to fetch elf/dwarf files by buildid, as a fallback. Instead of normal dynamic linking (thus pulling in a variety of curl dependencies), the libdwfl hooks use dlopen/dlsym. Server & tests coming in patch 2. Signed-off-by: Aaron Merey <amerey@redhat.com> Signed-off-by: Frank Ch. Eigler <fche@redhat.com>
This commit is contained in:
committed by
Mark Wielaard
parent
4bad7d1ead
commit
288f6b199a
@@ -1,3 +1,9 @@
|
||||
2019-10-28 Aaron Merey <amerey@redhat.com>
|
||||
|
||||
* debuginfod/: New directory for debuginfod code.
|
||||
* Makefile.am (SUBDIRS): Recurse there.
|
||||
* configure.ac (--enable-debuginfod): New flag & checks.
|
||||
|
||||
2019-08-25 Jonathon Anderson <jma14@rice.edu>
|
||||
|
||||
* configure.ac: Add new --enable-valgrind-annotations
|
||||
|
||||
@@ -29,6 +29,10 @@ pkginclude_HEADERS = version.h
|
||||
SUBDIRS = config m4 lib libelf libcpu backends libebl libdwelf libdwfl libdw \
|
||||
libasm src po doc tests
|
||||
|
||||
if DEBUGINFOD
|
||||
SUBDIRS += debuginfod
|
||||
endif
|
||||
|
||||
EXTRA_DIST = elfutils.spec GPG-KEY NOTES CONTRIBUTING \
|
||||
COPYING COPYING-GPLV2 COPYING-LGPLV3
|
||||
|
||||
|
||||
+2
-2
@@ -29,10 +29,10 @@
|
||||
## not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
EXTRA_DIST = elfutils.spec.in known-dwarf.awk 10-default-yama-scope.conf
|
||||
libelf.pc.in libdw.pc.in
|
||||
libelf.pc.in libdw.pc.in libdebuginfod.pc.in
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libelf.pc libdw.pc
|
||||
pkgconfig_DATA = libelf.pc libdw.pc libdebuginfod.pc
|
||||
|
||||
if MAINTAINER_MODE
|
||||
$(srcdir)/elfutils.spec.in: $(top_srcdir)/NEWS
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: debuginfod
|
||||
Description: elfutils library to query debuginfo files from debuginfod servers
|
||||
Version: @VERSION@
|
||||
URL: http://elfutils.org/
|
||||
|
||||
Libs: -L${libdir} -ldebuginfod
|
||||
Cflags: -I${includedir}
|
||||
+22
-1
@@ -60,6 +60,8 @@ AC_CONFIG_FILES([m4/Makefile])
|
||||
dnl The RPM spec file. We substitute a few values in the file.
|
||||
AC_CONFIG_FILES([elfutils.spec:config/elfutils.spec.in])
|
||||
|
||||
dnl debuginfo-server client & server parts.
|
||||
AC_CONFIG_FILES([debuginfod/Makefile])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
@@ -86,6 +88,8 @@ AS_IF([test "$use_locks" = yes],
|
||||
AH_TEMPLATE([USE_LOCKS], [Defined if libraries should be thread-safe.])
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, optional)
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_YACC
|
||||
AM_PROG_LEX
|
||||
@@ -567,7 +571,7 @@ AM_CONDITIONAL(STANDALONE, false)dnl Used in tests/Makefile.am, which see.
|
||||
AC_CONFIG_FILES([tests/Makefile])
|
||||
|
||||
dnl pkgconfig files
|
||||
AC_CONFIG_FILES([config/libelf.pc config/libdw.pc])
|
||||
AC_CONFIG_FILES([config/libelf.pc config/libdw.pc config/libdebuginfod.pc])
|
||||
|
||||
# Get the definitions necessary to create the Makefiles in the po
|
||||
# subdirectories. This is a small subset of the gettext rules.
|
||||
@@ -670,6 +674,22 @@ if test "$HAVE_BUNZIP2" = "no"; then
|
||||
AC_MSG_WARN([No bunzip2, needed to run make check])
|
||||
fi
|
||||
|
||||
# Look for libmicrohttpd, libcurl, libarchive, sqlite for debuginfo server
|
||||
# minimum versions as per rhel7. Single --enable-* option arranges to build
|
||||
# both client libs and server process.
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
AC_ARG_ENABLE([debuginfod], AC_HELP_STRING([--enable-debuginfod], [Build debuginfo server and client solib]))
|
||||
AS_IF([test "x$enable_debuginfod" = "xyes"], [
|
||||
AC_DEFINE([ENABLE_DEBUGINFOD],[1],[Build debuginfo-server])
|
||||
PKG_CHECK_MODULES([libmicrohttpd],[libmicrohttpd >= 0.9.33])
|
||||
PKG_CHECK_MODULES([libcurl],[libcurl >= 7.29.0])
|
||||
PKG_CHECK_MODULES([sqlite3],[sqlite3 >= 3.7.17])
|
||||
PKG_CHECK_MODULES([libarchive],[libarchive >= 3.1.2])
|
||||
], [enable_debuginfod="no"])
|
||||
AM_CONDITIONAL([DEBUGINFOD],[test "x$enable_debuginfod" = "xyes"])
|
||||
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
AC_MSG_NOTICE([
|
||||
@@ -699,6 +719,7 @@ AC_MSG_NOTICE([
|
||||
Deterministic archives by default : ${default_ar_deterministic}
|
||||
Native language support : ${USE_NLS}
|
||||
Extra Valgrind annotations : ${use_vg_annotations}
|
||||
Debuginfo client/server support : ${enable_debuginfod}
|
||||
|
||||
EXTRA TEST FEATURES (used with make check)
|
||||
have bunzip2 installed (required) : ${HAVE_BUNZIP2}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
2019-10-28 Aaron Merey <amerey@redhat.com>
|
||||
|
||||
* debuginfod-client.c: New file: debuginfod client library.
|
||||
* debuginfod.h: New file: header for same.
|
||||
* libdebuginfod.map: New file: govern its solib exports.
|
||||
* debuginfod-find.c: New file: command line frontend.
|
||||
* debuginfod-find.1, debuginfod_find_source.3,
|
||||
debuginfod_find_executable.3, debuginfod_find_debuginfo.3:
|
||||
New man pages.
|
||||
@@ -0,0 +1,114 @@
|
||||
## Makefile.am for libdebuginfod library subdirectory in elfutils.
|
||||
##
|
||||
## Process this file with automake to create Makefile.in
|
||||
##
|
||||
## Copyright (C) 2019 Red Hat, Inc.
|
||||
## This file is part of elfutils.
|
||||
##
|
||||
## This file is free software; you can redistribute it and/or modify
|
||||
## it under the terms of either
|
||||
##
|
||||
## * the GNU Lesser General Public License as published by the Free
|
||||
## Software Foundation; either version 3 of the License, or (at
|
||||
## your option) any later version
|
||||
##
|
||||
## or
|
||||
##
|
||||
## * the GNU General Public License as published by the Free
|
||||
## Software Foundation; either version 2 of the License, or (at
|
||||
## your option) any later version
|
||||
##
|
||||
## or both in parallel, as here.
|
||||
##
|
||||
## elfutils is distributed in the hope that it will be useful, but
|
||||
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
## General Public License for more details.
|
||||
##
|
||||
## You should have received copies of the GNU General Public License and
|
||||
## the GNU Lesser General Public License along with this program. If
|
||||
## not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
include $(top_srcdir)/config/eu.am
|
||||
AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
|
||||
-I$(srcdir)/../libdw -I$(srcdir)/../libdwelf
|
||||
VERSION = 1
|
||||
|
||||
# Disable eu- prefixing for artifacts (binaries & man pages) in this
|
||||
# directory, since they do not conflict with binutils tools.
|
||||
program_prefix=
|
||||
program_transform_name = s,x,x,
|
||||
|
||||
if BUILD_STATIC
|
||||
libasm = ../libasm/libasm.a
|
||||
libdw = ../libdw/libdw.a -lz $(zip_LIBS) $(libelf) $(libebl) -ldl
|
||||
libelf = ../libelf/libelf.a -lz
|
||||
libdebuginfod = ./libdebuginfod.a
|
||||
else
|
||||
libasm = ../libasm/libasm.so
|
||||
libdw = ../libdw/libdw.so
|
||||
libelf = ../libelf/libelf.so
|
||||
libdebuginfod = ./libdebuginfod.so
|
||||
endif
|
||||
libebl = ../libebl/libebl.a
|
||||
libeu = ../lib/libeu.a
|
||||
|
||||
AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw:.
|
||||
|
||||
bin_PROGRAMS = debuginfod-find
|
||||
|
||||
debuginfod_find_SOURCES = debuginfod-find.c
|
||||
debuginfod_find_LDADD = $(libeu) $(libdebuginfod)
|
||||
|
||||
noinst_LIBRARIES = libdebuginfod.a
|
||||
noinst_LIBRARIES += libdebuginfod_pic.a
|
||||
|
||||
libdebuginfod_a_SOURCES = debuginfod-client.c
|
||||
libdebuginfod_pic_a_SOURCES = debuginfod-client.c
|
||||
am_libdebuginfod_pic_a_OBJECTS = $(libdebuginfod_a_SOURCES:.c=.os)
|
||||
|
||||
pkginclude_HEADERS = debuginfod.h
|
||||
|
||||
libdebuginfod_so_LIBS = libdebuginfod_pic.a
|
||||
libdebuginfod_so_LDLIBS = $(libcurl_LIBS)
|
||||
libdebuginfod.so$(EXEEXT): $(srcdir)/libdebuginfod.map $(libdebuginfod_so_LIBS)
|
||||
$(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \
|
||||
-Wl,--soname,$@.$(VERSION) \
|
||||
-Wl,--version-script,$<,--no-undefined \
|
||||
-Wl,--whole-archive $(libdebuginfod_so_LIBS) -Wl,--no-whole-archive \
|
||||
$(libdebuginfod_so_LDLIBS)
|
||||
@$(textrel_check)
|
||||
$(AM_V_at)ln -fs $@ $@.$(VERSION)
|
||||
|
||||
install: install-am libdebuginfod.so
|
||||
$(mkinstalldirs) $(DESTDIR)$(libdir)
|
||||
$(INSTALL_PROGRAM) libdebuginfod.so $(DESTDIR)$(libdir)/libdebuginfod-$(PACKAGE_VERSION).so
|
||||
ln -fs libdebuginfod-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdebuginfod.so.$(VERSION)
|
||||
ln -fs libdebuginfod.so.$(VERSION) $(DESTDIR)$(libdir)/libdebuginfod.so
|
||||
|
||||
uninstall: uninstall-am
|
||||
rm -f $(DESTDIR)$(libdir)/libdebuginfod-$(PACKAGE_VERSION).so
|
||||
rm -f $(DESTDIR)$(libdir)/libdebuginfod.so.$(VERSION)
|
||||
rm -f $(DESTDIR)$(libdir)/libdebuginfod.so
|
||||
rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/elfutils
|
||||
|
||||
EXTRA_DIST = libdebuginfod.map
|
||||
MOSTLYCLEANFILES = $(am_libdebuginfod_pic_a_OBJECTS) libdebuginfod.so.$(VERSION)
|
||||
CLEANFILES += $(am_libdebuginfod_pic_a_OBJECTS) libdebuginfod.so
|
||||
|
||||
# automake std-options override: arrange to pass LD_LIBRARY_PATH
|
||||
installcheck-binPROGRAMS: $(bin_PROGRAMS)
|
||||
bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
|
||||
case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
|
||||
*" $$p "* | *" $(srcdir)/$$p "*) continue;; \
|
||||
esac; \
|
||||
f=`echo "$$p" | \
|
||||
sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
|
||||
for opt in --help --version; do \
|
||||
if LD_LIBRARY_PATH=$(DESTDIR)$(libdir) \
|
||||
$(DESTDIR)$(bindir)/$$f $$opt > c$${pid}_.out 2> c$${pid}_.err \
|
||||
&& test -n "`cat c$${pid}_.out`" \
|
||||
&& test -z "`cat c$${pid}_.err`"; then :; \
|
||||
else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
|
||||
done; \
|
||||
done; rm -f c$${pid}_.???; exit $$bad
|
||||
@@ -0,0 +1,665 @@
|
||||
/* Retrieve ELF / DWARF / source files from the debuginfod.
|
||||
Copyright (C) 2019 Red Hat, Inc.
|
||||
This file is part of elfutils.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify
|
||||
it under the terms of either
|
||||
|
||||
* the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at
|
||||
your option) any later version
|
||||
|
||||
or
|
||||
|
||||
* the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at
|
||||
your option) any later version
|
||||
|
||||
or both in parallel, as here.
|
||||
|
||||
elfutils is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
/* cargo-cult from libdwfl linux-kernel-modules.c */
|
||||
/* In case we have a bad fts we include this before config.h because it
|
||||
can't handle _FILE_OFFSET_BITS.
|
||||
Everything we need here is fine if its declarations just come first.
|
||||
Also, include sys/types.h before fts. On some systems fts.h is not self
|
||||
contained. */
|
||||
#ifdef BAD_FTS
|
||||
#include <sys/types.h>
|
||||
#include <fts.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "debuginfod.h"
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fts.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/limits.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* If fts.h is included before config.h, its indirect inclusions may not
|
||||
give us the right LFS aliases of these functions, so map them manually. */
|
||||
#ifdef BAD_FTS
|
||||
#ifdef _FILE_OFFSET_BITS
|
||||
#define open open64
|
||||
#define fopen fopen64
|
||||
#endif
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <fts.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* The cache_clean_interval_s file within the debuginfod cache specifies
|
||||
how frequently the cache should be cleaned. The file's st_mtime represents
|
||||
the time of last cleaning. */
|
||||
static const char *cache_clean_interval_filename = "cache_clean_interval_s";
|
||||
static const time_t cache_clean_default_interval_s = 86400; /* 1 day */
|
||||
|
||||
/* The cache_max_unused_age_s file within the debuginfod cache specifies the
|
||||
the maximum time since last access that a file will remain in the cache. */
|
||||
static const char *cache_max_unused_age_filename = "max_unused_age_s";
|
||||
static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */
|
||||
|
||||
/* Location of the cache of files downloaded from debuginfods.
|
||||
The default parent directory is $HOME, or '/' if $HOME doesn't exist. */
|
||||
static const char *cache_default_name = ".debuginfod_client_cache";
|
||||
static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR;
|
||||
|
||||
/* URLs of debuginfods, separated by url_delim.
|
||||
This env var must be set for debuginfod-client to run. */
|
||||
static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR;
|
||||
static const char *url_delim = " ";
|
||||
static const char url_delim_char = ' ';
|
||||
|
||||
/* Timeout for debuginfods, in seconds.
|
||||
This env var must be set for debuginfod-client to run. */
|
||||
static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR;
|
||||
static int server_timeout = 5;
|
||||
|
||||
/* Data associated with a particular CURL easy handle. Passed to
|
||||
the write callback. */
|
||||
struct handle_data
|
||||
{
|
||||
/* Cache file to be written to in case query is successful. */
|
||||
int fd;
|
||||
|
||||
/* URL queried by this handle. */
|
||||
char url[PATH_MAX];
|
||||
|
||||
/* This handle. */
|
||||
CURL *handle;
|
||||
|
||||
/* Pointer to handle that should write to fd. Initially points to NULL,
|
||||
then points to the first handle that begins writing the target file
|
||||
to the cache. Used to ensure that a file is not downloaded from
|
||||
multiple servers unnecessarily. */
|
||||
CURL **target_handle;
|
||||
};
|
||||
|
||||
static size_t
|
||||
debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
|
||||
{
|
||||
ssize_t count = size * nmemb;
|
||||
|
||||
struct handle_data *d = (struct handle_data*)data;
|
||||
|
||||
/* Indicate to other handles that they can abort their transfer. */
|
||||
if (*d->target_handle == NULL)
|
||||
*d->target_handle = d->handle;
|
||||
|
||||
/* If this handle isn't the target handle, abort transfer. */
|
||||
if (*d->target_handle != d->handle)
|
||||
return -1;
|
||||
|
||||
return (size_t) write(d->fd, (void*)ptr, count);
|
||||
}
|
||||
|
||||
/* Create the cache and interval file if they do not already exist.
|
||||
Return 0 if cache and config file are initialized, otherwise return
|
||||
the appropriate error code. */
|
||||
static int
|
||||
debuginfod_init_cache (char *cache_path, char *interval_path, char *maxage_path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
/* If the cache and config file already exist then we are done. */
|
||||
if (stat(cache_path, &st) == 0 && stat(interval_path, &st) == 0)
|
||||
return 0;
|
||||
|
||||
/* Create the cache and config files as necessary. */
|
||||
if (stat(cache_path, &st) != 0 && mkdir(cache_path, 0777) < 0)
|
||||
return -errno;
|
||||
|
||||
int fd = -1;
|
||||
|
||||
/* init cleaning interval config file. */
|
||||
fd = open(interval_path, O_CREAT | O_RDWR, 0666);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (dprintf(fd, "%ld", cache_clean_default_interval_s) < 0)
|
||||
return -errno;
|
||||
|
||||
/* init max age config file. */
|
||||
if (stat(maxage_path, &st) != 0
|
||||
&& (fd = open(maxage_path, O_CREAT | O_RDWR, 0666)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (dprintf(fd, "%ld", cache_default_max_unused_age_s) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Delete any files that have been unmodied for a period
|
||||
longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */
|
||||
static int
|
||||
debuginfod_clean_cache(char *cache_path, char *interval_path, char *max_unused_path)
|
||||
{
|
||||
struct stat st;
|
||||
FILE *interval_file;
|
||||
FILE *max_unused_file;
|
||||
|
||||
if (stat(interval_path, &st) == -1)
|
||||
{
|
||||
/* Create new interval file. */
|
||||
interval_file = fopen(interval_path, "w");
|
||||
|
||||
if (interval_file == NULL)
|
||||
return -errno;
|
||||
|
||||
int rc = fprintf(interval_file, "%ld", cache_clean_default_interval_s);
|
||||
fclose(interval_file);
|
||||
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Check timestamp of interval file to see whether cleaning is necessary. */
|
||||
time_t clean_interval;
|
||||
interval_file = fopen(interval_path, "r");
|
||||
if (fscanf(interval_file, "%ld", &clean_interval) != 1)
|
||||
clean_interval = cache_clean_default_interval_s;
|
||||
fclose(interval_file);
|
||||
|
||||
if (time(NULL) - st.st_mtime < clean_interval)
|
||||
/* Interval has not passed, skip cleaning. */
|
||||
return 0;
|
||||
|
||||
/* Read max unused age value from config file. */
|
||||
time_t max_unused_age;
|
||||
max_unused_file = fopen(max_unused_path, "r");
|
||||
if (max_unused_file)
|
||||
{
|
||||
if (fscanf(max_unused_file, "%ld", &max_unused_age) != 1)
|
||||
max_unused_age = cache_default_max_unused_age_s;
|
||||
fclose(max_unused_file);
|
||||
}
|
||||
else
|
||||
max_unused_age = cache_default_max_unused_age_s;
|
||||
|
||||
char * const dirs[] = { cache_path, NULL, };
|
||||
|
||||
FTS *fts = fts_open(dirs, 0, NULL);
|
||||
if (fts == NULL)
|
||||
return -errno;
|
||||
|
||||
FTSENT *f;
|
||||
while ((f = fts_read(fts)) != NULL)
|
||||
{
|
||||
switch (f->fts_info)
|
||||
{
|
||||
case FTS_F:
|
||||
/* delete file if max_unused_age has been met or exceeded. */
|
||||
/* XXX consider extra effort to clean up old tmp files */
|
||||
if (time(NULL) - f->fts_statp->st_atime >= max_unused_age)
|
||||
unlink (f->fts_path);
|
||||
break;
|
||||
|
||||
case FTS_DP:
|
||||
/* Remove if empty. */
|
||||
(void) rmdir (f->fts_path);
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
fts_close(fts);
|
||||
|
||||
/* Update timestamp representing when the cache was last cleaned. */
|
||||
utime (interval_path, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_BUILD_ID_BYTES 64
|
||||
|
||||
|
||||
/* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
|
||||
with the specified build-id, type (debuginfo, executable or source)
|
||||
and filename. filename may be NULL. If found, return a file
|
||||
descriptor for the target, otherwise return an error code. */
|
||||
static int
|
||||
debuginfod_query_server (const unsigned char *build_id,
|
||||
int build_id_len,
|
||||
const char *type,
|
||||
const char *filename,
|
||||
char **path)
|
||||
{
|
||||
char *urls_envvar;
|
||||
char *server_urls;
|
||||
char cache_path[PATH_MAX];
|
||||
char maxage_path[PATH_MAX*3]; /* These *3 multipliers are to shut up gcc -Wformat-truncation */
|
||||
char interval_path[PATH_MAX*4];
|
||||
char target_cache_dir[PATH_MAX*2];
|
||||
char target_cache_path[PATH_MAX*4];
|
||||
char target_cache_tmppath[PATH_MAX*5];
|
||||
char suffix[PATH_MAX*2];
|
||||
char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
|
||||
|
||||
/* Copy lowercase hex representation of build_id into buf. */
|
||||
if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
|
||||
(build_id_len == 0 &&
|
||||
sizeof(build_id_bytes) > MAX_BUILD_ID_BYTES*2 + 1))
|
||||
return -EINVAL;
|
||||
if (build_id_len == 0) /* expect clean hexadecimal */
|
||||
strcpy (build_id_bytes, (const char *) build_id);
|
||||
else
|
||||
for (int i = 0; i < build_id_len; i++)
|
||||
sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]);
|
||||
|
||||
if (filename != NULL)
|
||||
{
|
||||
if (filename[0] != '/') // must start with /
|
||||
return -EINVAL;
|
||||
|
||||
/* copy the filename to suffix, s,/,#,g */
|
||||
unsigned q = 0;
|
||||
for (unsigned fi=0; q < PATH_MAX-1; fi++)
|
||||
switch (filename[fi])
|
||||
{
|
||||
case '\0':
|
||||
suffix[q] = '\0';
|
||||
q = PATH_MAX-1; /* escape for loop too */
|
||||
break;
|
||||
case '/': /* escape / to prevent dir escape */
|
||||
suffix[q++]='#';
|
||||
suffix[q++]='#';
|
||||
break;
|
||||
case '#': /* escape # to prevent /# vs #/ collisions */
|
||||
suffix[q++]='#';
|
||||
suffix[q++]='_';
|
||||
break;
|
||||
default:
|
||||
suffix[q++]=filename[fi];
|
||||
}
|
||||
suffix[q] = '\0';
|
||||
/* If the DWARF filenames are super long, this could exceed
|
||||
PATH_MAX and truncate/collide. Oh well, that'll teach
|
||||
them! */
|
||||
}
|
||||
else
|
||||
suffix[0] = '\0';
|
||||
|
||||
/* set paths needed to perform the query
|
||||
|
||||
example format
|
||||
cache_path: $HOME/.debuginfod_cache
|
||||
target_cache_dir: $HOME/.debuginfod_cache/0123abcd
|
||||
target_cache_path: $HOME/.debuginfod_cache/0123abcd/debuginfo
|
||||
target_cache_path: $HOME/.debuginfod_cache/0123abcd/source#PATH#TO#SOURCE ?
|
||||
*/
|
||||
|
||||
if (getenv(cache_path_envvar))
|
||||
strcpy(cache_path, getenv(cache_path_envvar));
|
||||
else
|
||||
{
|
||||
if (getenv("HOME"))
|
||||
sprintf(cache_path, "%s/%s", getenv("HOME"), cache_default_name);
|
||||
else
|
||||
sprintf(cache_path, "/%s", cache_default_name);
|
||||
}
|
||||
|
||||
/* avoid using snprintf here due to compiler warning. */
|
||||
snprintf(target_cache_dir, sizeof(target_cache_dir), "%s/%s", cache_path, build_id_bytes);
|
||||
snprintf(target_cache_path, sizeof(target_cache_path), "%s/%s%s", target_cache_dir, type, suffix);
|
||||
snprintf(target_cache_tmppath, sizeof(target_cache_tmppath), "%s.XXXXXX", target_cache_path);
|
||||
|
||||
/* XXX combine these */
|
||||
snprintf(interval_path, sizeof(interval_path), "%s/%s", cache_path, cache_clean_interval_filename);
|
||||
snprintf(maxage_path, sizeof(maxage_path), "%s/%s", cache_path, cache_max_unused_age_filename);
|
||||
int rc = debuginfod_init_cache(cache_path, interval_path, maxage_path);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
rc = debuginfod_clean_cache(cache_path, interval_path, maxage_path);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
/* If the target is already in the cache then we are done. */
|
||||
int fd = open (target_cache_path, O_RDONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
/* Success!!!! */
|
||||
if (path != NULL)
|
||||
*path = strdup(target_cache_path);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
urls_envvar = getenv(server_urls_envvar);
|
||||
if (urls_envvar == NULL || urls_envvar[0] == '\0')
|
||||
{
|
||||
rc = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (getenv(server_timeout_envvar))
|
||||
server_timeout = atoi (getenv(server_timeout_envvar));
|
||||
|
||||
/* make a copy of the envvar so it can be safely modified. */
|
||||
server_urls = strdup(urls_envvar);
|
||||
if (server_urls == NULL)
|
||||
{
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* thereafter, goto out0 on error*/
|
||||
|
||||
/* create target directory in cache if not found. */
|
||||
struct stat st;
|
||||
if (stat(target_cache_dir, &st) == -1 && mkdir(target_cache_dir, 0700) < 0)
|
||||
{
|
||||
rc = -errno;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
/* NB: write to a temporary file first, to avoid race condition of
|
||||
multiple clients checking the cache, while a partially-written or empty
|
||||
file is in there, being written from libcurl. */
|
||||
fd = mkstemp (target_cache_tmppath);
|
||||
if (fd < 0)
|
||||
{
|
||||
rc = -errno;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
/* Count number of URLs. */
|
||||
int num_urls = 0;
|
||||
for (int i = 0; server_urls[i] != '\0'; i++)
|
||||
if (server_urls[i] != url_delim_char
|
||||
&& (i == 0 || server_urls[i - 1] == url_delim_char))
|
||||
num_urls++;
|
||||
|
||||
/* Tracks which handle should write to fd. Set to the first
|
||||
handle that is ready to write the target file to the cache. */
|
||||
CURL *target_handle = NULL;
|
||||
struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls);
|
||||
|
||||
/* Initalize handle_data with default values. */
|
||||
for (int i = 0; i < num_urls; i++)
|
||||
{
|
||||
data[i].handle = NULL;
|
||||
data[i].fd = -1;
|
||||
}
|
||||
|
||||
CURLM *curlm = curl_multi_init();
|
||||
if (curlm == NULL)
|
||||
{
|
||||
rc = -ENETUNREACH;
|
||||
goto out0;
|
||||
}
|
||||
/* thereafter, goto out1 on error. */
|
||||
|
||||
char *strtok_saveptr;
|
||||
char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
|
||||
|
||||
/* Initialize each handle. */
|
||||
for (int i = 0; i < num_urls && server_url != NULL; i++)
|
||||
{
|
||||
data[i].fd = fd;
|
||||
data[i].target_handle = &target_handle;
|
||||
data[i].handle = curl_easy_init();
|
||||
|
||||
if (data[i].handle == NULL)
|
||||
{
|
||||
rc = -ENETUNREACH;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Build handle url. Tolerate both http://foo:999 and
|
||||
http://foo:999/ forms */
|
||||
char *slashbuildid;
|
||||
if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
|
||||
slashbuildid = "buildid";
|
||||
else
|
||||
slashbuildid = "/buildid";
|
||||
|
||||
if (filename) /* must start with / */
|
||||
snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s%s", server_url,
|
||||
slashbuildid, build_id_bytes, type, filename);
|
||||
else
|
||||
snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url,
|
||||
slashbuildid, build_id_bytes, type);
|
||||
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url);
|
||||
curl_easy_setopt(data[i].handle,
|
||||
CURLOPT_WRITEFUNCTION,
|
||||
debuginfod_write_callback);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_TIMEOUT, (long) server_timeout);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1);
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, "");
|
||||
curl_easy_setopt(data[i].handle, CURLOPT_USERAGENT, (void*) PACKAGE_STRING);
|
||||
|
||||
curl_multi_add_handle(curlm, data[i].handle);
|
||||
server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
|
||||
}
|
||||
|
||||
/* Query servers in parallel. */
|
||||
int still_running;
|
||||
do
|
||||
{
|
||||
CURLMcode curl_res;
|
||||
|
||||
/* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */
|
||||
curl_multi_wait(curlm, NULL, 0, 1000, NULL);
|
||||
|
||||
/* If the target file has been found, abort the other queries. */
|
||||
if (target_handle != NULL)
|
||||
for (int i = 0; i < num_urls; i++)
|
||||
if (data[i].handle != target_handle)
|
||||
curl_multi_remove_handle(curlm, data[i].handle);
|
||||
|
||||
curl_res = curl_multi_perform(curlm, &still_running);
|
||||
if (curl_res != CURLM_OK)
|
||||
{
|
||||
switch (curl_res)
|
||||
{
|
||||
case CURLM_CALL_MULTI_PERFORM: continue;
|
||||
case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break;
|
||||
default: rc = -ENETUNREACH; break;
|
||||
}
|
||||
goto out1;
|
||||
}
|
||||
} while (still_running);
|
||||
|
||||
/* Check whether a query was successful. If so, assign its handle
|
||||
to verified_handle. */
|
||||
int num_msg;
|
||||
rc = -ENOENT;
|
||||
CURL *verified_handle = NULL;
|
||||
do
|
||||
{
|
||||
CURLMsg *msg;
|
||||
|
||||
msg = curl_multi_info_read(curlm, &num_msg);
|
||||
if (msg != NULL && msg->msg == CURLMSG_DONE)
|
||||
{
|
||||
if (msg->data.result != CURLE_OK)
|
||||
{
|
||||
/* Unsucessful query, determine error code. */
|
||||
switch (msg->data.result)
|
||||
{
|
||||
case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN
|
||||
case CURLE_URL_MALFORMAT: rc = -EINVAL; break;
|
||||
case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break;
|
||||
case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break;
|
||||
case CURLE_WRITE_ERROR: rc = -EIO; break;
|
||||
case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break;
|
||||
case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break;
|
||||
case CURLE_SEND_ERROR: rc = -ECONNRESET; break;
|
||||
case CURLE_RECV_ERROR: rc = -ECONNRESET; break;
|
||||
case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break;
|
||||
default: rc = -ENOENT; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Query completed without an error. Confirm that the
|
||||
response code is 200 and set verified_handle. */
|
||||
long resp_code = 500;
|
||||
CURLcode curl_res;
|
||||
|
||||
curl_res = curl_easy_getinfo(target_handle,
|
||||
CURLINFO_RESPONSE_CODE,
|
||||
&resp_code);
|
||||
|
||||
if (curl_res == CURLE_OK
|
||||
&& resp_code == 200
|
||||
&& msg->easy_handle != NULL)
|
||||
{
|
||||
verified_handle = msg->easy_handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (num_msg > 0);
|
||||
|
||||
if (verified_handle == NULL)
|
||||
goto out1;
|
||||
|
||||
/* we've got one!!!! */
|
||||
time_t mtime;
|
||||
CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
|
||||
if (curl_res != CURLE_OK)
|
||||
mtime = time(NULL); /* fall back to current time */
|
||||
|
||||
struct timeval tvs[2];
|
||||
tvs[0].tv_sec = tvs[1].tv_sec = mtime;
|
||||
tvs[0].tv_usec = tvs[1].tv_usec = 0;
|
||||
(void) futimes (fd, tvs); /* best effort */
|
||||
|
||||
/* rename tmp->real */
|
||||
rc = rename (target_cache_tmppath, target_cache_path);
|
||||
if (rc < 0)
|
||||
{
|
||||
rc = -errno;
|
||||
goto out1;
|
||||
/* Perhaps we need not give up right away; could retry or something ... */
|
||||
}
|
||||
|
||||
/* Success!!!! */
|
||||
for (int i = 0; i < num_urls; i++)
|
||||
curl_easy_cleanup(data[i].handle);
|
||||
|
||||
curl_multi_cleanup (curlm);
|
||||
free (data);
|
||||
free (server_urls);
|
||||
/* don't close fd - we're returning it */
|
||||
/* don't unlink the tmppath; it's already been renamed. */
|
||||
if (path != NULL)
|
||||
*path = strdup(target_cache_path);
|
||||
|
||||
return fd;
|
||||
|
||||
/* error exits */
|
||||
out1:
|
||||
for (int i = 0; i < num_urls; i++)
|
||||
curl_easy_cleanup(data[i].handle);
|
||||
|
||||
curl_multi_cleanup(curlm);
|
||||
unlink (target_cache_tmppath);
|
||||
(void) rmdir (target_cache_dir); /* nop if not empty */
|
||||
free(data);
|
||||
close (fd);
|
||||
|
||||
out0:
|
||||
free (server_urls);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* See debuginfod.h */
|
||||
int
|
||||
debuginfod_find_debuginfo (const unsigned char *build_id, int build_id_len,
|
||||
char **path)
|
||||
{
|
||||
return debuginfod_query_server(build_id, build_id_len,
|
||||
"debuginfo", NULL, path);
|
||||
}
|
||||
|
||||
|
||||
/* See debuginfod.h */
|
||||
int
|
||||
debuginfod_find_executable(const unsigned char *build_id, int build_id_len,
|
||||
char **path)
|
||||
{
|
||||
return debuginfod_query_server(build_id, build_id_len,
|
||||
"executable", NULL, path);
|
||||
}
|
||||
|
||||
/* See debuginfod.h */
|
||||
int debuginfod_find_source(const unsigned char *build_id,
|
||||
int build_id_len,
|
||||
const char *filename,
|
||||
char **path)
|
||||
{
|
||||
return debuginfod_query_server(build_id, build_id_len,
|
||||
"source", filename, path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* NB: these are thread-unsafe. */
|
||||
__attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void)
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
|
||||
/* NB: this is very thread-unsafe: it breaks other threads that are still in libcurl */
|
||||
__attribute__((destructor)) attribute_hidden void libdebuginfod_dtor(void)
|
||||
{
|
||||
/* ... so don't do this: */
|
||||
/* curl_global_cleanup(); */
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/* Command-line frontend for retrieving ELF / DWARF / source files
|
||||
from the debuginfod.
|
||||
Copyright (C) 2019 Red Hat, Inc.
|
||||
This file is part of elfutils.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
elfutils is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "printversion.h"
|
||||
#include "debuginfod.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <argp.h>
|
||||
|
||||
|
||||
/* Name and version of program. */
|
||||
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
|
||||
|
||||
/* Bug report address. */
|
||||
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
|
||||
|
||||
/* Short description of program. */
|
||||
static const char doc[] = N_("Request debuginfo-related content "
|
||||
"from debuginfods listed in $" DEBUGINFOD_URLS_ENV_VAR ".");
|
||||
|
||||
/* Strings for arguments in help texts. */
|
||||
static const char args_doc[] = N_("debuginfo BUILDID\n"
|
||||
"executable BUILDID\n"
|
||||
"source BUILDID /FILENAME");
|
||||
|
||||
/* Data structure to communicate with argp functions. */
|
||||
static struct argp argp =
|
||||
{
|
||||
NULL, NULL, args_doc, doc, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
int remaining;
|
||||
(void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_ARGS, &remaining, NULL);
|
||||
|
||||
if (argc < 2 || remaining+1 == argc) /* no arguments or at least two non-option words */
|
||||
{
|
||||
argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rc;
|
||||
char *cache_name;
|
||||
|
||||
/* Check whether FILETYPE is valid and call the appropriate
|
||||
debuginfod_find_* function. If FILETYPE is "source"
|
||||
then ensure a FILENAME was also supplied as an argument. */
|
||||
if (strcmp(argv[remaining], "debuginfo") == 0)
|
||||
rc = debuginfod_find_debuginfo((unsigned char *)argv[remaining+1], 0, &cache_name);
|
||||
else if (strcmp(argv[remaining], "executable") == 0)
|
||||
rc = debuginfod_find_executable((unsigned char *)argv[remaining+1], 0, &cache_name);
|
||||
else if (strcmp(argv[remaining], "source") == 0)
|
||||
{
|
||||
if (remaining+2 == argc || argv[3][0] != '/')
|
||||
{
|
||||
fprintf(stderr, "If FILETYPE is \"source\" then absolute /FILENAME must be given\n");
|
||||
return 1;
|
||||
}
|
||||
rc = debuginfod_find_source((unsigned char *)argv[remaining+1], 0,
|
||||
argv[remaining+2], &cache_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
fprintf(stderr, "Server query failed: %s\n", strerror(-rc));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s\n", cache_name);
|
||||
|
||||
free (cache_name);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* External declarations for the libdebuginfod client library.
|
||||
Copyright (C) 2019 Red Hat, Inc.
|
||||
This file is part of elfutils.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify
|
||||
it under the terms of either
|
||||
|
||||
* the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at
|
||||
your option) any later version
|
||||
|
||||
or
|
||||
|
||||
* the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at
|
||||
your option) any later version
|
||||
|
||||
or both in parallel, as here.
|
||||
|
||||
elfutils is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DEBUGINFOD_CLIENT_H
|
||||
#define _DEBUGINFOD_CLIENT_H 1
|
||||
|
||||
/* Names of environment variables that control the client logic. */
|
||||
#define DEBUGINFOD_URLS_ENV_VAR "DEBUGINFOD_URLS"
|
||||
#define DEBUGINFOD_CACHE_PATH_ENV_VAR "DEBUGINFOD_CACHE_PATH"
|
||||
#define DEBUGINFOD_TIMEOUT_ENV_VAR "DEBUGINFOD_TIMEOUT"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Query the urls contained in $DEBUGINFOD_URLS for a file with
|
||||
the specified type and build id. If build_id_len == 0, the
|
||||
build_id is supplied as a lowercase hexadecimal string; otherwise
|
||||
it is a binary blob of given legnth.
|
||||
|
||||
If successful, return a file descriptor to the target, otherwise
|
||||
return a posix error code. If successful, set *path to a
|
||||
strdup'd copy of the name of the same file in the cache.
|
||||
Caller must free() it later. */
|
||||
|
||||
int debuginfod_find_debuginfo (const unsigned char *build_id,
|
||||
int build_id_len,
|
||||
char **path);
|
||||
|
||||
int debuginfod_find_executable (const unsigned char *build_id,
|
||||
int build_id_len,
|
||||
char **path);
|
||||
|
||||
int debuginfod_find_source (const unsigned char *build_id,
|
||||
int build_id_len,
|
||||
const char *filename,
|
||||
char **path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _DEBUGINFOD_CLIENT_H */
|
||||
@@ -0,0 +1,7 @@
|
||||
ELFUTILS_0 { };
|
||||
ELFUTILS_0.178 {
|
||||
global:
|
||||
debuginfod_find_debuginfo;
|
||||
debuginfod_find_executable;
|
||||
debuginfod_find_source;
|
||||
} ELFUTILS_0;
|
||||
+9
-1
@@ -1,7 +1,7 @@
|
||||
## Process this file with automake to create Makefile.in
|
||||
## Configure input file for elfutils.
|
||||
##
|
||||
## Copyright (C) 1996-2001, 2002, 2005 Red Hat, Inc.
|
||||
## Copyright (C) 1996-2001, 2002, 2005, 2019 Red Hat, Inc.
|
||||
## This file is part of elfutils.
|
||||
##
|
||||
## This file is free software; you can redistribute it and/or modify
|
||||
@@ -19,3 +19,11 @@
|
||||
EXTRA_DIST = COPYING-GFDL README
|
||||
dist_man1_MANS=readelf.1 elfclassify.1
|
||||
notrans_dist_man3_MANS=elf_update.3 elf_getdata.3 elf_clone.3 elf_begin.3
|
||||
|
||||
notrans_dist_man1_MANS=
|
||||
|
||||
if DEBUGINFOD
|
||||
notrans_dist_man3_MANS += debuginfod_find_debuginfo.3 debuginfod_find_source.3 debuginfod_find_executable.3
|
||||
notrans_dist_man1_MANS += debuginfod-find.1
|
||||
endif
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
'\"! tbl | nroff \-man
|
||||
'\" t macro stdmacro
|
||||
|
||||
.de SAMPLE
|
||||
.br
|
||||
.RS 0
|
||||
.nf
|
||||
.nh
|
||||
..
|
||||
.de ESAMPLE
|
||||
.hy
|
||||
.fi
|
||||
.RE
|
||||
..
|
||||
|
||||
.TH DEBUGINFOD-FIND 1
|
||||
.SH NAME
|
||||
debuginfod-find \- request debuginfo-related data
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B debuginfod-find debuginfo \fIBUILDID\fP
|
||||
|
||||
.B debuginfod-find executable \fIBUILDID\fP
|
||||
|
||||
.B debuginfod-find source \fIBUILDID\fP \fI/FILENAME\fP
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBdebuginfod-find\fP queries one or more \fBdebuginfod\fP servers for
|
||||
debuginfo-related data. In case of a match, it saves the the
|
||||
requested file into a local cache, prints the file name to standard
|
||||
output, and exits with a success status of 0. In case of any error,
|
||||
it exits with a failure status and an error message to standard error.
|
||||
|
||||
.\" Much of the following text is duplicated with debuginfod.8
|
||||
|
||||
The debuginfod system uses buildids to identify debuginfo-related data.
|
||||
These are stored as binary notes in ELF/DWARF files, and are
|
||||
represented as lowercase hexadecimal. For example, for a program
|
||||
/bin/ls, look at the ELF note GNU_BUILD_ID:
|
||||
|
||||
.SAMPLE
|
||||
% readelf -n /bin/ls | grep -A4 build.id
|
||||
Note section [ 4] '.note.gnu.buildid' of 36 bytes at offset 0x340:
|
||||
Owner Data size Type
|
||||
GNU 20 GNU_BUILD_ID
|
||||
Build ID: 8713b9c3fb8a720137a4a08b325905c7aaf8429d
|
||||
.ESAMPLE
|
||||
|
||||
Then the hexadecimal BUILDID is simply:
|
||||
|
||||
.SAMPLE
|
||||
8713b9c3fb8a720137a4a08b325905c7aaf8429d
|
||||
.ESAMPLE
|
||||
|
||||
.SS debuginfo \fIBUILDID\fP
|
||||
|
||||
If the given buildid is known to a server, this request will result
|
||||
in a binary object that contains the customary \fB.*debug_*\fP
|
||||
sections. This may be a split debuginfo file as created by
|
||||
\fBstrip\fP, or it may be an original unstripped executable.
|
||||
|
||||
.SS executable \fIBUILDID\fP
|
||||
|
||||
If the given buildid is known to the server, this request will result
|
||||
in a binary object that contains the normal executable segments. This
|
||||
may be a executable stripped by \fBstrip\fP, or it may be an original
|
||||
unstripped executable. \fBET_DYN\fP shared libraries are considered
|
||||
to be a type of executable.
|
||||
|
||||
.SS source \fIBUILDID\fP \fI/SOURCE/FILE\fP
|
||||
|
||||
If the given buildid is known to the server, this request will result
|
||||
in a binary object that contains the source file mentioned. The path
|
||||
should be absolute. Relative path names commonly appear in the DWARF
|
||||
file's source directory, but these paths are relative to
|
||||
individual compilation unit AT_comp_dir paths, and yet an executable
|
||||
is made up of multiple CUs. Therefore, to disambiguate, debuginfod
|
||||
expects source queries to prefix relative path names with the CU
|
||||
compilation-directory, followed by a mandatory "/".
|
||||
|
||||
Note: the user should not elide \fB../\fP or \fB/./\fP or extraneous
|
||||
\fB///\fP sorts of path components in the directory names, because if
|
||||
this is how those names appear in the DWARF files, that is what
|
||||
debuginfod needs to see too.
|
||||
|
||||
For example:
|
||||
.TS
|
||||
l l.
|
||||
#include <stdio.h> source BUILDID /usr/include/stdio.h
|
||||
/path/to/foo.c source BUILDID /path/to/foo.c
|
||||
\../bar/foo.c AT_comp_dir=/zoo/ source BUILDID /zoo//../bar/foo.c
|
||||
.TE
|
||||
|
||||
.SH "SECURITY"
|
||||
|
||||
debuginfod-find \fBdoes not\fP include any particular security
|
||||
features. It trusts that the binaries returned by the debuginfod(s)
|
||||
are accurate. Therefore, the list of servers should include only
|
||||
trustworthy ones. If accessed across HTTP rather than HTTPS, the
|
||||
network should be trustworthy. Authentication information through
|
||||
the internal \fIlibcurl\fP library is not currently enabled, except
|
||||
for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style.
|
||||
(The debuginfod server does not perform authentication, but a front-end
|
||||
proxy server could.)
|
||||
|
||||
.SH "ENVIRONMENT VARIABLES"
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_URLS
|
||||
This environment variable contains a list of URL prefixes for trusted
|
||||
debuginfod instances. Alternate URL prefixes are separated by space.
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_TIMEOUT
|
||||
This environment variable governs the timeout for each debuginfod HTTP
|
||||
connection. A server that fails to respond within this many seconds
|
||||
is skipped. The default is 5.
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_CACHE_PATH
|
||||
This environment variable governs the location of the cache where
|
||||
downloaded files are kept. It is cleaned periodically as this program
|
||||
is reexecuted. Cache management parameters may be set by files under
|
||||
this directory: see the \fBdebuginfod_find_debuginfo(3)\fP man page
|
||||
for details. The default is $HOME/.debuginfod_client_cache.
|
||||
|
||||
.SH "FILES"
|
||||
.LP
|
||||
.PD .1v
|
||||
.TP 20
|
||||
.B $HOME/.debuginfod_client_cache
|
||||
Default cache directory.
|
||||
.PD
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.I "debuginfod(8)"
|
||||
.I "debuginfod_find_debuginfod(3)"
|
||||
@@ -0,0 +1,182 @@
|
||||
'\"! tbl | nroff \-man
|
||||
'\" t macro stdmacro
|
||||
|
||||
.de SAMPLE
|
||||
.br
|
||||
.RS 0
|
||||
.nf
|
||||
.nh
|
||||
..
|
||||
.de ESAMPLE
|
||||
.hy
|
||||
.fi
|
||||
.RE
|
||||
..
|
||||
|
||||
.TH DEBUGINFOD_FIND_DEBUGINFO 3
|
||||
.SH NAME
|
||||
debuginfod_find_debuginfo \- request debuginfo from debuginfod
|
||||
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <elfutils/debuginfod.h>
|
||||
.PP
|
||||
.BI "debuginfod_find_debuginfo(const unsigned char *" build_id ", int " build_id_len ", char ** " path ");"
|
||||
.BI "debuginfod_find_executable(const unsigned char *" build_id ", int " build_id_len ", char ** " path ");"
|
||||
.BI "debuginfod_find_source(const unsigned char *" build_id ", int " build_id_len ", const char *" filename ", char ** " path ");"
|
||||
|
||||
.SH DESCRIPTION
|
||||
.BR debuginfod_find_debuginfo (),
|
||||
.BR debuginfod_find_executable (),
|
||||
and
|
||||
.BR debuginfod_find_source ()
|
||||
query the debuginfod server URLs contained in
|
||||
.BR $DEBUGINFOD_URLS
|
||||
(see below) for the debuginfo, executable or source file with the
|
||||
given \fIbuild_id\fP. \fIbuild_id\fP should be a pointer to either
|
||||
a null-terminated, lowercase hex string or a binary blob. If
|
||||
\fIbuild_id\fP is given as a hex string, \fIbuild_id_len\fP should
|
||||
be 0. Otherwise \fIbuild_id_len\fP should be the number of bytes in
|
||||
the binary blob.
|
||||
|
||||
.BR debuginfod_find_source ()
|
||||
also requries a \fIfilename\fP in order to specify a particular
|
||||
source file. \fIfilename\fP should be an absolute path that includes
|
||||
the compilation directory of the CU associated with the source file.
|
||||
Relative path names commonly appear in the DWARF file's source directory,
|
||||
but these paths are relative to individual compilation unit AT_comp_dir
|
||||
paths, and yet an executable is made up of multiple CUs. Therefore, to
|
||||
disambiguate, debuginfod expects source queries to prefix relative path
|
||||
names with the CU compilation-directory, followed by a mandatory "/".
|
||||
|
||||
Note: the caller should not elide \fB../\fP or \fB/./\fP or extraneous
|
||||
\fB///\fP sorts of path components in the directory names, because if
|
||||
this is how those names appear in the DWARF files, that is what
|
||||
debuginfod needs to see too.
|
||||
|
||||
If \fIpath\fP is not NULL and the query is successful, \fIpath\fP is set
|
||||
to the path of the file in the cache. The caller must \fBfree\fP() this value.
|
||||
|
||||
The URLs in \fB$DEBUGINFOD_URLS\fP are queried in parallel. As soon as a
|
||||
debuginfod server begins transfering the target file all of the connections
|
||||
to the other servers are closed.
|
||||
|
||||
.SH "CACHE"
|
||||
If the query is successful, the \fBdebuginfod_find_*\fP() functions save
|
||||
the target file to a local cache. The location of the cache is controlled
|
||||
by the \fB$DEBUGINFOD_CACHE_PATH\fP environment variable (see below).
|
||||
Cleaning of the cache is controlled by the \fIcache_clean_interval_s\fP
|
||||
and \fImax_unused_age_s\fP files, which are found in the
|
||||
\fB$DEBUGINFOD_CACHE_PATH\fP directory. \fIcache_clean_interval_s\fP controls
|
||||
how frequently the cache is traversed for cleaning and \fImax_unused_age_s\fP
|
||||
controls how long a file can go unused (fstat(2) atime) before it's
|
||||
removed from the cache during cleaning. These files should contain only an
|
||||
ASCII decimal integer representing the interval or max unused age in seconds.
|
||||
The default is one day and one week, respectively. Values of zero mean "immediately".
|
||||
|
||||
.SH "SECURITY"
|
||||
.BR debuginfod_find_debuginfo (),
|
||||
.BR debuginfod_find_executable (),
|
||||
and
|
||||
.BR debuginfod_find_source ()
|
||||
\fBdo not\fP include any particular security
|
||||
features. They trust that the binaries returned by the debuginfod(s)
|
||||
are accurate. Therefore, the list of servers should include only
|
||||
trustworthy ones. If accessed across HTTP rather than HTTPS, the
|
||||
network should be trustworthy. Passing user authentication information
|
||||
through the internal \fIlibcurl\fP library is not currently enabled, except
|
||||
for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style.
|
||||
(The debuginfod server does not perform authentication, but a front-end
|
||||
proxy server could.)
|
||||
|
||||
.SH "ENVIRONMENT VARIABLES"
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_URLS
|
||||
This environment variable contains a list of URL prefixes for trusted
|
||||
debuginfod instances. Alternate URL prefixes are separated by space.
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_TIMEOUT
|
||||
This environment variable governs the timeout for each debuginfod HTTP
|
||||
connection. A server that fails to respond within this many seconds
|
||||
is skipped. The default is 5.
|
||||
|
||||
.TP 21
|
||||
.B DEBUGINFOD_CACHE_PATH
|
||||
This environment variable governs the location of the cache where
|
||||
downloaded files are kept. It is cleaned periodically as this
|
||||
program is reexecuted. The default is $HOME/.debuginfod_client_cache.
|
||||
|
||||
.SH "RETURN VALUE"
|
||||
If the query is successful, these functions save the target file
|
||||
to the client cache and return a file descriptor to that file.
|
||||
Otherwise an error code is returned.
|
||||
|
||||
.SH "ERRORS"
|
||||
The following list is not comprehensive. Error codes may also
|
||||
originate from calls to various C Library functions.
|
||||
|
||||
.TP
|
||||
.BR EACCESS
|
||||
Denied access to resource located at the URL.
|
||||
|
||||
.TP
|
||||
.BR ECONNREFUSED
|
||||
Unable to connect to remote host.
|
||||
|
||||
.TP
|
||||
.BR ECONNRESET
|
||||
Unable to either send or recieve network data.
|
||||
|
||||
.TP
|
||||
.BR EHOSTUNREACH
|
||||
Unable to resolve remote host.
|
||||
|
||||
.TP
|
||||
.BR EINVAL
|
||||
One or more arguments are incorrectly formatted. \fIbuild_id\fP may
|
||||
be too long (greater than 256 characters), \fIfilename\fP may not
|
||||
be an absolute path or a debuginfod URL is malformed.
|
||||
|
||||
.TP
|
||||
.BR EIO
|
||||
Unable to write data received from server to local file.
|
||||
|
||||
.TP
|
||||
.BR EMLINK
|
||||
Too many HTTP redirects.
|
||||
|
||||
.TP
|
||||
.BR ENETUNREACH
|
||||
Unable to initialize network connection.
|
||||
|
||||
.TP
|
||||
.BR ENOENT
|
||||
Could not find the resource located at URL. Often this error code
|
||||
indicates that a debuginfod server was successfully contacted but
|
||||
the server could not find the target file.
|
||||
|
||||
.TP
|
||||
.BR ENOMEM
|
||||
System is unable to allocate resources.
|
||||
|
||||
.TP
|
||||
.BR ENOSYS
|
||||
\fB$DEBUGINFOD_URLS\fP is not defined.
|
||||
|
||||
.TP
|
||||
.BR ETIME
|
||||
Query failed due to timeout. \fB$DEBUGINFOD_TIMEOUT\fP controls
|
||||
the timeout duration. See debuginfod(8) for more information.
|
||||
|
||||
.SH "FILES"
|
||||
.LP
|
||||
.PD .1v
|
||||
.TP 20
|
||||
.B $HOME/.debuginfod_client_cache
|
||||
Default cache directory.
|
||||
.PD
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.I "debuginfod(8)"
|
||||
@@ -0,0 +1 @@
|
||||
.so man3/debuginfod_find_debuginfo.3
|
||||
@@ -0,0 +1 @@
|
||||
.so man3/debuginfod_find_debuginfo.3
|
||||
@@ -1,3 +1,7 @@
|
||||
2019-10-28 Aaron Merey <amerey@redhat.com>
|
||||
|
||||
* Makefile.am (libdw_so_LDLIBS): Add -ldl for libdebuginfod.so dlopen.
|
||||
|
||||
2019-11-10 Mark Wielaard <mark@klomp.org>
|
||||
|
||||
* libdwP.h (libdw_unalloc): New define.
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ libdw_so_LIBS = ../libebl/libebl_pic.a ../backends/libebl_backends_pic.a \
|
||||
../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \
|
||||
../libdwfl/libdwfl_pic.a
|
||||
libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so
|
||||
libdw_so_LDLIBS = $(libdw_so_DEPS) -lz $(argp_LDADD) $(zip_LIBS) -pthread
|
||||
libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) -pthread
|
||||
libdw_so_SOURCES =
|
||||
libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS)
|
||||
$(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
2019-10-28 Aaron Merey <amerey@redhat.com>
|
||||
|
||||
* dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Call debuginfod
|
||||
client functions via dlopen to look for elf/dwarf files as fallback.
|
||||
* find-debuginfo.c (dwfl_standard_find_debuginfo): Ditto.
|
||||
|
||||
2019-10-07 Omar Sandoval <osandov@fb.com>
|
||||
|
||||
* dwfl_frame.c (dwfl_getthreads): Get rid of unnecessary
|
||||
|
||||
+2
-1
@@ -31,7 +31,7 @@
|
||||
##
|
||||
include $(top_srcdir)/config/eu.am
|
||||
AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
|
||||
-I$(srcdir)/../libdw -I$(srcdir)/../libdwelf
|
||||
-I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(srcdir)/../debuginfod
|
||||
VERSION = 1
|
||||
|
||||
noinst_LIBRARIES = libdwfl.a
|
||||
@@ -39,6 +39,7 @@ noinst_LIBRARIES += libdwfl_pic.a
|
||||
|
||||
pkginclude_HEADERS = libdwfl.h
|
||||
|
||||
|
||||
libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
|
||||
dwfl_module.c dwfl_report_elf.c relocate.c \
|
||||
dwfl_module_build_id.c dwfl_module_report_build_id.c \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Find an ELF file for a module from its build ID.
|
||||
Copyright (C) 2007-2010, 2014, 2015 Red Hat, Inc.
|
||||
Copyright (C) 2007-2010, 2014, 2015, 2019 Red Hat, Inc.
|
||||
This file is part of elfutils.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify
|
||||
@@ -34,7 +34,9 @@
|
||||
#include <inttypes.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include "system.h"
|
||||
#include "debuginfod.h"
|
||||
|
||||
|
||||
int
|
||||
@@ -187,7 +189,33 @@ dwfl_build_id_find_elf (Dwfl_Module *mod,
|
||||
free (*file_name);
|
||||
*file_name = NULL;
|
||||
}
|
||||
else if (errno == 0 && mod->build_id_len > 0)
|
||||
else {
|
||||
/* NB: this is slightly thread-unsafe */
|
||||
static __typeof__ (debuginfod_find_executable) *fp_debuginfod_find_executable;
|
||||
|
||||
if (fp_debuginfod_find_executable == NULL)
|
||||
{
|
||||
void *debuginfod_so = dlopen("libdebuginfod-" VERSION ".so", RTLD_LAZY);
|
||||
if (debuginfod_so == NULL)
|
||||
debuginfod_so = dlopen("libdebuginfod.so", RTLD_LAZY);
|
||||
if (debuginfod_so != NULL)
|
||||
fp_debuginfod_find_executable = dlsym (debuginfod_so, "debuginfod_find_executable");
|
||||
if (fp_debuginfod_find_executable == NULL)
|
||||
fp_debuginfod_find_executable = (void *) -1; /* never try again */
|
||||
}
|
||||
|
||||
if (fp_debuginfod_find_executable != NULL && fp_debuginfod_find_executable != (void *) -1)
|
||||
{
|
||||
/* If all else fails and a build-id is available, query the
|
||||
debuginfo-server if enabled. */
|
||||
if (fd < 0 && mod->build_id_len > 0)
|
||||
fd = (*fp_debuginfod_find_executable) (mod->build_id_bits,
|
||||
mod->build_id_len,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0 && errno == 0 && mod->build_id_len > 0)
|
||||
/* Setting this with no file yet loaded is a marker that
|
||||
the build ID is authoritative even if we also know a
|
||||
putative *FILE_NAME. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Standard find_debuginfo callback for libdwfl.
|
||||
Copyright (C) 2005-2010, 2014, 2015 Red Hat, Inc.
|
||||
Copyright (C) 2005-2010, 2014, 2015, 2019 Red Hat, Inc.
|
||||
This file is part of elfutils.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify
|
||||
@@ -31,9 +31,11 @@
|
||||
#endif
|
||||
|
||||
#include "libdwflP.h"
|
||||
#include "debuginfod.h"
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include "system.h"
|
||||
|
||||
@@ -359,7 +361,8 @@ dwfl_standard_find_debuginfo (Dwfl_Module *mod,
|
||||
other than just by finding nothing, that's all we do. */
|
||||
const unsigned char *bits;
|
||||
GElf_Addr vaddr;
|
||||
if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
|
||||
int bits_len;
|
||||
if ((bits_len = INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr)) > 0)
|
||||
{
|
||||
/* Dropping most arguments means we cannot rely on them in
|
||||
dwfl_build_id_find_debuginfo. But leave it that way since
|
||||
@@ -397,6 +400,30 @@ dwfl_standard_find_debuginfo (Dwfl_Module *mod,
|
||||
free (canon);
|
||||
}
|
||||
|
||||
{
|
||||
/* NB: this is slightly thread-unsafe */
|
||||
static __typeof__ (debuginfod_find_debuginfo) *fp_debuginfod_find_debuginfo;
|
||||
|
||||
if (fp_debuginfod_find_debuginfo == NULL)
|
||||
{
|
||||
void *debuginfod_so = dlopen("libdebuginfod-" VERSION ".so", RTLD_LAZY);
|
||||
if (debuginfod_so == NULL)
|
||||
debuginfod_so = dlopen("libdebuginfod.so", RTLD_LAZY);
|
||||
if (debuginfod_so != NULL)
|
||||
fp_debuginfod_find_debuginfo = dlsym (debuginfod_so, "debuginfod_find_debuginfo");
|
||||
if (fp_debuginfod_find_debuginfo == NULL)
|
||||
fp_debuginfod_find_debuginfo = (void *) -1; /* never try again */
|
||||
}
|
||||
|
||||
if (fp_debuginfod_find_debuginfo != NULL && fp_debuginfod_find_debuginfo != (void *) -1)
|
||||
{
|
||||
/* If all else fails and a build-id is available, query the
|
||||
debuginfo-server if enabled. */
|
||||
if (fd < 0 && bits_len > 0)
|
||||
fd = (*fp_debuginfod_find_debuginfo) (bits, bits_len, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
INTDEF (dwfl_standard_find_debuginfo)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2019-10-28 Aaron Merey <amerey@redhat.com>
|
||||
|
||||
* ax_check_compile_flag.m4, ax_cxx_compile_stdcxx.m4: New files.
|
||||
|
||||
2015-05-01 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* zip.m4: Explicitly set with_ to no, if not yes.
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@
|
||||
##
|
||||
|
||||
##m4-files-begin
|
||||
EXTRA_DIST = codeset.m4 gettext.m4 iconv.m4 lcmessage.m4 progtest.m4 zip.m4
|
||||
EXTRA_DIST = codeset.m4 gettext.m4 iconv.m4 lcmessage.m4 progtest.m4 zip.m4 ax_check_compile_flag.m4 ax_cxx_compile_stdcxx.m4
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 4
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_VAR_IF(CACHEVAR,yes,
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
||||
@@ -0,0 +1,556 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXX to
|
||||
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
|
||||
# (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 3
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
fi
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
||||
Reference in New Issue
Block a user