summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/dllservices/mozglue/LoaderObserver.cpp
blob: d2014365c4770299f7faf0034ff0b623955f1a1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "LoaderObserver.h"

#include "mozilla/AutoProfilerLabel.h"
#include "mozilla/BaseProfilerMarkers.h"
#include "mozilla/glue/WindowsUnicode.h"
#include "mozilla/StackWalk_windows.h"
#include "mozilla/TimeStamp.h"

namespace mozilla {

extern glue::Win32SRWLock gDllServicesLock;
extern glue::detail::DllServicesBase* gDllServices;

namespace glue {

void LoaderObserver::OnBeginDllLoad(void** aContext,
                                    PCUNICODE_STRING aRequestedDllName) {
  MOZ_ASSERT(aContext);
  if (IsProfilerPresent()) {
    UniquePtr<char[]> utf8RequestedDllName(WideToUTF8(aRequestedDllName));
    BASE_PROFILER_MARKER_TEXT(
        "DllLoad", OTHER, MarkerTiming::IntervalStart(),
        mozilla::ProfilerString8View::WrapNullTerminatedString(
            utf8RequestedDllName.get()));
    *aContext = utf8RequestedDllName.release();
  }

#ifdef _M_AMD64
  // Prevent the stack walker from suspending this thread when LdrLoadDll
  // holds the RtlLookupFunctionEntry lock.
  SuppressStackWalking();
#endif
}

bool LoaderObserver::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
                                      PHANDLE aOutHandle) {
  // Currently unsupported
  return false;
}

void LoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
                                  ModuleLoadInfo&& aModuleLoadInfo) {
#ifdef _M_AMD64
  DesuppressStackWalking();
#endif

  if (aContext) {
    UniquePtr<char[]> utf8RequestedDllName{static_cast<char*>(aContext)};
    BASE_PROFILER_MARKER_TEXT(
        "DllLoad", OTHER, MarkerTiming::IntervalEnd(),
        mozilla::ProfilerString8View::WrapNullTerminatedString(
            utf8RequestedDllName.get()));
  }

  // We want to record a denied DLL load regardless of |aNtStatus| because
  // |aNtStatus| is set to access-denied when DLL load was blocked.
  if ((!NT_SUCCESS(aNtStatus) && !aModuleLoadInfo.WasDenied()) ||
      !aModuleLoadInfo.WasMapped()) {
    return;
  }

  {  // Scope for lock
    AutoSharedLock lock(gDllServicesLock);
    if (gDllServices) {
      gDllServices->DispatchDllLoadNotification(std::move(aModuleLoadInfo));
      return;
    }
  }

  // No dll services, save for later
  AutoExclusiveLock lock(mLock);
  if (!mEnabled) {
    return;
  }

  if (!mModuleLoads) {
    mModuleLoads = new ModuleLoadInfoVec();
  }

  Unused << mModuleLoads->emplaceBack(
      std::forward<ModuleLoadInfo>(aModuleLoadInfo));
}

void LoaderObserver::Forward(nt::LoaderObserver* aNext) {
  MOZ_ASSERT_UNREACHABLE(
      "This implementation does not forward to any more "
      "nt::LoaderObserver objects");
}

void LoaderObserver::Forward(detail::DllServicesBase* aNext) {
  MOZ_ASSERT(aNext);
  if (!aNext) {
    return;
  }

  ModuleLoadInfoVec* moduleLoads = nullptr;

  {  // Scope for lock
    AutoExclusiveLock lock(mLock);
    moduleLoads = mModuleLoads;
    mModuleLoads = nullptr;
  }

  if (!moduleLoads) {
    return;
  }

  aNext->DispatchModuleLoadBacklogNotification(std::move(*moduleLoads));
  delete moduleLoads;
}

void LoaderObserver::Disable() {
  ModuleLoadInfoVec* moduleLoads = nullptr;

  {  // Scope for lock
    AutoExclusiveLock lock(mLock);
    moduleLoads = mModuleLoads;
    mModuleLoads = nullptr;
    mEnabled = false;
  }

  delete moduleLoads;
}

void LoaderObserver::OnForward(ModuleLoadInfoVec&& aInfo) {
  AutoExclusiveLock lock(mLock);
  if (!mModuleLoads) {
    mModuleLoads = new ModuleLoadInfoVec();
  }

  MOZ_ASSERT(mModuleLoads->empty());
  if (mModuleLoads->empty()) {
    *mModuleLoads = std::move(aInfo);
  } else {
    // This should not happen, but we can handle it
    for (auto&& item : aInfo) {
      Unused << mModuleLoads->append(std::move(item));
    }
  }
}

}  // namespace glue
}  // namespace mozilla