/** * Returns a _MappableBuffer instance with the given name and the given * length. */ static _MappableBuffer *Create(const char *name, size_t length) { AutoCloseFD fd; #ifdef ANDROID /* On Android, initialize an ashmem region with the given length */ fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600); if (fd == -1) return NULL; char str[ASHMEM_NAME_LEN]; strlcpy(str, name, sizeof(str)); ioctl(fd, ASHMEM_SET_NAME, str); if (ioctl(fd, ASHMEM_SET_SIZE, length)) return NULL; /* The Gecko crash reporter is confused by adjacent memory mappings of * the same file. On Android, subsequent mappings are growing in memory * address, and chances are we're going to map from the same file * descriptor right away. To avoid problems with the crash reporter, * create an empty anonymous page after the ashmem mapping. To do so, * allocate one page more than requested, then replace the last page with * an anonymous mapping. */ void *buf = ::mmap(NULL, length + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { ::mmap(reinterpret_cast<char *>(buf) + ((length + PAGE_SIZE - 1) & PAGE_MASK), PAGE_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); debug("Decompression buffer of size %d in ashmem \"%s\", mapped @%p", length, str, buf); return new _MappableBuffer(fd.forget(), buf, length); } #else /* On Linux, use /dev/shm as base directory for temporary files, assuming * it's on tmpfs */ /* TODO: check that /dev/shm is tmpfs */ char path[256]; sprintf(path, "/dev/shm/%s.XXXXXX", name); fd = mkstemp(path); if (fd == -1) return NULL; unlink(path); ftruncate(fd, length); void *buf = ::mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { debug("Decompression buffer of size %ld in \"%s\", mapped @%p", length, path, buf); return new _MappableBuffer(fd.forget(), buf, length); } #endif return NULL; }
/** * Returns a _MappableBuffer instance with the given name and the given * length. */ static _MappableBuffer *Create(const char *name, size_t length) { AutoCloseFD fd; #ifdef ANDROID /* On Android, initialize an ashmem region with the given length */ fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600); if (fd == -1) return nullptr; char str[ASHMEM_NAME_LEN]; strlcpy(str, name, sizeof(str)); ioctl(fd, ASHMEM_SET_NAME, str); if (ioctl(fd, ASHMEM_SET_SIZE, length)) return nullptr; /* The Gecko crash reporter is confused by adjacent memory mappings of * the same file and chances are we're going to map from the same file * descriptor right away. To avoid problems with the crash reporter, * create an empty anonymous page before or after the ashmem mapping, * depending on how mappings grow in the address space. */ #if defined(__arm__) // Address increases on ARM. void *buf = ::mmap(nullptr, length + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { ::mmap(AlignedEndPtr(reinterpret_cast<char *>(buf) + length, PAGE_SIZE), PAGE_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR " in ashmem \"%s\", mapped @%p", length, str, buf); return new _MappableBuffer(fd.forget(), buf, length); } #elif defined(__i386__) || defined(__aarch64__) // Address decreases on x86 and AArch64. size_t anon_mapping_length = length + PAGE_SIZE; void *buf = ::mmap(nullptr, anon_mapping_length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (buf != MAP_FAILED) { char *first_page = reinterpret_cast<char *>(buf); char *map_page = first_page + PAGE_SIZE; void *actual_buf = ::mmap(map_page, length, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); if (actual_buf == MAP_FAILED) { ::munmap(buf, anon_mapping_length); DEBUG_LOG("Fixed allocation of decompression buffer at %p failed", map_page); return nullptr; } DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR " in ashmem \"%s\", mapped @%p", length, str, actual_buf); return new _MappableBuffer(fd.forget(), actual_buf, length); } #else #error need to add a case for your CPU #endif #else /* On Linux, use /dev/shm as base directory for temporary files, assuming * it's on tmpfs */ /* TODO: check that /dev/shm is tmpfs */ char path[256]; sprintf(path, "/dev/shm/%s.XXXXXX", name); fd = mkstemp(path); if (fd == -1) return nullptr; unlink(path); ftruncate(fd, length); void *buf = ::mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { DEBUG_LOG("Decompression buffer of size %ld in \"%s\", mapped @%p", length, path, buf); return new _MappableBuffer(fd.forget(), buf, length); } #endif return nullptr; }
Mappable * MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream) { MOZ_ASSERT(zip && stream); const char *cachePath = getenv("MOZ_LINKER_CACHE"); if (!cachePath || !*cachePath) { WARN("MOZ_LINKER_EXTRACT is set, but not MOZ_LINKER_CACHE; " "not extracting"); return nullptr; } // Ensure that the cache dir is private. chmod(cachePath, 0770); UniquePtr<char[]> path = MakeUnique<char[]>(strlen(cachePath) + strlen(name) + 2); sprintf(path.get(), "%s/%s", cachePath, name); CacheValidator validator(path.get(), zip, stream); if (validator.IsValid()) { DEBUG_LOG("Reusing %s", static_cast<char *>(path.get())); return MappableFile::Create(path.get()); } DEBUG_LOG("Extracting to %s", static_cast<char *>(path.get())); AutoCloseFD fd; fd = open(path.get(), O_TRUNC | O_RDWR | O_CREAT | O_NOATIME, S_IRUSR | S_IWUSR); if (fd == -1) { ERROR("Couldn't open %s to decompress library", path.get()); return nullptr; } AutoUnlinkFile file(path.release()); if (stream->GetType() == Zip::Stream::DEFLATE) { if (ftruncate(fd, stream->GetUncompressedSize()) == -1) { ERROR("Couldn't ftruncate %s to decompress library", file.get()); return nullptr; } /* Map the temporary file for use as inflate buffer */ MappedPtr buffer(MemoryRange::mmap(nullptr, stream->GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0)); if (buffer == MAP_FAILED) { ERROR("Couldn't map %s to decompress library", file.get()); return nullptr; } zxx_stream zStream = stream->GetZStream(buffer); /* Decompress */ if (inflateInit2(&zStream, -MAX_WBITS) != Z_OK) { ERROR("inflateInit failed: %s", zStream.msg); return nullptr; } if (inflate(&zStream, Z_FINISH) != Z_STREAM_END) { ERROR("inflate failed: %s", zStream.msg); return nullptr; } if (inflateEnd(&zStream) != Z_OK) { ERROR("inflateEnd failed: %s", zStream.msg); return nullptr; } if (zStream.total_out != stream->GetUncompressedSize()) { ERROR("File not fully uncompressed! %ld / %d", zStream.total_out, static_cast<unsigned int>(stream->GetUncompressedSize())); return nullptr; } } else if (XZStream::IsXZ(stream->GetBuffer(), stream->GetSize())) { XZStream xzStream(stream->GetBuffer(), stream->GetSize()); if (!xzStream.Init()) { ERROR("Couldn't initialize XZ decoder"); return nullptr; } DEBUG_LOG("XZStream created, compressed=%" PRIuPTR ", uncompressed=%" PRIuPTR, xzStream.Size(), xzStream.UncompressedSize()); if (ftruncate(fd, xzStream.UncompressedSize()) == -1) { ERROR("Couldn't ftruncate %s to decompress library", file.get()); return nullptr; } MappedPtr buffer(MemoryRange::mmap(nullptr, xzStream.UncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0)); if (buffer == MAP_FAILED) { ERROR("Couldn't map %s to decompress library", file.get()); return nullptr; } const size_t written = xzStream.Decode(buffer, buffer.GetLength()); DEBUG_LOG("XZStream decoded %" PRIuPTR, written); if (written != buffer.GetLength()) { ERROR("Error decoding XZ file %s", file.get()); return nullptr; } } else if (stream->GetType() == Zip::Stream::STORE) { SeekableZStream zStream; if (!zStream.Init(stream->GetBuffer(), stream->GetSize())) { ERROR("Couldn't initialize SeekableZStream for %s", name); return nullptr; } if (ftruncate(fd, zStream.GetUncompressedSize()) == -1) { ERROR("Couldn't ftruncate %s to decompress library", file.get()); return nullptr; } MappedPtr buffer(MemoryRange::mmap(nullptr, zStream.GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0)); if (buffer == MAP_FAILED) { ERROR("Couldn't map %s to decompress library", file.get()); return nullptr; } if (!zStream.Decompress(buffer, 0, zStream.GetUncompressedSize())) { ERROR("%s: failed to decompress", name); return nullptr; } } else { return nullptr; } validator.CacheChecksum(); return new MappableExtractFile(fd.forget(), file.release()); }
Mappable * MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream) { const char *cachePath = getenv("MOZ_LINKER_CACHE"); if (!cachePath || !*cachePath) { LOG("Warning: MOZ_LINKER_EXTRACT is set, but not MOZ_LINKER_CACHE; " "not extracting"); return nullptr; } mozilla::ScopedDeleteArray<char> path; path = new char[strlen(cachePath) + strlen(name) + 2]; sprintf(path, "%s/%s", cachePath, name); struct stat cacheStat; if (stat(path, &cacheStat) == 0) { struct stat zipStat; stat(zip->GetName(), &zipStat); if (cacheStat.st_mtime > zipStat.st_mtime) { DEBUG_LOG("Reusing %s", static_cast<char *>(path)); return MappableFile::Create(path); } } DEBUG_LOG("Extracting to %s", static_cast<char *>(path)); AutoCloseFD fd; fd = open(path, O_TRUNC | O_RDWR | O_CREAT | O_NOATIME, S_IRUSR | S_IWUSR); if (fd == -1) { LOG("Couldn't open %s to decompress library", path.get()); return nullptr; } AutoUnlinkFile file; file = path.forget(); if (stream->GetType() == Zip::Stream::DEFLATE) { if (ftruncate(fd, stream->GetUncompressedSize()) == -1) { LOG("Couldn't ftruncate %s to decompress library", file.get()); return nullptr; } /* Map the temporary file for use as inflate buffer */ MappedPtr buffer(MemoryRange::mmap(nullptr, stream->GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0)); if (buffer == MAP_FAILED) { LOG("Couldn't map %s to decompress library", file.get()); return nullptr; } z_stream zStream = stream->GetZStream(buffer); /* Decompress */ if (inflateInit2(&zStream, -MAX_WBITS) != Z_OK) { LOG("inflateInit failed: %s", zStream.msg); return nullptr; } if (inflate(&zStream, Z_FINISH) != Z_STREAM_END) { LOG("inflate failed: %s", zStream.msg); return nullptr; } if (inflateEnd(&zStream) != Z_OK) { LOG("inflateEnd failed: %s", zStream.msg); return nullptr; } if (zStream.total_out != stream->GetUncompressedSize()) { LOG("File not fully uncompressed! %ld / %d", zStream.total_out, static_cast<unsigned int>(stream->GetUncompressedSize())); return nullptr; } } else if (stream->GetType() == Zip::Stream::STORE) { SeekableZStream zStream; if (!zStream.Init(stream->GetBuffer(), stream->GetSize())) { LOG("Couldn't initialize SeekableZStream for %s", name); return nullptr; } if (ftruncate(fd, zStream.GetUncompressedSize()) == -1) { LOG("Couldn't ftruncate %s to decompress library", file.get()); return nullptr; } MappedPtr buffer(MemoryRange::mmap(nullptr, zStream.GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0)); if (buffer == MAP_FAILED) { LOG("Couldn't map %s to decompress library", file.get()); return nullptr; } if (!zStream.Decompress(buffer, 0, zStream.GetUncompressedSize())) { LOG("%s: failed to decompress", name); return nullptr; } } else { return nullptr; } return new MappableExtractFile(fd.forget(), file.forget()); }