aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile7
-rw-r--r--elf/Versions7
-rw-r--r--elf/dl-deps.c79
-rw-r--r--elf/dl-dst.h72
-rw-r--r--elf/dl-load.c143
-rw-r--r--elf/dl-open.c53
-rw-r--r--elf/dlopen.c7
-rw-r--r--elf/dlopenold.c5
-rw-r--r--elf/ldsodefs.h3
-rw-r--r--elf/origtest.c38
10 files changed, 326 insertions, 88 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 26a08322df..d6c37c2bb8 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -22,7 +22,7 @@ subdir := elf
headers = elf.h bits/elfclass.h bits/dlfcn.h link.h dlfcn.h
routines = $(dl-routines) dl-open dl-close dl-symbol dl-support \
- dl-addr enbl-secure dl-profstub
+ dl-addr enbl-secure dl-profstub dl-origin
# The core dynamic linking functions are in libc for the static and
# profiled libraries.
@@ -78,7 +78,7 @@ install-rootsbin += ldconfig
endif
ifeq (yes,$(build-shared))
-tests = loadtest restest1 preloadtest loadfail multiload
+tests = loadtest restest1 preloadtest loadfail multiload origtest
endif
modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
testobj1_1 failobj
@@ -253,6 +253,9 @@ LDFLAGS-multiload = -rdynamic
CFLAGS-multiload.c = -DOBJDIR=\"$(elf-objpfx)\"
$(objpfx)multiload.out: $(objpfx)testobj1.so
+
+$(objpfx)origtest: $(libdl)
+$(objpfx)origtest.out: $(objpfx)testobj1.so
# muwahaha
diff --git a/elf/Versions b/elf/Versions
index 8e6dca878b..e26e096469 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -37,6 +37,13 @@ ld.so {
# functions used in other libraries
_dl_mcount; _dl_unload_cache;
}
+ GLIBC_2.1.1 {
+ # global variables
+ _dl_origin_path; _dl_platformlen;
+
+ # functions used in other libraries
+ _dl_dst_count; _dl_dst_substitute;
+ }
}
libdl {
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 7291cde787..56f91f6dd0 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -17,13 +17,15 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/param.h>
#include <elf/ldsodefs.h>
-#include <assert.h>
+#include <dl-dst.h>
/* Whether an shared object references one or more auxiliary objects
is signaled by the AUXTAG entry in l_info. */
@@ -39,6 +41,7 @@
reset if in _dl_close if the last global object is removed. */
size_t _dl_global_scope_alloc;
+extern size_t _dl_platformlen;
/* When loading auxiliary objects we must ignore errors. It's ok if
an object is missing. */
@@ -48,7 +51,7 @@ struct openaux_args
struct link_map *map;
int trace_mode;
const char *strtab;
- const ElfW(Dyn) *d;
+ const char *name;
/* The return value of openaux. */
struct link_map *aux;
@@ -59,7 +62,7 @@ openaux (void *a)
{
struct openaux_args *args = (struct openaux_args *) a;
- args->aux = _dl_map_object (args->map, args->strtab + args->d->d_un.d_val, 0,
+ args->aux = _dl_map_object (args->map, args->name, 0,
(args->map->l_type == lt_executable
? lt_library : args->map->l_type),
args->trace_mode);
@@ -84,6 +87,43 @@ struct list
};
+/* Macro to expand DST. It is an macro since we use `alloca'. */
+#define expand_dst(l, str, fatal) \
+ ({ \
+ const char *__str = (str); \
+ const char *__result = __str; \
+ size_t __cnt = DL_DST_COUNT(__str, 0); \
+ \
+ if (__cnt != 0) \
+ { \
+ char *__newp = (char *) alloca (DL_DST_REQUIRED (l, __str, \
+ strlen (__str), \
+ __cnt)); \
+ \
+ __result = DL_DST_SUBSTITUTE (l, __str, __newp, 0); \
+ \
+ if (*__result == '\0') \
+ { \
+ /* The replacement for the DST is not known. We can't \
+ processed. */ \
+ if (fatal) \
+ _dl_signal_error (0, __str, \
+ "empty dynamics string token substitution"); \
+ else \
+ { \
+ /* This is for DT_AUXILIARY. */ \
+ if (_dl_debug_libs) \
+ _dl_debug_message (1, "cannot load auxiliary `", __str, \
+ "' because of empty dynamic string" \
+ " token substitution\n", NULL); \
+ continue; \
+ } \
+ } \
+ } \
+ \
+ __result; })
+
+
unsigned int
internal_function
_dl_map_object_deps (struct link_map *map,
@@ -166,14 +206,21 @@ _dl_map_object_deps (struct link_map *map,
if (__builtin_expect (d->d_tag, DT_NEEDED) == DT_NEEDED)
{
/* Map in the needed object. */
- struct link_map *dep
- = _dl_map_object (l, strtab + d->d_un.d_val, 0,
- l->l_type == lt_executable ? lt_library :
- l->l_type, trace_mode);
+ struct link_map *dep;
/* Allocate new entry. */
- struct list *newp = alloca (sizeof (struct list));
+ struct list *newp;
+ /* Object name. */
+ const char *name;
+
+ /* Recognize DSTs. */
+ name = expand_dst (l, strtab + d->d_un.d_val, 0);
+
+ dep = _dl_map_object (l, name, 0,
+ l->l_type == lt_executable ? lt_library :
+ l->l_type, trace_mode);
/* Add it in any case to the duplicate list. */
+ newp = alloca (sizeof (struct list));
newp->map = dep;
newp->dup = NULL;
dtail->dup = newp;
@@ -202,17 +249,22 @@ _dl_map_object_deps (struct link_map *map,
{
char *errstring;
struct list *newp;
+ /* Object name. */
+ const char *name;
+
+ /* Recognize DSTs. */
+ name = expand_dst (l, strtab + d->d_un.d_val,
+ d->d_tag == DT_AUXILIARY);
if (d->d_tag == DT_AUXILIARY)
{
/* Store the tag in the argument structure. */
- args.d = d;
+ args.name = name;
/* Say that we are about to load an auxiliary library. */
if (_dl_debug_libs)
_dl_debug_message (1, "load auxiliary object=",
- strtab + d->d_un.d_val,
- " requested by file=",
+ name, " requested by file=",
l->l_name[0]
? l->l_name : _dl_argv[0],
"\n", NULL);
@@ -233,15 +285,14 @@ _dl_map_object_deps (struct link_map *map,
{
/* Say that we are about to load an auxiliary library. */
if (_dl_debug_libs)
- _dl_debug_message (1, "load filtered object=",
- strtab + d->d_un.d_val,
+ _dl_debug_message (1, "load filtered object=", name,
" requested by file=",
l->l_name[0]
? l->l_name : _dl_argv[0],
"\n", NULL);
/* For filter objects the dependency must be available. */
- args.aux = _dl_map_object (l, strtab + d->d_un.d_val, 0,
+ args.aux = _dl_map_object (l, name, 0,
(l->l_type == lt_executable
? lt_library : l->l_type),
trace_mode);
diff --git a/elf/dl-dst.h b/elf/dl-dst.h
new file mode 100644
index 0000000000..4b014d6ed5
--- /dev/null
+++ b/elf/dl-dst.h
@@ -0,0 +1,72 @@
+/* Handling of dynamic sring tokens.
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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 the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Determine the number of DST elements in the name. Only if IS_PATH is
+ nonzero paths are recognized (i.e., multiple, ':' separated filenames). */
+#define DL_DST_COUNT(name, is_path) \
+ ({ \
+ size_t __cnt = 0; \
+ const char *__sf = strchr (name, '$'); \
+ \
+ if (__sf != NULL) \
+ __cnt = _dl_dst_count (__sf, is_path); \
+ \
+ __cnt; })
+
+/* Prototype for used function. */
+extern size_t _dl_dst_count (const char *name, int is_path);
+
+
+/* Guess from the number of DSTs the length of the restul string. */
+#define DL_DST_REQUIRED(l, name, len, cnt) \
+ ({ \
+ size_t origin_len; \
+ size_t __len = (len); \
+ \
+ /* Now we make a guess how many extra characters on top of the length \
+ of S we need to represent the result. We know that we have CNT \
+ replacements. Each at most can use \
+ MAX (strlen (ORIGIN), strlen (_dl_platform)) \
+ minus 7 (which is the length of "$ORIGIN"). \
+ \
+ First get the origin string if it is not available yet. This can \
+ only happen for the map of the executable. */ \
+ if ((l)->l_origin == NULL) \
+ { \
+ assert ((l)->l_name[0] == '\0'); \
+ (l)->l_origin = _dl_get_origin (); \
+ origin_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \
+ ? strlen ((l)->l_origin) : 0); \
+ } \
+ else \
+ origin_len = (l)->l_origin == (char *) -1 ? 0 : strlen ((l)->l_origin); \
+ \
+ __len + cnt * (MAX (origin_len, _dl_platformlen) - 7); })
+
+/* Find origin of the executable. */
+extern const char *_dl_get_origin (void);
+
+
+/* Perform the DST substitution. */
+#define DL_DST_SUBSTITUTE(l, name, res, is_path) \
+ _dl_dst_substitute (l, name, res, is_path)
+
+/* Prototype for used function. */
+extern char *_dl_dst_substitute (struct link_map *l, const char *name,
+ char *result, int is_path);
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 46f0b67567..6e8b977195 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -31,8 +31,7 @@
#include "dynamic-link.h"
#include <stdio-common/_itoa.h>
-#include <dl-origin.h>
-
+#include <dl-dst.h>
/* On some systems, no flag bits are given to specify file mapping. */
#ifndef MAP_FILE
@@ -146,129 +145,139 @@ local_strdup (const char *s)
return (char *) memcpy (new, s, len);
}
-/* Return copy of argument with all recognized dynamic string tokens
- ($ORIGIN and $PLATFORM for now) replaced. On some platforms it
- might not be possible to determine the path from which the object
- belonging to the map is loaded. In this case the path element
- containing $ORIGIN is left out. */
-static char *
-expand_dynamic_string_token (struct link_map *l, const char *s)
+
+size_t
+_dl_dst_count (const char *name, int is_path)
{
- /* We make two runs over the string. First we determine how large the
- resulting string is and then we copy it over. Since this is now
- frequently executed operation we are looking here not for performance
- but rather for code size. */
- const char *sf;
size_t cnt = 0;
- size_t origin_len;
- size_t total;
- char *result, *last_elem, *wp;
- sf = strchr (s, '$');
- while (sf != NULL)
+ do
{
size_t len = 1;
/* $ORIGIN is not expanded for SUID/GUID programs. */
if ((((!__libc_enable_secure
- && strncmp (&sf[1], "ORIGIN", 6) == 0 && (len = 7) != 0)
- || (strncmp (&sf[1], "PLATFORM", 8) == 0 && (len = 9) != 0))
- && (s[len] == '\0' || s[len] == '/' || s[len] == ':'))
- || (s[1] == '{'
+ && strncmp (&name[1], "ORIGIN", 6) == 0 && (len = 7) != 0)
+ || (strncmp (&name[1], "PLATFORM", 8) == 0 && (len = 9) != 0))
+ && (name[len] == '\0' || name[len] == '/'
+ || (is_path && name[len] == ':')))
+ || (name[1] == '{'
&& ((!__libc_enable_secure
- && strncmp (&sf[2], "ORIGIN}", 7) == 0 && (len = 9) != 0)
- || (strncmp (&sf[2], "PLATFORM}", 9) == 0
+ && strncmp (&name[2], "ORIGIN}", 7) == 0 && (len = 9) != 0)
+ || (strncmp (&name[2], "PLATFORM}", 9) == 0
&& (len = 11) != 0))))
++cnt;
- sf = strchr (sf + len, '$');
+ name = strchr (name + len, '$');
}
+ while (name != NULL);
- /* If we do not have to replace anything simply copy the string. */
- if (cnt == 0)
- return local_strdup (s);
-
- /* Now we make a guess how many extra characters on top of the length
- of S we need to represent the result. We know that we have CNT
- replacements. Each at most can use
- MAX (strlen (ORIGIN), strlen (_dl_platform))
- minus 7 (which is the length of "$ORIGIN").
+ return cnt;
+}
- First get the origin string if it is not available yet. This can
- only happen for the map of the executable. */
- if (l->l_origin == NULL)
- {
- assert (l->l_name[0] == '\0');
- l->l_origin = get_origin ();
- origin_len = (l->l_origin && l->l_origin != (char *) -1
- ? strlen (l->l_origin) : 0);
- }
- else
- origin_len = l->l_origin == (char *) -1 ? 0 : strlen (l->l_origin);
- total = strlen (s) + cnt * (MAX (origin_len, _dl_platformlen) - 7);
- result = (char *) malloc (total + 1);
- if (result == NULL)
- return NULL;
+char *
+_dl_dst_substitute (struct link_map *l, const char *name, char *result,
+ int is_path)
+{
+ char *last_elem, *wp;
/* Now fill the result path. While copying over the string we keep
track of the start of the last path element. When we come accross
a DST we copy over the value or (if the value is not available)
leave the entire path element out. */
last_elem = wp = result;
+
do
{
- if (*s == '$')
+ if (*name == '$')
{
const char *repl;
size_t len;
- if ((((strncmp (&s[1], "ORIGIN", 6) == 0 && (len = 7) != 0)
- || (strncmp (&s[1], "PLATFORM", 8) == 0 && (len = 9) != 0))
- && (s[len] == '\0' || s[len] == '/' || s[len] == ':'))
- || (s[1] == '{'
- && ((strncmp (&s[2], "ORIGIN}", 7) == 0 && (len = 9) != 0)
- || (strncmp (&s[2], "PLATFORM}", 9) == 0
+ if ((((strncmp (&name[1], "ORIGIN", 6) == 0 && (len = 7) != 0)
+ || (strncmp (&name[1], "PLATFORM", 8) == 0 && (len = 9) != 0))
+ && (name[len] == '\0' || name[len] == '/'
+ || (is_path && name[len] == ':')))
+ || (name[1] == '{'
+ && ((strncmp (&name[2], "ORIGIN}", 7) == 0 && (len = 9) != 0)
+ || (strncmp (&name[2], "PLATFORM}", 9) == 0
&& (len = 11) != 0))))
{
- repl = ((len == 7 || s[2] == 'O')
+ repl = ((len == 7 || name[2] == 'O')
? (__libc_enable_secure ? NULL : l->l_origin)
: _dl_platform);
if (repl != NULL && repl != (const char *) -1)
{
wp = __stpcpy (wp, repl);
- s += len;
+ name += len;
}
else
{
/* We cannot use this path element, the value of the
replacement is unknown. */
wp = last_elem;
- s += len;
- while (*s != '\0' && *s != ':')
- ++s;
+ name += len;
+ while (*name != '\0' && (!is_path || *name != ':'))
+ ++name;
}
}
else
/* No DST we recognize. */
- *wp++ = *s++;
+ *wp++ = *name++;
}
- else if (*s == ':')
+ else if (is_path && *name == ':')
{
- *wp++ = *s++;
+ *wp++ = *name++;
last_elem = wp;
}
else
- *wp++ = *s++;
+ *wp++ = *name++;
}
- while (*s != '\0');
+ while (*name != '\0');
*wp = '\0';
return result;
}
+
+/* Return copy of argument with all recognized dynamic string tokens
+ ($ORIGIN and $PLATFORM for now) replaced. On some platforms it
+ might not be possible to determine the path from which the object
+ belonging to the map is loaded. In this case the path element
+ containing $ORIGIN is left out. */
+static char *
+expand_dynamic_string_token (struct link_map *l, const char *s)
+{
+ /* We make two runs over the string. First we determine how large the
+ resulting string is and then we copy it over. Since this is now
+ frequently executed operation we are looking here not for performance
+ but rather for code size. */
+ size_t cnt;
+ size_t total;
+ char *result;
+
+ /* Determine the nubmer of DST elements. */
+ cnt = DL_DST_COUNT (s, 1);
+
+ /* If we do not have to replace anything simply copy the string. */
+ if (cnt == 0)
+ return local_strdup (s);
+
+ /* Determine the length of the substituted string. */
+ total = DL_DST_REQUIRED (l, s, strlen (s), cnt);
+
+ /* Allocate the necessary memory. */
+ result = (char *) malloc (total + 1);
+ if (result == NULL)
+ return NULL;
+
+ return DL_DST_SUBSTITUTE (l, s, result, 1);
+}
+
+
/* Add `name' to the list of names for a particular shared object.
`name' is expected to have been allocated with malloc and will
be freed if the shared object already has this name.
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 2b3352b674..9a3c0939d8 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -17,14 +17,18 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h> /* Check whether MAP_COPY is defined. */
+#include <sys/param.h>
#include <bits/libc-lock.h>
#include <elf/ldsodefs.h>
+#include <dl-dst.h>
+
extern ElfW(Addr) _dl_sysdep_start (void **start_argptr,
void (*dl_main) (const ElfW(Phdr) *phdr,
@@ -57,6 +61,7 @@ static void show_scope (struct link_map *new);
At this time it is not anymore a problem to modify the tables. */
__libc_lock_define_initialized_recursive (, _dl_load_lock)
+extern size_t _dl_platformlen;
/* We must be carefull not to leave us in an inconsistent state. Thus we
catch any error and re-raise it after cleaning up. */
@@ -65,6 +70,7 @@ struct dl_open_args
{
const char *file;
int mode;
+ const void *caller;
struct link_map *map;
};
@@ -78,6 +84,50 @@ dl_open_worker (void *a)
ElfW(Addr) init;
struct r_debug *r;
unsigned int global_add;
+ const char *dst;
+
+ /* Maybe we have to expand a DST. */
+ dst = strchr (file, '$');
+ if (dst != NULL)
+ {
+ const void *caller = args->caller;
+ size_t len = strlen (file);
+ size_t required;
+ struct link_map *call_map;
+ char *new_file;
+
+ /* We have to find out from which object the caller is calling.
+ Find the highest-addressed object that ADDRESS is not below. */
+ call_map = NULL;
+ for (l = _dl_loaded; l; l = l->l_next)
+ if (l->l_addr != 0 /* Make sure we do not currently set this map up
+ in this moment. */
+ && caller >= (const void *) l->l_addr
+ && (call_map == NULL || call_map->l_addr < l->l_addr))
+ call_map = l;
+
+ if (call_map == NULL)
+ /* In this case we assume this is the main application. */
+ call_map = _dl_loaded;
+
+ /* Determine how much space we need. We have to allocate the
+ memory locally. */
+ required = DL_DST_REQUIRED (call_map, file, len, _dl_dst_count (dst, 0));
+
+ /* Get space for the new file name. */
+ new_file = (char *) alloca (required + 1);
+
+ /* Generate the new file name. */
+ DL_DST_SUBSTITUTE (call_map, file, new_file, 0);
+
+ /* If the substitution failed don't try to load. */
+ if (*new_file == '\0')
+ _dl_signal_error (0, "dlopen",
+ "empty dynamics string token substitution");
+
+ /* Now we have a new file name. */
+ file = new_file;
+ }
/* Load the named object. */
args->map = new = _dl_map_object (NULL, file, 0, lt_loaded, 0);
@@ -157,7 +207,7 @@ dl_open_worker (void *a)
struct link_map *
internal_function
-_dl_open (const char *file, int mode)
+_dl_open (const char *file, int mode, const void *caller)
{
struct dl_open_args args;
char *errstring;
@@ -172,6 +222,7 @@ _dl_open (const char *file, int mode)
args.file = file;
args.mode = mode;
+ args.caller = caller;
args.map = NULL;
errcode = _dl_catch_error (&errstring, dl_open_worker, &args);
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 496fc7d386..b72300df5c 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -1,5 +1,5 @@
/* Load a shared object at run time.
- Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -28,6 +28,8 @@ struct dlopen_args
int mode;
/* The return value of dlopen_doit. */
struct link_map *new;
+ /* Address of the caller. */
+ const void *caller;
};
@@ -36,7 +38,7 @@ dlopen_doit (void *a)
{
struct dlopen_args *args = (struct dlopen_args *) a;
- args->new = _dl_open (args->file ?: "", args->mode);
+ args->new = _dl_open (args->file ?: "", args->mode, args->caller);
}
@@ -46,6 +48,7 @@ __dlopen_check (const char *file, int mode)
struct dlopen_args args;
args.file = file;
args.mode = mode;
+ args.caller = __builtin_return_address (0);
return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
}
diff --git a/elf/dlopenold.c b/elf/dlopenold.c
index 7e7b462e1a..661a40579a 100644
--- a/elf/dlopenold.c
+++ b/elf/dlopenold.c
@@ -32,6 +32,8 @@ struct dlopen_args
int mode;
/* The return value of dlopen_doit. */
struct link_map *new;
+ /* Address of the caller. */
+ const void *caller;
};
@@ -40,7 +42,7 @@ dlopen_doit (void *a)
{
struct dlopen_args *args = (struct dlopen_args *) a;
- args->new = _dl_open (args->file ?: "", args->mode);
+ args->new = _dl_open (args->file ?: "", args->mode, args->caller);
}
@@ -49,6 +51,7 @@ __dlopen_nocheck (const char *file, int mode)
{
struct dlopen_args args;
args.file = file;
+ args.caller = __builtin_return_address (0);
if ((mode & RTLD_BINDING_MASK) == 0)
/* By default assume RTLD_LAZY. */
diff --git a/elf/ldsodefs.h b/elf/ldsodefs.h
index 392ea1787f..f8db76c32c 100644
--- a/elf/ldsodefs.h
+++ b/elf/ldsodefs.h
@@ -270,7 +270,8 @@ extern void _dl_setup_hash (struct link_map *map) internal_function;
/* Open the shared object NAME, relocate it, and run its initializer if it
hasn't already been run. MODE is as for `dlopen' (see <dlfcn.h>). If
the object is already opened, returns its existing map. */
-extern struct link_map *_dl_open (const char *name, int mode)
+extern struct link_map *_dl_open (const char *name, int mode,
+ const void *caller)
internal_function;
/* Close an object previously opened by _dl_open. */
diff --git a/elf/origtest.c b/elf/origtest.c
new file mode 100644
index 0000000000..11bd0f76cf
--- /dev/null
+++ b/elf/origtest.c
@@ -0,0 +1,38 @@
+#include <dlfcn.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h;
+ int (*fp) (int);
+ int res;
+
+ h = dlopen ("${ORIGIN}/testobj1.so", RTLD_LAZY);
+ if (h == NULL)
+ error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so",
+ dlerror ());
+
+ fp = dlsym (h, "obj1func1");
+ if (fp == NULL)
+ error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s",
+ "testobj1.so", dlerror ());
+
+ res = fp (10);
+ printf ("fp(10) = %d\n", res);
+
+ if (dlclose (h) != 0)
+ error (EXIT_FAILURE, 0, "while close `%s': %s",
+ "testobj1.so", dlerror ());
+
+ return res != 42;
+}
+
+
+int
+foo (int a)
+{
+ return a + 10;
+}