Beispiel #1
0
  /**
   * 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;
  }
Beispiel #2
0
  /**
   * 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;
  }
Beispiel #3
0
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());
}
Beispiel #4
0
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());
}