Use mmap and cache the view buffer for get_view
authorH.J. Lu <hjl.tools@gmail.com>
Fri, 6 Feb 2015 17:05:35 +0000 (09:05 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Fri, 6 Feb 2015 17:05:57 +0000 (09:05 -0800)
This patch uses mmap if it is available and works.  It also caches the
view buffer for get_view.

* configure.ac: Add AC_FUNC_MMAP.
* config.in: Regenerated.
* configure: Likewise.
* plugin.c: Include <sys/mman.h>.
(MAP_FAILED): New.  Defined if not defined.
(PROT_READ): Likewise.
(MAP_PRIVATE): Likewise.
(view_buffer_t): New.
(plugin_input_file_t): Add view_buffer.
(get_view): Try mmap and cache the view buffer.
(plugin_maybe_claim): Initialize view_buffer.

ld/ChangeLog
ld/config.in
ld/configure
ld/configure.ac
ld/plugin.c

index 4ae174b29b0e4580be6f7aa5780c99dc3c61cca7..e838ac853e06c9e2ae98288417adc35a9ce7d34b 100644 (file)
@@ -1,3 +1,17 @@
+2015-02-06  H.J. Lu  <hongjiu.lu@intel.com>
+
+       * configure.ac: Add AC_FUNC_MMAP.
+       * config.in: Regenerated.
+       * configure: Likewise.
+       * plugin.c: Include <sys/mman.h>.
+       (MAP_FAILED): New.  Defined if not defined.
+       (PROT_READ): Likewise.
+       (MAP_PRIVATE): Likewise.
+       (view_buffer_t): New.
+       (plugin_input_file_t): Add view_buffer.
+       (get_view): Try mmap and cache the view buffer.
+       (plugin_maybe_claim): Initialize view_buffer.
+
 2015-02-05  H.J. Lu  <hongjiu.lu@intel.com>
 
        * plugin.c (release_input_file): Set fd to -1 after closing it.
index 2ab4844876364026e49c828e9ea0994e1a6da606..ad015fe1a16de77a3a3eb931c28f0706d616e439 100644 (file)
@@ -56,6 +56,9 @@
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
 /* Define to 1 if you have the `glob' function. */
 #undef HAVE_GLOB
 
@@ -83,6 +86,9 @@
 /* Define to 1 if you have the `mkstemp' function. */
 #undef HAVE_MKSTEMP
 
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
 /* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
 #undef HAVE_NDIR_H
 
index 8a7bd207c5379a6b859e634721df65d4713d12db..7af2626d5bce3e03100544431adbb03a39daa36a 100755 (executable)
 fi
 
 
+for ac_header in stdlib.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+eval as_val=\$$as_ac_Header
+   if test "x$as_val" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in getpagesize
+do :
+  ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize"
+if test "x$ac_cv_func_getpagesize" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GETPAGESIZE 1
+_ACEOF
+
+fi
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working mmap" >&5
+$as_echo_n "checking for working mmap... " >&6; }
+if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then :
+  ac_cv_func_mmap_fixed_mapped=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+/* malloc might have been renamed as rpl_malloc. */
+#undef malloc
+
+/* Thanks to Mike Haertel and Jim Avera for this test.
+   Here is a matrix of mmap possibilities:
+       mmap private not fixed
+       mmap private fixed at somewhere currently unmapped
+       mmap private fixed at somewhere already mapped
+       mmap shared not fixed
+       mmap shared fixed at somewhere currently unmapped
+       mmap shared fixed at somewhere already mapped
+   For private mappings, we should verify that changes cannot be read()
+   back from the file, nor mmap's back from the file at a different
+   address.  (There have been systems where private was not correctly
+   implemented like the infamous i386 svr4.0, and systems where the
+   VM page cache was not coherent with the file system buffer cache
+   like early versions of FreeBSD and possibly contemporary NetBSD.)
+   For shared mappings, we should conversely verify that changes get
+   propagated back to all the places they're supposed to be.
+
+   Grep wants private fixed already mapped.
+   The main things grep needs to know about mmap are:
+   * does it exist and is it safe to write into the mmap'd area
+   * how to use it (BSD variants)  */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H
+char *malloc ();
+#endif
+
+/* This mess was copied from the GNU getpagesize.h.  */
+#ifndef HAVE_GETPAGESIZE
+/* Assume that all systems that can run configure have sys/param.h.  */
+# ifndef HAVE_SYS_PARAM_H
+#  define HAVE_SYS_PARAM_H 1
+# endif
+
+# ifdef _SC_PAGESIZE
+#  define getpagesize() sysconf(_SC_PAGESIZE)
+# else /* no _SC_PAGESIZE */
+#  ifdef HAVE_SYS_PARAM_H
+#   include <sys/param.h>
+#   ifdef EXEC_PAGESIZE
+#    define getpagesize() EXEC_PAGESIZE
+#   else /* no EXEC_PAGESIZE */
+#    ifdef NBPG
+#     define getpagesize() NBPG * CLSIZE
+#     ifndef CLSIZE
+#      define CLSIZE 1
+#     endif /* no CLSIZE */
+#    else /* no NBPG */
+#     ifdef NBPC
+#      define getpagesize() NBPC
+#     else /* no NBPC */
+#      ifdef PAGESIZE
+#       define getpagesize() PAGESIZE
+#      endif /* PAGESIZE */
+#     endif /* no NBPC */
+#    endif /* no NBPG */
+#   endif /* no EXEC_PAGESIZE */
+#  else /* no HAVE_SYS_PARAM_H */
+#   define getpagesize() 8192  /* punt totally */
+#  endif /* no HAVE_SYS_PARAM_H */
+# endif /* no _SC_PAGESIZE */
+
+#endif /* no HAVE_GETPAGESIZE */
+
+int
+main ()
+{
+  char *data, *data2, *data3;
+  int i, pagesize;
+  int fd;
+
+  pagesize = getpagesize ();
+
+  /* First, make a file with some known garbage in it. */
+  data = (char *) malloc (pagesize);
+  if (!data)
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    *(data + i) = rand ();
+  umask (0);
+  fd = creat ("conftest.mmap", 0600);
+  if (fd < 0)
+    return 1;
+  if (write (fd, data, pagesize) != pagesize)
+    return 1;
+  close (fd);
+
+  /* Next, try to mmap the file at a fixed address which already has
+     something else allocated at it.  If we can, also make sure that
+     we see the same garbage.  */
+  fd = open ("conftest.mmap", O_RDWR);
+  if (fd < 0)
+    return 1;
+  data2 = (char *) malloc (2 * pagesize);
+  if (!data2)
+    return 1;
+  data2 += (pagesize - ((long int) data2 & (pagesize - 1))) & (pagesize - 1);
+  if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_FIXED, fd, 0L))
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    if (*(data + i) != *(data2 + i))
+      return 1;
+
+  /* Finally, make sure that changes to the mapped area do not
+     percolate back to the file as seen by read().  (This is a bug on
+     some variants of i386 svr4.0.)  */
+  for (i = 0; i < pagesize; ++i)
+    *(data2 + i) = *(data2 + i) + 1;
+  data3 = (char *) malloc (pagesize);
+  if (!data3)
+    return 1;
+  if (read (fd, data3, pagesize) != pagesize)
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    if (*(data + i) != *(data3 + i))
+      return 1;
+  close (fd);
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_func_mmap_fixed_mapped=yes
+else
+  ac_cv_func_mmap_fixed_mapped=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_mmap_fixed_mapped" >&5
+$as_echo "$ac_cv_func_mmap_fixed_mapped" >&6; }
+if test $ac_cv_func_mmap_fixed_mapped = yes; then
+
+$as_echo "#define HAVE_MMAP 1" >>confdefs.h
+
+fi
+rm -f conftest.mmap
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
 $as_echo_n "checking for library containing dlopen... " >&6; }
 if test "${ac_cv_search_dlopen+set}" = set; then :
