Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 0.20240321.x
  • main
  • v0.20231013.x
  • wip/case_insensitive_fs
  • wip/denittis/line_based
  • wip/denittis/portal_helper
  • wip/denittis/t17372
  • wip/denittis/t20052
  • wip/denittis/t20177
  • wip/denittis/t7017
  • wip/denittis/tasks239
  • wip/denittis/up_libcapsule
  • wip/diagnose-task724
  • wip/fix_archlinux_ci
  • wip/flatpak_exports_for_run_host
  • wip/gameoverlayrenderer
  • wip/input-device-diagnostic
  • wip/input-portal
  • wip/issue18
  • wip/luigipiucco/nixos
  • wip/protected/ci
  • wip/protected/scout-on-soldier
  • wip/protected/smcv-ci
  • wip/protected/smcv-merge-slr
  • wip/protected/task679
  • wip/refi64/check-steam-running
  • wip/refi64/ci-coverage-demo
  • wip/refi64/mr_template_test
  • wip/refi64/openxr
  • wip/refi64/openxr-rt-pv
  • wip/refi64/openxr-sysinfo
  • wip/refi64/vk-layers
  • wip/regenerate-ldso-cache
  • wip/shorten_socket
  • wip/smcv/always-copy-runtime
  • wip/smcv/ci
  • wip/smcv/clang
  • wip/smcv/delete-in-background
  • wip/smcv/dev-lib-symlinks
  • wip/smcv/diagnose-sfl10602
  • wip/smcv/diffable
  • wip/smcv/doc
  • wip/smcv/docs
  • wip/smcv/fex-3
  • wip/smcv/gcc11
  • wip/smcv/guessed-library-arch
  • wip/smcv/inspect-library
  • wip/smcv/into-library
  • wip/smcv/ld-so-wrapper
  • wip/smcv/lxc
  • wip/smcv/no-nvidia-pkcs11
  • wip/smcv/pv-wrap-to-journal
  • wip/smcv/refcount-locks
  • wip/smcv/relocatable-bin
  • wip/smcv/spawn-really-async
  • wip/smcv/staging
  • wip/smcv/steam-portal
  • wip/smcv/task149-nonfhs
  • wip/smcv/task212-basename
  • wip/smcv/task370-debug
  • wip/smcv/test-diagnostics
  • wip/smcv/variant-init-statically
  • wip/smcv/vulkan-1-1
  • wip/sr640
  • wip/sr684
  • wip/task293-v1
  • wip/task342-3
  • wip/task466
  • wip/task466-more
  • wip/task481-test
  • wip/task488-test
  • wip/task595
  • wip/task634
  • wip/task647
  • wip/task88
  • wip/task88-pre
  • wip/vulkan_shaders
  • wip/xytovl/openxr
  • pressure-vessel-v0.20180430.0
  • pressure-vessel-v0.20180517.0
  • pressure-vessel-v0.20180517.1
  • pressure-vessel-v0.20180518.0
  • pressure-vessel-v0.20180518.1
  • pressure-vessel-v0.20190510.0
  • pressure-vessel-v0.20190815.0
  • pressure-vessel-v0.20190819.0
  • pressure-vessel-v0.20190820.0
  • pressure-vessel-v0.20190822.0
  • pressure-vessel-v0.20190823.0
  • pressure-vessel-v0.20190912.0
  • pressure-vessel-v0.20190926.0
  • pressure-vessel-v0.20190927.0
  • pressure-vessel-v0.20191112.0
  • pressure-vessel-v0.20191112.1
  • pressure-vessel-v0.20191115.0
  • pressure-vessel-v0.20191216.0
  • pressure-vessel-v0.20200108.0
  • pressure-vessel-v0.20200331.0
  • pressure-vessel-v0.20200624.0
  • pressure-vessel-v0.20200708.0
  • pressure-vessel-v0.20200709.0
  • pressure-vessel-v0.20200805.0
  • pressure-vessel-v0.20200819.0
  • pressure-vessel-v0.20200908.0
  • pressure-vessel-v0.20200909.0
  • steamrt/0.20190926.0-0+steamrt1.2
  • v0.20190717.0
  • v0.20190801.0
  • v0.20190802.0
  • v0.20190806.0
  • v0.20190816.0
  • v0.20190820.0
  • v0.20190820.1
  • v0.20190822.0
  • v0.20190909.0
  • v0.20190926.0
  • v0.20190926.1
  • v0.20191024.0
  • v0.20191120.0
  • v0.20191205.0
  • v0.20191209.0
  • v0.20200108.0
  • v0.20200109.0
  • v0.20200306.0
  • v0.20200331.0
  • v0.20200331.1
  • v0.20200415.0
  • v0.20200624.0
  • v0.20200908.0
  • v0.20200908.1
  • v0.20200914.0
  • v0.20201002.0
  • v0.20201007.0
  • v0.20201022.0
  • v0.20201124.0
  • v0.20201203.0
  • v0.20210105.0
  • v0.20210114.0
  • v0.20210126.0
  • v0.20210201.0
  • v0.20210203.0
  • v0.20210305.0
  • v0.20210312.0
  • v0.20210317.0
  • v0.20210331.0
  • v0.20210415.0
  • v0.20210430.0
  • v0.20210518.0
  • v0.20210608.0
  • v0.20210608.1
  • v0.20210608.2
  • v0.20210608.3
  • v0.20210617.0
  • v0.20210617.1
  • v0.20210630.0
  • v0.20210722.0
  • v0.20210806.0
  • v0.20210809.0
  • v0.20210809.1
  • v0.20210820.0
  • v0.20210906.0
  • v0.20210906.1
  • v0.20210906.2
  • v0.20211027.0
  • v0.20211207.0
  • v0.20220107.0
  • v0.20220111.0
  • v0.20220119.0
  • v0.20220131.0
  • v0.20220311.0
  • v0.20220315.0
  • v0.20220420.0
  • v0.20220601.0
  • v0.20220620.0
  • v0.20220624.0
  • v0.20220725.0
  • v0.20220725.1
  • v0.20220726.0
178 results

Target

Select target project
  • steamrt/steam-runtime-tools
1 result
Select Git revision
  • 0.20240321.x
  • main
  • v0.20231013.x
  • wip/case_insensitive_fs
  • wip/denittis/line_based
  • wip/denittis/portal_helper
  • wip/denittis/t17372
  • wip/denittis/t20052
  • wip/denittis/t20177
  • wip/denittis/t7017
  • wip/denittis/tasks239
  • wip/denittis/up_libcapsule
  • wip/diagnose-task724
  • wip/fix_archlinux_ci
  • wip/flatpak_exports_for_run_host
  • wip/gameoverlayrenderer
  • wip/input-device-diagnostic
  • wip/input-portal
  • wip/issue18
  • wip/luigipiucco/nixos
  • wip/protected/ci
  • wip/protected/scout-on-soldier
  • wip/protected/smcv-ci
  • wip/protected/smcv-merge-slr
  • wip/protected/task679
  • wip/refi64/check-steam-running
  • wip/refi64/ci-coverage-demo
  • wip/refi64/mr_template_test
  • wip/refi64/openxr
  • wip/refi64/openxr-rt-pv
  • wip/refi64/openxr-sysinfo
  • wip/refi64/vk-layers
  • wip/regenerate-ldso-cache
  • wip/shorten_socket
  • wip/smcv/always-copy-runtime
  • wip/smcv/ci
  • wip/smcv/clang
  • wip/smcv/delete-in-background
  • wip/smcv/dev-lib-symlinks
  • wip/smcv/diagnose-sfl10602
  • wip/smcv/diffable
  • wip/smcv/doc
  • wip/smcv/docs
  • wip/smcv/fex-3
  • wip/smcv/gcc11
  • wip/smcv/guessed-library-arch
  • wip/smcv/inspect-library
  • wip/smcv/into-library
  • wip/smcv/ld-so-wrapper
  • wip/smcv/lxc
  • wip/smcv/no-nvidia-pkcs11
  • wip/smcv/pv-wrap-to-journal
  • wip/smcv/refcount-locks
  • wip/smcv/relocatable-bin
  • wip/smcv/spawn-really-async
  • wip/smcv/staging
  • wip/smcv/steam-portal
  • wip/smcv/task149-nonfhs
  • wip/smcv/task212-basename
  • wip/smcv/task370-debug
  • wip/smcv/test-diagnostics
  • wip/smcv/variant-init-statically
  • wip/smcv/vulkan-1-1
  • wip/sr640
  • wip/sr684
  • wip/task293-v1
  • wip/task342-3
  • wip/task466
  • wip/task466-more
  • wip/task481-test
  • wip/task488-test
  • wip/task595
  • wip/task634
  • wip/task647
  • wip/task88
  • wip/task88-pre
  • wip/vulkan_shaders
  • wip/xytovl/openxr
  • pressure-vessel-v0.20180430.0
  • pressure-vessel-v0.20180517.0
  • pressure-vessel-v0.20180517.1
  • pressure-vessel-v0.20180518.0
  • pressure-vessel-v0.20180518.1
  • pressure-vessel-v0.20190510.0
  • pressure-vessel-v0.20190815.0
  • pressure-vessel-v0.20190819.0
  • pressure-vessel-v0.20190820.0
  • pressure-vessel-v0.20190822.0
  • pressure-vessel-v0.20190823.0
  • pressure-vessel-v0.20190912.0
  • pressure-vessel-v0.20190926.0
  • pressure-vessel-v0.20190927.0
  • pressure-vessel-v0.20191112.0
  • pressure-vessel-v0.20191112.1
  • pressure-vessel-v0.20191115.0
  • pressure-vessel-v0.20191216.0
  • pressure-vessel-v0.20200108.0
  • pressure-vessel-v0.20200331.0
  • pressure-vessel-v0.20200624.0
  • pressure-vessel-v0.20200708.0
  • pressure-vessel-v0.20200709.0
  • pressure-vessel-v0.20200805.0
  • pressure-vessel-v0.20200819.0
  • pressure-vessel-v0.20200908.0
  • pressure-vessel-v0.20200909.0
  • steamrt/0.20190926.0-0+steamrt1.2
  • v0.20190717.0
  • v0.20190801.0
  • v0.20190802.0
  • v0.20190806.0
  • v0.20190816.0
  • v0.20190820.0
  • v0.20190820.1
  • v0.20190822.0
  • v0.20190909.0
  • v0.20190926.0
  • v0.20190926.1
  • v0.20191024.0
  • v0.20191120.0
  • v0.20191205.0
  • v0.20191209.0
  • v0.20200108.0
  • v0.20200109.0
  • v0.20200306.0
  • v0.20200331.0
  • v0.20200331.1
  • v0.20200415.0
  • v0.20200624.0
  • v0.20200908.0
  • v0.20200908.1
  • v0.20200914.0
  • v0.20201002.0
  • v0.20201007.0
  • v0.20201022.0
  • v0.20201124.0
  • v0.20201203.0
  • v0.20210105.0
  • v0.20210114.0
  • v0.20210126.0
  • v0.20210201.0
  • v0.20210203.0
  • v0.20210305.0
  • v0.20210312.0
  • v0.20210317.0
  • v0.20210331.0
  • v0.20210415.0
  • v0.20210430.0
  • v0.20210518.0
  • v0.20210608.0
  • v0.20210608.1
  • v0.20210608.2
  • v0.20210608.3
  • v0.20210617.0
  • v0.20210617.1
  • v0.20210630.0
  • v0.20210722.0
  • v0.20210806.0
  • v0.20210809.0
  • v0.20210809.1
  • v0.20210820.0
  • v0.20210906.0
  • v0.20210906.1
  • v0.20210906.2
  • v0.20211027.0
  • v0.20211207.0
  • v0.20220107.0
  • v0.20220111.0
  • v0.20220119.0
  • v0.20220131.0
  • v0.20220311.0
  • v0.20220315.0
  • v0.20220420.0
  • v0.20220601.0
  • v0.20220620.0
  • v0.20220624.0
  • v0.20220725.0
  • v0.20220725.1
  • v0.20220726.0
