Bug 802240 - Expose an API to mmap the underlying file for a library loaded by faulty.lib. r=nfroyd
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 12 Apr 2013 10:23:12 +0200
changeset 128570 6d4badbe3db200c521419bbb61137c61b08ce8bc
parent 128569 214cafc09b49f27077aaddfad32fb36f6ad0a890
child 128571 0b32af5340c3bdaad55ccfc054558f41ee3d852a
push id24532
push userryanvm@gmail.com
push dateFri, 12 Apr 2013 19:06:49 +0000
treeherdermozilla-central@2aff2d574a1e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs802240
milestone23.0a1
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
Bug 802240 - Expose an API to mmap the underlying file for a library loaded by faulty.lib. r=nfroyd
mozglue/linker/CustomElf.cpp
mozglue/linker/CustomElf.h
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
mozglue/linker/Makefile.in
mozglue/linker/Mappable.cpp
mozglue/linker/Mappable.h
--- a/mozglue/linker/CustomElf.cpp
+++ b/mozglue/linker/CustomElf.cpp
@@ -741,8 +741,18 @@ CustomElf::CallFini()
        it < fini_array.rend(); ++it) {
     /* Android x86 NDK wrongly puts 0xffffffff in FINI_ARRAY */
     if (*it && *it != reinterpret_cast<void *>(-1))
       CallFunction(*it);
   }
   if (fini)
     CallFunction(fini);
 }
+
+Mappable *
+CustomElf::GetMappable() const
+{
+  if (!mappable)
+    return NULL;
+  if (mappable->GetKind() == Mappable::MAPPABLE_EXTRACT_FILE)
+    return mappable;
+  return ElfLoader::GetMappableFromPath(GetPath());
+}
--- a/mozglue/linker/CustomElf.h
+++ b/mozglue/linker/CustomElf.h
@@ -34,16 +34,20 @@ public:
 
   /**
    * Inherited from LibHandle
    */
   virtual ~CustomElf();
   virtual void *GetSymbolPtr(const char *symbol) const;
   virtual bool Contains(void *addr) const;
 
+protected:
+  virtual Mappable *GetMappable() const;
+
+public:
   /**
    * Shows some stats about the Mappable instance. The when argument is to be
    * used by the caller to give an identifier of the when the stats call is
    * made.
    */
   void stats(const char *when) const;
 
 private:
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include <string>
 #include <cstring>
 #include <cstdlib>
 #include <cstdio>
 #include <dlfcn.h>
 #include <unistd.h>
 #include <algorithm>
 #include <fcntl.h>
 #include "ElfLoader.h"
@@ -125,16 +126,44 @@ int
 
     int ret = callback(&info, sizeof(dl_phdr_info), data);
     if (ret)
       return ret;
   }
   return 0;
 }
 
