author | Michael Wu <mwu@mozilla.com> |
Thu, 28 Oct 2010 23:45:46 -0700 | |
changeset 56681 | 99233ad2ff7094d9c5c37eb7c8c26272c49f94ce |
parent 56680 | ce1801c6ba9510561eecf8b4c40c6179fc4e6c82 |
child 56682 | 799a83ba5f8637d6bbf730ce89ab89fbf33c4477 |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cjones, blocking-fennec |
bugs | 607534 |
milestone | 2.0b8pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -64,16 +64,20 @@ #include "mozilla/Omnijar.h" #include <sys/stat.h> #ifdef XP_WIN #include "nsIWinTaskbar.h" #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" #endif +#ifdef ANDROID +#include "APKOpen.h" +#endif + using mozilla::MonitorAutoEnter; using mozilla::ipc::GeckoChildProcessHost; template<> struct RunnableMethodTraits<GeckoChildProcessHost> { static void RetainCallee(GeckoChildProcessHost* obj) { } static void ReleaseCallee(GeckoChildProcessHost* obj) { } @@ -383,16 +387,30 @@ GeckoChildProcessHost::PerformAsyncLaunc #endif FilePath exePath; GetPathToBinary(exePath); #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 // STDOUT_FILENO, for example int srcChannelFd, dstChannelFd; channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd); mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd)); @@ -446,16 +464,20 @@ GeckoChildProcessHost::PerformAsyncLaunc // can't pretend being the child that's forked off. std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d", base::RandInt(0, std::numeric_limits<int>::max())); childArgv.push_back(mach_connection_name.c_str()); #endif childArgv.push_back(childProcessType); +#ifdef ANDROID + childArgv.push_back(cacheStr.get()); +#endif + base::LaunchApp(childArgv, mFileMap, #if defined(OS_LINUX) || defined(OS_MACOSX) newEnvVars, #endif false, &process, arch); #ifdef XP_MACOSX // Wait for the child process to send us its 'task_t' data.
--- a/ipc/glue/SharedMemoryBasic_android.cpp +++ b/ipc/glue/SharedMemoryBasic_android.cpp @@ -51,25 +51,17 @@ #include "base/process_util.h" #include "SharedMemoryBasic.h" // // 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 { static void LogError(const char* what) { __android_log_print(ANDROID_LOG_ERROR, "Gecko", @@ -97,17 +89,17 @@ SharedMemoryBasic::~SharedMemoryBasic() } bool 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; } if (ioctl(shmfd, ASHMEM_SET_SIZE, aNbytes)) { LogError("ShmemAndroid::Unmap():ioctl(SET_SIZE)"); close(shmfd);
--- a/other-licenses/android/APKOpen.cpp +++ b/other-licenses/android/APKOpen.cpp @@ -29,29 +29,39 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** 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" /* compression methods */ #define STORE 0 #define DEFLATE 8 #define LZMA 14 @@ -104,17 +114,37 @@ struct cdir_end { uint32_t cdir_size; uint32_t cdir_offset; uint16_t comment_size; char comment[0]; } __attribute__((__packed__)); 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) { int fd = open(file, O_RDONLY); if (fd == -1) { __android_log_print(ANDROID_LOG_ERROR, "GeckoMapFile", "map_file open %s", strerror(errno)); return NULL; } @@ -315,16 +345,90 @@ extractLib(const struct cdir_entry *entr ret = inflateEnd(&strm); if (ret != Z_OK) __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateEnd failed: %s", strm.msg); if (strm.total_out != letoh32(entry->uncompressed_size)) __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) { #ifdef DEBUG struct timeval t0, t1; gettimeofday(&t0, 0); #endif @@ -333,19 +437,17 @@ static void * mozload(const char * path, void * data = ((void *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size); void * handle; if (extractLibs) { char fullpath[256]; 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 gettimeofday(&t1, 0); __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path, (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) - (((long long)t0.tv_sec * 1000000LL) + @@ -353,42 +455,58 @@ static void * mozload(const char * path, #endif return handle; } size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size); 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); __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path, (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) - (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec)); #endif return handle; @@ -545,18 +663,22 @@ ChildProcessInit(int argc, char* argv[]) for (i = 0; i < (argc - 1); i++) { if (strcmp(argv[i], "-omnijar")) continue; i = i + 1; 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 = (XRE_StringToChildProcessType_t)__wrap_dlsym(xul_handle, "XRE_StringToChildProcessType"); XRE_InitChildProcess_t fXRE_InitChildProcess = (XRE_InitChildProcess_t)__wrap_dlsym(xul_handle, "XRE_InitChildProcess"); GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]);
--- a/other-licenses/android/APKOpen.h +++ b/other-licenses/android/APKOpen.h @@ -40,11 +40,21 @@ struct mapping_info { char * name; char * file_id; uintptr_t base; size_t len; 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 */
--- a/other-licenses/android/linker.c +++ b/other-licenses/android/linker.c @@ -925,28 +925,29 @@ load_segments(int fd, size_t offset, voi /* we want to map in the segment on a page boundary */ tmp = base + (phdr->p_vaddr & (~PAGE_MASK)); /* add the # of bytes we masked off above to the total length. */ len = phdr->p_filesz + (phdr->p_vaddr & PAGE_MASK); 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, (unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset); goto fail; } report_mapping(si->name, pbase, len, phdr->p_offset & (~PAGE_MASK)); @@ -1233,17 +1234,17 @@ load_mapped_library(const char * name, i si->dynamic = (unsigned *)-1; if (alloc_mem_region(si) < 0) goto fail; TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", 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; } goto fail; } /* this might not be right. Technically, we don't even need this info @@ -1389,16 +1390,38 @@ static int reloc_library(soinfo *si, Elf { Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; Elf32_Sym *s; unsigned base; 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); unsigned reloc = (unsigned)(rel->r_offset + si->base); unsigned sym_addr = 0; char *sym_name = NULL; DEBUG("%5d Processing '%s' relocation at index %d\n", pid, @@ -1474,16 +1497,33 @@ static int reloc_library(soinfo *si, Elf #endif sym_addr = (unsigned)(s->st_value + base); } COUNT_RELOC(RELOC_SYMBOL); } else { 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. */ switch(type){ #if defined(ANDROID_ARM_LINKER) case R_ARM_JUMP_SLOT: COUNT_RELOC(RELOC_ABSOLUTE); MARK(rel->r_offset); @@ -1579,16 +1619,17 @@ static int reloc_library(soinfo *si, Elf default: DL_ERR("%5d unknown reloc type %d @ %p (%d)", pid, type, rel, (int) (rel - start)); return -1; } rel++; } + munmap(copy_page, PAGE_SIZE); return 0; } #if defined(ANDROID_SH_LINKER) static int reloc_library_a(soinfo *si, Elf32_Rela *rela, unsigned count) { Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab;
--- a/toolkit/xre/nsAndroidStartup.cpp +++ b/toolkit/xre/nsAndroidStartup.cpp @@ -70,17 +70,17 @@ struct AutoAttachJavaThread { PRBool attached; }; 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); info++; } #endif AutoAttachJavaThread attacher;