Bug 607534 - Optimize custom dynamic loader to use less memory, r=cjones a=blocking-fennec

This commit is contained in:
Michael Wu 2010-10-28 23:45:46 -07:00
Родитель 16c6c3d7dd
Коммит 1a9d63506c
6 изменённых файлов: 217 добавлений и 30 удалений

Просмотреть файл

@ -69,6 +69,10 @@
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
#endif
#ifdef ANDROID
#include "APKOpen.h"
#endif
using mozilla::MonitorAutoEnter;
using mozilla::ipc::GeckoChildProcessHost;
@ -388,6 +392,20 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, b
#ifdef ANDROID
// The java wrapper unpacks this for us but can't make it executable
chmod(exePath.value().c_str(), 0700);
int cacheCount = 0;
const struct lib_cache_info * cache = getLibraryCache();
nsCString cacheStr;
while (cache &&
cacheCount++ < MAX_LIB_CACHE_ENTRIES &&
strlen(cache->name)) {
mFileMap.push_back(std::pair<int,int>(cache->fd, cache->fd));
cacheStr.Append(cache->name);
cacheStr.AppendPrintf(":%d;", cache->fd);
cache++;
}
// fill the last arg with something if there's no cache
if (cacheStr.IsEmpty())
cacheStr.AppendLiteral("-");
#endif
// remap the IPC socket fd to a well-known int, as the OS does for
@ -451,6 +469,10 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, b
childArgv.push_back(childProcessType);
#ifdef ANDROID
childArgv.push_back(cacheStr.get());
#endif
base::LaunchApp(childArgv, mFileMap,
#if defined(OS_LINUX) || defined(OS_MACOSX)
newEnvVars,

Просмотреть файл

@ -56,15 +56,7 @@
// Temporarily go directly to the kernel interface until we can
// interact better with libcutils.
//
#define ASHMEM_DEVICE "/dev/ashmem"
#define ASHMEM_NAME_LEN 256
#define __ASHMEMIOC 0x77
#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
#include <linux/ashmem.h>
namespace mozilla {
namespace ipc {
@ -102,7 +94,7 @@ SharedMemoryBasic::Create(size_t aNbytes)
NS_ABORT_IF_FALSE(-1 == mShmFd, "Already Create()d");
// Carve a new instance off of /dev/ashmem
int shmfd = open(ASHMEM_DEVICE, O_RDWR, 0600);
int shmfd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
if (-1 == shmfd) {
LogError("ShmemAndroid::Create():open");
return false;

Просмотреть файл

@ -34,19 +34,29 @@
*
* ***** END LICENSE BLOCK ***** */
/*
* This custom library loading code is only meant to be called
* during initialization. As a result, it takes no special
* precautions to be threadsafe. Any of the library loading functions
* like mozload should not be available to other code.
*/
#include <jni.h>
#include <android/log.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/limits.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <endian.h>
#include <unistd.h>
#include <zlib.h>
#include <linux/ashmem.h>
#include "dlfcn.h"
#include "APKOpen.h"
@ -109,7 +119,27 @@ struct cdir_end {
static size_t zip_size;
static int zip_fd;
NS_EXPORT struct mapping_info * lib_mapping;
static struct mapping_info * lib_mapping;
NS_EXPORT const struct mapping_info *
getLibraryMapping()
{
return lib_mapping;
}
static int
createAshmem(size_t bytes)
{
int fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
if (fd < 0)
return -1;
if (!ioctl(fd, ASHMEM_SET_SIZE, bytes))
return fd;
close(fd);
return -1;
}
static void * map_file (const char *file)
{
@ -320,6 +350,80 @@ extractLib(const struct cdir_entry *entry, void * data, void * dest)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "File not fully uncompressed! %d / %d", strm.total_out, letoh32(entry->uncompressed_size));
}
static int cache_count = 0;
static struct lib_cache_info *cache_mapping = NULL;
NS_EXPORT const struct lib_cache_info *
getLibraryCache()
{
return cache_mapping;
}
static void
ensureLibCache()
{
if (!cache_mapping)
cache_mapping = (struct lib_cache_info *)calloc(MAX_LIB_CACHE_ENTRIES,
sizeof(*cache_mapping));
}
static void
fillLibCache(const char *buf)
{
ensureLibCache();
char * str = strdup(buf);
if (!str)
return;
char * saveptr;
char * nextstr = str;
do {
struct lib_cache_info *info = &cache_mapping[cache_count];
char * name = strtok_r(nextstr, ":", &saveptr);
if (!name)
break;
nextstr = NULL;
char * fd_str = strtok_r(NULL, ";", &saveptr);
if (!fd_str)
break;
long int fd = strtol(fd_str, NULL, 10);
if (fd == LONG_MIN || fd == LONG_MAX)
break;
strncpy(info->name, name, MAX_LIB_CACHE_NAME_LEN - 1);
info->fd = fd;
} while (cache_count++ < MAX_LIB_CACHE_ENTRIES);
free(str);
}
static int
lookupLibCacheFd(const char *libName)
{
if (!cache_mapping)
return -1;
int count = cache_count;
while (count--) {
struct lib_cache_info *info = &cache_mapping[count];
if (!strcmp(libName, info->name))
return info->fd;
}
return -1;
}
static void
addLibCacheFd(const char *libName, int fd)
{
ensureLibCache();
struct lib_cache_info *info = &cache_mapping[cache_count++];
strncpy(info->name, libName, MAX_LIB_CACHE_NAME_LEN - 1);
info->fd = fd;
}
static void * mozload(const char * path, void *zip,
struct cdir_entry *cdir_start, uint16_t cdir_entries)
{
@ -338,9 +442,7 @@ static void * mozload(const char * path, void *zip,
snprintf(fullpath, 256, "/data/data/org.mozilla.fennec/%s", path + 4);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
extractFile(fullpath, entry, data);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: 1", fullpath);
handle = __wrap_dlopen(fullpath, RTLD_LAZY);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: 2", fullpath);
if (!handle)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", fullpath, __wrap_dlerror());
#ifdef DEBUG
@ -358,32 +460,48 @@ static void * mozload(const char * path, void *zip,
int fd = zip_fd;
void * buf = NULL;
uint32_t lib_size = letoh32(entry->uncompressed_size);
if (letoh16(file->compression) == DEFLATE) {
fd = -1;
buf = mmap(NULL, letoh32(entry->uncompressed_size),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int cache_fd = lookupLibCacheFd(path + 4);
fd = cache_fd;
if (fd < 0)
fd = createAshmem(lib_size);
#ifdef DEBUG
else
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s from cache", path + 4);
#endif
if (fd < 0) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get an ashmem buffer");
return NULL;
}
buf = mmap(NULL, lib_size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (buf == (void *)-1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't mmap decompression buffer");
close(fd);
return NULL;
}
offset = 0;
extractLib(entry, data, buf);
if (cache_fd < 0) {
extractLib(entry, data, buf);
addLibCacheFd(path + 4, fd);
}
data = buf;
}
#ifdef DEBUG
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d and offset %d", path, letoh32(entry->uncompressed_size), offset);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d (0x%08x) and offset %d (0x%08x)", path, lib_size, lib_size, offset, offset);
#endif
handle = moz_mapped_dlopen(path, RTLD_LAZY, fd, data,
letoh32(entry->uncompressed_size), offset);
lib_size, offset);
if (!handle)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", path, __wrap_dlerror());
if (buf)
munmap(buf, letoh32(entry->uncompressed_size));
munmap(buf, lib_size);
#ifdef DEBUG
gettimeofday(&t1, 0);
@ -550,8 +668,12 @@ ChildProcessInit(int argc, char* argv[])
break;
}
fillLibCache(argv[argc - 1]);
loadLibs(argv[i]);
// don't pass the last arg - it's only recognized by the lib cache
argc--;
typedef GeckoProcessType (*XRE_StringToChildProcessType_t)(char*);
typedef nsresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
XRE_StringToChildProcessType_t fXRE_StringToChildProcessType =

Просмотреть файл

@ -45,6 +45,16 @@ struct mapping_info {
size_t offset;
};
extern struct mapping_info * lib_mapping;
const struct mapping_info * getLibraryMapping();
#define MAX_LIB_CACHE_ENTRIES 32
#define MAX_LIB_CACHE_NAME_LEN 32
struct lib_cache_info {
char name[MAX_LIB_CACHE_NAME_LEN];
int fd;
};
const struct lib_cache_info * getLibraryCache();
#endif /* APKOpen_h */

Просмотреть файл

@ -930,18 +930,19 @@ load_segments(int fd, size_t offset, void *header, soinfo *si)
TRACE("[ %d - Trying to load segment from '%s' @ 0x%08x "
"(0x%08x). p_vaddr=0x%08x p_offset=0x%08x ]\n", pid, si->name,
(unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset);
if (fd == -1) {
if (fd == -1 || PFLAGS_TO_PROT(phdr->p_flags) & PROT_WRITE) {
pbase = mmap(tmp, len, PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
0);
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (pbase != MAP_FAILED) {
memcpy(pbase, header + ((phdr->p_offset) & (~PAGE_MASK)), len);
mprotect(pbase, len, PFLAGS_TO_PROT(phdr->p_flags));
}
} else
} else
DL_ERR("%s: Memcpy mapping of segment failed!", si->name);
} else {
pbase = mmap(tmp, len, PFLAGS_TO_PROT(phdr->p_flags),
MAP_PRIVATE | MAP_FIXED, fd,
MAP_SHARED | MAP_FIXED, fd,
offset + ((phdr->p_offset) & (~PAGE_MASK)));
}
if (pbase == MAP_FAILED) {
DL_ERR("%d failed to map segment from '%s' @ 0x%08x (0x%08x). "
"p_vaddr=0x%08x p_offset=0x%08x", pid, si->name,
@ -1238,7 +1239,7 @@ load_mapped_library(const char * name, int fd,
pid, name, (void *)si->base, (unsigned) ext_sz);
/* Now actually load the library's segments into right places in memory */
if (load_segments(offset ? fd : -1, offset, mem, si) < 0) {
if (load_segments(fd, offset, mem, si) < 0) {
if (si->ba_index >= 0) {
ba_free(&ba_nonprelink, si->ba_index);
si->ba_index = -1;
@ -1394,6 +1395,28 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
Elf32_Rel *start = rel;
unsigned idx;
/* crappy hack to ensure we don't write into the read-only region */
/* crappy hack part 1: find the read only region */
int cnt;
void * ro_region_end = si->base;
Elf32_Ehdr *ehdr = (Elf32_Ehdr *)si->base;
Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *)si->base + ehdr->e_phoff);
for (cnt = 0; cnt < ehdr->e_phnum; ++cnt, ++phdr) {
if (phdr->p_type != PT_LOAD ||
PFLAGS_TO_PROT(phdr->p_flags) & PROT_WRITE ||
phdr->p_vaddr != 0)
continue;
ro_region_end = si->base + phdr->p_filesz;
break;
}
void * remapped_page = NULL;
void * copy_page = mmap(NULL, PAGE_SIZE,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
for (idx = 0; idx < count; ++idx) {
unsigned type = ELF32_R_TYPE(rel->r_info);
unsigned sym = ELF32_R_SYM(rel->r_info);
@ -1479,6 +1502,23 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
s = NULL;
}
/* crappy hack part 2: make this page writable */
void * reloc_page = reloc & ~PAGE_MASK;
if (reloc < ro_region_end && reloc_page != remapped_page) {
if (remapped_page != NULL)
mprotect(remapped_page, PAGE_SIZE, PROT_READ | PROT_EXEC);
memcpy(copy_page, reloc_page, PAGE_SIZE);
munmap(reloc_page, PAGE_SIZE);
if (mmap(reloc_page, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0) == MAP_FAILED)
DL_ERR("failed to map page for %s at 0x%08x! errno=%d",
si->name, reloc_page, errno);
memcpy(reloc_page, copy_page, PAGE_SIZE);
remapped_page = reloc_page;
}
/* TODO: This is ugly. Split up the relocations by arch into
* different files.
*/
@ -1584,6 +1624,7 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
}
rel++;
}
munmap(copy_page, PAGE_SIZE);
return 0;
}

Просмотреть файл

@ -75,7 +75,7 @@ static void*
GeckoStart(void *data)
{
#ifdef MOZ_CRASHREPORTER
struct mapping_info *info = lib_mapping;
const struct mapping_info *info = getLibraryMapping();
while (info->name) {
CrashReporter::AddLibraryMapping(info->name, info->file_id, info->base,
info->len, info->offset);