+/**
+ * faulty.lib public API
+ */
+
+MFBT_API size_t
+__dl_get_mappable_length(void *handle) {
+  if (!handle)
+    return 0;
+  return reinterpret_cast<LibHandle *>(handle)->GetMappableLength();
+}
+
+MFBT_API void *
+__dl_mmap(void *handle, void *addr, size_t length, off_t offset)
+{
+  if (!handle)
+    return NULL;
+  return reinterpret_cast<LibHandle *>(handle)->MappableMMap(addr, length,
+                                                             offset);
+}
+
+MFBT_API void
+__dl_munmap(void *handle, void *addr, size_t length)
+{
+  if (!handle)
+    return;
+  return reinterpret_cast<LibHandle *>(handle)->MappableMUnmap(addr, length);
+}
+
 namespace {
 
 /**
  * Returns the part after the last '/' for the given path
  */
 const char *
 LeafName(const char *path)
 {
@@ -147,24 +176,53 @@ LeafName(const char *path)
 } /* Anonymous namespace */
 
 /**
  * LibHandle
  */
 LibHandle::~LibHandle()
 {
   free(path);
+  if (mappable->GetKind() != Mappable::MAPPABLE_EXTRACT_FILE)
+    delete mappable;
 }
 
 const char *
 LibHandle::GetName() const
 {
   return path ? LeafName(path) : NULL;
 }
 
+size_t
+LibHandle::GetMappableLength() const
+{
+  MOZ_ASSERT(mappable != NULL, "GetMappableLength needs to be called first,"
+                               " and only once");
+  mappable = GetMappable();
+  if (!mappable)
+    return 0;
+  return mappable->GetLength();
+}
+
+void *
+LibHandle::MappableMMap(void *addr, size_t length, off_t offset) const
+{
+  MOZ_ASSERT(mappable == NULL, "MappableMMap must be called after"
+                               " GetMappableLength");
+  return mappable->mmap(addr, length, PROT_READ, MAP_PRIVATE, offset);
+}
+
+void
+LibHandle::MappableMUnmap(void *addr, size_t length) const
+{
+  MOZ_ASSERT(mappable == NULL, "MappableMUnmap must be called after"
+                               " MappableMMap and GetMappableLength");
+  mappable->munmap(addr, length);
+}
+
 /**
  * SystemElf
  */
 TemporaryRef<LibHandle>
 SystemElf::Load(const char *path, int flags)
 {
   /* The Android linker returns a handle when the file name matches an
    * already loaded library, even when the full path doesn't exist */
@@ -198,16 +256,36 @@ void *
 SystemElf::GetSymbolPtr(const char *symbol) const
 {
   void *sym = dlsym(dlhandle, symbol);
   debug("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym);
   ElfLoader::Singleton.lastError = dlerror();
   return sym;
 }
 
+Mappable *
+SystemElf::GetMappable() const
+{
+  const char *path = GetPath();
+  if (!path)
+    return NULL;
+#ifdef ANDROID
+  /* On Android, if we don't have the full path, try in /system/lib */
+  const char *name = LeafName(path);
+  std::string systemPath;
+  if (name == path) {
+    systemPath = "/system/lib/";
+    systemPath += path;
+    path = systemPath.c_str();
+  }
+#endif
+
+  return MappableFile::Create(path);
+}
+
 /**
  * ElfLoader
  */
 
 /* Unique ElfLoader instance */
 ElfLoader ElfLoader::Singleton;
 
 TemporaryRef<LibHandle>
@@ -248,48 +326,17 @@ ElfLoader::Load(const char *path, int fl
     const char *parentPath = parent->GetPath();
     abs_path = new char[strlen(parentPath) + strlen(path)];
     strcpy(abs_path, parentPath);
     char *slash = strrchr(abs_path, '/');
     strcpy(slash + 1, path);
     path = abs_path;
   }
 
-  /* Create a mappable object for the given path. Paths in the form
-   *   /foo/bar/baz/archive!/directory/lib.so
-   * try to load the directory/lib.so in /foo/bar/baz/archive, provided
-   * that file is a Zip archive. */
-  Mappable *mappable = NULL;
-  RefPtr<Zip> zip;
-  const char *subpath;
-  if ((subpath = strchr(path, '!'))) {
-    char *zip_path = strndup(path, subpath - path);
-    while (*(++subpath) == '/') { }
-    zip = ZipCollection::GetZip(zip_path);
-    Zip::Stream s;
-    if (zip && zip->GetStream(subpath, &s)) {
-      /* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
-       * compressed libraries are going to be (temporarily) extracted as
-       * files, in the directory pointed by the MOZ_LINKER_CACHE
-       * environment variable. */
-      const char *extract = getenv("MOZ_LINKER_EXTRACT");
-      if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
-        mappable = MappableExtractFile::Create(name, zip, &s);
-      if (!mappable) {
-        if (s.GetType() == Zip::Stream::DEFLATE) {
-          mappable = MappableDeflate::Create(name, zip, &s);
-        } else if (s.GetType() == Zip::Stream::STORE) {
-          mappable = MappableSeekableZStream::Create(name, zip, &s);
-        }
-      }
-    }
-  }
-  /* If we couldn't load above, try with a MappableFile */
-  if (!mappable && !zip)
-    mappable = MappableFile::Create(path);
+  Mappable *mappable = GetMappableFromPath(path);
 
   /* Try loading with the custom linker if we have a Mappable */
   if (mappable)
     handle = CustomElf::Load(mappable, path, flags);
 
   /* Try loading with the system linker if everything above failed */
   if (!handle)
     handle = SystemElf::Load(path, flags);
@@ -313,16 +360,52 @@ ElfLoader::GetHandleByPtr(void *addr)
   /* Scan the list of handles we already have for a match */
   for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) {
     if ((*it)->Contains(addr))
       return *it;
   }
   return NULL;
 }
 
+Mappable *
+ElfLoader::GetMappableFromPath(const char *path)
+{
+  const char *name = LeafName(path);
+  Mappable *mappable = NULL;
+  RefPtr<Zip> zip;
+  const char *subpath;
+  if ((subpath = strchr(path, '!'))) {
+    char *zip_path = strndup(path, subpath - path);
+    while (*(++subpath) == '/') { }
+    zip = ZipCollection::GetZip(zip_path);
+    Zip::Stream s;
+    if (zip && zip->GetStream(subpath, &s)) {
+      /* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
+       * compressed libraries are going to be (temporarily) extracted as
+       * files, in the directory pointed by the MOZ_LINKER_CACHE
+       * environment variable. */
+      const char *extract = getenv("MOZ_LINKER_EXTRACT");
+      if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
+        mappable = MappableExtractFile::Create(name, zip, &s);
+      if (!mappable) {
+        if (s.GetType() == Zip::Stream::DEFLATE) {
+          mappable = MappableDeflate::Create(name, zip, &s);
+        } else if (s.GetType() == Zip::Stream::STORE) {
+          mappable = MappableSeekableZStream::Create(name, zip, &s);
+        }
+      }
+    }
+  }
+  /* If we couldn't load above, try with a MappableFile */
+  if (!mappable && !zip)
+    mappable = MappableFile::Create(path);
+
+  return mappable;
+}
+
 void
 ElfLoader::Register(LibHandle *handle)
 {
   handles.push_back(handle);
   if (dbg && !handle->IsSystemElf())
     dbg.Add(static_cast<CustomElf *>(handle));
 }
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -39,16 +39,29 @@ extern "C" {
     Elf::Addr dlpi_addr;
     const char *dlpi_name;
     const Elf::Phdr *dlpi_phdr;
     Elf::Half dlpi_phnum;
   };
 
   typedef int (*dl_phdr_cb)(struct dl_phdr_info *, size_t, void *);
   int __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data);
