Bug 1547113: Add support for section table parsing to nt::PEHeaders; r=mhowell
authorAaron Klotz <aklotz@mozilla.com>
Fri, 26 Apr 2019 15:55:11 +0000
changeset 530359 2eda2b8941d4640fe8c4e2de8629f8b629de0a9c
parent 530358 e8c59393c80952adfffd0be0de34c3ae7e48e36a
child 530360 9145204dc6e50fc1dc993934bc8dd919b9b9b0ee
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmhowell
bugs1547113
milestone68.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 1547113: Add support for section table parsing to nt::PEHeaders; r=mhowell Differential Revision: https://phabricator.services.mozilla.com/D28905
mozglue/misc/NativeNt.h
--- a/mozglue/misc/NativeNt.h
+++ b/mozglue/misc/NativeNt.h
@@ -12,16 +12,18 @@
 #include <winnt.h>
 #include <winternl.h>
 
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/LauncherResult.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Span.h"
 
 // The declarations within this #if block are intended to be used for initial
 // process initialization ONLY. You probably don't want to be using these in
 // normal Gecko code!
 #if !defined(MOZILLA_INTERNAL_API)
 
 extern "C" {
 
@@ -289,27 +291,27 @@ class MOZ_RAII PEHeaders final {
 
   explicit operator bool() const { return !!mImageLimit; }
 
   /**
    * This overload computes absolute virtual addresses relative to the base
    * address of the binary.
    */
   template <typename T, typename R>
-  T RVAToPtr(R aRva) {
+  T RVAToPtr(R aRva) const {
     return RVAToPtr<T>(mMzHeader, aRva);
   }
 
   /**
    * This overload computes a result by adding aRva to aBase, but also ensures
    * that the resulting pointer falls within the bounds of this binary's memory
    * mapping.
    */
   template <typename T, typename R>
-  T RVAToPtr(void* aBase, R aRva) {
+  T RVAToPtr(void* aBase, R aRva) const {
     if (!mImageLimit) {
       return nullptr;
     }
 
     char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
     if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
         absAddress > reinterpret_cast<char*>(mImageLimit)) {
       return nullptr;
@@ -472,16 +474,63 @@ class MOZ_RAII PEHeaders final {
       return nullptr;
     }
 
     auto dataEntry =
         RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
     return RVAToPtr<T>(dataEntry->OffsetToData);
   }
 
+  template <size_t N>
+  Maybe<Span<const uint8_t>> FindSection(const char (&aSecName)[N],
+                                         DWORD aCharacteristicsMask) const {
+    static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME,
+                  "Section names must be at most 8 characters excluding null "
+                  "terminator");
+
+    if (!(*this)) {
+      return Nothing();
+    }
+
+    Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable();
+    for (auto&& sectionHeader : sectionTable) {
+      if (strncmp(reinterpret_cast<const char*>(sectionHeader.Name), aSecName,
+                  IMAGE_SIZEOF_SHORT_NAME)) {
+        continue;
+      }
+
+      if (!(sectionHeader.Characteristics & aCharacteristicsMask)) {
+        // We found the section but it does not have the expected
+        // characteristics
+        return Nothing();
+      }
+
+      DWORD rva = sectionHeader.VirtualAddress;
+      if (!rva) {
+        return Nothing();
+      }
+
+      DWORD size = sectionHeader.Misc.VirtualSize;
+      if (!size) {
+        return Nothing();
+      }
+
+      auto base = RVAToPtr<const uint8_t*>(rva);
+      return Some(MakeSpan(base, size));
+    }
+
+    return Nothing();
+  }
+
+  // There may be other code sections in the binary besides .text
+  Maybe<Span<const uint8_t>> GetTextSectionInfo() const {
+    return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE |
+                                IMAGE_SCN_MEM_READ);
+  }
+
   static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
     return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
   }
 
   static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
     return aImgThunk && aImgThunk->u1.Ordinal != 0;
   }
 
@@ -494,20 +543,30 @@ class MOZ_RAII PEHeaders final {
     }
 
     return RVAToPtr<T>(dirEntry->VirtualAddress);
   }
 
   // This private variant does not have bounds checks, because we need to be
   // able to resolve the bounds themselves.
   template <typename T, typename R>
-  T RVAToPtrUnchecked(R aRva) {
+  T RVAToPtrUnchecked(R aRva) const {
     return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
   }
 
+  Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
+    MOZ_ASSERT(*this);
+    auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(&mPeHeader->OptionalHeader,
+                         mPeHeader->FileHeader.SizeOfOptionalHeader);
+    // The Windows loader has an internal limit of 96 sections (per PE spec)
+    auto numSections = std::min(mPeHeader->FileHeader.NumberOfSections,
+                                WORD(96));
+    return MakeSpan(base, numSections);
+  }
+
   PIMAGE_RESOURCE_DIRECTORY_ENTRY
   FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) {
     // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
     // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
     // searches by ID, we need to skip past any named entries before iterating.
     auto dirEnt =
         reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
         aCurLevel->NumberOfNamedEntries;