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:
Aaron Merey
2019-10-28 13:29:26 -04:00
committed by Mark Wielaard
parent 4bad7d1ead
commit 288f6b199a
26 changed files with 2048 additions and 11 deletions
+6
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
+12
View File
@@ -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
View File
@@ -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}
+9
View File
@@ -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.
+114
View File
@@ -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
+665
View File
@@ -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(); */
}
+101
View File
@@ -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;
}
+69
View File
@@ -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 */
+7
View File
@@ -0,0 +1,7 @@
ELFUTILS_0 { };
ELFUTILS_0.178 {
global:
debuginfod_find_debuginfo;
debuginfod_find_executable;
debuginfod_find_source;
} ELFUTILS_0;
+9 -1
View File
@@ -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
+137
View File
@@ -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)"
+182
View File
@@ -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)"
+1
View File
@@ -0,0 +1 @@
.so man3/debuginfod_find_debuginfo.3
+1
View File
@@ -0,0 +1 @@
.so man3/debuginfod_find_debuginfo.3
+4
View File
@@ -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
View File
@@ -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 $@ \
+6
View File
@@ -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
View File
@@ -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 \
+30 -2
View File
@@ -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. */
+29 -2
View File
@@ -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)
+4
View File
@@ -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
View File
@@ -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
+74
View File
@@ -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
+556
View File
@@ -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
]])