+
+/**
+ * faulty.lib public API
+ */
+MFBT_API size_t
+__dl_get_mappable_length(void *handle);
+
+MFBT_API void *
+__dl_mmap(void *handle, void *addr, size_t length, off_t offset);
+
+MFBT_API void
+__dl_munmap(void *handle, void *addr, size_t length);
+
 }
 
 /**
  * Specialize RefCounted template for LibHandle. We may get references to
  * LibHandles during the execution of their destructor, so we need
  * RefCounted<LibHandle>::Release to support some reentrancy. See further
  * below.
  */
@@ -60,29 +73,32 @@ template <> inline void RefCounted<LibHa
 
 template <> inline RefCounted<LibHandle>::~RefCounted()
 {
   MOZ_ASSERT(refCnt == 0x7fffdead);
 }
 
 } /* namespace mozilla */
 
+/* Forward declaration */
+class Mappable;
+
 /**
  * Abstract class for loaded libraries. Libraries may be loaded through the
  * system linker or this linker, both cases will be derived from this class.
  */
 class LibHandle: public mozilla::RefCounted<LibHandle>
 {
 public:
   /**
    * Constructor. Takes the path of the loaded library and will store a copy
    * of the leaf name.
    */
   LibHandle(const char *path)
-  : directRefCnt(0), path(path ? strdup(path) : NULL) { }
+  : directRefCnt(0), path(path ? strdup(path) : NULL), mappable(NULL) { }
 
   /**
    * Destructor.
    */
   virtual ~LibHandle();
 
   /**
    * Returns the pointer to the address to which the given symbol resolves
@@ -142,29 +158,55 @@ public:
   /**
    * Returns the number of direct references
    */
   int DirectRefCount()
   {
     return directRefCnt;
   }
 
+  /**
+   * Returns the complete size of the file or stream behind the library
+   * handle.
+   */
+  size_t GetMappableLength() const;
+
+  /**
+   * Returns a memory mapping of the file or stream behind the library
+   * handle.
+   */
+  void *MappableMMap(void *addr, size_t length, off_t offset) const;
+
+  /**
+   * Unmaps a memory mapping of the file or stream behind the library
+   * handle.
+   */
+  void MappableMUnmap(void *addr, size_t length) const;
+
 protected:
   /**
+   * Returns a mappable object for use by MappableMMap and related functions.
+   */
+  virtual Mappable *GetMappable() const = 0;
+
+  /**
    * Returns whether the handle is a SystemElf or not. (short of a better way
    * to do this without RTTI)
    */
   friend class ElfLoader;
   friend class CustomElf;
   friend class SEGVHandler;
   virtual bool IsSystemElf() const { return false; }
 
 private:
   int directRefCnt;
   char *path;
+
+  /* Mappable object keeping the result of GetMappable() */
+  mutable Mappable *mappable;
 };
 
 /**
  * Specialized RefCounted<LibHandle>::Release. Under normal operation, when
  * refCnt reaches 0, the LibHandle is deleted. Its refCnt is however increased
  * to 1 on normal builds, and 0x7fffdead on debug builds so that the LibHandle
  * can still be referenced while the destructor is executing. The refCnt is
  * allowed to grow > 0x7fffdead, but not to decrease under that value, which
@@ -207,16 +249,18 @@ public:
   /**
    * Inherited from LibHandle
    */
   virtual ~SystemElf();
   virtual void *GetSymbolPtr(const char *symbol) const;
   virtual bool Contains(void *addr) const { return false; /* UNIMPLEMENTED */ }
 
 protected:
