1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "LoaderObserver.h"
8
9 #include "mozilla/AutoProfilerLabel.h"
10 #include "mozilla/BaseProfilerMarkers.h"
11 #include "mozilla/glue/WindowsUnicode.h"
12 #include "mozilla/StackWalk_windows.h"
13 #include "mozilla/TimeStamp.h"
14
15 namespace {
16
17 struct LoadContext {
LoadContext__anond0ef0d1c0111::LoadContext18 LoadContext(mozilla::ProfilerLabel&& aLabel,
19 mozilla::UniquePtr<char[]>&& aDynamicStringStorage)
20 : mProfilerLabel(std::move(aLabel)),
21 mDynamicStringStorage(std::move(aDynamicStringStorage)),
22 mStartTime(mozilla::TimeStamp::Now()) {}
23 mozilla::ProfilerLabel mProfilerLabel;
24 mozilla::UniquePtr<char[]> mDynamicStringStorage;
25 mozilla::TimeStamp mStartTime;
26 };
27
28 } // anonymous namespace
29
30 namespace mozilla {
31
32 extern glue::Win32SRWLock gDllServicesLock;
33 extern glue::detail::DllServicesBase* gDllServices;
34
35 namespace glue {
36
OnBeginDllLoad(void ** aContext,PCUNICODE_STRING aRequestedDllName)37 void LoaderObserver::OnBeginDllLoad(void** aContext,
38 PCUNICODE_STRING aRequestedDllName) {
39 MOZ_ASSERT(aContext);
40 if (IsProfilerPresent()) {
41 UniquePtr<char[]> utf8RequestedDllName(WideToUTF8(aRequestedDllName));
42 const char* dynamicString = utf8RequestedDllName.get();
43 *aContext = new LoadContext(
44 ProfilerLabelBegin("mozilla::glue::LoaderObserver::OnBeginDllLoad",
45 dynamicString, &aContext),
46 std::move(utf8RequestedDllName));
47 }
48
49 #ifdef _M_AMD64
50 // Prevent the stack walker from suspending this thread when LdrLoadDll
51 // holds the RtlLookupFunctionEntry lock.
52 SuppressStackWalking();
53 #endif
54 }
55
SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,PHANDLE aOutHandle)56 bool LoaderObserver::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
57 PHANDLE aOutHandle) {
58 // Currently unsupported
59 return false;
60 }
61
OnEndDllLoad(void * aContext,NTSTATUS aNtStatus,ModuleLoadInfo && aModuleLoadInfo)62 void LoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
63 ModuleLoadInfo&& aModuleLoadInfo) {
64 #ifdef _M_AMD64
65 DesuppressStackWalking();
66 #endif
67
68 UniquePtr<LoadContext> loadContext(static_cast<LoadContext*>(aContext));
69 if (loadContext && IsValidProfilerLabel(loadContext->mProfilerLabel)) {
70 ProfilerLabelEnd(loadContext->mProfilerLabel);
71 BASE_PROFILER_MARKER_TEXT(
72 "DllLoad", OTHER,
73 MarkerTiming::IntervalUntilNowFrom(loadContext->mStartTime),
74 mozilla::ProfilerString8View::WrapNullTerminatedString(
75 loadContext->mDynamicStringStorage.get()));
76 }
77
78 // We want to record a denied DLL load regardless of |aNtStatus| because
79 // |aNtStatus| is set to access-denied when DLL load was blocked.
80 if ((!NT_SUCCESS(aNtStatus) && !aModuleLoadInfo.WasDenied()) ||
81 !aModuleLoadInfo.WasMapped()) {
82 return;
83 }
84
85 { // Scope for lock
86 AutoSharedLock lock(gDllServicesLock);
87 if (gDllServices) {
88 gDllServices->DispatchDllLoadNotification(std::move(aModuleLoadInfo));
89 return;
90 }
91 }
92
93 // No dll services, save for later
94 AutoExclusiveLock lock(mLock);
95 if (!mEnabled) {
96 return;
97 }
98
99 if (!mModuleLoads) {
100 mModuleLoads = new ModuleLoadInfoVec();
101 }
102
103 Unused << mModuleLoads->emplaceBack(
104 std::forward<ModuleLoadInfo>(aModuleLoadInfo));
105 }
106
Forward(nt::LoaderObserver * aNext)107 void LoaderObserver::Forward(nt::LoaderObserver* aNext) {
108 MOZ_ASSERT_UNREACHABLE(
109 "This implementation does not forward to any more "
110 "nt::LoaderObserver objects");
111 }
112
Forward(detail::DllServicesBase * aNext)113 void LoaderObserver::Forward(detail::DllServicesBase* aNext) {
114 MOZ_ASSERT(aNext);
115 if (!aNext) {
116 return;
117 }
118
119 ModuleLoadInfoVec* moduleLoads = nullptr;
120
121 { // Scope for lock
122 AutoExclusiveLock lock(mLock);
123 moduleLoads = mModuleLoads;
124 mModuleLoads = nullptr;
125 }
126
127 if (!moduleLoads) {
128 return;
129 }
130
131 aNext->DispatchModuleLoadBacklogNotification(std::move(*moduleLoads));
132 delete moduleLoads;
133 }
134
Disable()135 void LoaderObserver::Disable() {
136 ModuleLoadInfoVec* moduleLoads = nullptr;
137
138 { // Scope for lock
139 AutoExclusiveLock lock(mLock);
140 moduleLoads = mModuleLoads;
141 mModuleLoads = nullptr;
142 mEnabled = false;
143 }
144
145 delete moduleLoads;
146 }
147
OnForward(ModuleLoadInfoVec && aInfo)148 void LoaderObserver::OnForward(ModuleLoadInfoVec&& aInfo) {
149 AutoExclusiveLock lock(mLock);
150 if (!mModuleLoads) {
151 mModuleLoads = new ModuleLoadInfoVec();
152 }
153
154 MOZ_ASSERT(mModuleLoads->empty());
155 if (mModuleLoads->empty()) {
156 *mModuleLoads = std::move(aInfo);
157 } else {
158 // This should not happen, but we can handle it
159 for (auto&& item : aInfo) {
160 Unused << mModuleLoads->append(std::move(item));
161 }
162 }
163 }
164
165 } // namespace glue
166 } // namespace mozilla
167