Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 11 Apr 2014 16:55:26 +0000 (UTC)
From:      Alan Cox <alc@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r264346 - head/libexec/rtld-elf
Message-ID:  <201404111655.s3BGtQjh041582@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: alc
Date: Fri Apr 11 16:55:25 2014
New Revision: 264346
URL: http://svnweb.freebsd.org/changeset/base/264346

Log:
  Before calling mmap() on a shared library's text and data sections, rtld
  first calls mmap() with the arguments PROT_NONE and MAP_ANON to reserve a
  single, contiguous range of virtual addresses for the entire shared library.
  Later, rtld calls mmap() with the the shared library's file descriptor
  and the argument MAP_FIXED to place the text and data sections within the
  reserved range.  The rationale for mapping shared libraries in this way is
  explained in the commit message for Revision 190885.  However, this approach
  does have an unintended, negative consequence.  Since the first call to
  mmap() specifies MAP_ANON and not the shared library's file descriptor, the
  kernel has no idea what alignment the vm object backing the file prefers.
  As a result, the reserved range's alignment is unlikely to be the same as
  the vm object's, and so mapping with superpages becomes impossible.  To
  address this problem, this revision adds the argument MAP_ALIGNED_SUPER to
  the first call to mmap() if the text section is larger than the smallest
  superpage size.
  
  To determine if the text section is larger than the smallest superpage
  size, rtld must always fetch the page size information.  As a result, the
  private code for fetching the base page size in rtld's builtin malloc is
  redundant.  Eliminate it.  Requested by: kib
  
  Tested by:	zbb (on arm)
  Reviewed by:	kib (an earlier version)
  Discussed with:	jhb

Modified:
  head/libexec/rtld-elf/malloc.c
  head/libexec/rtld-elf/map_object.c
  head/libexec/rtld-elf/rtld.c
  head/libexec/rtld-elf/rtld.h

Modified: head/libexec/rtld-elf/malloc.c
==============================================================================
--- head/libexec/rtld-elf/malloc.c	Fri Apr 11 16:51:35 2014	(r264345)
+++ head/libexec/rtld-elf/malloc.c	Fri Apr 11 16:55:25 2014	(r264346)
@@ -139,25 +139,14 @@ botch(s)
 /* Debugging stuff */
 #define TRACE()	rtld_printf("TRACE %s:%d\n", __FILE__, __LINE__)
 
-extern int pagesize;
-
-static int
-rtld_getpagesize(void)
-{
-	int mib[2];
-	size_t size;
-
-	if (pagesize != 0)
-		return (pagesize);
-
-	mib[0] = CTL_HW;
-	mib[1] = HW_PAGESIZE;
-	size = sizeof(pagesize);
-	if (sysctl(mib, 2, &pagesize, &size, NULL, 0) == -1)
-		return (-1);
-	return (pagesize);
-
-}
+/*
+ * The array of supported page sizes is provided by the user, i.e., the
+ * program that calls this storage allocator.  That program must initialize
+ * the array before making its first call to allocate storage.  The array
+ * must contain at least one page size.  The page sizes must be stored in
+ * increasing order.
+ */
+extern size_t *pagesizes;
 
 void *
 malloc(nbytes)
