Bug 842681 - Refactor the linker Zip code and allow to use an existing memory buffer as a Zip file. r=mwu
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 08 Mar 2013 09:24:46 +0100
changeset 134608 f17a3abbce2400ddf7e670738c294ce9738460a0
parent 134607 b20599257ae765088e8e0e3a271e4332bec718c7
child 134609 eec1db8d628402dab17f675342ec71cbabd10995
push id2452
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 16:59:38 +0000
treeherdermozilla-beta@d4b152d29d8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu
bugs842681
milestone22.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 842681 - Refactor the linker Zip code and allow to use an existing memory buffer as a Zip file. r=mwu
mozglue/android/APKOpen.cpp
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
mozglue/linker/Zip.cpp
mozglue/linker/Zip.h
mozglue/tests/TestZip.cpp
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -207,17 +207,17 @@ static mozglueresult
 loadGeckoLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
   MOZTime t0 = MOZ_Now();
   struct rusage usage1;
   getrusage(RUSAGE_THREAD, &usage1);
   
-  RefPtr<Zip> zip = new Zip(apkName);
+  RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
 
 #ifdef MOZ_CRASHREPORTER
   file_ids = (char *)extractBuf("lib.id", zip);
 #endif
 
   char *file = new char[strlen(apkName) + sizeof("!/libxpcom.so")];
   sprintf(file, "%s!/libxpcom.so", apkName);
   __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
@@ -256,17 +256,17 @@ loadGeckoLibs(const char *apkName)
   XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1);
   return SUCCESS;
 }
 
 static int loadSQLiteLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
-  RefPtr<Zip> zip = new Zip(apkName);
+  RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
   if (!lib_mapping) {
     lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
   }
 
 #ifdef MOZ_CRASHREPORTER
   file_ids = (char *)extractBuf("lib.id", zip);
 #endif
 
@@ -289,17 +289,17 @@ static int loadSQLiteLibs(const char *ap
   return SUCCESS;
 }
 
 static mozglueresult
 loadNSSLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
-  RefPtr<Zip> zip = new Zip(apkName);
+  RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
   if (!lib_mapping) {
     lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
   }
 
 #ifdef MOZ_CRASHREPORTER
   file_ids = (char *)extractBuf("lib.id", zip);
 #endif
 
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -255,17 +255,17 @@ ElfLoader::Load(const char *path, int fl
    * 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 = zips.GetZip(zip_path);
+    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' */))
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -407,19 +407,16 @@ protected:
     void *object;
     void *dso_handle;
   };
 
 private:
   /* Keep track of all registered destructors */
   std::vector<DestructorCaller> destructors;
 