+  virtual Mappable *GetMappable() const;
+
   /**
    * Returns whether the handle is a SystemElf or not. (short of a better way
    * to do this without RTTI)
    */
   friend class ElfLoader;
   virtual bool IsSystemElf() const { return true; }
 
   /**
@@ -309,16 +353,24 @@ public:
   /**
    * Returns the handle of the library containing the given address in
    * its virtual address space, i.e. the library handle for which
    * LibHandle::Contains returns true. Its purpose is to allow to
    * implement dladdr().
    */
   mozilla::TemporaryRef<LibHandle> GetHandleByPtr(void *addr);
 
+  /**
+   * Returns a Mappable object for the path. Paths in the form
+   *   /foo/bar/baz/archive!/directory/lib.so
+   * try to load the directory/lib.so in /foo/bar/baz/archive, provided
+   * that file is a Zip archive.
+   */
+  static Mappable *GetMappableFromPath(const char *path);
+
 protected:
   /**
    * Registers the given handle. This method is meant to be called by
    * LibHandle subclass creators.
    */
   void Register(LibHandle *handle);
 
   /**
--- a/mozglue/linker/Makefile.in
+++ b/mozglue/linker/Makefile.in
@@ -28,16 +28,18 @@ HOST_LIBS = -lz
 
 CPPSRCS += \
   ElfLoader.cpp \
   CustomElf.cpp \
   Mappable.cpp \
   SeekableZStream.cpp \
   $(NULL)
 
+DEFINES += -DIMPL_MFBT
+
 include $(topsrcdir)/config/rules.mk
 
 ifeq (arm,$(TARGET_CPU))
 ifdef MOZ_THUMB2
 HOST_CXXFLAGS += -DTARGET_THUMB
 else
 HOST_CXXFLAGS += -DTARGET_ARM
 endif
--- a/mozglue/linker/Mappable.cpp
+++ b/mozglue/linker/Mappable.cpp
@@ -60,16 +60,23 @@ MappableFile::mmap(const void *addr, siz
 
 void
 MappableFile::finalize()
 {
   /* Close file ; equivalent to close(fd.forget()) */
   fd = -1;
 }
 
+size_t
+MappableFile::GetLength() const
+{
+  struct stat st;
+  return fstat(fd, &st) ? 0 : st.st_size;
+}
+
 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 NULL;
@@ -337,16 +344,22 @@ MappableDeflate::finalize()
   /* Free zlib internal buffers */
   inflateEnd(&zStream);
   /* Free decompression buffer */
   buffer = NULL;
   /* Remove reference to Zip archive */
   zip = NULL;
 }
 
+size_t
+MappableDeflate::GetLength() const
+{
+  return buffer->GetLength();
+}
+
 Mappable *
 MappableSeekableZStream::Create(const char *name, Zip *zip,
                                 Zip::Stream *stream)
 {
   MOZ_ASSERT(stream->GetType() == Zip::Stream::STORE);
   mozilla::ScopedDeletePtr<MappableSeekableZStream> mappable;
   mappable = new MappableSeekableZStream(zip);
 
@@ -538,8 +551,14 @@ MappableSeekableZStream::stats(const cha
     if ((j == len) || (i == nEntries - 1)) {
       map[j + 1] = ']';
       map[j + 2] = '\0';
       debug("%s", static_cast<char *>(map));
       j = 0;
     }
   }
 }