@@ -173,7 +162,7 @@ malloc(nbytes)
 	 * align break pointer so all data will be page aligned.
 	 */
 	if (pagesz == 0) {
-		pagesz = n = rtld_getpagesize();
+		pagesz = n = pagesizes[0];
 		if (morepages(NPOOLPAGES) == 0)
 			return NULL;
 		op = (union overhead *)(pagepool_start);

Modified: head/libexec/rtld-elf/map_object.c
==============================================================================
--- head/libexec/rtld-elf/map_object.c	Fri Apr 11 16:51:35 2014	(r264345)
+++ head/libexec/rtld-elf/map_object.c	Fri Apr 11 16:55:25 2014	(r264346)
@@ -68,6 +68,7 @@ map_object(int fd, const char *path, con
     Elf_Addr base_vaddr;
     Elf_Addr base_vlimit;
     caddr_t base_addr;
+    int base_flags;
     Elf_Off data_offset;
     Elf_Addr data_vaddr;
     Elf_Addr data_vlimit;
@@ -176,9 +177,11 @@ map_object(int fd, const char *path, con
     base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz);
     mapsize = base_vlimit - base_vaddr;
     base_addr = (caddr_t) base_vaddr;
+    base_flags = MAP_PRIVATE | MAP_ANON | MAP_NOCORE;
+    if (npagesizes > 1 && round_page(segs[0]->p_filesz) >= pagesizes[1])
+	base_flags |= MAP_ALIGNED_SUPER;
 
-    mapbase = mmap(base_addr, mapsize, PROT_NONE, MAP_ANON | MAP_PRIVATE |
-      MAP_NOCORE, -1, 0);
+    mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0);
     if (mapbase == (caddr_t) -1) {
 	_rtld_error("%s: mmap of entire address space failed: %s",
 	  path, rtld_strerror(errno));

Modified: head/libexec/rtld-elf/rtld.c
==============================================================================
--- head/libexec/rtld-elf/rtld.c	Fri Apr 11 16:51:35 2014	(r264345)
+++ head/libexec/rtld-elf/rtld.c	Fri Apr 11 16:55:25 2014	(r264346)
@@ -97,6 +97,7 @@ static void *fill_search_info(const char
 static char *find_library(const char *, const Obj_Entry *);
 static const char *gethints(bool);
 static void init_dag(Obj_Entry *);
+static void init_pagesizes(Elf_Auxinfo **aux_info);
 static void init_rtld(caddr_t, Elf_Auxinfo **);
 static void initlist_add_neededs(Needed_Entry *, Objlist *);
 static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *);
@@ -205,7 +206,8 @@ extern Elf_Dyn _DYNAMIC;
 #define	RTLD_IS_DYNAMIC()	(&_DYNAMIC != NULL)
 #endif
 
-int osreldate, pagesize;
+int npagesizes, osreldate;
+size_t *pagesizes;
 
 long __stack_chk_guard[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 
@@ -1822,8 +1824,9 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo *
     /* Now that non-local variables can be accesses, copy out obj_rtld. */
     memcpy(&obj_rtld, &objtmp, sizeof(obj_rtld));
 
-    if (aux_info[AT_PAGESZ] != NULL)
-	    pagesize = aux_info[AT_PAGESZ]->a_un.a_val;
+    /* The page size is required by the dynamic memory allocator. */
+    init_pagesizes(aux_info);
+
     if (aux_info[AT_OSRELDATE] != NULL)
 	    osreldate = aux_info[AT_OSRELDATE]->a_un.a_val;
 
@@ -1837,6 +1840,50 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo *
 }
 
 /*
+ * Retrieve the array of supported page sizes.  The kernel provides the page
+ * sizes in increasing order.
+ */
+static void
+init_pagesizes(Elf_Auxinfo **aux_info)
+{
+	static size_t psa[MAXPAGESIZES];
+	int mib[2];
+	size_t len, size;
+
+	if (aux_info[AT_PAGESIZES] != NULL && aux_info[AT_PAGESIZESLEN] !=
+	    NULL) {
+		size = aux_info[AT_PAGESIZESLEN]->a_un.a_val;
+		pagesizes = aux_info[AT_PAGESIZES]->a_un.a_ptr;
+	} else {
+		len = 2;
+		if (sysctlnametomib("hw.pagesizes", mib, &len) == 0)
+			size = sizeof(psa);
+		else {
+			/* As a fallback, retrieve the base page size. */
+			size = sizeof(psa[0]);
+			if (aux_info[AT_PAGESZ] != NULL) {
+				psa[0] = aux_info[AT_PAGESZ]->a_un.a_val;
+				goto psa_filled;
+			} else {
+				mib[0] = CTL_HW;
+				mib[1] = HW_PAGESIZE;
+				len = 2;
+			}
+		}
+		if (sysctl(mib, len, psa, &size, NULL, 0) == -1) {
+			_rtld_error("sysctl for hw.pagesize(s) failed");
+			die();
+		}
+psa_filled:
+		pagesizes = psa;
+	}
+	npagesizes = size / sizeof(pagesizes[0]);
+	/* Discard any invalid entries at the end of the array. */
+	while (npagesizes > 0 && pagesizes[npagesizes - 1] == 0)
+		npagesizes--;
+}
+
+/*
  * Add the init functions from a needed object list (and its recursive
  * needed objects) to "list".  This is not used directly; it is a helper
  * function for initlist_add_objects().  The write lock must be held

Modified: head/libexec/rtld-elf/rtld.h
==============================================================================
--- head/libexec/rtld-elf/rtld.h	Fri Apr 11 16:51:35 2014	(r264345)
+++ head/libexec/rtld-elf/rtld.h	Fri Apr 11 16:55:25 2014	(r264346)
@@ -71,6 +71,9 @@ extern size_t tls_static_space;
 extern int tls_dtv_generation;
 extern int tls_max_index;
 
+extern int npagesizes;
+extern size_t *pagesizes;
+
 extern int main_argc;
 extern char **main_argv;
 extern char **environ;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404111655.s3BGtQjh041582>