index 043c597794f3396fd1efc266db6f4bea69c31f49..e926c03dbfcd198d6f2f4a05fff3806467f65d7b 100644 (file)
@@ -195,6 +195,9 @@ AC_CHECK_FUNCS(glob mkstemp realpath sbrk setlocale waitpid)
 AC_CHECK_FUNCS(open lseek close)
 AC_HEADER_DIRENT
 
+dnl AC_CHECK_HEADERS(sys/mman.h)
+AC_FUNC_MMAP
+
 AC_SEARCH_LIBS([dlopen], [dl])
 AM_CONDITIONAL([ENABLE_PLUGINS], [test x$plugins = xyes])
 
index ae0ac8976c4df30907849143c5a9a3f8f6338c94..7ee45a2eccf0db032538508fc9aed5b963857d52 100644 (file)
 #include "plugin.h"
 #include "plugin-api.h"
 #include "elf-bfd.h"
+#if HAVE_MMAP
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+#  define MAP_FAILED ((void *) -1)
+# endif
+# ifndef PROT_READ
+#  define PROT_READ 0
+# endif
+# ifndef MAP_PRIVATE
+#  define MAP_PRIVATE 0
+# endif
+#endif
 #include <errno.h>
 #if !(defined(errno) || defined(_MSC_VER) && defined(_INC_ERRNO))
 extern int errno;
