From 2aec968d4df313f893f239a1a69aef2392a16b85 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Fri, 6 Feb 2015 09:05:35 -0800 Subject: [PATCH] Use mmap and cache the view buffer for get_view 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 . (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 | 14 +++++ ld/config.in | 6 ++ ld/configure | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ld/configure.ac | 3 + ld/plugin.c | 89 +++++++++++++++++++++------ 5 files changed, 275 insertions(+), 19 deletions(-) diff --git a/ld/ChangeLog b/ld/ChangeLog index 4ae174b..e838ac8 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,17 @@ +2015-02-06 H.J. Lu + + * configure.ac: Add AC_FUNC_MMAP. + * config.in: Regenerated. + * configure: Likewise. + * plugin.c: Include . + (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 * plugin.c (release_input_file): Set fd to -1 after closing it. diff --git a/ld/config.in b/ld/config.in index 2ab4844..ad015fe 100644 --- a/ld/config.in +++ b/ld/config.in @@ -56,6 +56,9 @@ /* Define to 1 if you have the 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 header file, and it defines `DIR'. */ #undef HAVE_NDIR_H diff --git a/ld/configure b/ld/configure index 8a7bd20..7af2626 100755 --- a/ld/configure +++ b/ld/configure @@ -16549,6 +16549,188 @@ fi 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 +#include + +#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 +# 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 : diff --git a/ld/configure.ac b/ld/configure.ac index 043c597..e926c03 100644 --- a/ld/configure.ac +++ b/ld/configure.ac @@ -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]) diff --git a/ld/plugin.c b/ld/plugin.c index ae0ac89..7ee45a2 100644 --- a/ld/plugin.c +++ b/ld/plugin.c @@ -32,6 +32,18 @@ #include "plugin.h" #include "plugin-api.h" #include "elf-bfd.h" +#if HAVE_MMAP +# include +# 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 #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; -- 2.7.4