Bug 1081034 part 2 - Move initialization of self_elf to its own separate class. r=nfroyd
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 16 Oct 2014 09:20:06 +0900
changeset 234962 349536e12ec038ab93f1d29783216fa1b8b7c40d
parent 234961 ab905a93d0eace4ca8fd85f8e0abe2fe80f303f2
child 234963 fdf75d54f631cff6440ab5aee8957ed913f47de4
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs1081034
milestone36.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 1081034 part 2 - Move initialization of self_elf to its own separate class. r=nfroyd The new class is kind of like SystemElf, but using our linker's own symbol resolution. This also adds some initialization from ELF program headers that weren't done previously for self_elf, as well as registration as for CustomElf instances.
mozglue/linker/BaseElf.cpp
mozglue/linker/BaseElf.h
mozglue/linker/CustomElf.cpp
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
--- a/mozglue/linker/BaseElf.cpp
+++ b/mozglue/linker/BaseElf.cpp
@@ -1,17 +1,19 @@
 /* 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 "BaseElf.h"
 #include "Elfxx.h"
 #include "Logging.h"
+#include "mozilla/RefPtr.h"
 
 using namespace Elf;
+using namespace mozilla;
 
 unsigned long
 BaseElf::Hash(const char *symbol)
 {
   const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol);
   unsigned long h = 0, g;
   while (*sym) {
     h = (h << 4) + *sym++;
@@ -73,8 +75,141 @@ BaseElf::FindExidx(int *pcount) const
   if (arm_exidx) {
     *pcount = arm_exidx.numElements();
     return arm_exidx;
   }
   *pcount = 0;
   return nullptr;
 }
 #endif
+
+mozilla::TemporaryRef<LibHandle>
+LoadedElf::Create(const char *path, void *base_addr)
+{
+  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);
+
+  uint8_t mapped;
+  /* If the page is not mapped, mincore returns an error. If base_addr is
+   * nullptr, as would happen if the corresponding binary is prelinked with
+   * the prelink look (but not with the android apriori tool), no page being
+   * mapped there (right?), mincore returns an error, too, which makes
+   * prelinked libraries on glibc unsupported. This is not an interesting
+   * use case for now, so don't try supporting that case.
+   */
+  if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
+    return nullptr;
+
+  RefPtr<LoadedElf> elf = new LoadedElf(path);
+
+  const Ehdr *ehdr = Ehdr::validate(base_addr);
+  if (!ehdr)
+    return nullptr;
+
+  Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+  Addr max_vaddr = 0;         // virtual address used by this Elf.
+  const Phdr *dyn = nullptr;
+#ifdef __ARM_EABI__
+  const Phdr *arm_exidx_phdr = nullptr;
+#endif
+
+  Array<Phdr> phdrs(reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff,
+                    ehdr->e_phnum);
+  for (auto phdr = phdrs.begin(); phdr < phdrs.end(); ++phdr) {
+    switch (phdr->p_type) {
+      case PT_LOAD:
+        if (phdr->p_vaddr < min_vaddr)
+          min_vaddr = phdr->p_vaddr;
+        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+        break;
+      case PT_DYNAMIC:
+        dyn = &*phdr;
+        break;
+#ifdef __ARM_EABI__
+      case PT_ARM_EXIDX:
+        /* We cannot initialize arm_exidx here
+           because we don't have a base yet */
+        arm_exidx_phdr = &*phdr;
+        break;
+#endif
+    }
+  }
+
+  /* If the lowest PT_LOAD virtual address in headers is not 0, then the ELF
+   * is either prelinked or a non-PIE executable. The former case is not
+   * possible, because base_addr would be nullptr and the mincore test above
+   * would already have made us return.
+   * For a non-PIE executable, PT_LOADs contain absolute addresses, so in
+   * practice, this means min_vaddr should be equal to base_addr. max_vaddr
+   * can thus be adjusted accordingly.
+   */
+  if (min_vaddr != 0) {
+    void *min_vaddr_ptr = reinterpret_cast<void *>(
+      static_cast<uintptr_t>(min_vaddr));
+    if (min_vaddr_ptr != base_addr) {
+      LOG("%s: %p != %p", elf->GetPath(), min_vaddr_ptr, base_addr);
+      return nullptr;
+    }
+    max_vaddr -= min_vaddr;
+  }
+  if (!dyn) {
+    LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
+    return nullptr;
+  }
+
+  elf->base.Assign(base_addr, max_vaddr);
+
+  if (!elf->InitDyn(dyn))
+    return nullptr;
+
+#ifdef __ARM_EABI__
+  if (arm_exidx_phdr)
+    elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
+                            arm_exidx_phdr->p_memsz);
+#endif
+
+  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
+    static_cast<void *>(elf));
+
+  ElfLoader::Singleton.Register(elf);
+  return elf;
+}
+
+bool
+LoadedElf::InitDyn(const Phdr *pt_dyn)
+{
+  Array<Dyn> dyns;
+  dyns.InitSize(GetPtr<Dyn>(pt_dyn->p_vaddr), pt_dyn->p_filesz);
+
+  size_t symnum = 0;
+  for (auto dyn = dyns.begin(); dyn < dyns.end() && dyn->d_tag; ++dyn) {
+    switch (dyn->d_tag) {
+      case DT_HASH:
+        {
+          DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_HASH", dyn->d_un.d_val);
+          const Elf::Word *hash_table_header = \
+            GetPtr<Elf::Word>(dyn->d_un.d_ptr);
+          symnum = hash_table_header[1];
+          buckets.Init(&hash_table_header[2], hash_table_header[0]);
+          chains.Init(&*buckets.end());
+        }
+        break;
+      case DT_STRTAB:
+        DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val);
+        strtab.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_SYMTAB:
+        DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", dyn->d_un.d_val);
+        symtab.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+    }
+  }
+  if (!buckets || !symnum) {
+    ERROR("%s: Missing or broken DT_HASH", GetPath());
+  } else if (!strtab) {
+    ERROR("%s: Missing DT_STRTAB", GetPath());
+  } else if (!symtab) {
+    ERROR("%s: Missing DT_SYMTAB", GetPath());
+  } else {
+    return true;
+  }
+  return false;
+}
--- a/mozglue/linker/BaseElf.h
+++ b/mozglue/linker/BaseElf.h
@@ -98,9 +98,44 @@ public:
   UnsizedArray<Elf::Sym> symtab;
 
 #ifdef __ARM_EABI__
   /* ARM.exidx information used by FindExidx */
   Array<uint32_t[2]> arm_exidx;
 #endif
 };
 