@@ -76,11 +88,19 @@ typedef struct plugin
   bfd_boolean cleanup_done;
 } plugin_t;
 
+typedef struct view_buffer
+{
+  char *addr;
+  size_t filesize;
+  off_t offset;
+} view_buffer_t;
+
 /* The internal version of struct ld_plugin_input_file with a BFD
    pointer.  */
 typedef struct plugin_input_file
 {
   bfd *abfd;
+  view_buffer_t view_buffer;
   char *name;
   int fd;
   off_t offset;
@@ -475,35 +495,63 @@ get_input_file (const void *handle, struct ld_plugin_input_file *file)
 static enum ld_plugin_status
 get_view (const void *handle, const void **viewp)
 {
-  const plugin_input_file_t *input = handle;
+  plugin_input_file_t *input = (plugin_input_file_t *) handle;
   char *buffer;
-  size_t size;
+  size_t size = input->filesize;
 
   ASSERT (called_plugin);
 
-  if (lseek (input->fd, input->offset, SEEK_SET) < 0)
-    return LDPS_ERR;
+  /* FIXME: einfo should support %lld.  */
+  if ((off_t) size != input->filesize)
+    einfo (_("%P%F: unsupported input file size: %s (%ld bytes)\n"),
+          input->name, (long) input->filesize);
 
-  size = input->filesize;
-  buffer = bfd_alloc (input->abfd, size);
-  if (buffer == NULL)
-    return LDPS_ERR;
-  *viewp = buffer;
+  /* Check the cached view buffer.  */
+  if (input->view_buffer.addr != NULL
+      && input->view_buffer.filesize == size
+      && input->view_buffer.offset == input->offset)
+    {
+      *viewp = input->view_buffer.addr;
+      return LDPS_OK;
+    }
+
+  input->view_buffer.filesize = size;
+  input->view_buffer.offset = input->offset;
 
-  do
+#if HAVE_MMAP
+  buffer = mmap (NULL, size, PROT_READ, MAP_PRIVATE, input->fd,
+                input->offset);
+  if (buffer == MAP_FAILED)
+#endif
     {
-      ssize_t got = read (input->fd, buffer, size);
-      if (got == 0)
-       break;
-      else if (got > 0)
+      char *p;
+
+      if (lseek (input->fd, input->offset, SEEK_SET) < 0)
+       return LDPS_ERR;
+
+      buffer = bfd_alloc (input->abfd, size);
+      if (buffer == NULL)
+       return LDPS_ERR;
+
+      p = buffer;
+      do
        {
-         buffer += got;
-         size -= got;
+         ssize_t got = read (input->fd, p, size);
+         if (got == 0)
+           break;
+         else if (got > 0)
+           {
+             p += got;
+             size -= got;
+           }
+         else if (errno != EINTR)
+           return LDPS_ERR;
        }
-      else if (errno != EINTR)
-       return LDPS_ERR;
+      while (size > 0);
     }
-  while (size > 0);
+
+  input->view_buffer.addr = buffer;
+  *viewp = buffer;
 
   return LDPS_OK;
 }
@@ -951,6 +999,9 @@ plugin_maybe_claim (struct ld_plugin_input_file *file,
           bfd_get_error ());
 
   input->abfd = abfd;
+  input->view_buffer.addr = NULL;
+  input->view_buffer.filesize = 0;
+  input->view_buffer.offset = 0;
   input->fd = file->fd;
   input->offset  = file->offset;
   input->filesize = file->filesize;