aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Hastings <abh@cray.com>2011-01-05 16:03:37 -0600
committerEric B Munson <emunson@mgebm.net>2011-01-21 14:09:27 -0700
commit433d6c777dff1f96718b5f92e7e7957989b0860b (patch)
tree216e537fcf2e22f546d4816ac0ab51e936aa8971
parent0c460f66d8500909b1ecc1b57263f49fabbff889 (diff)
downloadlibhugetlbfs-433d6c777dff1f96718b5f92e7e7957989b0860b.tar.gz
shm.c: Allow static linking with shmget()
libhugetlbfs redefines shmget() so that it can transparently optionally add the hugepage flag. Unfortunately, it uses dlsym() to to find the next shmget() to call in the dynamic library call chain. This does not work on statically-linked executables. Work around this by making all references to libdl weak, and testing for its presence. If libdl symbols are not not present, directly make the shmget syscall. Making the dl* symbols weak does not affect dynamic executables as libdl.so is: * Automatically added to the library dependency list when liked with -lhugetlbfs * Mapped into the programs address space when used with LD_PRELOAD. Based on a patch originally authored by Dean Luick <luick@cray.com>. Signed-off-by: Andrew Hastings <abh@cray.com> on behalf of Cray Inc. Signed-off-by: Eric B Munson <emunson@mgebm.net>
-rw-r--r--shm.c71
1 files changed, 67 insertions, 4 deletions
diff --git a/shm.c b/shm.c
index 6847c48..1f82cab 100644
--- a/shm.c
+++ b/shm.c
@@ -28,6 +28,61 @@
#include <sys/types.h>
#include "libhugetlbfs_internal.h"
#include "hugetlbfs.h"
+#include <sys/syscall.h>
+
+#if defined(SYS_shmget) || defined(SYS_ipc)
+#define HAVE_SHMGET_SYSCALL
+#endif
+
+#ifdef HAVE_SHMGET_SYSCALL
+/*
+ * The calls to dlsym() and dlerror() in the shmget() wrapper below force
+ * a dependency on libdl.so. This does not work for static executables
+ * as the glibc dynamic library implementation does not automatically
+ * have static dl* function stubs linked into static executables.
+ *
+ * Work around this problem by adding a weak attribute to the declarations
+ * of dlsym() and dlerror(). (The declaration is otherwise the same as in
+ * <dlfcn.h>). This allows a static executable to be linked without -ldl.
+ * If &dlsym is NULL then this is a static executable and a call to the
+ * system shmget() may be performed without worry as there is no dynamic
+ * call chain.
+ */
+extern void *dlsym (void *__restrict __handle, __const char *__restrict __name)
+ __attribute__((weak)) __THROW __nonnull ((2));
+extern char *dlerror (void) __attribute__((weak)) __THROW;
+
+
+/* call syscall shmget through the generic syscall mechanism */
+static int syscall_shmget(key_t key, size_t size, int shmflg)
+{
+#ifdef SYS_shmget
+ return syscall(SYS_shmget, key, size, shmflg);
+#else
+ /*
+ * Some platforms do not have have a direct shmget syscall. Instead,
+ * all SysV IPC calls are funneled through the ipc() system call.
+ *
+ * ipc() is expected to only be used by libc implementors, so using
+ * it has not been smoothed out. There is no function declaration.
+ * The needed define for SHMGET is in linux/ipc.h, but that file
+ * also includes a conflicting definition of ipc_perm. So,
+ * just define the needed items here.
+ *
+ * When compiling -m32 on x86_64, the ipc glibc wrapper does not
+ * exist. Instead, just use SYS_ipc.
+ *
+ * The ipc system call below does not set the IPC_64 version flag
+ * with SHMGET because that would have required more private defines
+ * and the version number is not used for the SHMGET call.
+ */
+ #define SHMGET 23
+
+ return syscall(SYS_ipc, SHMGET, key, size, shmflg, (void *)NULL, 0L);
+#endif
+}
+
+#endif /* HAVE_SHMGET_SYSCALL */
int shmget(key_t key, size_t size, int shmflg)
{
@@ -40,10 +95,18 @@ int shmget(key_t key, size_t size, int shmflg)
/* Get a handle to the "real" shmget system call */
if (!real_shmget) {
- real_shmget = dlsym(RTLD_NEXT, "shmget");
- if ((error = dlerror()) != NULL) {
- ERROR("%s", error);
- return -1;
+#ifdef HAVE_SHMGET_SYSCALL
+ if (&dlsym == NULL) {
+ /* in a static executable, call shmget directly */
+ real_shmget = syscall_shmget;
+ } else
+#endif /* HAVE_SHMGET_SYSCALL */
+ {
+ real_shmget = dlsym(RTLD_NEXT, "shmget");
+ if ((error = dlerror()) != NULL) {
+ ERROR("%s", error);
+ return -1;
+ }
}
}