зеркало из https://github.com/mozilla/gecko-dev.git
Bug 607534 - Optimize custom dynamic loader to use less memory, r=cjones a=blocking-fennec
This commit is contained in:
Родитель
16c6c3d7dd
Коммит
1a9d63506c
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче