Mercurial > hg > octave-lyh > gnulib-hg
changeset 8265:20006894dae4
Infrastructure for relocatable installation, from GNU gettext.
See http://lists.gnu.org/archive/html/bug-gnulib/2003-03/msg00020.html
author | Bruno Haible <bruno@clisp.org> |
---|---|
date | Thu, 01 Mar 2007 01:15:40 +0000 |
parents | 4dfb3310f018 |
children | c38f0e90bab8 |
files | ChangeLog build-aux/config.libpath build-aux/install-reloc build-aux/reloc-ldflags lib/relocatable.c lib/relocatable.h lib/relocwrapper.c m4/relocatable.m4 |
diffstat | 8 files changed, 1251 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-02-28 Bruno Haible <bruno@clisp.org> + + Import --enable-relocatable infrastructure. + * build-aux/config.libpath: New file, from GNU gettext. + * build-aux/install-reloc: New file, from GNU gettext. + * build-aux/reloc-ldflags: New file, from GNU gettext. + * lib/relocatable.h: New file, from GNU gettext. + * lib/relocatable.c: New file, from GNU gettext. + * lib/relocwrapper.c: New file, from GNU gettext. + * m4/relocatable.m4: New file, from GNU gettext. + 2007-02-28 Bruno Haible <bruno@clisp.org> * MODULES.html.sh (File system functions): Add xreadlink-with-size.
new file mode 100755 --- /dev/null +++ b/build-aux/config.libpath @@ -0,0 +1,148 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable at run time. +# +# Copyright 1996-2005 Free Software Foundation, Inc. +# Taken from GNU libtool, 2003 +# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 +# +# 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 2 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, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variable LD should be set by the caller. +# +# The set of defined variables is at the end of this script. + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +shlibpath_var= +case $host_os in + aix3*) + shlibpath_var=LIBPATH + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + shlibpath_var=LD_LIBRARY_PATH + else + shlibpath_var=LIBPATH + fi + ;; + beos*) + shlibpath_var=LIBRARY_PATH + ;; + bsdi4*) + shlibpath_var=LD_LIBRARY_PATH + ;; + cygwin* | mingw* | pw32*) + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + darwin* | rhapsody*) + shlibpath_var=DYLD_LIBRARY_PATH + ;; + freebsd1*) + ;; + kfreebsd*-gnu) + shlibpath_var=LD_LIBRARY_PATH + ;; + freebsd*) + shlibpath_var=LD_LIBRARY_PATH + ;; + gnu*) + shlibpath_var=LD_LIBRARY_PATH + ;; + hpux9* | hpux10* | hpux11*) + shlibpath_var=SHLIB_PATH + ;; + irix5* | irix6* | nonstopux*) + case $host_os in + irix5* | nonstopux*) + shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 ") shlibsuff= ;; + *-n32|*"-n32 ") shlibsuff=N32 ;; + *-64|*"-64 ") shlibsuff=64 ;; + *) shlibsuff= ;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + ;; + linux-gnu*) + shlibpath_var=LD_LIBRARY_PATH + ;; + knetbsd*-gnu) + shlibpath_var=LD_LIBRARY_PATH + ;; + netbsd*) + shlibpath_var=LD_LIBRARY_PATH + ;; + newsos6) + shlibpath_var=LD_LIBRARY_PATH + ;; + openbsd*) + shlibpath_var=LD_LIBRARY_PATH + ;; + os2*) + shlibpath_var=LIBPATH + ;; + osf3* | osf4* | osf5*) + shlibpath_var=LD_LIBRARY_PATH + ;; + sco3.2v5*) + shlibpath_var=LD_LIBRARY_PATH + ;; + solaris*) + shlibpath_var=LD_LIBRARY_PATH + ;; + sunos4*) + shlibpath_var=LD_LIBRARY_PATH + ;; + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + shlibpath_var=LD_LIBRARY_PATH + ;; + uts4*) + shlibpath_var=LD_LIBRARY_PATH + ;; + dgux*) + shlibpath_var=LD_LIBRARY_PATH + ;; + sysv4*MP*) + if test -d /usr/nec ;then + shlibpath_var=LD_LIBRARY_PATH + fi + ;; +esac + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +EOF
new file mode 100755 --- /dev/null +++ b/build-aux/install-reloc @@ -0,0 +1,130 @@ +#!/bin/sh +# install-reloc - install a program including a relocating wrapper +# Copyright (C) 2003, 2005-2006 Free Software Foundation, Inc. +# Written by Bruno Haible <bruno@clisp.org>, 2003. +# +# 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 2, 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, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: +# install-reloc library_path_var library_path_value prefix compile_command srcdir config_h_dir exeext install_command... destprog +# where +# - library_path_var is the platform dependent runtime library path variable +# - library_path_value is a colon separated list of directories that contain +# the libraries at installation time (use this instead of -rpath) +# - prefix is the base directory at installation time +# - compile_command is a C compiler compilation and linking command +# - srcdir is the directory where to find relocwrapper.c and its dependencies +# - builddir is the directory where to find built dependencies (namely, +# alloca.h and stdbool.h) +# - config_h_dir is the directory where to find config.h +# - exeext is platform dependent suffix of executables +# - install-command is the install command line, excluding the final destprog +# - destprog is the destination program name +# install-reloc renames destprog to destprog.bin and installs a relocating +# wrapper in the place of destprog. + +progname=$0 + +if test $# -eq 2; then + # Get arguments from environment variables. + library_path_var=$RELOC_LIBRARY_PATH_VAR + library_path_value=$RELOC_LIBRARY_PATH_VALUE + prefix=$RELOC_PREFIX + compile_command=$RELOC_COMPILE_COMMAND + srcdir=$RELOC_SRCDIR + builddir=$RELOC_BUILDDIR + config_h_dir=$RELOC_CONFIG_H_DIR + exeext=$RELOC_EXEEXT + install_prog=$RELOC_INSTALL_PROG # including the "-c" option +else + if test $# -ge 9; then + # Get fixed position arguments. + library_path_var=$1 + library_path_value=$2 + prefix=$3 + compile_command=$4 + srcdir=$5 + builddir=$6 + config_h_dir=$7 + exeext=$8 + install_prog=$9 # maybe not including the "-c" option + shift + shift + shift + shift + shift + shift + shift + shift + shift + else + echo "Usage: $0 library_path_var library_path_value prefix compile_command srcdir builddir config_h_dir exeext install_command... destprog" 1>&2 + exit 1 + fi +fi + +# Get destprog, last argument. +destprog= +for arg +do + destprog=$arg +done +# Remove trailing $exeext, if present. +if test -n "$exeext"; then + sedexpr='s|'`echo "$exeext" | sed -e 's,\.,\\\.,g'`'$||' + destprog=`echo "$destprog" | sed -e "$sedexpr"` +fi + +# Outputs a command and runs it. +func_verbose () +{ + echo "$@" + "$@" +} + +# Run install_command. +func_verbose $install_prog "$@" || exit $? + +# If the platform doesn't support LD_LIBRARY_PATH or similar, we cannot build +# a wrapper. +test -n "$library_path_var" || exit 0 + +libdirs= +save_IFS="$IFS"; IFS=":" +for dir in $library_path_value; do + IFS="$save_IFS" + if test -n "$dir"; then + case "$libdirs" in + *"\"$dir\""*) ;; # remove duplicate + *) libdirs="$libdirs\"$dir\"," ;; + esac + fi +done +IFS="$save_IFS" +# If there are no library directories to add at runtime, we don't need a +# wrapper. +test -n "$libdirs" || exit 0 + +# Compile wrapper. +installdir=`echo "$destprog" | sed -e 's,/[^/]*$,,'` +func_verbose $compile_command -I"$builddir" -I"$srcdir" -I"$config_h_dir" -DHAVE_CONFIG_H -DIN_RELOCWRAPPER -DNO_XMALLOC -D"INSTALLPREFIX=\"$prefix\"" -D"INSTALLDIR=\"$installdir\"" -D"LIBPATHVAR=\"$library_path_var\"" -D"LIBDIRS=$libdirs" -D"EXEEXT=\"$exeext\"" "$srcdir"/relocwrapper.c "$srcdir"/progname.c "$srcdir"/progreloc.c "$srcdir"/xreadlink.c "$srcdir"/readlink.c "$srcdir"/canonicalize-lgpl.c "$srcdir"/allocsa.c "$srcdir"/relocatable.c "$srcdir"/setenv.c "$srcdir"/strerror.c -o "$destprog.wrapper$exeext" || exit $? + +# Rename $destprog.wrapper -> $destprog -> $destprog.bin. +ln -f "$destprog$exeext" "$destprog.bin$exeext" \ + || { rm -f "$destprog.bin$exeext" && cp -p "$destprog$exeext" "$destprog.bin$exeext"; } \ + || exit 1 +mv "$destprog.wrapper$exeext" "$destprog$exeext" || exit 1 + +exit 0
new file mode 100755 --- /dev/null +++ b/build-aux/reloc-ldflags @@ -0,0 +1,102 @@ +#! /bin/sh +# Output a system dependent linker command for putting a relocatable library +# search path into an executable. +# +# Copyright 2003 Free Software Foundation, Inc. +# Written by Bruno Haible <bruno@clisp.org>, 2003. +# +# 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 2 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, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variable LD should be set by the caller. +# +# The second argument is a colon separated list of directories that contain +# the libraries at installation time. +# +# The third argument is the directory into which the executable is going to be +# installed. + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +library_path_value=$2 + +installdir=$3 + +# Verify that installdir is absolute. +case "$installdir" in + /*) ;; + *) + echo "installdir is not absolute: $installdir" 1>&2 + exit 1 + ;; +esac + +case "$host_os" in + linux*) # Supported since Linux 2.1 and glibc 2.1. + rpath= + save_IFS="$IFS"; IFS=":" + for dir in $library_path_value; do + IFS="$save_IFS" + case "$dir" in + /*) + # Make dir relative to installdir. (Works only if dir is absolute.) + idir="$installdir" + while true; do + dfirst=`echo "$dir" | sed -n -e 's,^//*\([^/]*\).*$,/\1,p'` + ifirst=`echo "$idir" | sed -n -e 's,^//*\([^/]*\).*$,/\1,p'` + if test -z "$dfirst" || test -z "$ifirst"; then + break + fi + if test "$dfirst" != "$ifirst"; then + break + fi + dir=`echo "$dir" | sed -e 's,^//*[^/]*,,'` + idir=`echo "$idir" | sed -e 's,^//*[^/]*,,'` + done + dir="\$ORIGIN"`echo "$idir" | sed -e 's,//*[^/]*,/..,g'`"$dir" + # Add dir to rpath. + rpath="${rpath}${rpath:+ }$dir" + ;; + *) + if test -n "$dir"; then + echo "libdir is not absolute: $dir" 1>&2 + fi + ;; + esac + done + IFS="$save_IFS" + # Output it. + if test -n "$rpath"; then + echo "-Wl,-rpath,$rpath" + fi + ;; + *) + echo "relocation via rpath not supported on this system: $host" 1>&2 + exit 1 + ;; +esac + +exit 0
new file mode 100644 --- /dev/null +++ b/lib/relocatable.c @@ -0,0 +1,468 @@ +/* Provide relocatable packages. + Copyright (C) 2003-2006 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2003. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + + +/* Tell glibc's <stdio.h> to provide a prototype for getline(). + This must come before <config.h> because <config.h> may include + <features.h>, and once <features.h> has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#include <config.h> + +/* Specification. */ +#include "relocatable.h" + +#if ENABLE_RELOCATABLE + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef NO_XMALLOC +# define xmalloc malloc +#else +# include "xalloc.h" +#endif + +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif + +#if DEPENDS_ON_LIBCHARSET +# include <libcharset.h> +#endif +#if DEPENDS_ON_LIBICONV && HAVE_ICONV +# include <iconv.h> +#endif +#if DEPENDS_ON_LIBINTL && ENABLE_NLS +# include <libintl.h> +#endif + +/* Faked cheap 'bool'. */ +#undef bool +#undef false +#undef true +#define bool int +#define false 0 +#define true 1 + +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. + */ +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +# define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ + && (P)[1] == ':') +# define IS_PATH_WITH_DIR(P) \ + (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) +# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0) +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) +# define FILE_SYSTEM_PREFIX_LEN(P) 0 +#endif + +/* Original installation prefix. */ +static char *orig_prefix; +static size_t orig_prefix_len; +/* Current installation prefix. */ +static char *curr_prefix; +static size_t curr_prefix_len; +/* These prefixes do not end in a slash. Anything that will be concatenated + to them must start with a slash. */ + +/* Sets the original and the current installation prefix of this module. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +static void +set_this_relocation_prefix (const char *orig_prefix_arg, + const char *curr_prefix_arg) +{ + if (orig_prefix_arg != NULL && curr_prefix_arg != NULL + /* Optimization: if orig_prefix and curr_prefix are equal, the + relocation is a nop. */ + && strcmp (orig_prefix_arg, curr_prefix_arg) != 0) + { + /* Duplicate the argument strings. */ + char *memory; + + orig_prefix_len = strlen (orig_prefix_arg); + curr_prefix_len = strlen (curr_prefix_arg); + memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1); +#ifdef NO_XMALLOC + if (memory != NULL) +#endif + { + memcpy (memory, orig_prefix_arg, orig_prefix_len + 1); + orig_prefix = memory; + memory += orig_prefix_len + 1; + memcpy (memory, curr_prefix_arg, curr_prefix_len + 1); + curr_prefix = memory; + return; + } + } + orig_prefix = NULL; + curr_prefix = NULL; + /* Don't worry about wasted memory here - this function is usually only + called once. */ +} + +/* Sets the original and the current installation prefix of the package. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +void +set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg) +{ + set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg); + + /* Now notify all dependent libraries. */ +#if DEPENDS_ON_LIBCHARSET + libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109 + libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix + libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg); +#endif +} + +#if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR) + +/* Convenience function: + Computes the current installation prefix, based on the original + installation prefix, the original installation directory of a particular + file, and the current pathname of this file. Returns NULL upon failure. */ +#ifdef IN_LIBRARY +#define compute_curr_prefix local_compute_curr_prefix +static +#endif +const char * +compute_curr_prefix (const char *orig_installprefix, + const char *orig_installdir, + const char *curr_pathname) +{ + const char *curr_installdir; + const char *rel_installdir; + + if (curr_pathname == NULL) + return NULL; + + /* Determine the relative installation directory, relative to the prefix. + This is simply the difference between orig_installprefix and + orig_installdir. */ + if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix)) + != 0) + /* Shouldn't happen - nothing should be installed outside $(prefix). */ + return NULL; + rel_installdir = orig_installdir + strlen (orig_installprefix); + + /* Determine the current installation directory. */ + { + const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname); + const char *p = curr_pathname + strlen (curr_pathname); + char *q; + + while (p > p_base) + { + p--; + if (ISSLASH (*p)) + break; + } + + q = (char *) xmalloc (p - curr_pathname + 1); +#ifdef NO_XMALLOC + if (q == NULL) + return NULL; +#endif + memcpy (q, curr_pathname, p - curr_pathname); + q[p - curr_pathname] = '\0'; + curr_installdir = q; + } + + /* Compute the current installation prefix by removing the trailing + rel_installdir from it. */ + { + const char *rp = rel_installdir + strlen (rel_installdir); + const char *cp = curr_installdir + strlen (curr_installdir); + const char *cp_base = + curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir); + + while (rp > rel_installdir && cp > cp_base) + { + bool same = false; + const char *rpi = rp; + const char *cpi = cp; + + while (rpi > rel_installdir && cpi > cp_base) + { + rpi--; + cpi--; + if (ISSLASH (*rpi) || ISSLASH (*cpi)) + { + if (ISSLASH (*rpi) && ISSLASH (*cpi)) + same = true; + break; + } + /* Do case-insensitive comparison if the filesystem is always or + often case-insensitive. It's better to accept the comparison + if the difference is only in case, rather than to fail. */ +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS - case insignificant filesystem */ + if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi) + != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi)) + break; +#else + if (*rpi != *cpi) + break; +#endif + } + if (!same) + break; + /* The last pathname component was the same. opi and cpi now point + to the slash before it. */ + rp = rpi; + cp = cpi; + } + + if (rp > rel_installdir) + /* Unexpected: The curr_installdir does not end with rel_installdir. */ + return NULL; + + { + size_t curr_prefix_len = cp - curr_installdir; + char *curr_prefix; + + curr_prefix = (char *) xmalloc (curr_prefix_len + 1); +#ifdef NO_XMALLOC + if (curr_prefix == NULL) + return NULL; +#endif + memcpy (curr_prefix, curr_installdir, curr_prefix_len); + curr_prefix[curr_prefix_len] = '\0'; + + return curr_prefix; + } + } +} + +#endif /* !IN_LIBRARY || PIC */ + +#if defined PIC && defined INSTALLDIR + +/* Full pathname of shared library, or NULL. */ +static char *shared_library_fullname; + +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ + +/* Determine the full pathname of the shared library when it is loaded. */ + +BOOL WINAPI +DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved) +{ + (void) reserved; + + if (event == DLL_PROCESS_ATTACH) + { + /* The DLL is being loaded into an application's address range. */ + static char location[MAX_PATH]; + + if (!GetModuleFileName (module_handle, location, sizeof (location))) + /* Shouldn't happen. */ + return FALSE; + + if (!IS_PATH_WITH_DIR (location)) + /* Shouldn't happen. */ + return FALSE; + + { +#if defined __CYGWIN__ + /* On Cygwin, we need to convert paths coming from Win32 system calls + to the Unix-like slashified notation. */ + static char location_as_posix_path[2 * MAX_PATH]; + /* There's no error return defined for cygwin_conv_to_posix_path. + See cygwin-api/func-cygwin-conv-to-posix-path.html. + Does it overflow the buffer of expected size MAX_PATH or does it + truncate the path? I don't know. Let's catch both. */ + cygwin_conv_to_posix_path (location, location_as_posix_path); + location_as_posix_path[MAX_PATH - 1] = '\0'; + if (strlen (location_as_posix_path) >= MAX_PATH - 1) + /* A sign of buffer overflow or path truncation. */ + return FALSE; + shared_library_fullname = strdup (location_as_posix_path); +#else + shared_library_fullname = strdup (location); +#endif + } + } + + return TRUE; +} + +#else /* Unix except Cygwin */ + +static void +find_shared_library_fullname () +{ +#if defined __linux__ && __GLIBC__ >= 2 + /* Linux has /proc/self/maps. glibc 2 has the getline() function. */ + FILE *fp; + + /* Open the current process' maps file. It describes one VMA per line. */ + fp = fopen ("/proc/self/maps", "r"); + if (fp) + { + unsigned long address = (unsigned long) &find_shared_library_fullname; + for (;;) + { + unsigned long start, end; + int c; + + if (fscanf (fp, "%lx-%lx", &start, &end) != 2) + break; + if (address >= start && address <= end - 1) + { + /* Found it. Now see if this line contains a filename. */ + while (c = getc (fp), c != EOF && c != '\n' && c != '/') + continue; + if (c == '/') + { + size_t size; + int len; + + ungetc (c, fp); + shared_library_fullname = NULL; size = 0; + len = getline (&shared_library_fullname, &size, fp); + if (len >= 0) + { + /* Success: filled shared_library_fullname. */ + if (len > 0 && shared_library_fullname[len - 1] == '\n') + shared_library_fullname[len - 1] = '\0'; + } + } + break; + } + while (c = getc (fp), c != EOF && c != '\n') + continue; + } + fclose (fp); + } +#endif +} + +#endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */ + +/* Return the full pathname of the current shared library. + Return NULL if unknown. + Guaranteed to work only on Linux, Cygwin and Woe32. */ +static char * +get_shared_library_fullname () +{ +#if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__) + static bool tried_find_shared_library_fullname; + if (!tried_find_shared_library_fullname) + { + find_shared_library_fullname (); + tried_find_shared_library_fullname = true; + } +#endif + return shared_library_fullname; +} + +#endif /* PIC */ + +/* Returns the pathname, relocated according to the current installation + directory. */ +const char * +relocate (const char *pathname) +{ +#if defined PIC && defined INSTALLDIR + static int initialized; + + /* Initialization code for a shared library. */ + if (!initialized) + { + /* At this point, orig_prefix and curr_prefix likely have already been + set through the main program's set_program_name_and_installdir + function. This is sufficient in the case that the library has + initially been installed in the same orig_prefix. But we can do + better, to also cover the cases that 1. it has been installed + in a different prefix before being moved to orig_prefix and (later) + to curr_prefix, 2. unlike the program, it has not moved away from + orig_prefix. */ + const char *orig_installprefix = INSTALLPREFIX; + const char *orig_installdir = INSTALLDIR; + const char *curr_prefix_better; + + curr_prefix_better = + compute_curr_prefix (orig_installprefix, orig_installdir, + get_shared_library_fullname ()); + if (curr_prefix_better == NULL) + curr_prefix_better = curr_prefix; + + set_relocation_prefix (orig_installprefix, curr_prefix_better); + + initialized = 1; + } +#endif + + /* Note: It is not necessary to perform case insensitive comparison here, + even for DOS-like filesystems, because the pathname argument was + typically created from the same Makefile variable as orig_prefix came + from. */ + if (orig_prefix != NULL && curr_prefix != NULL + && strncmp (pathname, orig_prefix, orig_prefix_len) == 0) + { + if (pathname[orig_prefix_len] == '\0') + /* pathname equals orig_prefix. */ + return curr_prefix; + if (ISSLASH (pathname[orig_prefix_len])) + { + /* pathname starts with orig_prefix. */ + const char *pathname_tail = &pathname[orig_prefix_len]; + char *result = + (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1); + +#ifdef NO_XMALLOC + if (result != NULL) +#endif + { + memcpy (result, curr_prefix, curr_prefix_len); + strcpy (result + curr_prefix_len, pathname_tail); + return result; + } + } + } + /* Nothing to relocate. */ + return pathname; +} + +#endif
new file mode 100644 --- /dev/null +++ b/lib/relocatable.h @@ -0,0 +1,79 @@ +/* Provide relocatable packages. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2003. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +#ifndef _RELOCATABLE_H +#define _RELOCATABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This can be enabled through the configure --enable-relocatable option. */ +#if ENABLE_RELOCATABLE + +/* When building a DLL, we must export some functions. Note that because + this is a private .h file, we don't need to use __declspec(dllimport) + in any case. */ +#if HAVE_VISIBILITY && BUILDING_DLL +# define RELOCATABLE_DLL_EXPORTED __attribute__((__visibility__("default"))) +#elif defined _MSC_VER && BUILDING_DLL +# define RELOCATABLE_DLL_EXPORTED __declspec(dllexport) +#else +# define RELOCATABLE_DLL_EXPORTED +#endif + +/* Sets the original and the current installation prefix of the package. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +extern RELOCATABLE_DLL_EXPORTED void + set_relocation_prefix (const char *orig_prefix, + const char *curr_prefix); + +/* Returns the pathname, relocated according to the current installation + directory. */ +extern const char * relocate (const char *pathname); + +/* Memory management: relocate() leaks memory, because it has to construct + a fresh pathname. If this is a problem because your program calls + relocate() frequently, think about caching the result. */ + +/* Convenience function: + Computes the current installation prefix, based on the original + installation prefix, the original installation directory of a particular + file, and the current pathname of this file. Returns NULL upon failure. */ +extern const char * compute_curr_prefix (const char *orig_installprefix, + const char *orig_installdir, + const char *curr_pathname); + +#else + +/* By default, we use the hardwired pathnames. */ +#define relocate(pathname) (pathname) + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _RELOCATABLE_H */
new file mode 100644 --- /dev/null +++ b/lib/relocwrapper.c @@ -0,0 +1,194 @@ +/* Relocating wrapper program. + Copyright (C) 2003, 2005-2006 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2003. + + 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Dependencies: + relocwrapper + -> progname + -> progreloc + -> xreadlink + -> readlink + -> canonicalize + -> allocsa + -> relocatable + -> setenv + -> allocsa + -> strerror + + Macros that need to be set while compiling this file: + - ENABLE_RELOCATABLE 1 + - INSTALLPREFIX the base installation directory + - INSTALLDIR the directory into which this program is installed + - LIBPATHVAR the platform dependent runtime library path variable + - LIBDIRS a comma-terminated list of strings representing the list of + directories that contain the libraries at installation time + + We don't want to internationalize this wrapper because then it would + depend on libintl and therefore need relocation itself. So use only + libc functions, no gettext(), no error(), no xmalloc(), no xsetenv(). + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <errno.h> + +#include "progname.h" +#include "relocatable.h" +#include "setenv.h" + +/* Return a copy of the filename, with an extra ".bin" at the end. + More generally, it replaces "${EXEEXT}" at the end with ".bin${EXEEXT}". */ +static char * +add_dotbin (const char *filename) +{ + size_t filename_len = strlen (filename); + char *result = (char *) malloc (filename_len + 4 + 1); + + if (result != NULL) + { + if (sizeof (EXEEXT) > sizeof ("")) + { + /* EXEEXT handling. */ + const size_t exeext_len = sizeof (EXEEXT) - sizeof (""); + static const char exeext[] = EXEEXT; + if (filename_len > exeext_len) + { + /* Compare using an inlined copy of c_strncasecmp(), because + the filenames may have undergone a case conversion since + they were packaged. In other words, EXEEXT may be ".exe" + on one system and ".EXE" on another. */ + const char *s1 = filename + filename_len - exeext_len; + const char *s2 = exeext; + for (; *s1 != '\0'; s1++, s2++) + { + unsigned char c1 = *s1; + unsigned char c2 = *s2; + if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1) + != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2)) + goto simple_append; + } + /* Insert ".bin" before EXEEXT or its equivalent. */ + memcpy (result, filename, filename_len - exeext_len); + memcpy (result + filename_len - exeext_len, ".bin", 4); + memcpy (result + filename_len - exeext_len + 4, + filename + filename_len - exeext_len, + exeext_len + 1); + return result; + } + } + simple_append: + /* Simply append ".bin". */ + memcpy (result, filename, filename_len); + memcpy (result + filename_len, ".bin", 4 + 1); + return result; + } + else + { + fprintf (stderr, "%s: %s\n", program_name, "memory exhausted"); + exit (1); + } +} + +/* List of directories that contain the libraries. */ +static const char *libdirs[] = { LIBDIRS NULL }; +/* Verify that at least one directory is given. */ +typedef int verify1[2 * (sizeof (libdirs) / sizeof (libdirs[0]) > 1) - 1]; + +/* Relocate the list of directories that contain the libraries. */ +static void +relocate_libdirs () +{ + size_t i; + + for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++) + libdirs[i] = relocate (libdirs[i]); +} + +/* Activate the list of directories in the LIBPATHVAR. */ +static void +activate_libdirs () +{ + const char *old_value; + size_t total; + size_t i; + char *value; + char *p; + + old_value = getenv (LIBPATHVAR); + if (old_value == NULL) + old_value = ""; + + total = 0; + for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++) + total += strlen (libdirs[i]) + 1; + total += strlen (old_value) + 1; + + value = (char *) malloc (total); + if (value == NULL) + { + fprintf (stderr, "%s: %s\n", program_name, "memory exhausted"); + exit (1); + } + p = value; + for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++) + { + size_t len = strlen (libdirs[i]); + memcpy (p, libdirs[i], len); + p += len; + *p++ = ':'; + } + if (old_value[0] != '\0') + strcpy (p, old_value); + else + p[-1] = '\0'; + + if (setenv (LIBPATHVAR, value, 1) < 0) + { + fprintf (stderr, "%s: %s\n", program_name, "memory exhausted"); + exit (1); + } +} + +int +main (int argc, char *argv[]) +{ + char *full_program_name; + + /* Set the program name and perform preparations for + get_full_program_name() and relocate(). */ + set_program_name_and_installdir (argv[0], INSTALLPREFIX, INSTALLDIR); + + /* Get the full program path. (Important if accessed through a symlink.) */ + full_program_name = get_full_program_name (); + if (full_program_name == NULL) + full_program_name = argv[0]; + + /* Invoke the real program, with suffix ".bin". */ + argv[0] = add_dotbin (full_program_name); + relocate_libdirs (); + activate_libdirs (); + execv (argv[0], argv); + fprintf (stderr, "%s: could not execute %s: %s\n", + program_name, argv[0], strerror (errno)); + exit (127); +}
new file mode 100644 --- /dev/null +++ b/m4/relocatable.m4 @@ -0,0 +1,119 @@ +# relocatable.m4 serial 7 (gettext-0.16) +dnl Copyright (C) 2003, 2005-2006 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl Support for relocateble programs. +AC_DEFUN([AC_RELOCATABLE], +[ + AC_REQUIRE([AC_RELOCATABLE_BODY]) + if test $RELOCATABLE = yes; then + AC_LIBOBJ([relocatable]) + fi +]) +dnl The guts of AC_RELOCATABLE. Needs to be expanded only once. +AC_DEFUN([AC_RELOCATABLE_BODY], +[ + AC_REQUIRE([AC_PROG_INSTALL]) + dnl This AC_BEFORE invocation leads to unjustified autoconf warnings + dnl when AC_RELOCATABLE_BODY is invoked more than once. + dnl We need this AC_BEFORE because AC_PROG_INSTALL is documented to + dnl overwrite earlier settings of INSTALL and INSTALL_PROGRAM (even + dnl though in autoconf-2.52..2.60 it doesn't do so), but we want this + dnl macro's setting of INSTALL_PROGRAM to persist. + AC_BEFORE([AC_PROG_INSTALL],[AC_RELOCATABLE_BODY]) + AC_REQUIRE([AC_LIB_LIBPATH]) + AC_REQUIRE([AC_RELOCATABLE_LIBRARY]) + AC_REQUIRE([AC_EXEEXT]) + is_noop=no + use_elf_origin_trick=no + if test $RELOCATABLE = yes; then + # --enable-relocatable implies --disable-rpath + enable_rpath=no + AC_DEFINE([ENABLE_RELOCATABLE], 1, + [Define to 1 if the package shall run at any location in the filesystem.]) + AC_CHECK_HEADERS([unistd.h mach-o/dyld.h]) + AC_CHECK_FUNCS([_NSGetExecutablePath]) + case "$host_os" in + mingw*) is_noop=yes ;; + linux*) use_elf_origin_trick=yes ;; + esac + if test $is_noop = yes; then + SET_RELOCATABLE="RELOCATABLE_LDFLAGS = :" + else + if test $use_elf_origin_trick = yes; then + dnl Use the dynamic linker's support for relocatable programs. + case "$ac_aux_dir" in + /*) reloc_ldflags="$ac_aux_dir/reloc-ldflags" ;; + *) reloc_ldflags="\$(top_builddir)/$ac_aux_dir/reloc-ldflags" ;; + esac + SET_RELOCATABLE="RELOCATABLE_LDFLAGS = \"$reloc_ldflags\" \"\$(host)\" \"\$(RELOCATABLE_LIBRARY_PATH)\"" + else + dnl Unfortunately we cannot define INSTALL_PROGRAM to a command + dnl consisting of more than one word - libtool doesn't support this. + dnl So we abuse the INSTALL_PROGRAM_ENV hook, originally meant for the + dnl 'install-strip' target. + SET_RELOCATABLE="INSTALL_PROGRAM_ENV = RELOC_LIBRARY_PATH_VAR=\"$shlibpath_var\" RELOC_LIBRARY_PATH_VALUE=\"\$(RELOCATABLE_LIBRARY_PATH)\" RELOC_PREFIX=\"\$(prefix)\" RELOC_COMPILE_COMMAND=\"\$(CC) \$(CPPFLAGS) \$(CFLAGS) \$(LDFLAGS)\" RELOC_SRCDIR=\"\$(RELOCATABLE_SRC_DIR)\" RELOC_BUILDDIR=\"\$(RELOCATABLE_BUILD_DIR)\" RELOC_CONFIG_H_DIR=\"\$(RELOCATABLE_CONFIG_H_DIR)\" RELOC_EXEEXT=\"\$(EXEEXT)\" RELOC_INSTALL_PROG=\"$INSTALL_PROGRAM\"" + case "$ac_aux_dir" in + /*) INSTALL_PROGRAM="$ac_aux_dir/install-reloc" ;; + *) INSTALL_PROGRAM="\$(top_builddir)/$ac_aux_dir/install-reloc" ;; + esac + fi + fi + else + SET_RELOCATABLE= + fi + AC_SUBST([SET_RELOCATABLE]) + AM_CONDITIONAL([RELOCATABLE_VIA_LD], + [test $is_noop = yes || test $use_elf_origin_trick = yes]) +]) + +dnl Support for relocatable libraries. +AC_DEFUN([AC_RELOCATABLE_LIBRARY], +[ + AC_REQUIRE([AC_RELOCATABLE_NOP]) + dnl Easier to put this here once, instead of into the DEFS of each Makefile. + if test "X$prefix" = "XNONE"; then + reloc_final_prefix="$ac_default_prefix" + else + reloc_final_prefix="$prefix" + fi + AC_DEFINE_UNQUOTED([INSTALLPREFIX], ["${reloc_final_prefix}"], + [Define to the value of ${prefix}, as a string.]) +]) + +dnl Support for relocatable packages for which it is a nop. +AC_DEFUN([AC_RELOCATABLE_NOP], +[ + AC_MSG_CHECKING([whether to activate relocatable installation]) + AC_ARG_ENABLE(relocatable, + [ --enable-relocatable install a package that can be moved in the filesystem], + [if test "$enableval" != no; then + RELOCATABLE=yes + else + RELOCATABLE=no + fi + ], RELOCATABLE=no) + AC_SUBST(RELOCATABLE) + AC_MSG_RESULT([$RELOCATABLE]) +]) + +dnl Determine the platform dependent parameters needed to use relocatability: +dnl shlibpath_var. +AC_DEFUN([AC_LIB_LIBPATH], +[ + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library path variable], acl_cv_libpath, [ + LD="$LD" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.libpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_libpath=${acl_cv_shlibpath_var:-none} + ]) + shlibpath_var="$acl_cv_shlibpath_var" +])