+
+/**
+ * Class for ELF libraries that already loaded in memory.
+ */
+class LoadedElf: public BaseElf
+{
+public:
+  /**
+   * Returns a LoadedElf corresponding to the already loaded ELF
+   * at the given base address.
+   */
+  static mozilla::TemporaryRef<LibHandle> Create(const char *path,
+                                                 void *base_addr);
+
+private:
+  LoadedElf(const char *path)
+  : BaseElf(path) { }
+
+  ~LoadedElf()
+  {
+    /* Avoid base's destructor unmapping something that doesn't actually
+     * belong to it. */
+    base.release();
+    ElfLoader::Singleton.Forget(this);
+  }
+
+  /**
+   * Initializes the library according to information found in the given
+   * PT_DYNAMIC header.
+   * Returns whether this succeeded or failed.
+   */
+  bool InitDyn(const Elf::Phdr *pt_dyn);
+};
+
+
 #endif /* BaseElf_h */
--- a/mozglue/linker/CustomElf.cpp
+++ b/mozglue/linker/CustomElf.cpp
@@ -324,29 +324,34 @@ CustomElf::GetSymbolPtrInDeps(const char
   void *sym;
 
   unsigned long hash = Hash(symbol);
 
   /* self_elf should never be NULL, but better safe than sorry. */
   if (ElfLoader::Singleton.self_elf) {
     /* We consider the library containing this code a permanent LD_PRELOAD,
      * so, check if the symbol exists here first. */
-    sym = ElfLoader::Singleton.self_elf->GetSymbolPtr(symbol, hash);
+    sym = static_cast<BaseElf *>(
+      ElfLoader::Singleton.self_elf.get())->GetSymbolPtr(symbol, hash);
     if (sym)
       return sym;
   }
 
   /* Then search the symbol in our dependencies. Since we already searched in
    * libraries the system linker loaded, skip those (on glibc systems). We
    * also assume the symbol is to be found in one of the dependent libraries
    * directly, not in their own dependent libraries. Building libraries with
    * --no-allow-shlib-undefined ensures such indirect symbol dependency don't
    * happen. */
   for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin();
        it < dependencies.end(); ++it) {
+    /* Skip if it's the library containing this code, since we've already
+     * looked at it above. */
+    if (*it == ElfLoader::Singleton.self_elf)
+      continue;
     if (BaseElf *be = (*it)->AsBaseElf()) {
       sym = be->GetSymbolPtr(symbol, hash);
     } else {
       sym = (*it)->GetSymbolPtr(symbol);
     }
     if (sym)
       return sym;
   }
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -492,59 +492,27 @@ ElfLoader::Forget(CustomElf *handle)
 void
 ElfLoader::Init()
 {
   Dl_info info;
   /* On Android < 4.1 can't reenter dl* functions. So when the library
    * containing this code is dlopen()ed, it can't call dladdr from a
    * static initializer. */
   if (dladdr(_DYNAMIC, &info) != 0) {
-    /* Ideally, we wouldn't be initializing self_elf this way, but until
-     * SystemElf actually inherits from BaseElf, we'll just do it this
-     * (gross) way. */
-    UniquePtr<BaseElf> elf = mozilla::MakeUnique<BaseElf>(info.dli_fname);
-    elf->base.Assign(info.dli_fbase, -1);
-    size_t symnum = 0;
-    for (const Elf::Dyn *dyn = _DYNAMIC; dyn->d_tag; dyn++) {
-      switch (dyn->d_tag) {
-        case DT_HASH:
-          {
-            DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_HASH", dyn->d_un.d_val);
-            const Elf::Word *hash_table_header = \
-              elf->GetPtr<Elf::Word>(dyn->d_un.d_ptr);
-            symnum = hash_table_header[1];
-            elf->buckets.Init(&hash_table_header[2], hash_table_header[0]);
-            elf->chains.Init(&*elf->buckets.end());
-          }
-          break;
-        case DT_STRTAB:
-          DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val);
-          elf->strtab.Init(elf->GetPtr(dyn->d_un.d_ptr));
-          break;
-        case DT_SYMTAB:
-          DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", dyn->d_un.d_val);
-          elf->symtab.Init(elf->GetPtr(dyn->d_un.d_ptr));
-          break;
-      }
-    }
-    if (!elf->buckets || !symnum) {
-      ERROR("%s: Missing or broken DT_HASH", info.dli_fname);
-    } else if (!elf->strtab) {
-      ERROR("%s: Missing DT_STRTAB", info.dli_fname);
-    } else if (!elf->symtab) {
-      ERROR("%s: Missing DT_SYMTAB", info.dli_fname);
-    } else {
-      self_elf = Move(elf);
-    }
+    self_elf = LoadedElf::Create(info.dli_fname, info.dli_fbase);
   }
 }
 
 ElfLoader::~ElfLoader()
 {
   LibHandleList list;
+
+  /* Release self_elf */
+  self_elf = nullptr;
+
   /* Build up a list of all library handles with direct (external) references.
    * We actually skip system library handles because we want to keep at least
    * some of these open. Most notably, Mozilla codebase keeps a few libgnome
    * libraries deliberately open because of the mess that libORBit destruction
    * is. dlclose()ing these libraries actually leads to problems. */
   for (LibHandleList::reverse_iterator it = handles.rbegin();
        it < handles.rend(); ++it) {
     if ((*it)->DirectRefCount()) {
@@ -573,20 +541,16 @@ ElfLoader::~ElfLoader()
                   "[%d direct refs, %d refs total]", (*it)->GetPath(),
                   (*it)->DirectRefCount(), (*it)->refCount());
         /* Not removing, since it could have references to other libraries,
          * destroying them as a side effect, and possibly leaving dangling
          * pointers in the handle list we're scanning */
       }
     }
   }
-  /* Avoid self_elf->base destructor unmapping something that doesn't actually
-   * belong to it. */
-  if (self_elf)
-    self_elf->base.release();
 }
 
 void
 ElfLoader::stats(const char *when)
 {
   if (MOZ_LIKELY(!Logging::isVerbose()))
     return;
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -456,24 +456,25 @@ protected:
 private:
   ~ElfLoader();
 
   /* Initialization code that can't run during static initialization. */
   void Init();
 
   /* System loader handle for the library/program containing our code. This
    * is used to resolve wrapped functions. */
-  mozilla::UniquePtr<BaseElf> self_elf;
+  mozilla::RefPtr<LibHandle> self_elf;
 
   /* Bookkeeping */
   typedef std::vector<LibHandle *> LibHandleList;
   LibHandleList handles;
 
 protected:
   friend class CustomElf;
+  friend class LoadedElf;
   /**
    * Show some stats about Mappables in CustomElfs. The when argument is to
    * be used by the caller to give an identifier of the when the stats call
    * is made.
    */
   static void stats(const char *when);
 
   /* Definition of static destructors as to be used for C++ ABI compatibility */