diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 28f444a21411..7442c566bd48 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -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 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(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 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, diff --git a/ipc/glue/SharedMemoryBasic_android.cpp b/ipc/glue/SharedMemoryBasic_android.cpp index 6f457b107c41..34385c08995c 100644 --- a/ipc/glue/SharedMemoryBasic_android.cpp +++ b/ipc/glue/SharedMemoryBasic_android.cpp @@ -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 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; diff --git a/other-licenses/android/APKOpen.cpp b/other-licenses/android/APKOpen.cpp index f8f9a85f1fdf..7de334c6d630 100644 --- a/other-licenses/android/APKOpen.cpp +++ b/other-licenses/android/APKOpen.cpp @@ -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 #include #include #include #include #include +#include #include #include #include +#include #include #include #include #include +#include #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 = diff --git a/other-licenses/android/APKOpen.h b/other-licenses/android/APKOpen.h index 7d4fa4b16d1f..c5425441b8b8 100644 --- a/other-licenses/android/APKOpen.h +++ b/other-licenses/android/APKOpen.h @@ -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 */ diff --git a/other-licenses/android/linker.c b/other-licenses/android/linker.c index b33e6dcafd80..28636db11c18 100644 --- a/other-licenses/android/linker.c +++ b/other-licenses/android/linker.c @@ -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; } diff --git a/toolkit/xre/nsAndroidStartup.cpp b/toolkit/xre/nsAndroidStartup.cpp index b8e36515b1f8..5a8599bd241e 100644 --- a/toolkit/xre/nsAndroidStartup.cpp +++ b/toolkit/xre/nsAndroidStartup.cpp @@ -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);