Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "LoaderPrivateAPI.h"
#include "mozilla/Assertions.h"
#include "mozilla/Types.h"
#include "mozilla/Unused.h"
#include "../DllBlocklistInit.h"
#include "../ErrorHandler.h"
#include "SharedSection.h"
using GlobalInitializerFn = void(__cdecl*)(void);
// Allocation of static initialization section for the freestanding library
#pragma section(".freestd$a", read)
__declspec(allocate(
".freestd$a")) static const GlobalInitializerFn FreeStdStart =
reinterpret_cast<GlobalInitializerFn>(0);
#pragma section(".freestd$z", read)
__declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd =
reinterpret_cast<GlobalInitializerFn>(0);
namespace mozilla {
namespace freestanding {
static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT;
// The contract for this callback is identical to the InitOnceCallback from
// Win32 land; we're just using ntdll-layer types instead.
static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
PVOID* aContext) {
// Invoke every static initializer in the .freestd section
const GlobalInitializerFn* cur = &FreeStdStart + 1;
while (cur < &FreeStdEnd) {
if (*cur) {
(*cur)();
}
++cur;
}
return TRUE;
}
/**
* This observer is only used until the mozglue observer connects itself.
* All we do here is accumulate the module loads into a vector.
* As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver
* to pass our vector on for further processing. This object then becomes
* defunct.
*/
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final
: public nt::LoaderObserver {
public:
constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {}
void OnBeginDllLoad(void** aContext,
PCUNICODE_STRING aRequestedDllName) final {}
bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
PHANDLE aOutHandle) final {
return false;
}
void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
ModuleLoadInfo&& aModuleLoadInfo) final;
void Forward(nt::LoaderObserver* aNext) final;
void OnForward(ModuleLoadInfoVec&& aInfo) final {
MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver");
}
private:
mozilla::nt::SRWLock mLock;
ModuleLoadInfoVec* mModuleLoads;
};
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final
: public LoaderPrivateAPI {
public:
// LoaderAPI
ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
void** aContext, PCUNICODE_STRING aRequestedDllName) final;
bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
PHANDLE aOutHandle) final;
void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
ModuleLoadInfo&& aModuleLoadInfo) final;
nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final;
nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final;
nt::SharedSection* GetSharedSection() final;
// LoaderPrivateAPI
void NotifyBeginDllLoad(void** aContext,
PCUNICODE_STRING aRequestedDllName) final;
void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext,
PCUNICODE_STRING aRequestedDllName) final;
void SetObserver(nt::LoaderObserver* aNewObserver) final;
bool IsDefaultObserver() const final;
nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
};
static void Init() {
DebugOnly<NTSTATUS> ntStatus =
::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr);
MOZ_ASSERT(NT_SUCCESS(ntStatus));
}
} // namespace freestanding
} // namespace mozilla
static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver;
static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI;
static mozilla::nt::SRWLock gLoaderObserverLock;
static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver;
static CONDITION_VARIABLE gLoaderObserverNoOngoingLoadsCV =
CONDITION_VARIABLE_INIT;
static mozilla::Atomic<uint32_t> gLoaderObserverOngoingLoadsCount(0);
namespace mozilla {
namespace freestanding {
LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI;
void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
ModuleLoadInfo&& aModuleLoadInfo) {
// If the DLL load failed, or if the DLL was loaded by a previous request
// and thus was not mapped by this request, we do not save the ModuleLoadInfo.
if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) {
return;
}
nt::AutoExclusiveLock lock(mLock);
if (!mModuleLoads) {
mModuleLoads = RtlNew<ModuleLoadInfoVec>();
if (!mModuleLoads) {
return;
}
}
Unused << mModuleLoads->emplaceBack(
std::forward<ModuleLoadInfo>(aModuleLoadInfo));
}
/**
* Pass mModuleLoads's data off to |aNext| for further processing.
*/
void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) {
MOZ_ASSERT(aNext);
if (!aNext) {
return;
}
ModuleLoadInfoVec* moduleLoads = nullptr;
{ // Scope for lock
nt::AutoExclusiveLock lock(mLock);
moduleLoads = mModuleLoads;
mModuleLoads = nullptr;
}
if (!moduleLoads) {
return;
}
aNext->OnForward(std::move(*moduleLoads));
RtlDelete(moduleLoads);
}
ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad(
void** aContext, PCUNICODE_STRING aRequestedDllName) {
ModuleLoadInfo loadInfo(aRequestedDllName);
NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName);
return loadInfo;
}
bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
PHANDLE aOutHandle) {
// This method should only be called between NotifyBeginDllLoad and
// NotifyEndDllLoad, so it is already protected against gLoaderObserver
// change, through gLoaderObserverOngoingLoadsCount.
MOZ_RELEASE_ASSERT(gLoaderObserverOngoingLoadsCount);
return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
}
void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext,
NTSTATUS aLoadNtStatus,
ModuleLoadInfo&& aModuleLoadInfo) {
aModuleLoadInfo.SetEndLoadTimeStamp();
if (NT_SUCCESS(aLoadNtStatus)) {
aModuleLoadInfo.CaptureBacktrace();
}
// This method should only be called after a matching call to
// NotifyBeginDllLoad, so it is already protected against gLoaderObserver
// change, through gLoaderObserverOngoingLoadsCount.
// We need to notify the observer that the DLL load has ended even when
// |aLoadNtStatus| indicates a failure. This is to ensure that any resources
// acquired by the observer during OnBeginDllLoad are cleaned up.
gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
std::move(aModuleLoadInfo));
auto previousValue = gLoaderObserverOngoingLoadsCount--;
MOZ_RELEASE_ASSERT(previousValue);
if (previousValue == 1) {
::RtlWakeAllConditionVariable(&gLoaderObserverNoOngoingLoadsCV);
}
}
nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
void* aSectionAddr) {
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
nt::MemorySectionNameBuf buf;
NTSTATUS ntStatus =
::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
&buf, sizeof(buf), nullptr);
if (!NT_SUCCESS(ntStatus)) {
return nt::AllocatedUnicodeString();
}
return nt::AllocatedUnicodeString(&buf.mSectionFileName);
}
nt::LoaderAPI::InitDllBlocklistOOPFnPtr
LoaderPrivateAPIImp::GetDllBlocklistInitFn() {
return &InitializeDllBlocklistOOP;
}
nt::LoaderAPI::HandleLauncherErrorFnPtr
LoaderPrivateAPIImp::GetHandleLauncherErrorFn() {
return &HandleLauncherError;
}
nt::SharedSection* LoaderPrivateAPIImp::GetSharedSection() {
return &gSharedSection;
}
nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
void* aSectionAddr) {
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
nt::MemorySectionNameBuf buf;
NTSTATUS ntStatus =
::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
&buf, sizeof(buf), nullptr);
if (!NT_SUCCESS(ntStatus)) {
return nt::MemorySectionNameBuf();
}
return buf;
}
void LoaderPrivateAPIImp::NotifyBeginDllLoad(
void** aContext, PCUNICODE_STRING aRequestedDllName) {
nt::AutoSharedLock lock(gLoaderObserverLock);
++gLoaderObserverOngoingLoadsCount;
gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
}
void LoaderPrivateAPIImp::NotifyBeginDllLoad(
ModuleLoadInfo& aModuleLoadInfo, void** aContext,
PCUNICODE_STRING aRequestedDllName) {
NotifyBeginDllLoad(aContext, aRequestedDllName);
aModuleLoadInfo.SetBeginLoadTimeStamp();
}
void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) {
nt::LoaderObserver* prevLoaderObserver = nullptr;
nt::AutoExclusiveLock lock(gLoaderObserverLock);
while (gLoaderObserverOngoingLoadsCount) {
NTSTATUS status = ::RtlSleepConditionVariableSRW(
&gLoaderObserverNoOngoingLoadsCV, &gLoaderObserverLock, nullptr, 0);
MOZ_RELEASE_ASSERT(NT_SUCCESS(status) && status != STATUS_TIMEOUT);
}
MOZ_ASSERT(aNewObserver);
if (!aNewObserver) {
// This is unlikely, but we always want a valid observer, so use the
// gDefaultObserver if necessary.
gLoaderObserver = &gDefaultObserver;
return;
}
prevLoaderObserver = gLoaderObserver;
gLoaderObserver = aNewObserver;
MOZ_ASSERT(prevLoaderObserver);
if (!prevLoaderObserver) {
return;
}
// Now that we have a new observer, the previous observer must forward its
// data on to the new observer for processing.
prevLoaderObserver->Forward(aNewObserver);
}
bool LoaderPrivateAPIImp::IsDefaultObserver() const {
nt::AutoSharedLock lock(gLoaderObserverLock);
return gLoaderObserver == &gDefaultObserver;
}
void EnsureInitialized() { Init(); }
} // namespace freestanding
} // namespace mozilla