summaryrefslogtreecommitdiffstats
path: root/libexec
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2014-04-11 16:55:25 +0000
committeralc <alc@FreeBSD.org>2014-04-11 16:55:25 +0000
commit5f3ef7ae53bd38122a8d32692598ac90512e1f06 (patch)
tree2fd3b189804ef6bb37e0ba59394962017e10dc50 /libexec
parent134a8ae0443c21eff9d1b2d5592f8c594e2a622c (diff)
downloadFreeBSD-src-5f3ef7ae53bd38122a8d32692598ac90512e1f06.zip
FreeBSD-src-5f3ef7ae53bd38122a8d32692598ac90512e1f06.tar.gz
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
Diffstat (limited to 'libexec')
-rw-r--r--libexec/rtld-elf/malloc.c29
-rw-r--r--libexec/rtld-elf/map_object.c7
-rw-r--r--libexec/rtld-elf/rtld.c53
-rw-r--r--libexec/rtld-elf/rtld.h3
4 files changed, 67 insertions, 25 deletions
diff --git a/libexec/rtld-elf/malloc.c b/libexec/rtld-elf/malloc.c
index 8e34362..9f7dbe0 100644
--- a/libexec/rtld-elf/malloc.c
+++ b/libexec/rtld-elf/malloc.c
@@ -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);
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
index 0f75cca..2e17fbf 100644
--- a/libexec/rtld-elf/map_object.c
+++ b/libexec/rtld-elf/map_object.c
@@ -68,6 +68,7 @@ map_object(int fd, const char *path, const struct stat *sb)
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, const struct stat *sb)
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));
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 24e56b7..f96b8e7 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -97,6 +97,7 @@ static void *fill_search_info(const char *, size_t, void *);
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 **aux_info)
/* 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 **aux_info)
}
/*
+ * 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
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index d186dcc..cbeff66 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -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;
OpenPOWER on IntegriCloud