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
This commit is contained in:
Alan Cox 2014-04-11 16:55:25 +00:00
parent 1ff3b1375f
commit ea8577c712
4 changed files with 67 additions and 25 deletions

View File

@ -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);

View File

@ -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));

View File

@ -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;
@ -1836,6 +1839,50 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info)
r_debug.r_state = RT_CONSISTENT;
}
/*
* 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

View File

@ -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;