178 results
Show changes
Showing
with 3773 additions and 0 deletions
#!/bin/sh
set -e
# Run this to generate all the initial makefiles, etc.
[ -n "$srcdir" ] || srcdir=$(dirname "$0");
[ -n "$srcdir" ] || srcdir=.;
( cd $srcdir
if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ];
then
(echo "*** WARNING: Running 'configure' with no arguments.";
echo "*** configure arguments should be passed as:" ;
echo "*** $0 ARGS..." ;
echo "" ) >&2;
fi;
aclocal -I m4 --install || exit $?;
gtkdocize || exit $?;
autoreconf -ivf || exit $?; );
if [ "$NOCONFIGURE" = "" ];
then
$srcdir/configure "$@" || exit $?;
if [ "$1" = "--help" ]; then exit 0; fi;
echo "Now type 'make' to compile";
else
echo "Skipping configure process.";
fi
#!/bin/sh
# Print a version string.
scriptversion=2017-09-13.06; # UTC
# Copyright (C) 2007-2017 Free Software Foundation, Inc.
#
# 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 <https://www.gnu.org/licenses/>.
# This script is derived from GIT-VERSION-GEN from GIT: https://git-scm.com/.
# It may be run two ways:
# - from a git repository in which the "git describe" command below
# produces useful output (thus requiring at least one signed tag)
# - from a non-git-repo directory containing a .tarball-version file, which
# presumes this script is invoked like "./git-version-gen .tarball-version".
# In order to use intra-version strings in your project, you will need two
# separate generated version string files:
#
# .tarball-version - present only in a distribution tarball, and not in
# a checked-out repository. Created with contents that were learned at
# the last time autoconf was run, and used by git-version-gen. Must not
# be present in either $(srcdir) or $(builddir) for git-version-gen to
# give accurate answers during normal development with a checked out tree,
# but must be present in a tarball when there is no version control system.
# Therefore, it cannot be used in any dependencies. GNUmakefile has
# hooks to force a reconfigure at distribution time to get the value
# correct, without penalizing normal development with extra reconfigures.
#
# .version - present in a checked-out repository and in a distribution
# tarball. Usable in dependencies, particularly for files that don't
# want to depend on config.h but do want to track version changes.
# Delete this file prior to any autoconf run where you want to rebuild
# files to pick up a version string change; and leave it stale to
# minimize rebuild time after unrelated changes to configure sources.
#
# As with any generated file in a VC'd directory, you should add
# /.version to .gitignore, so that you don't accidentally commit it.
# .tarball-version is never generated in a VC'd directory, so needn't
# be listed there.
#
# Use the following line in your configure.ac, so that $(VERSION) will
# automatically be up-to-date each time configure is run (and note that
# since configure.ac no longer includes a version string, Makefile rules
# should not depend on configure.ac for version updates).
#
# AC_INIT([GNU project],
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
# [bug-project@example])
#
# Then use the following lines in your Makefile.am, so that .version
# will be present for dependencies, and so that .version and
# .tarball-version will exist in distribution tarballs.
#
# EXTRA_DIST = $(top_srcdir)/.version
# BUILT_SOURCES = $(top_srcdir)/.version
# $(top_srcdir)/.version:
# echo $(VERSION) > $@-t && mv $@-t $@
# dist-hook:
# echo $(VERSION) > $(distdir)/.tarball-version
me=$0
version="git-version-gen $scriptversion
Copyright 2011 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
usage="\
Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT]
Print a version string.
Options:
--prefix PREFIX prefix of git tags (default 'v')
--fallback VERSION
fallback version to use if \"git --version\" fails
--help display this help and exit
--version output version information and exit
Running without arguments will suffice in most cases."
prefix=v
fallback=
while test $# -gt 0; do
case $1 in
--help) echo "$usage"; exit 0;;
--version) echo "$version"; exit 0;;
--prefix) shift; prefix=${1?};;
--fallback) shift; fallback=${1?};;
-*)
echo "$0: Unknown option '$1'." >&2
echo "$0: Try '--help' for more information." >&2
exit 1;;
*)
if test "x$tarball_version_file" = x; then
tarball_version_file="$1"
elif test "x$tag_sed_script" = x; then
tag_sed_script="$1"
else
echo "$0: extra non-option argument '$1'." >&2
exit 1
fi;;
esac
shift
done
if test "x$tarball_version_file" = x; then
echo "$usage"
exit 1
fi
tag_sed_script="${tag_sed_script:-s/x/x/}"
nl='
'
# Avoid meddling by environment variable of the same name.
v=
v_from_git=
# First see if there is a tarball-only version file.
# then try "git describe", then default.
if test -f $tarball_version_file
then
v=`cat $tarball_version_file` || v=
case $v in
*$nl*) v= ;; # reject multi-line output
[0-9]*) ;;
*) v= ;;
esac
test "x$v" = x \
&& echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
fi
if test "x$v" != x
then
: # use $v
# Otherwise, if there is at least one git commit involving the working
# directory, and "git describe" output looks sensible, use that to
# derive a version string.
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
&& v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
&& case $v in
$prefix[0-9]*) ;;
*) (exit 1) ;;
esac
then
# Is this a new git that lists number of commits since the last
# tag or the previous older version that did not?
# Newer: v6.10-77-g0f8faeb
# Older: v6.10-g0f8faeb
vprefix=`expr "X$v" : 'X\(.*\)-g[^-]*$'` || vprefix=$v
case $vprefix in
*-*) : git describe is probably okay three part flavor ;;
*)
: git describe is older two part flavor
# Recreate the number of commits and rewrite such that the
# result is the same as if we were using the newer version
# of git describe.
vtag=`echo "$v" | sed 's/-.*//'`
commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
|| { commit_list=failed;
echo "$0: WARNING: git rev-list failed" 1>&2; }
numcommits=`echo "$commit_list" | wc -l`
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
test "$commit_list" = failed && v=UNKNOWN
;;
esac
# Change the penultimate "-" to ".", for version-comparing tools.
# Remove the "g" to save a byte.
v=`echo "$v" | sed 's/-\([^-]*\)-g\([^-]*\)$/.\1-\2/'`;
v_from_git=1
elif test "x$fallback" = x || git --version >/dev/null 2>&1; then
v=UNKNOWN
else
v=$fallback
fi
v=`echo "$v" |sed "s/^$prefix//"`
# Test whether to append the "-dirty" suffix only if the version
# string we're using came from git. I.e., skip the test if it's "UNKNOWN"
# or if it came from .tarball-version.
if test "x$v_from_git" != x; then
# Don't declare a version "dirty" merely because a timestamp has changed.
git update-index --refresh > /dev/null 2>&1
dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
case "$dirty" in
'') ;;
*) # Append the suffix only if there isn't one already.
case $v in
*-dirty) ;;
*) v="$v-dirty" ;;
esac ;;
esac
fi
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
printf %s "$v"
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
// Copyright © 2017 Collabora Ltd
//
// This file is part of libcapsule.
//
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// libcapsule 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
// Standard implementation of _int_dlopen(), the implementation used
// when a library inside the capsule calls dlopen(). To override this
// implementation, copy this file to shim/capsule/_int_dlopen.h in
// your shim library and modify as needed.
static void *
_int_dlopen (const char *filename, int flag)
{
if( flag & RTLD_GLOBAL )
{
fprintf( stderr, "Warning: libcapsule dlopen wrapper cannot pass "
"RTLD_GLOBAL to underlying dlmopen(%s...) call\n",
filename );
flag = (flag & ~RTLD_GLOBAL) & 0xfffff;
}
return capsule_shim_dlopen( cap, filename, flag );
}
// if the libc instances aren't unified (ie > 1 libc) then
// we must try to dispatch the to-be-freed pointer to the one
// that actually allocated it.
// This is far from foolproof:
static void
_wrapped_free (void *ptr)
{
if (ptr)
capsule_shim_free( cap, ptr );
}
static void *
_wrapped_realloc (void *ptr, size_t size)
{
return capsule_shim_realloc( cap, ptr, size );
}
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule 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 Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <libelf.h>
#include <gelf.h>
#include <dlfcn.h>
#include <capsule/capsule.h>
#include "capsule/capsule-private.h"
#include "utils/utils.h"
#include "utils/dump.h"
#include "utils/mmap-info.h"
#include "utils/process-pt-dynamic.h"
#include "utils/ld-cache.h"
#include "utils/ld-libs.h"
// ==========================================================================
// some pretty printers for debugging:
// dump out the contents of the ld cache to stderr:
static void
dump_ld_cache (ld_libs *ldlibs)
{
ld_cache_foreach( &ldlibs->ldcache, ld_entry_dump, stderr );
}
/*
* wrap:
* @name:
* @base: Starting address of the program header in memory.
* Addresses are normally relative to this, except for when they are
* absolute (see fix_addr()).
* @dyn: An array of ElfW(Dyn) structures, somewhere after @base
* @wrappers: Relocations to apply
*/
static void
wrap (const char *name,
ElfW(Addr) base,
ElfW(Dyn) *dyn,
capsule_item *wrappers)
{
int mmap_errno = 0;
const char *mmap_error = NULL;
ElfW(Addr) start = (ElfW(Addr)) dyn - base;
// we don't know the size so we'll have to rely on the linker putting
// well formed entries into the mmap()ed DSO region.
// (tbf if the linker is putting duff entries here we're boned anyway)
//
// dyn is the address of the dynamic section
// base is the start of the program header in memory
// start should be the offset from the program header to its dyn section
//
// the utility functions expect an upper bound though so set that to
// something suitably large:
relocation_data rdata = { 0 };
DEBUG( DEBUG_WRAPPERS,
"\"%s\": base address %" PRIxPTR ", dynamic section at %p",
name, base, dyn );
rdata.debug = debug_flags;
rdata.error = NULL;
rdata.relocs = wrappers;
// if RELRO linking has happened we'll need to tweak the mprotect flags
// before monkeypatching the symbol tables, for which we will need the
// sizes, locations and current protections of any mmap()ed regions:
rdata.mmap_info = load_mmap_info( &mmap_errno, &mmap_error );
if( mmap_errno || mmap_error )
{
DEBUG( DEBUG_MPROTECT,
"mmap/mprotect flags information load error (errno: %d): %s",
mmap_errno, mmap_error );
DEBUG( DEBUG_MPROTECT,
"relocation will be unable to handle RELRO linked libraries" );
}
// make all the mmap()s writable:
for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ )
if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) )
add_mmap_protection( &rdata.mmap_info[i], PROT_WRITE );
// if we're debugging wrapper installation in detail we
// will end up in a path that's normally only DEBUG_ELF
// debugged:
if( debug_flags & DEBUG_WRAPPERS )
debug_flags = debug_flags | DEBUG_RELOCS;
// install any required wrappers inside the capsule:
process_pt_dynamic( start, // offset from phdr to dyn section
0, // fake size value
(void *) base, // address of phdr in memory
process_dt_rela,
process_dt_rel,
&rdata );
// put the debug flags back in case we changed them
debug_flags = rdata.debug;
// put the mmap()/mprotect() permissions back the way they were:
for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ )
if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) )
reset_mmap_protection( &rdata.mmap_info[i] );
free_mmap_info( rdata.mmap_info );
rdata.mmap_info = NULL;
}
static inline int
excluded_from_wrap (const char *name, char **exclude)
{
const char *dso = strrchr(name, '/');
// we can't ever subvert the runtime linker itself:
if( strncmp( "/ld-", dso, 4 ) == 0 )
return 1;
for( char **x = exclude; x && *x; x++ )
if( strcmp ( *x, dso + 1 ) == 0 )
return 1;
return 0;
}
/*
* @exclude: (array zero-terminated=1): same as for capsule_dlmopen()
* @errcode: (out): errno
* @error: (out) (transfer full) (optional): Error string
*/
// replace calls out to dlopen in the encapsulated DSO with a wrapper
// which should take care of preserving the /path-prefix and namespace
// wrapping of the original capsule_dlmopen() call.
//
// strictly speaking we can wrap things other than dlopen(),
// but that's currently all we use this for:
static int install_wrappers ( void *dl_handle,
capsule_item *wrappers,
const char **exclude,
int *errcode,
char **error)
{
int replacements = 0;
struct link_map *map;
if( dlinfo( dl_handle, RTLD_DI_LINKMAP, &map ) != 0 )
{
const char *local_error = dlerror();
if( error )
*error = xstrdup( local_error );
if( errcode )
*errcode = EINVAL;
DEBUG( DEBUG_WRAPPERS, "mangling capsule symbols: %s", local_error );
return -1;
}
DEBUG( DEBUG_WRAPPERS, "link_map: %p <- %p -> %p",
map ? map->l_next : NULL ,
map ? map : NULL ,
map ? map->l_prev : NULL );
// no guarantee that we're at either end of the link map:
while( map->l_prev )
map = map->l_prev;
unsigned long df = debug_flags;
if( debug_flags & DEBUG_WRAPPERS )
debug_flags |= DEBUG_RELOCS;
if (map->l_next)
{
for( struct link_map *m = map; m; m = m->l_next )
{
if( excluded_from_wrap(m->l_name, (char **)exclude) )
{
DEBUG( DEBUG_WRAPPERS, "%s excluded from wrapping",
m->l_name );
}
else
{
wrap( m->l_name, m->l_addr, m->l_ld, wrappers );
}
}
}
debug_flags = df;
return replacements;
}
// dump the link map info for the given dl handle (NULL = default)
static void
dump_link_map( void *dl_handle )
{
struct link_map *map;
void *handle;
if( !dl_handle )
handle = dlopen( NULL, RTLD_LAZY|RTLD_NOLOAD );
else
handle = dl_handle;
if( dlinfo( handle, RTLD_DI_LINKMAP, &map ) != 0 )
{
DEBUG( DEBUG_CAPSULE, "failed to access link_map for handle %p-%p: %s",
dl_handle, handle, dlerror() );
return;
}
// be kind, rewind the link map:
while( map->l_prev )
map = map->l_prev;
fprintf( stderr, "(dl-handle %s", dl_handle ? "CAPSULE" : "DEFAULT" );
for( struct link_map *m = map; m; m = m->l_next )
fprintf( stderr, "\n [prev: %p] %p: \"%s\" [next: %p]",
m->l_prev, m, m->l_name, m->l_next );
fprintf( stderr, ")\n" );
}
// ==========================================================================
void *
_capsule_load (const capsule cap,
capsule_item *wrappers,
int *errcode,
char **error)
{
void *ret = NULL;
ld_libs ldlibs = {};
if( !ld_libs_init( &ldlibs,
(const char **) cap->ns->combined_exclude,
cap->ns->prefix, debug_flags, errcode, error ) )
return NULL;
// ==================================================================
// read in the ldo.so.cache - this will contain all architectures
// currently installed (x86_64, i386, x32) in no particular order
if( ld_libs_load_cache( &ldlibs, errcode, error ) )
{
if( debug_flags & DEBUG_LDCACHE )
dump_ld_cache( &ldlibs );
}
else
{
return NULL;
}
// ==================================================================
// find the starting point of our capsule
if( !ld_libs_set_target( &ldlibs, cap->meta->soname, errcode, error ) )
goto cleanup;
// ==================================================================
// once we have the starting point recursively find all its DT_NEEDED
// entries, except for the linker itself and libc, which must not
// be different between the capsule and the "real" DSO environment:
if( !ld_libs_find_dependencies( &ldlibs, errcode, error ) )
goto cleanup;
// ==================================================================
// load the stack of DSOs we need:
ret = ld_libs_load( &ldlibs, &cap->ns->ns, 0, errcode, error );
if( debug_flags & DEBUG_CAPSULE )
{
dump_link_map( ret );
dump_link_map( NULL );
}
if( !ret )
goto cleanup;
// =====================================================================
// stash any free/malloc/etc implementations _before_ we overwrite them:
// (only really need to deal with functions that deal with pre-alloc'd
// pointers, ie free and realloc)
if( cap->ns->mem->free == NULL )
cap->ns->mem->free = dlsym( ret, "free" );
if( cap->ns->mem->realloc == NULL )
cap->ns->mem->realloc = dlsym( ret, "realloc" );
// =====================================================================
// TODO: failure in the dlopen fixup phase should probably be fatal:
if( ret != NULL && // no errors so far
wrappers != NULL ) // have a dlopen fixup function
install_wrappers( ret, wrappers,
(const char **)cap->ns->combined_exclude,
errcode, error );
cap->dl_handle = ret;
cleanup:
ld_libs_finish( &ldlibs );
return ret;
}
#include <capsule/capsule.h>
#include "capsule/capsule-private.h"
#include "utils/utils.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <ctype.h>
#include <sys/param.h>
#include <unistd.h>
#define CAP_ENV_PREFIX "CAPSULE_"
#define DUMP_STRV(what, css) \
({ int _i = 0; for( char **_c = css; _c && *_c; _c++, _i++ ) \
DEBUG( DEBUG_CAPSULE, " ->%s[ %02d ]: %s", #what, _i, *_c ); })
#define DUMP_CAPSULE(x, cap) ({ \
DEBUG( DEBUG_CAPSULE, "\nCAPSULE #%d\n", (int) x ); \
DEBUG( DEBUG_CAPSULE, " \n" \
"struct _capsule %p" "\n" \
"{" "\n" \
" void *dl_handle; %p" "\n" \
" capsule_metadata *meta; %p" "\n" \
" {" "\n" \
" const char *soname; %s" "\n" \
" const char *default_prefix; %s" "\n" \
" const char **exclude; %p" "\n" \
" const char **export; %p" "\n" \
" };" "\n" \
" capsule_namespace *ns; %p" "\n" \
" {" "\n" \
" Lmid_t ns; %ld" "\n" \
" char *prefix; %s" "\n" \
" char **combined_exclude; %p" "\n" \
" char **combined_export; %p" "\n" \
" };" "\n" \
"};" "\n", \
cap , \
cap->dl_handle , \
cap->meta , \
cap->meta->soname , \
cap->meta->default_prefix , \
cap->meta->exclude , \
cap->meta->export , \
cap->ns , \
cap->ns->ns , \
cap->ns->prefix , \
cap->ns->combined_exclude , \
cap->ns->combined_export);})
/*
* never_encapsulated:
*
* Array of SONAMEs that never have a private copy inside a capsule.
* Only one copy of each of these will be loaded, and they will always
* be loaded without respecting the #capsule_namespace's @prefix, even
* if loaded from inside a capsule.
*
* This currently contains the libraries built by the glibc source package.
*/
static const char * const never_encapsulated[] =
{
"libBrokenLocale.so.1",
"libanl.so.1",
"libc.so.6",
"libcidn.so.1",
"libcrypt.so.1",
"libdl.so.2",
"libm.so.6",
"libmvec.so.1",
"libnsl.so.1",
"libpthread.so.0",
"libresolv.so.2",
"librt.so.1",
"libthread_db.so.1",
"libutil.so.1",
};
static ptr_list *namespaces = NULL;
ptr_list *_capsule_list = NULL;
dlsymfunc _capsule_original_dlsym = NULL;
dlopnfunc _capsule_original_dlopen = NULL;
freefunc _capsule_original_free = NULL;
mallocfunc _capsule_original_malloc = NULL;
callocfunc _capsule_original_calloc = NULL;
rallocfunc _capsule_original_realloc = NULL;
palignfunc _capsule_original_pmalign = NULL;
static int str_equal (const void *a, const void *b)
{
if( a == b )
return 1;
if( !a || !b )
return 0;
return strcmp( (char *)a, (char *)b ) == 0;
}
static const char *get_prefix_nocopy (const char *dflt, const char *soname);
static capsule_namespace *
get_namespace (const char *default_prefix, const char *soname)
{
const char *prefix = get_prefix_nocopy( default_prefix, soname );
capsule_namespace *ns;
// Normalize so that libraries with prefix NULL, "" or "/" are all
// treated as equivalent
if( prefix == NULL || prefix[0] == '\0' )
prefix = "/";
if( namespaces == NULL )
namespaces = ptr_list_alloc( 4 );
for( size_t x = 0; x < namespaces->next; x++ )
{
ns = ptr_list_nth_ptr( namespaces, x );
if( !ns || strcmp( prefix, ns->prefix ) )
continue;
return ns;
}
ns = calloc( 1, sizeof(capsule_namespace) );
ns->ns = LM_ID_NEWLM;
ns->prefix = strdup( prefix );
ns->exclusions = ptr_list_alloc( 4 );
ns->exports = ptr_list_alloc( 4 );
ns->mem = calloc( 1, sizeof(capsule_memory) );
ptr_list_push_ptr( namespaces, ns );
return ns;
}
// items with names starting with '-' are initialised by hand
// and will be skipped in the automatic section below:
static capsule_item alloc_func[] =
{
{ "-dlopen" , (capsule_addr) NULL },
{ "-free" , (capsule_addr) NULL },
{ "-realloc" , (capsule_addr) NULL },
{ "malloc" , (capsule_addr) &_capsule_original_malloc },
{ "calloc" , (capsule_addr) &_capsule_original_calloc },
{ "posix_memalign", (capsule_addr) &_capsule_original_pmalign },
{ NULL }
};
static void
get_capsule_metadata (struct link_map *map, const char *only)
{
ElfW(Addr) base = map->l_addr;
ElfW(Dyn) *dyn = map->l_ld;
ElfW(Dyn) *entry;
ElfW(Sym) *symbol;
const void *strtab = NULL;
const void *symtab = NULL;
if( map->l_name == NULL || *map->l_name == '\0' )
return;
for( entry = dyn; entry->d_tag != DT_NULL; entry++ )
{
switch( entry->d_tag )
{
case DT_SYMTAB:
symtab = (const void *) fix_addr( (void *)base, entry->d_un.d_ptr );
break;
case DT_STRTAB:
strtab = (const void *) fix_addr( (void *)base, entry->d_un.d_ptr );
break;
default:
break;
}
}
if( !strtab || !symtab )
return;
for( symbol = (ElfW(Sym) *)symtab;
( (ELFW_ST_TYPE(symbol->st_info) < STT_NUM) &&
(ELFW_ST_BIND(symbol->st_info) < STB_NUM) );
symbol++ )
{
capsule_metadata *meta = NULL;
capsule cap = NULL;
char *name = (char *)strtab + symbol->st_name;
if( !name || strcmp( "capsule_meta", name ) )
continue;
meta = (capsule_metadata *)fix_addr( (void *)base, symbol->st_value );
// if we were asked for a specific soname's meta data then ignore
// everything else:
if( only && strcmp( only, meta->soname ) )
continue;
// not a version of the ABI we understand? skip it.
if( meta->capsule_abi != 0 )
continue;
cap = meta->handle;
if( cap == NULL )
{
// Functions we must override in the DSOs
// inside the capsule (mostly to take account of the fact that
// they're pulled in from a tree with a filesystem prefix like /host)
// NOTE: the shim address here isn't used, but we give it the same
// value as the real function address so it's never accidentally
// a value the capsule code will care about:
int i = 0;
static const capsule_item no_wrapper = { NULL };
const capsule_item int_dlopen_wrapper =
{
"dlopen",
(capsule_addr) meta->int_dlopen,
(capsule_addr) meta->int_dlopen,
};
const capsule_item int_free_wrapper =
{
"free",
(capsule_addr) meta->int_free,
(capsule_addr) meta->int_free,
};
const capsule_item int_realloc_wrapper =
{
"realloc",
(capsule_addr) meta->int_realloc,
(capsule_addr) meta->int_realloc,
};
cap = xcalloc( 1, sizeof(struct _capsule) );
cap->ns = get_namespace( meta->default_prefix, meta->soname );
DEBUG( DEBUG_CAPSULE,
"Creating new capsule %p for metadata %p (%s … %s)",
cap, meta, cap->ns->prefix, meta->soname );
cap->meta = meta;
cap->seen.all = ptr_list_alloc( 32 );
cap->seen.some = ptr_list_alloc( 32 );
cap->internal_wrappers[ 0 ] = int_dlopen_wrapper;
cap->internal_wrappers[ 1 ] = int_free_wrapper;
cap->internal_wrappers[ 2 ] = int_realloc_wrapper;
for( i = 3; alloc_func[ i ].name != NULL; i++ )
{
// pre-populated functions that we should skip are
// flagged with an invalid name starting with '-'.
// in a properly laid out file they'll all be at the start of
// alloc_func and we should never see them, but just in case:
if( alloc_func[ i ].name[ 0 ] == '-' )
continue;
capsule_item *capi = &cap->internal_wrappers[ i ];
void **slot = (void **) alloc_func[ i ].real;
// the addresses held in alloc_func are actually the
// addresses of pointers to the real implementations,
// which are populated during capsule_init: we deref
// those here:
capi->name = alloc_func[ i ].name;
capi->shim = capi->real = (capsule_addr) *slot;
}
cap->internal_wrappers[ i ] = no_wrapper;
meta->handle = cap;
ptr_list_push_ptr( _capsule_list, cap );
}
DEBUG( DEBUG_CAPSULE,
"found metadata for %s … %s at %p (capsule: %p)",
cap->ns->prefix, meta->soname, meta, cap );
break;
}
}
/**
* cook_list:
* @list: a ptr_list containing statically-allocated strings
* @extras: (array length=n_extras): extra entries to be added
* @n_extras: number of extra entries
*
* Return @list + @extras as an array of strings. The strings are not
* copied, so the result is only valid as long as the strings are.
*
* Returns: (transfer container) (array zero-terminated=1) (element-type utf8):
* a shallow copy of everything in either @list or @extras
*/
static char **
cook_list (ptr_list *list,
const char * const *extras,
size_t n_extras)
{
char **cooked = calloc( list->next + n_extras + 1, sizeof(char *) );
for( size_t j = 0; j < list->next; j++ )
cooked[j] = (char *)ptr_list_nth_ptr( list, j );
for( size_t j = 0; j < n_extras; j++ )
cooked[j + list->next] = (char *)extras[j];
*(cooked + list->next + n_extras) = NULL;
return cooked;
}
/**
* add_new_strings_to_ptrlist:
* @list: a ptr_list
* @strings: (transfer none) (array zero-terminated=1) (nullable):
*
* Add each string in @strings to @list, unless a string with the same
* content is already present. The strings must continue to exist as long
* as @list does.
*/
static void
add_new_strings_to_ptrlist (ptr_list *list, const char * const *strings)
{
for( const char * const *c = strings; c && *c; c++ )
ptr_list_add_ptr( list, (char *) *c, str_equal );
}
/**
* free_strv:
* @strv: (transfer container) (array zero-terminated=1) (element-type utf8):
*
* Free @strv, but not its contents.
*/
static void
free_strv (char **strv)
{
if( !strv )
return;
free( strv );
}
static void update_namespaces (void);
static void
update_metadata (const char *match)
{
struct link_map *map;
void *handle;
handle = dlmopen( LM_ID_BASE, NULL, RTLD_LAZY|RTLD_NOLOAD );
dlinfo( handle, RTLD_DI_LINKMAP, &map );
// we're not guaranteed to be at the start of the link map chain:
while( map->l_prev )
map = map->l_prev;
// pick up any capsule metadata we can see:
// if the soname (match) is NULL, this means _all_ metadata,
// otherwise just the metadata that is string-equal:
if (map->l_next)
for( struct link_map *m = map; m; m = m->l_next )
get_capsule_metadata( m, match );
update_namespaces();
}
static void
update_namespaces (void)
{
if( !namespaces )
return;
// wipe out the namespaces' merged lists of exclusions etc. -
// they contain strings that point into capsule metadata that might
// no longer be valid, if we dlclosed a shim library
for( size_t i = 0; i < namespaces->next; i++ )
{
capsule_namespace *ns = ptr_list_nth_ptr( namespaces, i );
if( !ns )
continue;
DEBUG( DEBUG_CAPSULE, "Resetting namespace #%zu %p \"%s\"",
i, ns, ns->prefix );
// We don't free the actual strings because we don't own them;
// just truncate the list to 0 entries
ns->exclusions->next = 0;
ns->exports->next = 0;
}
// merge the string lists for each active prefix:
// ie all excludes for /host should be in one exclude list,
// all export entries for /host should bein another list, all
// excludes for /badgerbadger should be in another, etc etc:
for( size_t i = 0; i < _capsule_list->next; i++ )
{
capsule cap = ptr_list_nth_ptr( _capsule_list, i );
if( !cap )
continue;
DEBUG( DEBUG_CAPSULE,
"Collecting strings from capsule #%zu %p \"%s\" into namespace "
"%p \"%s\"",
i, cap, cap->meta->soname, cap->ns, cap->ns->prefix );
add_new_strings_to_ptrlist( cap->ns->exclusions, cap->meta->exclude );
add_new_strings_to_ptrlist( cap->ns->exports, cap->meta->export );
}
// now squash the meta data ptr_lists into char** that
// the underlying infrastructure actually uses:
for( size_t i = 0; i < namespaces->next; i++ )
{
capsule_namespace *ns = ptr_list_nth_ptr( namespaces, i );
if( !ns )
continue;
free_strv( ns->combined_exclude );
free_strv( ns->combined_export );
ns->combined_exclude = cook_list( ns->exclusions,
never_encapsulated,
N_ELEMENTS( never_encapsulated ) );
ns->combined_export = cook_list( ns->exports, NULL, 0 );
}
}
static void __attribute__ ((constructor)) _init_capsule (void)
{
_capsule_list = ptr_list_alloc( 16 );
#ifdef HAVE_SECURE_GETENV
set_debug_flags( secure_getenv("CAPSULE_DEBUG") );
#endif
// these are needed if there is > 1 libc instance:
_capsule_original_free = dlsym( RTLD_DEFAULT, "free" );
_capsule_original_malloc = dlsym( RTLD_DEFAULT, "malloc" );
_capsule_original_calloc = dlsym( RTLD_DEFAULT, "calloc" );
_capsule_original_realloc = dlsym( RTLD_DEFAULT, "realloc" );
_capsule_original_pmalign = dlsym( RTLD_DEFAULT, "posix_memalign" );
update_metadata( NULL );
_capsule_original_dlsym = dlsym( RTLD_DEFAULT, "dlsym" );
_capsule_original_dlopen = dlsym( RTLD_DEFAULT, "dlopen" );
if( !(debug_flags & DEBUG_CAPSULE) )
return;
for( unsigned int x = 0; x < _capsule_list->next; x++ )
{
capsule cap = _capsule_list->loc[ x ].ptr;
if( !cap )
continue;
const char **soname = cap->meta->exclude;
DEBUG( DEBUG_CAPSULE, "[%02d] %s metadata:\n", x, cap->meta->soname );
while( soname && *soname )
fprintf( stderr, " %s\n", *(soname++) );
}
}
char *
capsule_get_prefix (const char *dflt, const char *soname)
{
const char *prefix = get_prefix_nocopy( dflt, soname );
if (prefix != NULL)
return xstrdup( prefix );
return NULL;
}
static const char *
get_prefix_nocopy (const char *dflt, const char *soname)
{
#ifdef HAVE_SECURE_GETENV
char env_var[PATH_MAX] = CAP_ENV_PREFIX;
const size_t offs = strlen( CAP_ENV_PREFIX );
size_t x = 0;
const char *prefix = NULL;
for( ; (x < strlen(soname)) && (x + offs < PATH_MAX); x++ )
{
char c = *(soname + x);
env_var[ x + offs ] = isalnum( c ) ? toupper( c ) : '_';
}
safe_strncpy( &env_var[ x + offs ], "_PREFIX", PATH_MAX - (x + offs + 1) );
env_var[ PATH_MAX - 1 ] = '\0';
DEBUG( DEBUG_CAPSULE, "checking %s\n", &env_var[0] );
if( (prefix = secure_getenv( &env_var[0] )) )
{
DEBUG( DEBUG_SEARCH, "Capsule prefix is %s: %s", &env_var[0], prefix );
return prefix;
}
DEBUG( DEBUG_CAPSULE, "checking %s\n", CAP_ENV_PREFIX "PREFIX" );
if( (prefix = secure_getenv( CAP_ENV_PREFIX "PREFIX" )) )
{
DEBUG( DEBUG_SEARCH, "Capsule prefix is "CAP_ENV_PREFIX"PREFIX: %s",
prefix );
return prefix;
}
#endif
if( dflt )
{
DEBUG( DEBUG_SEARCH, "Capsule prefix is built-in: %s", dflt );
return dflt;
}
DEBUG( DEBUG_SEARCH, "Capsule prefix is missing" );
return NULL;
}
static capsule
get_capsule_by_soname (const char *soname)
{
for( size_t n = 0; n < _capsule_list->next; n++ )
{
capsule cap = ptr_list_nth_ptr( _capsule_list, n );
if( !cap || strcmp( cap->meta->soname, soname ) )
continue;
DUMP_CAPSULE(n, cap);
return cap;
}
return NULL;
}
capsule
capsule_init (const char *soname)
{
capsule cap;
void *dso;
int capsule_errno = 0;
char *capsule_error = NULL;
DEBUG( DEBUG_CAPSULE, "Initializing shim library %s", soname );
cap = get_capsule_by_soname( soname );
if( !cap )
{
DEBUG( DEBUG_CAPSULE, "no metadata for %s registered: "
"may be a dlopened capsule", soname );
DEBUG( DEBUG_CAPSULE, "updating capsule metadata" );
update_metadata( soname );
cap = get_capsule_by_soname( soname );
}
if( !cap )
{
fprintf( stderr,
"libcapsule: %s: Fatal error: cannot initialize shim "
"library (capsule_meta not found)\n",
soname );
abort();
}
for( size_t i = 0; i < _capsule_list->next; i++ )
{
capsule other = ptr_list_nth_ptr( _capsule_list, i );
if( !other )
continue;
DEBUG( DEBUG_CAPSULE, " ");
DUMP_CAPSULE( i, other );
DUMP_STRV( excluded, other->ns->combined_exclude );
DUMP_STRV( exported, other->ns->combined_export );
}
dso = _capsule_load( cap, cap->internal_wrappers,
&capsule_errno, &capsule_error );
if( !dso )
{
fprintf( stderr, "libcapsule: fatal error: %s: %s\n",
soname, capsule_error );
abort();
}
int rloc = _capsule_relocate( cap, &capsule_error );
if( rloc != 0 ) // relocation failed. we're dead.
{
fprintf( stderr,
"libcapsule: fatal error: %s could not install "
"relocations\n",
soname );
abort();
}
rloc = _capsule_relocate_dlopen( cap, &capsule_error );
if( rloc != 0 ) // we may survive this depending on how dlopen is used
fprintf( stderr,
"libcapsule: warning: %s could not install dlopen() "
"dlopen wrappers. This error may or may not be "
"fatal later.\n",
soname );
return cap;
}
#define CLEAR(f,x) ({ f( x ); x = NULL; })
void
capsule_close (capsule cap)
{
capsule_metadata *meta = cap->meta;
DEBUG( DEBUG_CAPSULE, "Uninitializing shim library %s", meta->soname );
// scrub all entries in the manifest pointing to this metadata
for( size_t n = 0; n < _capsule_list->next; n++ )
{
capsule other = ptr_list_nth_ptr( _capsule_list, n );
if( other == cap )
{
_capsule_list->loc[ n ].ptr = NULL;
}
else if( other != NULL )
{
// There should only be one capsule per capsule_metadata
assert( other->meta != meta );
}
}
// Remove any pointers from the namespaces into this capsule
update_namespaces();
meta->handle = NULL;
CLEAR( ptr_list_free, cap->seen.all );
CLEAR( ptr_list_free, cap->seen.some );
// poison the capsule struct and free it
memset( cap, 'X', sizeof(struct _capsule) );
free( cap );
}
#pragma once
#define CAPSULE_MALLOC_EXTRA_CHECKS
#define DEBUG_MALLOC_VOODOO 0
#if DEBUG_MALLOC_VOODOO
#include <stdio.h>
#include <sys/param.h>
// can't printf inside malloc/free et al
// can't jump to an fputs from a different libc either
typedef int (*fputsfunc) (const char *buf, FILE *s);
static fputsfunc wf = NULL;
#define CAN_DEBUG_ALLOCS (wf != NULL)
static void dump_ptr (const char *label, const void *ptr)
{
char ptrbuf[ (sizeof(void *) * 2) + 1 ];
unsigned long x = (unsigned long) ptr;
static const char pattern[] = "0123456789abcdef";
ssize_t start = sizeof(long) * 2;
ptrbuf[ sizeof(ptrbuf) - 1 ] = '\0';
for( size_t i = 0; i < sizeof(long) * 2; i++ )
{
size_t c = (x >> ((((sizeof(long) * 2) - 1) - i) * 4)) & 0b01111;
ptrbuf[ i ] = pattern[ c ];
if( (start == (sizeof(long) * 2)) && (ptrbuf[i] != '0') )
start = i;
}
start = MAX( 0, start - 1 );
wf( label , stderr );
wf( ": <0x", stderr );
wf( ptrbuf + start, stderr );
wf( ">\n" , stderr );
}
#define FETCH_FPUTS() \
if(UNLIKELY(!wf)) wf = (fputsfunc) dlsym(cap->dl_handle, "fputs")
#else
#define dump_ptr(a,b)
#define FETCH_FPUTS()
#define CAN_DEBUG_ALLOCS 0
#endif
////////////////////////////////////////////////////////////////////////////
// copy some vodoo out of libc.
// it is to be hoped that this is a temporary hack but, well…
#define SIZE_SZ (sizeof(size_t))
struct malloc_chunk
{
size_t prev_size; /* Size of previous chunk (if free). */
size_t size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)
////////////////////////////////////////////////////////////////////////////
// extra voodoo. not clear if we need this:
#ifdef CAPSULE_MALLOC_EXTRA_CHECKS
#define PREV_INUSE 0x1
/* similarly for non-main-arena chunks */
#define NON_MAIN_ARENA 0x4
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)
typedef struct malloc_chunk *mfastbinptr;
typedef int mutex_t;
#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])
/* offset 2 to use otherwise unindexable first 2 bins */
#define fastbin_index(sz) \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
/* The maximum fastbin request size we support */
#define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
#define MALLOC_ALIGNMENT (2 *SIZE_SZ < __alignof__ (long double) ? \
__alignof__ (long double) : 2 *SIZE_SZ)
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
#define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)
#define NBINS 128
#define BINMAPSHIFT 5
#define BITSPERMAP (1U << BINMAPSHIFT)
#define BINMAPSIZE (NBINS / BITSPERMAP)
struct malloc_state
{
/* Serialize access. */
mutex_t mutex;
/* Flags (formerly in max_fast). */
int flags;
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];
/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
size_t attached_threads;
/* Memory allocated from the system in this arena. */
size_t system_mem;
size_t max_system_mem;
};
typedef struct malloc_state *mstate;
typedef struct _heap_info
{
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size; /* Current size in bytes. */
size_t mprotect_size; /* Size in bytes that has been mprotected
PROT_READ|PROT_WRITE. */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
#ifndef DEFAULT_MMAP_THRESHOLD_MAX
/* For 32-bit platforms we cannot increase the maximum mmap
threshold much because it is also the minimum value for the
maximum heap size and its alignment. Going above 512k (i.e., 1M
for new heaps) wastes too much address space. */
# if __WORDSIZE == 32
# define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024)
# else
# define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long))
# endif
#endif
#define HEAP_MIN_SIZE (32 * 1024)
#ifndef HEAP_MAX_SIZE
# ifdef DEFAULT_MMAP_THRESHOLD_MAX
# define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX)
# else
# define HEAP_MAX_SIZE (1024 * 1024)
# endif
#endif
#define heap_for_ptr(ptr) \
((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
#define arena_for_chunk(ptr) \
(chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : NULL)
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))
#define NONCONTIGUOUS_BIT (2U)
#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0)
#define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0)
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
#endif // CAPSULE_MALLOC_EXTRA_CHECKS
////////////////////////////////////////////////////////////////////////////
#pragma once
#include <link.h>
#include "utils/utils.h"
typedef void * (*dlsymfunc) (void *handle, const char *symbol);
typedef void * (*dlopnfunc) (const char *file, int flags);
// we need these to transplant the *alloc/free cluster into
// the capsule so that the memory allocation implementation
// is unified (at least until we can force libc to be shared):
typedef void (*freefunc) (void *ptr);
typedef void * (*mallocfunc) (size_t size);
typedef void * (*callocfunc) (size_t nmem, size_t size);
typedef void * (*rallocfunc) (void *ptr, size_t size);
typedef int (*palignfunc) (void **memptr, size_t alignment, size_t size);
typedef struct _capsule_memory
{
freefunc free;
rallocfunc realloc;
mallocfunc malloc; // UNUSED
callocfunc calloc; // UNUSED
palignfunc posix_memalign; // UNUSED
} capsule_memory;
typedef struct _capsule_namespace
{
Lmid_t ns; // dlmopen namespace. LM_ID_NEWLM to create
const char *prefix; // default library tree prefix, eg /host
ptr_list *exclusions; // sonames to ignore
ptr_list *exports; // sonames to expose/export
char **combined_exclude; // combined exclude & export lists from all
char **combined_export; // capsules DSOs sharing the same namespace
capsule_memory *mem;
} capsule_namespace;
struct _capsule
{
void *dl_handle;
struct { ptr_list *all; ptr_list *some; } seen;
capsule_metadata *meta;
capsule_namespace *ns;
capsule_item internal_wrappers[7];
};
extern ptr_list *_capsule_list;
extern dlsymfunc _capsule_original_dlsym;
extern dlopnfunc _capsule_original_dlopen;
extern freefunc _capsule_original_free;
extern callocfunc _capsule_original_calloc;
extern mallocfunc _capsule_original_malloc;
extern rallocfunc _capsule_original_realloc;
extern palignfunc _capsule_original_pmalign;
/*
* _capsule_load:
* @capsule: a #capsule handle as returned by capsule_init()
* @dso: The name of the DSO to open (cf dlopen()) - eg libGL.so.1
* @namespace: (out) Address of an #Lmid_t value.
* @wrappers: Array of #capsule_item used to replace symbols in the namespace
* @errcode: (out): location in which to store the error code (errno) on failure
* @error: (out) (transfer full) (optional): location in which to store
* an error message on failure, or %NULL to ignore.
* Free with free().
*
* Returns: A (void *) DSO handle, as per dlopen(), or %NULL on error
*
* Opens @dso (a library) from a filesystem mounted at @prefix into a
* symbol namespace specified by @namespace, using dlmopen().
*
* Any symbols specified in @wrappers will be replaced with the
* corresponding address from @wrappers (allowing you to replace
* function definitions inside the namespace with your own).
*
* This is normally used to replace calls inside the namespace to
* certain functions (such as dlopen()) which need to be overridden
* to operate correctly inside a private namespace associated with
* a nonstandard filsystem tree.
*
* The #Lmid_t value stored in @namespace gives the symbol namespace
* into which the target DSO was loaded. It should normally be #LM_ID_NEWLM.
*
* In addition to a bare libFOO.so.X style name, @dso may be an
* absolute path (or even a relative one) and in those cases should
* have the same effect as passing those values to dlopen(). This is
* not a normal use case and has not been heavily tested.
*
*/
void *_capsule_load (const capsule capsule,
capsule_item *wrappers,
int *errcode,
char **error);
/*
* _capsule_relocate:
* @capsule: a #capsule handle as returned by capsule_init()
* @error: (out) (transfer full) (optional): location in which to store
* an error message on failure, or %NULL to ignore.
* Free with free().
*
* Returns: 0 on success, non-zero on failure.
*
* This function updates the GOT entries in all DSOs outside the capsule
* so that when they call any function listed in the @items of the
* #capsule_metadata, they invoke the copy of that function inside the capsule.
*
* In the unlikely event that an error message is returned in @error it is the
* caller's responsibility to free() it.
*/
int _capsule_relocate (const capsule capsule,
char **error);
/*
* _capsule_relocate_dlopen:
* @capsule: a #capsule handle as returned by capsule_init()
* @error: (out) (transfer full) (optional): location in which to store
* an error message on failure, or %NULL to ignore.
* Free with free().
*
* Returns: 0 on success, non-zero on failure.
*
* This function updates the GOT entries in all DSOs outside the capsule
* _except_ those listed in @except: When they call dlopen(), instead
* they invoke the copy of that function provided by libcapsule.
*
* In the unlikely event that an error message is returned in @error it is the
* caller's responsibility to free() it.
*/
int _capsule_relocate_dlopen (const capsule capsule,
char **error);
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule 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 Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <capsule/capsule.h>
#include "capsule/capsule-private.h"
#include "utils/dump.h"
#include "utils/utils.h"
#include "utils/process-pt-dynamic.h"
// ==========================================================================
// some entries require us to peer into others to make sense of them:
// can't make full sense of relocations without looking names up
// in the stringtab, which does not have to occur at any fixed point in
// in the PT_DYNAMIC entry.
// IOW PT_DYNAMIC contains both relocations (DT_RELA, DT_REL) and a stringtab
// (DT_STRTAB) in arbitrary order but the former do not make sense without
// the latter.
// =========================================================================
/*
* process_phdr:
* @info: struct dl_phdr_info describing the shared object
* @size: sizeof(info)
* @rdata: the closure that we passed to dl_iterate_phdr()
*
* Callback called for each shared object loaded into the program.
*/
static int
process_phdr (struct dl_phdr_info *info,
size_t size,
relocation_data *rdata)
{
int ret = 0;
for( int j = 0; !ret && (j < info->dlpi_phnum); j++ )
{
if( info->dlpi_phdr[j].p_type == PT_DYNAMIC )
{
ret = process_pt_dynamic( info->dlpi_phdr[j].p_vaddr,
info->dlpi_phdr[j].p_memsz,
(void *) info->dlpi_addr,
process_dt_rela,
process_dt_rel,
rdata );
}
}
if( ret == 0 && rdata->seen != NULL )
ptr_list_push_addr( rdata->seen, info->dlpi_addr );
return ret;
}
static int
dso_is_blacklisted (const char *path, relocation_flags flags)
{
const char * const *soname;
const char * const libc[] =
{
"libc.so",
"libdl.so",
"libpthread.so",
NULL
};
static const char *never = "libcapsule.so";
if( soname_matches_path( never, path ) )
return 1;
if( flags & RELOCATION_FLAGS_AVOID_LIBC )
for( soname = libc; soname && *soname; soname++ )
if( soname_matches_path( *soname, path ) )
return 1;
return 0;
}
static int
dso_has_been_relocated (ptr_list *seen, ElfW(Addr) base)
{
if( seen == NULL )
return 0;
if( ptr_list_contains( seen, base ) )
return 1;
return 0;
}
// first level of the callback: all we're doing here is skipping over
// any program headers that (for whatever reason) we decide we're not
// interested in.
// In practice we have to handle all existing DSOs, as any of them may
// call into the library we are acting as a shim for.
static int
relocate_cb (struct dl_phdr_info *info, size_t size, void *data)
{
relocation_data *rdata = data;
const char *dso_path = *info->dlpi_name ? info->dlpi_name : "-elf-";
if( dso_is_blacklisted( dso_path, rdata->flags ) )
{
DEBUG( DEBUG_RELOCS, "skipping %s %p (blacklisted)",
dso_path, (void *) info->dlpi_addr );
return 0;
}
if( dso_has_been_relocated( rdata->seen, info->dlpi_addr ) )
{
DEBUG( DEBUG_RELOCS, "skipping %s %p (already relocated)",
dso_path, (void *) info->dlpi_addr );
return 0;
}
DEBUG( DEBUG_RELOCS, "processing %s %p",
dso_path, (void *) info->dlpi_addr );
return process_phdr( info, size, rdata );
}
static int relocate (const capsule cap,
capsule_item *relocations,
relocation_flags flags,
ptr_list *seen,
char **error)
{
relocation_data rdata = { 0 };
capsule_item *map;
int mmap_errno = 0;
const char *mmap_error = NULL;
int rval = 0;
// load the relevant metadata into the callback argument:
rdata.debug = debug_flags;
rdata.error = NULL;
rdata.flags = flags;
rdata.mmap_info = load_mmap_info( &mmap_errno, &mmap_error );
rdata.relocs = relocations;
rdata.seen = seen;
if( mmap_errno || mmap_error )
{
DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT,
"mmap/mprotect flags information load error (errno: %d): %s\n",
mmap_errno, mmap_error );
DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT,
"relocation will be unable to handle relro linked libraries" );
}
// no source dl handle means we must have a pre-populated
// map of shim-to-real function pointers in `relocations',
// otherwise populate the map using [the real] dlsym():
if( cap->dl_handle )
for( map = cap->meta->items; map->name; map++ )
{
if( !map->shim )
map->shim = (ElfW(Addr)) _capsule_original_dlsym( RTLD_DEFAULT, map->name );
if( !map->real )
map->real = (ElfW(Addr)) _capsule_original_dlsym( cap->dl_handle, map->name );
}
// time to enter some sort of ... dangerous... zone:
// we need the mmap()ed DSO regions to have the PROT_WRITE
// flag set, so that if they've been RELRO linked we can still
// overwrite their GOT entries.
for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ )
if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) )
add_mmap_protection( &rdata.mmap_info[i], PROT_WRITE );
dl_iterate_phdr( relocate_cb, &rdata );
// and now we put those PROT_WRITE permissions back the way they were:
for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ )
if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) )
reset_mmap_protection( &rdata.mmap_info[i] );
if( rdata.error )
{
if( error )
*error = rdata.error;
else
free( rdata.error );
rval = (rdata.count.failure == 0) ? -1 : rdata.count.failure;
}
free_mmap_info( rdata.mmap_info );
rdata.mmap_info = NULL;
return rval;
}
int
_capsule_relocate (const capsule cap, char **error)
{
DEBUG( DEBUG_RELOCS, "beginning global symbol relocation:" );
return relocate( cap, cap->meta->items, RELOCATION_FLAGS_NONE, cap->seen.all, error );
}
static capsule_item capsule_external_dl_relocs[] =
{
{ "dlopen",
(capsule_addr) capsule_external_dlopen ,
(capsule_addr) capsule_external_dlopen },
{ NULL }
};
int
_capsule_relocate_dlopen (const capsule cap,
char **error)
{
unsigned long df = debug_flags;
if( debug_flags & DEBUG_DLFUNC )
debug_flags |= DEBUG_RELOCS;
DEBUG( DEBUG_RELOCS, "beginning restricted symbol relocation:" );
int rv = relocate( cap, capsule_external_dl_relocs,
RELOCATION_FLAGS_AVOID_LIBC, cap->seen.some, error );
debug_flags = df;
return rv;
}
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule 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 Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <execinfo.h>
#include <capsule/capsule.h>
#define UNVERSIONED_STUB(name) \
void name (void) __attribute__((noreturn,visibility("default"))); \
void name (void) \
{ \
fprintf(stderr, "! SHIM " #name " called\n" ); \
backtrace_shim_call(); \
abort(); \
}
#define VERSIONED_STUB(name,version) \
UNVERSIONED_STUB(name);
static void
backtrace_shim_call (void)
{
void *trace[16] = { NULL };
int traced = 0;
char **symbols = NULL;
traced = backtrace( trace, sizeof(trace)/sizeof(void *) );
symbols = backtrace_symbols( trace, traced );
for( int x = 1; x < traced; x++ )
fprintf( stderr, " -> %s\n", symbols[x] );
free( symbols );
}
// We don't support versioned symbols properly yet, they need som
// asm magic that looks like this and I'm not clear on the details:
// __asm__(".symver _" #name "," #name #version)
#include "capsule/capsule.h"
#include "capsule/capsule-private.h"
#include "capsule/capsule-malloc.h"
#include "utils/utils.h"
#include "utils/ld-libs.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
static int
dso_is_exported (const char *dsopath, char **exported)
{
for( char **ex = exported; ex && *ex; ex++ )
if( soname_matches_path( *ex, dsopath ) )
return 1;
return 0;
}
static void *
_dlsym_from_capsules (const char *symbol)
{
void *addr = NULL;
for( size_t n = 0; n < _capsule_list->next; n++ )
{
capsule cap = ptr_list_nth_ptr( _capsule_list, n );
if( !cap )
continue;
// TODO: If handle != cap->dl_handle, should we skip it?
// TODO: RTLD_NEXT isn't implemented (is it implementable?)
addr = _capsule_original_dlsym ( cap->dl_handle, symbol );
if( addr )
{
Dl_info dso = { 0 };
// only keep addr from the capsule if it's from an exported DSO:
// or if we are unable to determine where it came from (what?)
if( dladdr( addr, &dso ) )
{
if( !dso_is_exported( dso.dli_fname, cap->ns->combined_export ) )
addr = NULL;
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"symbol %s is from soname %s - %s",
symbol, dso.dli_fname, addr ? "OK" : "Ignored" );
if( addr )
break;
}
}
}
return addr;
}
static int
_dlsymbol_is_encapsulated (const void *addr)
{
Dl_info dso = { 0 };
// no info, symbol may not even be valid:
if( !dladdr( addr, &dso ) )
return 0;
// no file name, can't be a shim:
if( !dso.dli_fname || *dso.dli_fname == '\0' )
return 0;
// check to see if addr came from a registered capsule:
for( size_t n = 0; n < _capsule_list->next; n++ )
{
capsule cap = ptr_list_nth_ptr( _capsule_list, n );
if( cap && soname_matches_path( cap->meta->soname, dso.dli_fname) )
return 1;
}
return 0;
}
// TODO: Implement dlvsym()?
// TODO: RTLD_NEXT needs special handling
// revised algorithm here:
//
// use the vanilla dlsym
// if nothing is found, peek into the whole capsule; return the result
//
// if a symbol is found, check to see if it came from a shim
// if it did (ie it is a dummy), peek into the capsule as above
// if it did not, return what was found
//
// The main weakness here is that if the caller expects to find a
// symbol XYZ via ‘handle’ which does _not_ come from the capsule
// but the capsule also has a symbol XYZ which is from an explicitly
// exported-from soname then the caller will get the capsule's
// XYZ symbol.
//
// We can't just check for RTLD_DEFAULT as the handle since
// dlopen( NULL, … ) and/or the RTLD_GLOBAL flag can be used to
// promote symbols that would otherwise not be visible from a given
// handle (libGL does this).
void *
capsule_external_dlsym (void *handle, const char *symbol)
{
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, "dlsym(%s)", symbol );
void *addr = _capsule_original_dlsym ( handle, symbol );
// nothing found, must be from a capsule or nowhere at all:
if( !addr )
{
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"%s not found, searching capsule", symbol );
addr = _dlsym_from_capsules( symbol );
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"capsule %s has address %p", symbol, addr );
return addr;
}
// found something. is it a dummy symbol from a shim?
if( _dlsymbol_is_encapsulated( addr ) )
{
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"dummy %s found, searching capsule", symbol );
addr = _dlsym_from_capsules( symbol );
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"capsule %s has address %p", symbol, addr );
return addr;
}
DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS,
"vanilla %s found at %p", symbol, addr );
return addr;
}
void *
capsule_external_dlopen(const char *file, int flag)
{
void *handle = NULL;
char *error = NULL;
if( _capsule_original_dlopen )
{
handle = _capsule_original_dlopen ( file, flag );
}
else
{
fprintf( stderr,
"capsule_external_dlopen() has no dlopen() implementation\n" );
abort();
}
if( handle != NULL )
{
unsigned long df = debug_flags;
if( debug_flags & DEBUG_DLFUNC )
debug_flags |= DEBUG_RELOCS;
// This may not even be necessary, so it should not be fatal.
// We do want to log it though as it might be an important clue:
for( size_t n = 0; n < _capsule_list->next; n++ )
{
const capsule c = ptr_list_nth_ptr( _capsule_list, n );
if( !c )
continue;
if( _capsule_relocate( c, &error ) != 0 )
{
fprintf( stderr,
"relocation from %s after dlopen(%s, …) failed: %s\n",
c->meta->soname, file, error );
free( error );
}
if( _capsule_relocate_dlopen( c, &error ) != 0 )
{
fprintf( stderr,
"dl-wrapper relocation from %s after "
"dlopen(%s, …) failed: %s\n",
c->meta->soname, file, error );
free( error );
}
}
debug_flags = df;
}
return handle;
}
void *
capsule_shim_dlopen(const capsule cap, const char *file, int flag)
{
void *res = NULL;
int code = 0;
char *errors = NULL;
ld_libs ldlibs = {};
DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC,
"dlopen(%s, %x) wrapper: LMID: %ld; prefix: %s;",
file, flag, cap->ns->ns, cap->ns->prefix );
if( cap->ns->prefix && strcmp(cap->ns->prefix, "/") )
{
if( !ld_libs_init( &ldlibs, (const char **)cap->ns->combined_exclude,
cap->ns->prefix, debug_flags, &code, &errors ) )
{
DEBUG( DEBUG_LDCACHE|DEBUG_WRAPPERS|DEBUG_DLFUNC,
"Initialising ld_libs data failed: error %d: %s",
code, errors);
goto cleanup;
}
if( !ld_libs_load_cache( &ldlibs, &code, &errors ) )
{
DEBUG( DEBUG_LDCACHE|DEBUG_WRAPPERS|DEBUG_DLFUNC,
"Loading ld.so.cache from %s: error %d: %s", cap->ns->prefix,
code, errors );
goto cleanup;
}
// find the initial DSO (ie what the caller actually asked for):
if( !ld_libs_set_target( &ldlibs, file, &code, &errors ) )
{
DEBUG( DEBUG_SEARCH|DEBUG_WRAPPERS|DEBUG_DLFUNC,
"Not found: %s under %s: error %d: %s",
file, cap->ns->prefix, code, errors );
goto cleanup;
}
// harvest all the requested DSO's dependencies:
if( !ld_libs_find_dependencies( &ldlibs, &code, &errors ) )
{
DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC,
"capsule dlopen error %d: %s", code, errors );
goto cleanup;
}
// load them up in reverse dependency order:
res = ld_libs_load( &ldlibs, &cap->ns->ns, flag, &code, &errors );
if( !res )
DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC,
"capsule dlopen error %d: %s", code, errors );
goto cleanup;
}
else // no prefix: straightforward dlmopen into our capsule namespace:
{
res = dlmopen( cap->ns->ns, file, flag );
if( !res )
DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC,
"capsule dlopen error %s: %s", file, dlerror() );
}
return res;
cleanup:
ld_libs_finish( &ldlibs );
free( errors );
return res;
}
#ifdef CAPSULE_MALLOC_EXTRA_CHECKS
static inline int chunk_is_vanilla (mchunkptr p, void *ptr)
{
mstate av = arena_for_chunk (p);
// arena_for_chunk can't find the main arena... but if this poiner
// is from the _main_ main arena then I think it would have been
// trapped by the heap check in capsule_shim_free already, so
// this did not come from the main instance of libc:
if( LIKELY(!av) )
return 0;
size_t size = chunksize (p);
mchunkptr nextchunk = chunk_at_offset(p, size);
// invalid next size (fast)
if( UNLIKELY( nextchunk->size <= 2 * SIZE_SZ ) ||
UNLIKELY( chunksize( nextchunk ) >= av->system_mem) )
return 0;
// double free or corruption (out)
if( UNLIKELY( contiguous(av) &&
(char *)nextchunk >= (char *)av->top + chunksize( av->top )) )
return 0;
return 1;
}
#else
static inline chunk_is_vanilla (mchunkptr p, void *ptr) { return 0; }
#endif
static int address_within_main_heap (ElfW(Addr) addr)
{
static ElfW(Addr) base = (ElfW(Addr)) NULL;
ElfW(Addr) top = (ElfW(Addr)) sbrk( 0 );
// past the end of the heap:
if( top <= addr )
return 0;
if( base == (ElfW(Addr)) NULL )
{
struct mallinfo mi = mallinfo();
base = top - (ElfW(Addr)) mi.arena;
}
// address is below heap base
// ∴ either a mmapped address, non-malloc'd memory
// or an address from a secondary arena
if( base > addr )
return 0;
return 1;
}
void *
capsule_shim_realloc (const capsule cap, void *ptr, size_t size)
{
if( !ptr || address_within_main_heap( (ElfW(Addr)) ptr ) )
return realloc( ptr, size );
mchunkptr p = mem2chunk( ptr );
if( chunk_is_mmapped( p ) || chunk_is_vanilla( p, ptr ) )
return realloc( ptr, size );
return cap->ns->mem->realloc( ptr, size );
}
void
capsule_shim_free (const capsule cap, void *ptr)
{
if( !ptr )
return;
// from the main heap: ie from the vanilla libc outside the capsule
if( address_within_main_heap( (ElfW(Addr)) ptr ) )
{
free( ptr );
return;
}
mchunkptr p = mem2chunk( ptr );
// mmapped pointer/chunk: can't tell whose this is but since we
// override the malloc/free cluster as early as possible we're
// kind of hoping we don't have any of these from inside the capsule
//
// we'd only have such a pointer if the libraries we dlmopen() into
// the capsule allocated large chunks of memory in their initialiser(s):
if( chunk_is_mmapped( p ) || chunk_is_vanilla( p, ptr ) )
{
free( ptr );
return;
}
// doesn't look like a valid pointer to the main libc,
// pass it to the capsule libc and hope for the best:
cap->ns->mem->free( ptr );
}
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule 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 Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <link.h>
#define _CAPSULE_PUBLIC __attribute__((visibility("default"))) extern
/**
* capsule_addr:
*
* Identical to an ElfW(Addr) from libelf. You may treat this as
* equivalent to a void * when assigning to it.
*/
typedef ElfW(Addr) capsule_addr;
/**
* capsule
*
* A handle returned by capsule_init: A required parameter for all
* other capsule calls.
*/
typedef struct _capsule *capsule;
/**
* capsule_item:
* @name: The name of the symbol to be relocated
* @real: address of the ‘real’ symbol in the target library
* @shim: address of the ‘fake’ symbol in the proxy library
*
* @real and @shim may typically be left empty by the shim library.
*
* Both slots will typically hold the correct values after a successful
* capsule… call. While this is sometimes important internally it is
* not usually of interest to the caller (except maybe for debugging)
*/
typedef struct _capsule_item capsule_item;
struct _capsule_item
{
const char *name;
capsule_addr real;
capsule_addr shim;
/*< private >*/
void *unused0;
void *unused1;
void *unused2;
void *unused3;
};
/**
* capsule_metadata:
* @capsule_abi: Version of the libcapsule ABI implmented by this struct
* @soname: The soname of the encapsulated library
* @default_prefix: The default root location of the filesystem from which the
* encapsulated library should be loaded
* @exclude: (array zero-terminated=1): an array of char *, each
* specifying a DSO not to load, terminated by a %NULL entry
* @export: (array zero-terminated=1): an array of char *, each
* specifying a DSO whose symbols should be exported from this
* capsule
* @items: (array zero-terminated=1): Array of capsule_item
* specifying which symbols to export, terminated by a
* #capsule_item whose @name is %NULL
* @int_dlopen: Implementation of the same API as `dlopen`
*
* This struct allows libcapsule proxy libraries to statically declare
* metadata about themselves that libcapsule needs at link time in order
* to function properly.
*
* The #capsule_item entries in @items need only specify the symbol
* name: The shim and real fields will be populated automatically if they
* are not pre-filled (this is the normal use case, as it would be unusual
* to know these value in advance).
*/
typedef struct _capsule_metadata capsule_metadata;
struct _capsule_metadata
{
/*< public >*/
const int capsule_abi;
const char *soname;
const char *default_prefix;
const char **exclude;
const char **export;
capsule_item *items;
void *(*int_dlopen) (const char *filename, int flag);
void (*int_free) (void *ptr);
void *(*int_realloc) (void *ptr, size_t size);
/*< private >*/
capsule handle;
};
/**
* capsule_init:
* @soname: the soname of the target library
*
* Returns: a #capsule handle.
*
* Does any initialisation necessary to use libcapsule’s functions.
*
* Initialises internal accounting structures within the capsule
* and triggers the metadata setup if this caspsule has been
* acquired via dlopen(), and finishes registering the capsule
* proxy with libcapsule itself.
*/
_CAPSULE_PUBLIC
capsule capsule_init (const char *soname);
////////////////////////////////////////////////////////////////////////////
// do not move the Returns declaration in this next doc-comment:
// gtk-doc does not tolerate any non-whitespace text between the
// last parameter and the Returns line if the name of the function
// is capsule_shim_dlopen. This was discovered empirically.
// As this is clearly insane, it must be a bad interaction with something
// else which shares some element of that name, but I have no idea what.
////////////////////////////////////////////////////////////////////////////
/**
* capsule_shim_dlopen:
* @cap: The capsule from which `dlopen` was called
* @file: SONAME or filename to be opened
* @flag: `dlopen` flags to pass to the real `dlopen` call
*
* Returns: A handle as if for `dlopen`
*
* An implementation of dlopen suitable to be called from inside a
* namespace. Load @file into @cap namespace.
*
* If @cap has a non-trivial prefix, load @file and its recursive
* dependencies from @cap prefix instead of from the root filesystem.
*
* This helper function exists because dlopen() cannot safely be called
* by a DSO opened into a private namespace. It takes @file and @flag
* arguments cf dlopen() and a @cap handle,
* and performs a safe dlmopen() call instead.
*
* Typically this function is used to implement a safe wrapper for dlopen()
* which is assigned to the int_dlopen member of the #capsule_metadata.
* This * replaces calls to dlopen() by all DSOs in the capsule,
* allowing libraries which use dlopen() to work inside the capsule.
*
* Limitations: RTLD_GLOBAL is not supported in @flag. This is a glibc
* limitation in the dlmopen() implementation.
*/
_CAPSULE_PUBLIC
void *capsule_shim_dlopen (const capsule cap, const char *file, int flag);
/**
* capsule_external_dlsym:
* @handle: A dl handle, as passed to dlsym()
* @symbol: A symbol name, as passed to dlsym()
*
* An implementation of `dlsym`, used when it is called by the executable
* or by a library outside the capsule.
*
* Some libraries have a use pattern in which their caller/user
* uses dlsym() to obtain symbols rather than using those symbols
* directly in its own code (libGL is an example of this).
*
* Since the target library may have a different symbol set than the
* one the libcapsule proxy shim was generated from we can’t rely on
* dlsym() finding those symbols in the shim’s symbol table.
*
* Instead we must intercept dlsym() calls made outside the capsule
* and attempt to look for the required symbol in the namespace defined
* by the active capsules first - If the required symbol is found there
* AND is from one of the DSO names present in the exported list then that
* symbol is returned. If either of those conditions is not met then
* a normal dlsym call with the passed handle is made.
*
* This function provides the functionality described above, and is
* normally used automatically by libcapsule. It is exposed as API in
* case a libcapsule proxy library needs to provide its own specialised
* symbol lookup mechanism.
*
* Returns: The address associated with @symbol (as if for `dlsym`), or %NULL
*/
_CAPSULE_PUBLIC
void *capsule_external_dlsym (void *handle, const char *symbol);
/**
* capsule_external_dlopen:
* @file: A soname, filename or path as passed to dlopen()
* @flag: The dl flags, as per dlopen()
*
* An implementation of `dlopen`, used when it is called by the executable
* or by a library outside the capsule.
*
* This wrapper is meant to be replace normal calls to dlopen() made by the
* main program or a non-capsule library - it is necessary because en ELF
* object loaded by dlopen() may need us to trigger the _capsule_relocate()
* operation in order to make sure its GOT entries are correctly updated.
*
* This wrapper carries out a normal dlopen() and then re-triggers the
* initial _capsule_relocate() call immediately, before returning
* the same value that dlopen() would have, given the same @file and @flag
* arguments.
*
* Returns: The handle returned by `dlopen`
*/
_CAPSULE_PUBLIC
void *capsule_external_dlopen(const char *file, int flag);
/**
* capsule_close:
* @cap: a #capsule handle as returned by capsule_init()
*
* This function should be called from a capsule proxy library’s destructor:
* Its job is to clean up capsule-specific allocated memory and metadata when
* a capsule proxy is discarded via dlclose(), and ensure that libproxy itself
* won’t try to access any related invalidated memory afterwards.
*/
_CAPSULE_PUBLIC
void capsule_close (capsule cap);
/**
* capsule_get_prefix:
* @dflt: A default capsule prefix path
* @soname: The soname of the library we are encapsulating
*
* Returns: A newly allocated char * pointing to the prefix path
*
* libcapsule provides a proxy to a library, potentially from a foreign
* filesystem tree (found at, for example, ‘/host’).
*
* Since it is useful for this location to be overrideable at startup
* on a per-target-library basis this function standardises the prefix
* selection algorithm as follows:
*
* - An environment variable based on @soname:
* libGL.so.1 would map to CAPSULE_LIBGL_SO_1_PREFIX
* - If that is unset, the CAPSULE_PREFIX environment variable
* - Next: The default to the value passed in @dflt
* - And if all that failed, NULL (which is internally equivalent to "/")
*
* The environment variables are ignored if the process is privileged
* (setuid, setgid, given special capabilities, or marked as privileged
* by a LSM), or if libcapsule was compiled against a glibc version
* older than 2.17.
*
* Although the value is newly allocated it will typically be cached
* in a structure that needs to survive the entire lifespan of the
* running program, so freeing it is unlikely to be a concern.
**/
_CAPSULE_PUBLIC
char *capsule_get_prefix(const char *dflt, const char *soname);
/**
* capsule_shim_free:
* @cap: The capsule from which `free` was called
* @ptr: The pointer to be freed
*
* Tries to safely route an allocated pointer to the correct free()
* implementation.
*
*/
_CAPSULE_PUBLIC
void capsule_shim_free (const capsule cap, void *ptr);
_CAPSULE_PUBLIC
void * capsule_shim_realloc (const capsule cap, void *ptr, size_t size);
#!/usr/bin/env groovy
/*
* SPDX-License-Identifier: LGPL-2.1+
*
* Copyright © 2017-2018 Collabora Ltd
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This package 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this package. If not, see
* <http://www.gnu.org/licenses/>.
*/
@Library('steamos-ci') _
pipeline {
agent none
options {
timestamps()
skipDefaultCheckout()
}
stages {
stage ("deb-build-snapshot") {
agent {
docker {
label 'docker-slave'
image "docker-registry.internal.collabora.com/steamos/package-builder:${env.CI_SUITE ?: 'brewmaster'}"
registryUrl 'https://docker-registry.internal.collabora.com'
registryCredentialsId 'ccu-docker-internal-collabora-com'
alwaysPull true
}
}
environment {
HOME="${env.WORKSPACE}"
TMPDIR="${env.WORKSPACE}"
PYTHONUNBUFFERED="1"
}
steps {
sh '''
git config --global user.name Jenkins
git config --global user.email nobody@example.com
'''
checkoutCollaboraGitlab('vivek/libcapsule', 'master', 'src')
sh '''
mkdir -p debs
echo "jenkins::$(id -u):$(id -g):Jenkins:$(pwd):/bin/sh" > passwd
export NSS_WRAPPER_PASSWD=$(pwd)/passwd
export NSS_WRAPPER_GROUP=/dev/null
export LD_PRELOAD=libnss_wrapper.so
( cd src && deb-build-snapshot -d ../debs -s -u localhost )
'''
stash name: 'debs', includes: 'debs/**'
archiveArtifacts 'debs/**'
}
post {
always {
deleteDir()
}
}
}
stage ("autopkgtest") {
agent {
docker {
label 'docker-slave'
args '--device=/dev/kvm'
image 'docker-registry.internal.collabora.com/steamos/package-builder:stretch'
registryUrl 'https://docker-registry.internal.collabora.com'
registryCredentialsId 'ccu-docker-internal-collabora-com'
alwaysPull true
}
}
environment {
HOME="${env.WORKSPACE}"
TMPDIR="${env.WORKSPACE}"
PYTHONUNBUFFERED="1"
}
steps {
sh 'git config --global user.name Jenkins'
sh 'git config --global user.email nobody@example.com'
checkoutCollaboraGitlab('vivek/libcapsule', 'master', 'src')
unstash 'debs'
sh '''
mkdir -p tmp
mkdir -p output
wget --progress=dot:giga -O autopkgtest.qcow2 https://images.collabora.co.uk/steam/autopkgtest-virt-qemu/${CI_SUITE:=brewmaster}_amd64.qcow2
case "${CI_SUITE}" in
(brewmaster)
set -- --add-apt-source='deb http://deb.debian.org/debian jessie main'
for x in /etc/apt/trusted.gpg.d/*.gpg; do
set -- --copy="$x:$x" "$@"
done
;;
(*)
set --
;;
esac
autopkgtest "$@" --apt-upgrade --no-built-binaries --output-dir ./output/ debs/*.deb ./src/ -- qemu --qemu-options="-drive format=raw,read-only,if=pflash,file=/usr/share/ovmf/OVMF.fd" ./autopkgtest.qcow2
'''
}
post {
always {
archiveArtifacts 'output/**'
deleteDir()
}
}
}
}
}
/* vim:set sts=2 sw=2 et: */
# Copyright © 2019 Collabora Ltd.
#
# SPDX-License-Identifier: MIT
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
variables:
BUILD_IMAGE: 'debian:buster-slim'
CI_DISABLE_LIBRARY: ''
CI_DISTCHECK: ''
CI_MAKE_J: '-j -l2'
# A known-good version of deb-build-snapshot
DEB_BUILD_SNAPSHOT_TAG: v0.20190828.0
before_script:
- |
set -eux
apt-get -y update
apt-get install -y --no-install-recommends \
autoconf \
autoconf-archive \
automake \
bubblewrap \
build-essential \
ca-certificates \
chrpath \
debhelper \
devscripts \
dh-autoreconf \
git \
gtk-doc-tools \
libelf-dev \
libipc-run-perl \
libglib2.0-dev \
libxml2 \
perl \
python3-debian \
rsync \
xsltproc \
zlib1g \
${NULL+}
.build:
image: ${BUILD_IMAGE}
script: |
set -eux
NOCONFIGURE=1 ./autogen.sh
./configure \
CC=${CC-cc} \
${CI_DISABLE_LIBRARY+--disable-library} \
SANITIZE_CFLAGS="${SANITIZE_CFLAGS-}" \
${NULL+}
make ${CI_MAKE_J}
make ${CI_MAKE_J} check
if [ -n "$CI_DISTCHECK" ]; then
make ${CI_MAKE_J} distcheck
fi
artifacts:
paths:
- config.log
- test-suite.log
build:ubsan:
extends: .build
variables:
SANITIZE_CFLAGS: "-fsanitize=undefined -fno-sanitize=alignment -static-libubsan"
build:asan:
extends: .build
variables:
# The actual libcapsule library doesn't work with AddressSanitizer,
# but at least we can unit-test utility code and test the tools
CI_DISABLE_LIBRARY: "yes"
SANITIZE_CFLAGS: "-fsanitize-recover=all -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fPIE -pie"
.deb-build-snapshot:
image: ${BUILD_IMAGE}
script: |
set -eux
set -- --upstream --source
mkdir -p "debian/tmp/artifacts/${CI_JOB_NAME}"
set -- "$@" --download "$(pwd)/debian/tmp/artifacts/${CI_JOB_NAME}"
if [ -n "${CI_DISPOSABLE_ENVIRONMENT-}" ]; then
set -- "$@" --install --force-local-install
fi
tempdir="$(mktemp -d)"
git clone --branch ${DEB_BUILD_SNAPSHOT_TAG} https://gitlab.collabora.com/smcv/deb-build-snapshot.git "$tempdir/deb-build-snapshot"
"$tempdir/deb-build-snapshot/deb-build-snapshot" "$@" localhost
rm -fr "$tempdir"
artifacts:
paths:
- debian/tmp/artifacts/${CI_JOB_NAME}
build:stretch:
extends: .deb-build-snapshot
variables:
BUILD_IMAGE: 'debian:stretch-slim'
build:buster:
extends: .deb-build-snapshot
variables:
BUILD_IMAGE: 'debian:buster-slim'
build:bionic:
extends: .deb-build-snapshot
variables:
BUILD_IMAGE: 'ubuntu:bionic'
build:arch:
when: manual
extends: .build
variables:
BUILD_IMAGE: 'archlinux'
before_script:
# bubblewrap-suid instead of bubblewrap will be necessary on some
# kernels.
#
# Full test coverage is likely to require running with
# docker run --privileged, which Gitlab-CI doesn't normally do;
# as much as anything, this is here to document what's necessary
# to build libcapsule with full test coverage.
- |
pacman -Syu --needed --noconfirm --noprogressbar \
autoconf \
autoconf-archive \
automake \
bubblewrap \
chrpath \
gcc \
glib2 \
gtk-doc \
libelf \
libtool \
libxml2 \
libxslt \
make \
perl \
perl-ipc-run \
pkgconf \
sudo \
${NULL+}
dnl Copyright © 2017 Collabora Ltd
dnl This file is part of libcapsule.
dnl libcapsule is free software: you can redistribute it and/or modify
dnl it under the terms of the GNU Lesser General Public License as
dnl published by the Free Software Foundation; either version 2.1 of the
dnl License, or (at your option) any later version.
dnl libcapsule is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU Lesser General Public License for more details.
dnl You should have received a copy of the GNU Lesser General Public
dnl License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ([2.60])
AC_INIT([libcapsule],
m4_esyscmd([build-aux/git-version-gen .tarball-version]),
[], [], [https://gitlab.collabora.com/vivek/libcapsule])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_ARG_VAR([PERL])
AC_PATH_PROG([PERL], [perl], [perl])
m4_pattern_forbid(
[^AX_(IS_RELEASE|COMPILER_FLAGS)],
[Please install GNU autoconf-archive 2016.09.16 or later])
dnl don't kvetch about gnu makefile syntax
dnl and don't require ChangeLog &co if they don't exist
AM_INIT_AUTOMAKE([-Wno-portability foreign])
AM_SILENT_RULES([yes])
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
# So we can install x86_64-linux-gnu-capsule-symbols etc.
AC_CANONICAL_HOST
# Make sure we are not mixing up --host=i686-linux-gnu with a 64-bit compiler
# or vice versa
CAPSULE_MATCHING_ABI
LT_INIT
AX_IS_RELEASE([dash-version])
dnl -Wno-pointer-arith: We do a lot of arithmetic on void *, relying on gcc
dnl making (void * + int) the same as (char * + int)
dnl -Wno-declaration-after-statement: Stylistic choice, this is C99
AX_COMPILER_FLAGS([], [], [], [], [ \
-Wno-pointer-arith \
-Wno-declaration-after-statement \
])
WARN_CFLAGS=$(echo "$WARN_CFLAGS" | sed -Ee 's,-W(error=)?declaration-after-statement,,')
WARN_CFLAGS=$(echo "$WARN_CFLAGS" | sed -Ee 's,-W(error=)?pointer-arith,,')
AC_CHECK_FUNCS_ONCE([secure_getenv])
AC_CHECK_HEADERS_ONCE([stdalign.h])
AM_CONDITIONAL([ENABLE_SHARED], [test "x$enable_shared" = xyes])
AC_ARG_ENABLE([library],
[AS_HELP_STRING([--enable-library],
[build the libcapsule library (disable if you only want CLI tools)])],
[],
[enable_library="$enable_shared"])
AS_IF([test "x$enable_shared" != xyes && test "x$enable_library" = xyes],
[AC_MSG_ERROR([libcapsule is only useful as a shared library])])
AM_CONDITIONAL([ENABLE_LIBRARY], [test "x$enable_library" = xyes])
dnl make sure we've got the _right_ libelf (doesn't always ship a .pc)
PKG_CHECK_MODULES([LIBELF], [libelf >= 0.152], [have_libelf=yes], [
AC_MSG_WARN([libelf.pc not found])
AC_MSG_WARN([Set LIBELF_CFLAGS, LIBELF_LIBS if -lelf is insufficient])
save_LIBS="$LIBS"
AC_CHECK_TYPE([Elf_Cmd], [], [], [#include <libelf.h>])
AC_CHECK_LIB([elf], [elf_begin], [],
[AC_MSG_ERROR([libelf not found])])
AC_MSG_CHECKING([Elf_Cmd ELF_C_READ_MMAP])
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <libelf.h>],
[Elf_Cmd x = ELF_C_READ_MMAP;])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])]
[AC_MSG_ERROR([ELF_C_READ_MMAP missing: libelfg vs libelf0?])])
LIBS="$save_LIBS"
LIBELF_LIBS=-lelf
])
AC_ARG_WITH([glib],
[AS_HELP_STRING([--with-glib=yes/no],
[use GLib for better test coverage [default=yes]])],
[],
[with_glib=auto])
AS_IF([test "$with_glib" != no],
[PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.38], [have_glib=yes], [have_glib=no])],
[have_glib=no])
AS_IF([test "$with_glib" = yes && test "$have_glib" = no],
[AC_MSG_ERROR([GLib required but not found])])
AM_CONDITIONAL([HAVE_GLIB], [test "x$have_glib" = xyes])
AC_ARG_ENABLE([host-prefix],
[AS_HELP_STRING([--enable-host-prefix=PREFIX/yes/no],
[install link to tools with a prefix [default=$host-]])],
[],
[enable_host_prefix=yes])
AM_CONDITIONAL([ENABLE_HOST_PREFIX], [test "x$enable_host_prefix" != xno])
AS_IF([test "x$enable_host_prefix" = xyes],
[enable_host_prefix="$host-"])
AS_IF([test "x$enable_host_prefix" = xno],
[enable_host_prefix=""])
AC_SUBST([HOST_PREFIX], [$enable_host_prefix])
AC_ARG_ENABLE([tools-rpath],
[AS_HELP_STRING([--enable-tools-rpath=RPATH/yes/no],
[install link to tools with a RPATH ready to override with chrpath])],
[],
[enable_tools_rpath=no])
AS_IF([test "x$enable_tools_rpath" = xyes],
[enable_tools_rpath="/_ORIGIN_/__/lib/$host"])
AS_IF([test "x$enable_tools_rpath" = xno],
[enable_tools_rpath=""])
AC_SUBST([enable_tools_rpath])
AM_CONDITIONAL([ENABLE_TOOLS_RPATH], [test "x$enable_tools_rpath" != x])
AC_SEARCH_LIBS([dlmopen], [dl])
AC_PATH_PROG([XSLTPROC], [xsltproc])
AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != x])
GTK_DOC_CHECK
AC_ARG_VAR([SANITIZE_CFLAGS],
[Extra CFLAGS for modules that are instrumented for error-checking])
AC_CONFIG_FILES([Makefile data/libcapsule.pc data/libcapsule-uninstalled.pc data/libcapsule-tools.pc data/libcapsule-tools-uninstalled.pc data/dirconf.txt])
AC_OUTPUT
#!/bin/bash
set -e;
set -u;
me=$(basename $0);
usage ()
{
cat << EOF
Usage: $me [OPTIONS] TARGET...
Options:
--capsule-pkgdatadir=PATH
Use supporting files from PATH
[default: libcapsule's \${pkgdatadir}]
--capsule-symbols-tool=PATH
Use replacement capsule-symbols(1)
[default: ${CAPSULE_SYMBOLS_TOOL:-ask pkg-config}]
--capsule-version-tool=PATH
Use replacement capsule-version(1)
[default: ${CAPSULE_VERSION_TOOL:-ask pkg-config}]
--search-tree=PATH Find libraries to be proxied in this
chroot, sysroot or container now
[default: /]
--set-version=VERSION Use this as the library's version number
(major, major.minor or major.minor.micro)
[default: based on first TARGET]
--symbols-from-dir=PATH Use symbols from files in this directory
instead of running capsule-symbols(1)
--runtime-tree=PATH Generated code will find proxied
libraries in this chroot, sysroot or
container at runtime if CAPSULE_PREFIX
is unset [default: /host]
--package-name=PACKAGE Use this name for the generated package
[default: based on name of first TARGET]
--destination=PATH Create the project here
[default: ./PACKAGE]
TARGET... SONAMEs of the libraries to be proxied
(libfoo.so.2), or libfoo.so.2/2.1.3
to force a version
EOF
exit "${1:-0}"
}
getopt_temp="$(getopt -o 'h' \
-l 'autoreconf,no-autoreconf,capsule-pkgdatadir:,capsule-symbols-tool:,capsule-version-tool:,destination:,package-name:,search-tree:,set-version:,symbols-from-dir:,runtime-tree:,help' \
-n "$me" -- "$@")"
eval set -- "$getopt_temp"
autoreconf=yes
dest=
package_name=
runtime_tree=
search_tree=
symbols_from=
ver=
while true;
do
case "$1" in
(--help|-h)
usage 0;
;;
(--autoreconf)
autoreconf=yes;
shift;
continue;
;;
(--no-autoreconf)
autoreconf=;
shift;
continue;
;;
(--capsule-pkgdatadir)
CAPSULE_MKINC="$2"
shift 2;
continue;
;;
(--capsule-symbols-tool)
CAPSULE_SYMBOLS_TOOL="$2"
shift 2;
continue;
;;
(--capsule-version-tool)
CAPSULE_VERSION_TOOL="$2"
shift 2;
continue;
;;
(--destination)
dest="$2"
shift 2;
continue;
;;
(--package-name)
package_name="$2"
shift 2;
continue;
;;
(--runtime-tree)
runtime_tree="$2"
shift 2;
continue;
;;
(--search-tree)
search_tree="$2"
shift 2;
continue;
;;
(--set-version)
ver="$2"
shift 2;
continue;
;;
(--symbols-from-dir)
symbols_from="$2"
shift 2;
continue;
;;
(--)
shift;
break;
;;
(*)
echo "$me: Internal error" >&2;
usage 2 >&2;
;;
esac
done
if [ "$#" -lt 1 ];
then
usage 2 >&2;
fi;
first_target_maybe_ver="$1";
first_target="${first_target_maybe_ver%%/*}";
if [ -z "$package_name" ];
then
package_name="${first_target%%.so.*}";
package_name="${package_name%%.so}-proxy";
fi;
if [ -z "$dest" ];
then
dest="$package_name";
fi;
: "${PKG_CONFIG:=pkg-config}"
: "${CAPSULE_MKINC:="$($PKG_CONFIG --variable=makeinc libcapsule-tools)"}"
: "${CAPSULE_SYMBOLS_TOOL:="$($PKG_CONFIG --variable=CAPSULE_SYMBOLS_TOOL libcapsule-tools)"}"
: "${CAPSULE_VERSION_TOOL:="$($PKG_CONFIG --variable=CAPSULE_VERSION_TOOL libcapsule-tools)"}"
: "${runtime_tree:="/host"}"
: "${search_tree:="/"}"
if [ -z "$ver" ];
then
if [ "x$first_target" != "x$first_target_maybe_ver" ];
then
ver="${first_target_maybe_ver#*/}";
else
read x x ver x < <("$CAPSULE_VERSION_TOOL" "$first_target" "$search_tree");
fi;
fi;
echo "Generating project for $package_name, shims for $runtime_tree libraries: $*";
echo "Creating project directory $dest";
mkdir -p "$dest";
cd $dest;
mkdir -p m4;
mkdir -p shim;
echo "Working in $PWD";
escaped_target="$(echo "$first_target" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
escaped_search_tree="$(echo "$search_tree" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
escaped_runtime_tree="$(echo "$runtime_tree" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
sed \
-e "1,/^\$/d" \
-e "s,@TARGET@,$escaped_target," \
< "$CAPSULE_MKINC/shim-README.in" \
> README;
echo Initialising configure.ac;
sed \
-e "1,/^\$/d" \
-e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \
-e "s,@SEARCH_TREE@,$escaped_search_tree," \
-e "s,@PACKAGE@,$package_name," \
-e "s,@VER@,$ver," \
< "$CAPSULE_MKINC/shim-configure.ac.in" \
> configure.ac;
echo Initialising Makefile.am;
sed \
-e "1,/^\$/d" \
-e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \
-e "s,@SEARCH_TREE@,$escaped_search_tree," \
-e "s,@PACKAGE@,$package_name," \
-e "s,@VER@,$ver," \
< "$CAPSULE_MKINC/shim-Makefile.am" \
> Makefile.am;
for target_maybe_ver in "$@"; do
target="${target_maybe_ver%%/*}";
if [ "x$target" != "x$target_maybe_ver" ];
then
target_ver="${target_maybe_ver#*/}";
else
read x x target_ver x < <("$CAPSULE_VERSION_TOOL" "$target" "$search_tree");
fi;
base=${target%%.so.*};
base=${base%.so};
name=${base#lib};
lcbase=${base,,};
amname=${name//-/_};
escaped_target="$(echo "$target" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
automake_target="${escaped_target//-/_}"
escaped_lib="$(echo "$name" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
automake_lib="$(echo "$amname" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')"
maj=${ver%%.*};
echo "Preparing proxy source files: $target"
touch shim/$target.{shared,excluded};
if [ -z "$symbols_from" ]; then
echo Extracting dynamic symbols from $search_tree : $target;
"$CAPSULE_SYMBOLS_TOOL" "$target" "$search_tree" > "shim/$target.symbols.tmp";
LC_ALL=C sort -u "shim/$target.symbols.tmp" > "shim/$target.symbols";
rm -f "shim/$target.symbols.tmp";
else
LC_ALL=C sort -u "$symbols_from/$target.symbols" > "shim/$target.symbols";
fi
touch "shim/$target.symbols.updated-for";
sed \
-e "1,/^\$/d" \
-e "s,@TARGET@,$escaped_target," \
-e "s,@AMTARGET@,$automake_target," \
-e "s,@LIB@,$escaped_lib," \
-e "s,@AMLIB@,$automake_lib," \
-e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \
-e "s,@SEARCH_TREE@,$escaped_search_tree," \
-e "s,@PACKAGE@,$package_name," \
-e "s,@TARGET_VER@,$target_ver," \
< "$CAPSULE_MKINC/shim-target-Makefile.am" \
>> Makefile.am;
done
cp "$CAPSULE_MKINC/shim-.gitignore" .gitignore
if [ -n "$autoreconf" ]; then
echo Running initial configuration;
autoreconf -ivf
fi
cat - <<EOF
$PWD is ready for ./configure, see README for details.
You will probably want to add the following files to your version
control system:
Makefile.am
README
configure.ac
EOF
for target_maybe_ver in "$@"; do
target="${target_maybe_ver%%/*}";
cat - <<EOF
shim/$target.excluded
shim/$target.shared
shim/$target.symbols
shim/$target.symbols.updated-for
EOF
done
#!/bin/bash
# Copyright © 2017 Collabora Ltd
# This file is part of libcapsule.
# libcapsule is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
# libcapsule 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 Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
set -u
set -e
declare -A NODE;
top=$(dirname $0);
top=${top:-.};
me=$(basename $0);
usage ()
{
cat << EOF
Usage: $me [OPTIONS] TARGET EXCLUDES EXPORTS OUTPUT [DSO-VERSION [TREE]]"
Options:
--capsule-pkgdatadir=PATH
Use supporting files from PATH
[default: libcapsule's \${pkgdatadir}]
--capsule-symbols-tool=PATH
Use replacement capsule-symbols(1)
--symbols-from=PATH Use symbols from this file
[default: \${OUTPUT%.c}.symbols]
--[no-]update-symbols Do or don't update symbols list if EXPORTS
is newer than it [default: do]
--search-tree=PATH Find libraries to be proxied in this
chroot, sysroot or container now
[default: TREE or /]
--runtime-tree=PATH Generated code will find proxied
libraries in this chroot, sysroot or
container at runtime if CAPSULE_PREFIX
is unset [default: TREE or /host]
Positional parameters:
TARGET SONAME of shared library to proxy,
for example libGL.so.1
EXCLUDES List of SONAMEs never loaded from TREE
in addition to libdl.so.2
EXPORTS List of extra SONAMEs whose symbols are
to be exported from the shim, in addition
to TARGET itself
OUTPUT C source code filename to output,
for example libGL.so.c
DSO-VERSION Major or MAJOR:MINOR:MICRO version (as for
libtool -version-number) to export, for
example 1 or 1:x:y for libGL.so.1
[default: parse SONAME]
TREE Chroot or container to find libraries in,
both now and at runtime; overridden by
--runtime-tree and --search-tree
EOF
exit "${1:-0}"
}
update_symbols=
getopt_temp="$(getopt -o 'h' \
-l 'capsule-symbols-tool:,search-tree:,runtime-tree:,symbols-from:,update-symbols,no-update-symbols,help' \
-n "$me" -- "$@")"
eval set -- "$getopt_temp"
while true;
do
case "$1" in
(--capsule-symbols-tool)
CAPSULE_SYMBOLS_TOOL="$2"
shift 2;
continue;
;;
(--runtime-tree)
runtime_tree="$2"
shift 2;
continue;
;;
(--search-tree)
search_tree="$2"
shift 2;
continue;
;;
(--symbols-from)
symbol_file="$2"
shift 2;
continue;
;;
(--update-symbols)
update_symbols=yes;
shift;
continue;
;;
(--no-update-symbols)
update_symbols=;
shift;
continue;
;;
(--help|-h)
usage 0;
;;
(--)
shift;
break;
;;
(*)
echo "$me: Internal error" >&2;
usage 2 >&2;
;;
esac
done
: "${PKG_CONFIG:=pkg-config}"
: "${CAPSULE_SYMBOLS_TOOL:="$($PKG_CONFIG --variable=CAPSULE_SYMBOLS_TOOL libcapsule-tools)"}"
if [ "$#" -lt 4 ] || [ "$#" -gt 6 ];
then
usage 2 >&2;
fi;
proxied_dso=$1; shift;
proxy_excluded=$1; shift;
proxy_extra=$1; shift;
proxy_src=$1; shift;
if [ "$#" -gt 0 ];
then
ltver=$1; shift;
fi
if [ "$#" -gt 0 ];
then
proxy_tree=$1; shift;
else
proxy_tree=
fi
if [ -n "${ltver:-}" ];
then
major_version="${ltver%%:*}"
elif [ "x${proxied_dso%.so.*}" != "x$proxied_dso" ]; then
major_version="${proxied_dso##*.so.}"
fi
: "${runtime_tree:="$proxy_tree"}"
: "${runtime_tree:=/host}"
: "${search_tree:="$proxy_tree"}"
: "${search_tree:=/}"
dso_base=${proxied_dso#lib}
dso_base=${dso_base%.so*}
proxied_dso=lib${dso_base}.so.${major_version};
map_file=${proxy_src%.c}.map;
if [ -z "${symbol_file:-}" ];
then
symbol_file=${proxy_src%.c}.symbols;
fi
exec >$proxy_src.tmp;
echo '#include "capsule/capsule-shim.h"'
# generate the .symbols file if it doesn't exist, or if the proxy_extra
# control file has been updated:
if [ -n "$update_symbols" ] && [ $symbol_file -ot $proxy_extra ];
then
echo -n > $symbol_file;
if (for pt in $proxied_dso $(cat $proxy_extra);
do
if [ x$V = x1 ];
then
echo " $CAPSULE_SYMBOLS_TOOL $pt $search_tree" >&2;
else
echo " SYMBOLS $pt $search_tree" >&2;
fi;
$CAPSULE_SYMBOLS_TOOL $pt $search_tree || exit 1;
done) > $symbol_file.tmp;
then
LC_ALL=C sort -u "$symbol_file.tmp" > "$symbol_file.tmp2";
rm -f "$symbol_file.tmp";
mv "$symbol_file.tmp2" "$symbol_file";
else
code=$?;
rm $symbol_file.tmp
exit $code;
fi;
fi;
while read symbol version dependency;
do
case $version in
@*)
echo "VERSIONED_STUB ( $symbol, $version );";
node=${version##*@};
NODE[$node]=${NODE[$node]:-}" "$symbol;
;;
*)
echo "UNVERSIONED_STUB( $symbol );";
;;
esac;
done < $symbol_file;
cat - <<EOF
static capsule cap;
static const char soname[] = "$proxied_dso";
// _int_dlopen() from libcapsule or a locally overridden version
#include "capsule/_int_dlopen.h"
// Array of SONAMEs that will not have a private copy inside a capsule.
// Only one copy of each of these will be loaded, and they will
// be loaded without respecting the #capsule_namespace's @prefix.
static const char *exclude[] =
{
EOF
while read excluded x;
do
case $excluded in lib*) printf "%32s \"%s\",\n" "" $excluded; ;; esac;
done < $proxy_excluded;
cat - <<EOF
NULL
};
// DSOs to restrict dlsym lookups to:
static const char *valid_dlsym_sources[] =
{
soname,
EOF
if [ -f "${proxy_extra}" ];
then
for pt in $(cat $proxy_extra);
do
cat - <<EOF
"$pt",
EOF
done;
fi;
cat - <<EOF
NULL
};
// -------------------------------------------------------------
// this is an array of the functions we want to act as a shim for:
static capsule_item relocs[] =
{
EOF
while read sym x;
do
cat - <<EOF
{ "$sym" },
EOF
done < $symbol_file;
cat - <<EOF
{ "dlsym", (capsule_addr) capsule_external_dlsym,
(capsule_addr) capsule_external_dlsym, },
{ NULL }
};
// make sure this symbol has global visibility so
// that the libcapsule initialisation can find it
__attribute__ ((visibility("default")))
capsule_metadata capsule_meta =
{
.capsule_abi = 0,
.soname = soname,
.default_prefix = "/host",
.exclude = exclude,
.export = valid_dlsym_sources,
.items = relocs,
.int_dlopen = _int_dlopen,
.int_free = _wrapped_free,
.int_realloc = _wrapped_realloc,
};
static void __attribute__ ((constructor)) _capsule_init (void)
{
// Don't use capsule_meta.soname here, because if we did, it would be
// a relocatable, interposable reference to capsule_meta (assuming the
// shim library wasn't linked with -Bsymbolic). At runtime, we'd get
// an arbitrarily chosen one of the capsule_meta symbols defined by
// the other shim libraries sharing our global symbol namespace,
// and in particular not necessarily our own, with predictably bad
// results.
cap = capsule_init( soname );
}
static void __attribute__ ((destructor)) _capsule_exit (void)
{
capsule_close( cap );
cap = NULL;
}
EOF
echo -n > $map_file;
exec >& $map_file;
for node in ${!NODE[@]};
do
echo "$node {";
echo " global:";
for symbol in ${NODE[$node]};
do
echo " $symbol;";
done;
echo "};";
echo;
done;
# scrub the (symbol version) map file if it's empty:
if [ ! -s $map_file ];
then
rm -f $map_file;
fi;
mv "$proxy_src.tmp" "$proxy_src";
# allow stub file generation to be quiet or verbose per the value of V
V ?= 0
GENSTUB_V1 =
GENSTUB_V0 = @echo " GENSTUB " $@;
GENSTUB = $(GENSTUB_V$(V))
CAPSULE_TREE ?= /host
CAPSULE_RUNTIME_TREE ?= $(CAPSULE_TREE)
CAPSULE_SEARCH_TREE ?= $(CAPSULE_TREE)
comma = ,
define foreach_shim =
shim_ldflags_$(subst -,_,$(1)) = -version-number $$(subst .,:,$$(CAPSULE_VERSION_$(subst -,_,$(1)))) \
$$(patsubst %,-Wl$$(comma)--version-script=%,$$(wildcard shim/$(1).map))
maintainer-update-capsule-symbols: maintainer-update-capsule-symbols/$(1)
endef
$(foreach CAPSULE_SONAME,$(CAPSULE_SONAMES),$(eval $(call foreach_shim,$(CAPSULE_SONAME))))
# regenerate if any dependencies get updated:
shim/%.c: $(srcdir)/shim/%.excluded $(srcdir)/shim/%.shared $(srcdir)/shim/%.symbols
$(GENSTUB)V=$V \
$(CAPSULE_MKSTUBLIB_TOOL) \
--no-update-symbols \
--symbols-from=$(srcdir)/shim/$*.symbols \
$(AM_CAPSULE_MKSTUBLIB_FLAGS) \
$(CAPSULE_MKSTUBLIB_FLAGS) \
$* \
$(srcdir)/shim/$*.excluded \
$(srcdir)/shim/$*.shared \
$@ \
$(subst .,:,$(CAPSULE_VERSION_$(subst -,_,$*))) \
$(CAPSULE_RUNTIME_TREE)
# error out when it's time to regenerate the exportable symbols list
# (we do not do this automatically because it's part of our ABI)
$(srcdir)/shim/%.symbols: $(srcdir)/shim/%.shared
@if cmp -s $< $@.updated-for; then \
touch $@; \
else \
echo "*** ERROR: List of shared libraries has changed" >&2; \
echo "*** Run: make maintainer-update-capsule-symbols [CAPSULE_SEARCH_TREE=/some-sysroot]" >&2; \
exit 1; \
fi
maintainer-update-capsule-symbols: always
@true
.PHONY: maintainer-update-capsule-symbols
maintainer-update-capsule-symbols/%: always
$(AM_V_GEN)set -e; \
shared=$(srcdir)/shim/$*.shared; \
out=$(srcdir)/shim/$*.symbols; \
rm -f $$out.updated-for; \
for dso in $* $$(cat "$$shared"); \
do \
$(CAPSULE_SYMBOLS_TOOL) $$dso $(CAPSULE_SEARCH_TREE); \
done > "$$out.tmp"; \
LC_ALL=C sort -u "$$out.tmp" > "$$out.tmp2"; \
rm -f "$$out.tmp"; \
mv "$$out.tmp2" "$$out"; \
cp $(srcdir)/shim/$*.shared $$out.updated-for
.PHONY: always
datarootdir : @datarootdir@
bindir : @bindir@
datadir : @datadir@
datarootdir : @datarootdir@
docdir : @docdir@
dvidir : @dvidir@
exec_prefix : @exec_prefix@
htmldir : @htmldir@
includedir : @includedir@
infodir : @infodir@
libdir : @libdir@
bindir : @bindir@
localedir : @localedir@
localstatedir : @localstatedir@
mandir : @mandir@
oldincludedir : @oldincludedir@
pdfdir : @pdfdir@
prefix : @prefix@
psdir : @psdir@
sbindir : @sbindir@
sharedstatedir: @sharedstatedir@
sysconfdir : @sysconfdir@
# all these catch-all rules do is slow things down
%:: %,v
%:: RCS/%
%:: RCS/%,v
%:: SCCS/s.%
%:: s.%
makeinc=@abs_top_srcdir@/data
CAPSULE_CAPTURE_LIBS_TOOL=@abs_top_builddir@/capsule-capture-libs
CAPSULE_INIT_PROJECT_TOOL=@abs_top_srcdir@/data/capsule-init-project
CAPSULE_MKSTUBLIB_TOOL=@abs_top_srcdir@/data/capsule-mkstublib
CAPSULE_SYMBOLS_TOOL=@abs_top_builddir@/capsule-symbols
CAPSULE_VERSION_TOOL=@abs_top_builddir@/capsule-version
Name: libcapsule-tools
Description: segregated dynamic linking library - development tools
Version: @VERSION@