+
+size_t
+MappableSeekableZStream::GetLength() const
+{
+  return buffer->GetLength();
+}
--- a/mozglue/linker/Mappable.h
+++ b/mozglue/linker/Mappable.h
@@ -25,23 +25,33 @@
 class Mappable
 {
 public:
   virtual ~Mappable() { }
 
   virtual void *mmap(const void *addr, size_t length, int prot, int flags,
                      off_t offset) = 0;
 
+  enum Kind {
+    MAPPABLE_FILE,
+    MAPPABLE_EXTRACT_FILE,
+    MAPPABLE_DEFLATE,
+    MAPPABLE_SEEKABLE_ZSTREAM
+  };
+
+  virtual Kind GetKind() const = 0;
+
 private:
   virtual void munmap(void *addr, size_t length) {
     ::munmap(addr, length);
   }
   /* Limit use of Mappable::munmap to classes that keep track of the address
    * and size of the mapping. This allows to ignore ::munmap return value. */
   friend class Mappable1stPagePtr;
+  friend class LibHandle;
 
 public:
   /**
    * Ensures the availability of the memory pages for the page(s) containing
    * the given address. Returns whether the pages were successfully made
    * available.
    */
   virtual bool ensure(const void *addr) { return true; }
@@ -55,16 +65,22 @@ public:
    * Shows some stats about the Mappable instance.
    * Meant for MappableSeekableZStream only.
    * As Mappables don't keep track of what they are instanciated for, the name
    * argument is used to make the stats logging useful to the reader. The when
    * argument is to be used by the caller to give an identifier of the when
    * the stats call is made.
    */
   virtual void stats(const char *when, const char *name) const { }
+
+  /**
+   * Returns the maximum length that can be mapped from this Mappable for
+   * offset = 0.
+   */
+  virtual size_t GetLength() const = 0;
 };
 
 /**
  * Mappable implementation for plain files
  */
 class MappableFile: public Mappable
 {
 public:
@@ -73,17 +89,19 @@ public:
   /**
    * Create a MappableFile instance for the given file path.
    */
   static Mappable *Create(const char *path);
 
   /* Inherited from Mappable */
   virtual void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
   virtual void finalize();
+  virtual size_t GetLength() const;
 
+  virtual Kind GetKind() const { return MAPPABLE_FILE; };
 protected:
   MappableFile(int fd): fd(fd) { }
 
 private:
   /* File descriptor */
   AutoCloseFD fd;
 };
 
@@ -97,16 +115,17 @@ public:
   ~MappableExtractFile();
 
   /**
    * Create a MappableExtractFile instance for the given Zip stream. The name
    * argument is used to create the cache file in the cache directory.
    */
   static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
 
+  virtual Kind GetKind() const { return MAPPABLE_EXTRACT_FILE; };
 private:
   MappableExtractFile(int fd, char *path)
   : MappableFile(fd), path(path), pid(getpid()) { }
 
   /**
    * AutoUnlinkFile keeps track or a file name and removes (unlinks) the file
    * when the instance is destroyed.
    */
@@ -145,17 +164,19 @@ public:
    * argument is used for an appropriately named temporary file, and the Zip
    * instance is given for the MappableDeflate to keep a reference of it.
    */
   static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
 
   /* Inherited from Mappable */
   virtual void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
   virtual void finalize();
+  virtual size_t GetLength() const;
 
+  virtual Kind GetKind() const { return MAPPABLE_DEFLATE; };
 private:
   MappableDeflate(_MappableBuffer *buf, Zip *zip, Zip::Stream *stream);
 
   /* Zip reference */
   mozilla::RefPtr<Zip> zip;
 
   /* Decompression buffer */
   mozilla::ScopedDeletePtr<_MappableBuffer> buffer;
@@ -183,17 +204,19 @@ public:
                                          Zip::Stream *stream);
 
   /* Inherited from Mappable */
   virtual void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
   virtual void munmap(void *addr, size_t length);
   virtual void finalize();
   virtual bool ensure(const void *addr);
   virtual void stats(const char *when, const char *name) const;
+  virtual size_t GetLength() const;
 
+  virtual Kind GetKind() const { return MAPPABLE_SEEKABLE_ZSTREAM; };
 private:
   MappableSeekableZStream(Zip *zip);
 
   /* Zip reference */
   mozilla::RefPtr<Zip> zip;
 
   /* Decompression buffer */
   mozilla::ScopedDeletePtr<_MappableBuffer> buffer;