-  /* Keep track of Zips used for library loading */
-  ZipCollection zips;
-
   /* Forward declaration, see further below */
   class DebuggerHelper;
 public:
   /* Loaded object descriptor for the debugger interface below*/
   struct link_map {
     /* Base address of the loaded object. */
     const void *l_addr;
     /* File name */
--- a/mozglue/linker/Zip.cpp
+++ b/mozglue/linker/Zip.cpp
@@ -7,59 +7,83 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <unistd.h>
 #include <cstdlib>
 #include <algorithm>
 #include "Logging.h"
 #include "Zip.h"
 
-Zip::Zip(const char *filename, ZipCollection *collection)
-: name(strdup(filename))
-, mapped(MAP_FAILED)
-, nextDir(NULL)
-, entries(NULL)
-, parent(collection)
+mozilla::TemporaryRef<Zip>
+Zip::Create(const char *filename)
 {
   /* Open and map the file in memory */
-  AutoCloseFD fd(open(name, O_RDONLY));
+  AutoCloseFD fd(open(filename, O_RDONLY));
   if (fd == -1) {
     log("Error opening %s: %s", filename, strerror(errno));
-    return;
+    return NULL;
   }
   struct stat st;
   if (fstat(fd, &st) == -1) {
     log("Error stating %s: %s", filename, strerror(errno));
-    return;
+    return NULL;
   }
-  size = st.st_size;
+  size_t size = st.st_size;
   if (size <= sizeof(CentralDirectoryEnd)) {
     log("Error reading %s: too short", filename);
-    return;
+    return NULL;
   }
-  mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+  void *mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
   if (mapped == MAP_FAILED) {
     log("Error mmapping %s: %s", filename, strerror(errno));
-    return;
+    return NULL;
   }
   debug("Mapped %s @%p", filename, mapped);
 
-  /* Store the first Local File entry */
-  nextFile = LocalFile::validate(mapped);
+  return Create(filename, mapped, size);
+}
+
+mozilla::TemporaryRef<Zip>
+Zip::Create(const char *filename, void *mapped, size_t size)
+{
+  mozilla::RefPtr<Zip> zip = new Zip(filename, mapped, size);
+
+  // If neither the first Local File entry nor central directory entries
+  // have been found, the zip was invalid.
+  if (!zip->nextFile && !zip->entries) {
+    log("%s - Invalid zip", filename);
+    return NULL;
+  }
+
+  ZipCollection::Singleton.Register(zip);
+  return zip;
+}
+
+Zip::Zip(const char *filename, void *mapped, size_t size)
+: name(filename ? strdup(filename) : NULL)
+, mapped(mapped)
+, size(size)
+, nextFile(LocalFile::validate(mapped)) // first Local File entry
+, nextDir(NULL)
+, entries(NULL)
+{
+  // If the first local file entry couldn't be found (which can happen
+  // with optimized jars), check the first central directory entry.
+  if (!nextFile)
+    GetFirstEntry();
 }
 
 Zip::~Zip()
 {
-  if (parent)
-    parent->Forget(this);
-  if (mapped != MAP_FAILED) {
+  ZipCollection::Forget(this);
+  if (name) {
     munmap(mapped, size);
     debug("Unmapped %s @%p", name, mapped);
+    free(name);
   }
-  free(name);
 }
 
 bool
 Zip::GetStream(const char *path, Zip::Stream *out) const
 {
   debug("%s - GetFile %s", name, path);
   /* Fast path: if the Local File header on store matches, we can return the
    * corresponding stream right away.
@@ -127,18 +151,18 @@ Zip::GetStream(const char *path, Zip::St
   nextDir = nextDir->GetNext();
   nextFile = NULL;
   return true;
 }
 
 const Zip::DirectoryEntry *
 Zip::GetFirstEntry() const
 {
-  if (entries || mapped == MAP_FAILED)
-    return entries; // entries is NULL in the second case above
+  if (entries)
+    return entries;
 
   const CentralDirectoryEnd *end = NULL;
   const char *_end = static_cast<const char *>(mapped) + size
                      - sizeof(CentralDirectoryEnd);
 
   /* Scan for the Central Directory End */
   for (; _end > mapped && !end; _end--)
     end = CentralDirectoryEnd::validate(_end);
@@ -150,32 +174,40 @@ Zip::GetFirstEntry() const
   entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
                                  + end->offset);
   if (!entries) {
     log("%s - Couldn't find central directory record", name);
   }
   return entries;
 }
 
+ZipCollection ZipCollection::Singleton;
+
 mozilla::TemporaryRef<Zip>
 ZipCollection::GetZip(const char *path)
 {
   /* Search the list of Zips we already have for a match */
-  for (std::vector<Zip *>::iterator it = zips.begin(); it < zips.end(); ++it) {
-    if (strcmp((*it)->GetName(), path) == 0)
+  for (std::vector<Zip *>::iterator it = Singleton.zips.begin();
+       it < Singleton.zips.end(); ++it) {
+    if ((*it)->GetName() && (strcmp((*it)->GetName(), path) == 0))
       return *it;
   }
-  Zip *zip = new Zip(path, this);
-  zips.push_back(zip);
-  return zip;
+  return Zip::Create(path);
+}
+
+void
+ZipCollection::Register(Zip *zip)
+{
+  Singleton.zips.push_back(zip);
 }
 
 void
 ZipCollection::Forget(Zip *zip)
 {
   debug("ZipCollection::Forget(\"%s\")", zip->GetName());
-  std::vector<Zip *>::iterator it = std::find(zips.begin(), zips.end(), zip);
+  std::vector<Zip *>::iterator it = std::find(Singleton.zips.begin(),
+                                              Singleton.zips.end(), zip);
   if (*it == zip) {
-    zips.erase(it);
+    Singleton.zips.erase(it);
   } else {
     debug("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
   }
 }
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -25,21 +25,38 @@ class ZipCollection;
  * code fail in bad ways. However, since the only intended use is to load
  * libraries from Zip archives, there is no interest in making this code
  * safe, since the libraries could contain malicious code anyways.
  */
 class Zip: public mozilla::RefCounted<Zip>
 {
 public:
   /**
-   * Create a Zip instance for the given file name. In case of error, the
-   * Zip instance is still created but methods will error out.
+   * Create a Zip instance for the given file name. Returns NULL in case
+   * of failure.
+   */
+  static mozilla::TemporaryRef<Zip> Create(const char *filename);
+
+  /**
+   * Create a Zip instance using the given buffer.
    */
-  Zip(const char *filename, ZipCollection *collection = NULL);
+  static mozilla::TemporaryRef<Zip> Create(void *buffer, size_t size) {
+    return Create(NULL, buffer, size);
+  }
 
+private:
+  static mozilla::TemporaryRef<Zip> Create(const char *filename,
+                                           void *buffer, size_t size);
+
+  /**
+   * Private constructor
+   */
+  Zip(const char *filename, void *buffer, size_t size);
+
+public:
   /**
    * Destructor
    */
   ~Zip();
 
   /**
    * Class used to access Zip archive item streams
    */
@@ -298,39 +315,44 @@ private:
    * requested entry is that one. */
   mutable const LocalFile *nextFile;
 
   /* Likewise for the next Directory entry */
   mutable const DirectoryEntry *nextDir;
 
   /* Pointer to the Directory entries */
   mutable const DirectoryEntry *entries;
-
-  /* ZipCollection containing this Zip */
-  mutable ZipCollection *parent;
 };
 
 /**
  * Class for bookkeeping Zip instances
  */
 class ZipCollection
 {
 public:
+  static ZipCollection Singleton;
+
   /**
    * Get a Zip instance for the given path. If there is an existing one
    * already, return that one, otherwise create a new one.
    */
-  mozilla::TemporaryRef<Zip> GetZip(const char *path);
+  static mozilla::TemporaryRef<Zip> GetZip(const char *path);
 
 protected:
+  friend class Zip;
+  /**
+   * Register the given Zip instance. This method is meant to be called
+   * by Zip::Create.
+   */
+  static void Register(Zip *zip);
+
   /**
    * Forget about the given Zip instance. This method is meant to be called
    * by the Zip destructor.
    */
-  friend Zip::~Zip();
-  void Forget(Zip *zip);
+  static void Forget(Zip *zip);
 
 private:
   /* Zip instances bookkept in this collection */
   std::vector<Zip *> zips;
 };
 
 #endif /* Zip_h */
--- a/mozglue/tests/TestZip.cpp
+++ b/mozglue/tests/TestZip.cpp
@@ -1,15 +1,16 @@
 /* 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 <cstdio>
 #include <unistd.h>
 #include "Zip.h"
+#include "mozilla/RefPtr.h"
 
 extern "C" void report_mapping() { }
 
 /**
  * test.zip is a basic test zip file with a central directory. It contains
  * four entries, in the following order:
  * "foo", "bar", "baz", "qux".
  * The entries are going to be read out of order.
@@ -39,29 +40,29 @@ const char *no_central_dir_entries[] = {
 int main(int argc, char *argv[])
 {
   if (argc != 2) {
     fprintf(stderr, "TEST-FAIL | TestZip | Expecting the directory containing test Zips\n");
     return 1;
   }
   chdir(argv[1]);
   Zip::Stream s;
-  Zip z("test.zip");
+  mozilla::RefPtr<Zip> z = ZipCollection::GetZip("test.zip");
   for (size_t i = 0; i < sizeof(test_entries) / sizeof(*test_entries); i++) {
-    if (!z.GetStream(test_entries[i], &s)) {
+    if (!z->GetStream(test_entries[i], &s)) {
       fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | test.zip: Couldn't get entry \"%s\"\n", test_entries[i]);
       return 1;
     }
   }
   fprintf(stderr, "TEST-PASS | TestZip | test.zip could be accessed fully\n");
 
-  Zip z2("no_central_dir.zip");
+  z = ZipCollection::GetZip("no_central_dir.zip");
   for (size_t i = 0; i < sizeof(no_central_dir_entries)
                          / sizeof(*no_central_dir_entries); i++) {
-    if (!z2.GetStream(no_central_dir_entries[i], &s)) {
+    if (!z->GetStream(no_central_dir_entries[i], &s)) {
       fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | no_central_dir.zip: Couldn't get entry \"%s\"\n", no_central_dir_entries[i]);
       return 1;
     }
   }
   fprintf(stderr, "TEST-PASS | TestZip | no_central_dir.zip could be accessed in order\n");
 
   return 0;
 }