diff options
Diffstat (limited to 'src/debug/ildbsymlib')
-rw-r--r-- | src/debug/ildbsymlib/.gitmirror | 1 | ||||
-rw-r--r-- | src/debug/ildbsymlib/CMakeLists.txt | 18 | ||||
-rw-r--r-- | src/debug/ildbsymlib/classfactory.h | 95 | ||||
-rw-r--r-- | src/debug/ildbsymlib/dirs.proj | 19 | ||||
-rw-r--r-- | src/debug/ildbsymlib/ildbsymbols.cpp | 155 | ||||
-rw-r--r-- | src/debug/ildbsymlib/ildbsymlib.props | 29 | ||||
-rw-r--r-- | src/debug/ildbsymlib/ildbsymlib.vcproj | 213 | ||||
-rw-r--r-- | src/debug/ildbsymlib/pch.h | 41 | ||||
-rw-r--r-- | src/debug/ildbsymlib/pdbdata.h | 92 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symbinder.cpp | 163 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symbinder.h | 73 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symread.cpp | 2765 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symread.h | 554 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symwrite.cpp | 1553 | ||||
-rw-r--r-- | src/debug/ildbsymlib/symwrite.h | 1226 | ||||
-rw-r--r-- | src/debug/ildbsymlib/umisc.h | 69 |
16 files changed, 7066 insertions, 0 deletions
diff --git a/src/debug/ildbsymlib/.gitmirror b/src/debug/ildbsymlib/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/debug/ildbsymlib/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/debug/ildbsymlib/CMakeLists.txt b/src/debug/ildbsymlib/CMakeLists.txt new file mode 100644 index 0000000000..1bd1096ed6 --- /dev/null +++ b/src/debug/ildbsymlib/CMakeLists.txt @@ -0,0 +1,18 @@ +if(WIN32) + #use static crt + add_definitions(-MT) +endif(WIN32) + +set( ILDBSYMLIB_SOURCES + symread.cpp + symbinder.cpp + ildbsymbols.cpp + symwrite.cpp +) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_library_clr(ildbsymlib ${ILDBSYMLIB_SOURCES}) + diff --git a/src/debug/ildbsymlib/classfactory.h b/src/debug/ildbsymlib/classfactory.h new file mode 100644 index 0000000000..2b38167747 --- /dev/null +++ b/src/debug/ildbsymlib/classfactory.h @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: ClassFactory.h +// + +// =========================================================================== + +//***************************************************************************** +// ClassFactory.h +// +// Class factories are used by the pluming in COM to activate new objects. +// This module contains the class factory code to instantiate the +// ISymUnmanaged Reader,Writer and Binder +//***************************************************************************** +#ifndef __ILDBClassFactory__h__ +#define __ILDBClassFactory__h__ + +// This typedef is for a function which will create a new instance of an object. +typedef HRESULT (* PFN_CREATE_OBJ)(REFIID riid, void**ppvObject); + +//***************************************************************************** +// This structure is used to declare a global list of coclasses. The class +// factory object is created with a pointer to the correct one of these, so +// that when create instance is called, it can be created. +//***************************************************************************** +struct COCLASS_REGISTER +{ + const GUID *pClsid; // Class ID of the coclass. + LPCWSTR szProgID; // Prog ID of the class. + PFN_CREATE_OBJ pfnCreateObject; // Creation function for an instance. +}; + + + +//***************************************************************************** +// One class factory object satifies all of our clsid's, to reduce overall +// code bloat. +//***************************************************************************** +class CIldbClassFactory : + public IClassFactory +{ + CIldbClassFactory() { } // Can't use without data. + +public: + CIldbClassFactory(const COCLASS_REGISTER *pCoClass) + : m_cRef(1), m_pCoClass(pCoClass) + { } + + virtual ~CIldbClassFactory() {} + + // + // IUnknown methods. + // + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject); + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + LONG cRef = InterlockedDecrement(&m_cRef); + if (cRef <= 0) + DELETE(this); + return (cRef); + } + + + // + // IClassFactory methods. + // + + virtual HRESULT STDMETHODCALLTYPE CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject); + + virtual HRESULT STDMETHODCALLTYPE LockServer( + BOOL fLock); + + +private: + LONG m_cRef; // Reference count. + const COCLASS_REGISTER *m_pCoClass; // The class we belong to. +}; + + + +#endif // __ClassFactory__h__ diff --git a/src/debug/ildbsymlib/dirs.proj b/src/debug/ildbsymlib/dirs.proj new file mode 100644 index 0000000000..b171f7bca5 --- /dev/null +++ b/src/debug/ildbsymlib/dirs.proj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\ildbsymlib.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/debug/ildbsymlib/ildbsymbols.cpp b/src/debug/ildbsymlib/ildbsymbols.cpp new file mode 100644 index 0000000000..1c9219ce62 --- /dev/null +++ b/src/debug/ildbsymlib/ildbsymbols.cpp @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: ildbsymbols.cpp +// + +// =========================================================================== + +#include "pch.h" + +#include "classfactory.h" + +// GUID identifying the ILDB format version. +extern "C" const GUID ILDB_VERSION_GUID = {0x9e02e5b6, 0x8aef, 0x4d06, { 0x82, 0xe8, 0xe, 0x9b, 0x45, 0x49, 0x97, 0x16} }; + +// Version used for the "first source release", no longer supported. +extern "C" const GUID ILDB_VERSION_GUID_FSR = {0xCB2F6723, 0xAB3A, 0x11d, { 0x9C, 0x40, 0x00, 0xC0, 0x4F, 0xA3, 0x0A, 0x3E} }; + +// This map contains the list of coclasses which are exported from this module. +const COCLASS_REGISTER g_CoClasses[] = +{ +// pClsid szProgID pfnCreateObject + { &CLSID_CorSymReader_SxS, W("CorSymReader"), SymReader::NewSymReader}, + { &CLSID_CorSymWriter_SxS, W("CorSymWriter"), SymWriter::NewSymWriter}, + { &CLSID_CorSymBinder_SxS, W("CorSymBinder"), SymBinder::NewSymBinder}, + { NULL, NULL, NULL } +}; + +STDAPI IldbSymbolsGetClassObject(REFCLSID rclsid, REFIID riid, void** ppvObject) +{ + CIldbClassFactory *pClassFactory; // To create class factory object. + const COCLASS_REGISTER *pCoClass; // Loop control. + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + _ASSERTE(IsValidCLSID(rclsid)); + _ASSERTE(IsValidIID(riid)); + _ASSERTE(IsValidWritePtr(ppvObject, void*)); + + if (ppvObject) + { + *ppvObject = NULL; + + // Scan for the right one. + for (pCoClass=g_CoClasses; pCoClass->pClsid; pCoClass++) + { + if (*pCoClass->pClsid == rclsid) + { + // Allocate the new factory object. + pClassFactory = NEW(CIldbClassFactory(pCoClass)); + if (!pClassFactory) + return (E_OUTOFMEMORY); + + // Pick the v-table based on the caller's request. + hr = pClassFactory->QueryInterface(riid, ppvObject); + + // Always release the local reference, if QI failed it will be + // the only one and the object gets freed. + pClassFactory->Release(); + break; + } + } + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +/* ------------------------------------------------------------------------- * + * CIldbClassFactory class + * ------------------------------------------------------------------------- */ + +//***************************************************************************** +// QueryInterface is called to pick a v-table on the co-class. +//***************************************************************************** +HRESULT STDMETHODCALLTYPE CIldbClassFactory::QueryInterface( + REFIID riid, + void **ppvObject) +{ + HRESULT hr; + + if (ppvObject == NULL) + { + return E_INVALIDARG; + } + + // Avoid confusion. + *ppvObject = NULL; + + // Pick the right v-table based on the IID passed in. + if (riid == IID_IUnknown) + *ppvObject = (IUnknown *) this; + else if (riid == IID_IClassFactory) + *ppvObject = (IClassFactory *) this; + + // If successful, add a reference for out pointer and return. + if (*ppvObject) + { + hr = S_OK; + AddRef(); + } + else + hr = E_NOINTERFACE; + return (hr); +} + + +//***************************************************************************** +// CreateInstance is called to create a new instance of the coclass for which +// this class was created in the first place. The returned pointer is the +// v-table matching the IID if there. +//***************************************************************************** +HRESULT STDMETHODCALLTYPE CIldbClassFactory::CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject) +{ + HRESULT hr; + + _ASSERTE(IsValidIID(riid)); + _ASSERTE(IsValidWritePtr(ppvObject, void*)); + + // Avoid confusion. + *ppvObject = NULL; + _ASSERTE(m_pCoClass); + + // Aggregation is not supported by these objects. + if (pUnkOuter) + return (CLASS_E_NOAGGREGATION); + + // Ask the object to create an instance of itself, and check the iid. + hr = (*m_pCoClass->pfnCreateObject)(riid, ppvObject); + return (hr); +} + +// Version of CreateInstance called directly from clients +STDAPI IldbSymbolsCreateInstance(REFCLSID rclsid, REFIID riid, void** ppvIUnknown) +{ + IClassFactory *pClassFactory = NULL; + HRESULT hr = IldbSymbolsGetClassObject(rclsid, IID_IClassFactory, (void**)&pClassFactory); + if (SUCCEEDED(hr)) + hr = pClassFactory->CreateInstance(NULL, riid, ppvIUnknown); + if (pClassFactory) + pClassFactory->Release(); + return hr; +} + +HRESULT STDMETHODCALLTYPE CIldbClassFactory::LockServer( + BOOL fLock) +{ + return (S_OK); +} diff --git a/src/debug/ildbsymlib/ildbsymlib.props b/src/debug/ildbsymlib/ildbsymlib.props new file mode 100644 index 0000000000..2a64f2eb7e --- /dev/null +++ b/src/debug/ildbsymlib/ildbsymlib.props @@ -0,0 +1,29 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--*****************************************************--> + <!--This MSBuild project file was automatically generated--> + <!--from the original SOURCES/DIRS file by the KBC tool.--> + <!--*****************************************************--> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <TargetType>LIBRARY</TargetType> + <OutputPath>$(ClrLibDest)</OutputPath> + <LinkSubsystem>windows</LinkSubsystem> + <UseMsvcrt /> + <ExceptionHandling>$(Sehonly)</ExceptionHandling> + <UserIncludes>$(UserIncludes); + ..\; + ..\..\..\inc; + </UserIncludes> + <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <CppCompile Include="..\symread.cpp" /> + <CppCompile Include="..\symbinder.cpp" /> + <CppCompile Include="..\ildbsymbols.cpp" /> + <CppCompile Include="..\symwrite.cpp" /> + </ItemGroup> + +</Project> diff --git a/src/debug/ildbsymlib/ildbsymlib.vcproj b/src/debug/ildbsymlib/ildbsymlib.vcproj new file mode 100644 index 0000000000..07202121d9 --- /dev/null +++ b/src/debug/ildbsymlib/ildbsymlib.vcproj @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="ildbsymlib" + ProjectGUID="{08C26436-55DD-4332-804C-C963A859E4DE}" + RootNamespace="ildbsymlib" + Keyword="Win32Proj" + TargetFrameworkVersion="131072" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="4" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories=".;..\..\inc" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;-DUNICODE -D_UNICODE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="4" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories=".;..\..\inc" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB;-DUNICODE -D_UNICODE" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath=".\classfactory.h" + > + </File> + <File + RelativePath="..\..\inc\IldbSymLib.h" + > + </File> + <File + RelativePath=".\pch.h" + > + </File> + <File + RelativePath=".\pdbdata.h" + > + </File> + <File + RelativePath=".\symbinder.h" + > + </File> + <File + RelativePath=".\SymRead.h" + > + </File> + <File + RelativePath=".\SymWrite.h" + > + </File> + <File + RelativePath=".\umisc.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\ildbsymbols.cpp" + > + </File> + <File + RelativePath=".\symbinder.cpp" + > + </File> + <File + RelativePath=".\SymRead.cpp" + > + </File> + <File + RelativePath=".\symwrite.cpp" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/src/debug/ildbsymlib/pch.h b/src/debug/ildbsymlib/pch.h new file mode 100644 index 0000000000..ddd4787aa0 --- /dev/null +++ b/src/debug/ildbsymlib/pch.h @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: pch.h +// + +// =========================================================================== + +#ifndef _ILDBSYMLIB_PCH_H_ +#define _ILDBSYMLIB_PCH_H_ + +#include "ole2.h" + +#include "winwrap.h" +#include "umisc.h" + +#include "corhdr.h" +#include "corsym.h" +#include "palclr.h" +#include "cor.h" +#include "genericstackprobe.h" + +// I'm not sure why this code uses these macros for memory management (they should at least be +// in-line functions). DELETE is a symbol defined in WinNt.h as an access-type. We're probably +// not going to try and use that, so we'll just override it for now. +#ifdef DELETE +#undef DELETE +#endif + + +#define NEW( x ) ( ::new (nothrow) x ) +#define DELETE( x ) ( ::delete(x) ) +#define DELETEARRAY( x ) (::delete[] (x)) + +#include "ildbsymlib.h" +#include "symwrite.h" +#include "symread.h" +#include "symbinder.h" + +#endif diff --git a/src/debug/ildbsymlib/pdbdata.h b/src/debug/ildbsymlib/pdbdata.h new file mode 100644 index 0000000000..cf09d782e7 --- /dev/null +++ b/src/debug/ildbsymlib/pdbdata.h @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: pdbdata.h +// + +// =========================================================================== + +#ifndef PDBDATA_H_ +#define PDBDATA_H_ + +#include "umisc.h" +#include "palclr.h" + +struct SymMethodInfo; +struct SymLexicalScope; +struct SymVariable; +struct SymUsingNamespace; +struct SymConstant; +struct SequencePoint; +struct DocumentInfo; +struct MethodInfo; + +extern "C" const GUID ILDB_VERSION_GUID_FSR; +extern "C" const GUID ILDB_VERSION_GUID; + +#define ILDB_MINOR_VERSION_NUMBER 0 +#define ILDB_SIGNATURE "_ildb_signature" +#define ILDB_SIGNATURE_SIZE (16) + +typedef struct PDBInfo { + + // Entry point of the PE + mdMethodDef m_userEntryPoint; + + UINT32 m_CountOfMethods; + UINT32 m_CountOfScopes; + UINT32 m_CountOfVars; + UINT32 m_CountOfUsing; + UINT32 m_CountOfConstants; + UINT32 m_CountOfDocuments; + UINT32 m_CountOfSequencePoints; + UINT32 m_CountOfBytes; + UINT32 m_CountOfStringBytes; + +public: + PDBInfo() + { + memset(this, 0, sizeof(PDBInfo)); + // Make sure m_userEntryPoint initialized correctly + _ASSERTE(mdTokenNil == 0); + } + +#if BIGENDIAN + void ConvertEndianness() { + m_userEntryPoint = VAL32(m_userEntryPoint); + m_CountOfMethods = VAL32(m_CountOfMethods); + m_CountOfScopes = VAL32(m_CountOfScopes); + m_CountOfVars = VAL32(m_CountOfVars); + m_CountOfUsing = VAL32(m_CountOfUsing); + m_CountOfConstants = VAL32(m_CountOfConstants); + m_CountOfSequencePoints = VAL32(m_CountOfSequencePoints); + m_CountOfDocuments = VAL32(m_CountOfDocuments); + m_CountOfBytes = VAL32(m_CountOfBytes); + m_CountOfStringBytes = VAL32(m_CountOfStringBytes); + } +#else + void ConvertEndianness() {} +#endif + +} PDBInfo; + +// The signature, Guid version + PDBInfo data +#define ILDB_HEADER_SIZE (ILDB_SIGNATURE_SIZE + sizeof(GUID) + sizeof(PDBInfo) ) + +typedef struct PDBDataPointers +{ + SymMethodInfo * m_pMethods; // Method information + SymLexicalScope *m_pScopes; // Scopes + SymVariable *m_pVars; // Local Variables + SymUsingNamespace *m_pUsings; // list of using/imports + SymConstant *m_pConstants; // Constants + DocumentInfo *m_pDocuments; // Documents + SequencePoint *m_pSequencePoints; // Sequence Points + // Array of various bytes (variable signature, etc) + BYTE *m_pBytes; + // Strings + BYTE *m_pStringsBytes; +} PDBDataPointers; + +#endif /* PDBDATA_H_ */ diff --git a/src/debug/ildbsymlib/symbinder.cpp b/src/debug/ildbsymlib/symbinder.cpp new file mode 100644 index 0000000000..d3cf1c647b --- /dev/null +++ b/src/debug/ildbsymlib/symbinder.cpp @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: symbinder.cpp +// + +// =========================================================================== + +#include "pch.h" +#include "symbinder.h" + +/* ------------------------------------------------------------------------- * + * SymBinder class + * ------------------------------------------------------------------------- */ + +HRESULT +SymBinder::QueryInterface( + REFIID riid, + void **ppvObject + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(IsValidIID(riid)); + _ASSERTE(IsValidWritePtr(ppvObject, void*)); + + IfFalseGo( ppvObject, E_INVALIDARG ); + + if (riid == IID_ISymUnmanagedBinder) + { + *ppvObject = (ISymUnmanagedBinder*) this; + } + else if (riid == IID_ISymUnmanagedBinder2) + { + *ppvObject = (ISymUnmanagedBinder2*) this; + } + else if (riid == IID_IUnknown) + { + *ppvObject = (IUnknown*)this; + } + else + { + *ppvObject = NULL; + hr = E_NOINTERFACE; + } + + if (*ppvObject) + { + AddRef(); + } + +ErrExit: + + return hr; +} + +HRESULT +SymBinder::NewSymBinder( + REFCLSID clsid, + void** ppObj + ) +{ + HRESULT hr = S_OK; + SymBinder* pSymBinder = NULL; + + _ASSERTE(IsValidCLSID(clsid)); + _ASSERTE(IsValidWritePtr(ppObj, IUnknown*)); + + if (clsid != IID_ISymUnmanagedBinder) + return (E_UNEXPECTED); + + IfFalseGo( ppObj, E_INVALIDARG ); + + *ppObj = NULL; + + IfNullGo( pSymBinder = NEW(SymBinder()) ); + *ppObj = pSymBinder; + pSymBinder->AddRef(); + pSymBinder = NULL; + +ErrExit: + + RELEASE( pSymBinder ); + + return hr; +} + +//----------------------------------------------------------- +// GetReaderForFile +//----------------------------------------------------------- +HRESULT +SymBinder::GetReaderForFile( + IUnknown *importer, // IMetaDataImporter + const WCHAR *fileName, // File we're looking symbols for + const WCHAR *searchPath, // Search path for file + ISymUnmanagedReader **ppRetVal) // Out: SymReader for file +{ + HRESULT hr = S_OK; + ISymUnmanagedReader *pSymReader = NULL; + IfFalseGo( ppRetVal && fileName && fileName[0] != '\0', E_INVALIDARG ); + + // Init Out parameter + *ppRetVal = NULL; + + // Call the class factory directly. + IfFailGo(IldbSymbolsCreateInstance(CLSID_CorSymReader_SxS, + IID_ISymUnmanagedReader, + (void**)&pSymReader)); + + IfFailGo(pSymReader->Initialize(importer, fileName, searchPath, NULL)); + + // Transfer ownership to the out parameter + *ppRetVal = pSymReader; + pSymReader = NULL; + +ErrExit: + RELEASE(pSymReader); + return hr; +} + +HRESULT +SymBinder::GetReaderFromStream( + IUnknown *importer, + IStream *pStream, + ISymUnmanagedReader **ppRetVal + ) +{ + HRESULT hr = S_OK; + ISymUnmanagedReader *pSymReader = NULL; + IfFalseGo( ppRetVal && importer && pStream, E_INVALIDARG ); + + // Init Out parameter + *ppRetVal = NULL; + + // Call the class factory directly + IfFailGo(IldbSymbolsCreateInstance(CLSID_CorSymReader_SxS, + IID_ISymUnmanagedReader, + (void**)&pSymReader)); + + IfFailGo(pSymReader->Initialize(importer, NULL, NULL, pStream)); + + // Transfer ownership to the out parameter + *ppRetVal = pSymReader; + pSymReader = NULL; + +ErrExit: + RELEASE(pSymReader); + return hr; +} + +HRESULT SymBinder::GetReaderForFile2( + IUnknown *importer, + const WCHAR *fileName, + const WCHAR *searchPath, + ULONG32 searchPolicy, + ISymUnmanagedReader **pRetVal) +{ + // This API exists just to allow VS to function properly. + // ILDB doesn't support any search policy or search path - we only look + // next to the image file. + return GetReaderForFile(importer, fileName, searchPath, pRetVal); +} diff --git a/src/debug/ildbsymlib/symbinder.h b/src/debug/ildbsymlib/symbinder.h new file mode 100644 index 0000000000..a0dcf7d4c7 --- /dev/null +++ b/src/debug/ildbsymlib/symbinder.h @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: SymBinder.h +// + +// =========================================================================== + +#ifndef SYMBINDER_H_ +#define SYMBINDER_H_ + +/* ------------------------------------------------------------------------- * + * SymBinder class + * ------------------------------------------------------------------------- */ + +class SymBinder : ISymUnmanagedBinder2 +{ +// ctor/dtor +public: + SymBinder() + { + m_refCount = 0; + } + + virtual ~SymBinder() {} + + static HRESULT NewSymBinder( REFCLSID clsid, void** ppObj ); + +// IUnknown methods +public: + + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + + // ISymUnmanagedBinder +public: + + STDMETHOD(GetReaderForFile)( IUnknown *importer, + const WCHAR *fileName, + const WCHAR *searchPath, + ISymUnmanagedReader **pRetVal); + STDMETHOD(GetReaderFromStream)(IUnknown *importer, + IStream *pstream, + ISymUnmanagedReader **pRetVal); + + // ISymUnmanagedBinder2 + STDMETHOD(GetReaderForFile2)( IUnknown *importer, + const WCHAR *fileName, + const WCHAR *searchPath, + ULONG32 searchPolicy, + ISymUnmanagedReader **pRetVal); + +private: + SIZE_T m_refCount; + +}; +#endif diff --git a/src/debug/ildbsymlib/symread.cpp b/src/debug/ildbsymlib/symread.cpp new file mode 100644 index 0000000000..3fe3c8c9cc --- /dev/null +++ b/src/debug/ildbsymlib/symread.cpp @@ -0,0 +1,2765 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: symread.cpp +// + +// =========================================================================== +#include "pch.h" +#include "symread.h" +#include "corimage.h" + +#define CODE_WITH_NO_SOURCE 0xfeefee +// ------------------------------------------------------------------------- +// SymReader class +// ------------------------------------------------------------------------- + +//----------------------------------------------------------- +// NewSymReader +// Static function used to create a new instance of SymReader +//----------------------------------------------------------- +HRESULT +SymReader::NewSymReader( + REFCLSID clsid, + void** ppObj + ) +{ + HRESULT hr = S_OK; + SymReader* pSymReader = NULL; + + _ASSERTE(IsValidCLSID(clsid)); + _ASSERTE(IsValidWritePtr(ppObj, IUnknown*)); + + if (clsid != IID_ISymUnmanagedReader) + return (E_UNEXPECTED); + + IfFalseGo(ppObj, E_INVALIDARG); + + *ppObj = NULL; + IfNullGo( pSymReader = NEW(SymReader())); + + *ppObj = pSymReader; + pSymReader->AddRef(); + pSymReader = NULL; + +ErrExit: + + RELEASE( pSymReader ); + + return hr; +} + + +//----------------------------------------------------------- +// ~SymReader +//----------------------------------------------------------- +SymReader::~SymReader() +{ + Cleanup(); +} + +//----------------------------------------------------------- +// Cleanup +// Release all memory and clear initialized data structures +// (eg. as a result of a failed Initialization attempt) +//----------------------------------------------------------- +void SymReader::Cleanup() +{ + if (m_pDocs) + { + unsigned i; + for(i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + RELEASE(m_pDocs[i]); + } + } + + DELETE(m_pPDBInfo); + m_pPDBInfo = NULL; + + // If we loaded from stream, then free the memory we allocated + if (m_fInitializeFromStream) + { + DELETEARRAY(m_DataPointers.m_pBytes); + DELETEARRAY(m_DataPointers.m_pConstants); + DELETEARRAY(m_DataPointers.m_pDocuments); + DELETEARRAY(m_DataPointers.m_pMethods); + DELETEARRAY(m_DataPointers.m_pScopes); + DELETEARRAY(m_DataPointers.m_pSequencePoints); + DELETEARRAY(m_DataPointers.m_pStringsBytes); + DELETEARRAY(m_DataPointers.m_pUsings); + DELETEARRAY(m_DataPointers.m_pVars); + } + + DELETEARRAY(m_pDocs); + m_pDocs = NULL; + + RELEASE(m_pImporter); + m_pImporter = NULL; + + memset(&m_DataPointers, 0, sizeof(PDBDataPointers)); + m_szPath[0] = '\0'; +} + +//----------------------------------------------------------- +// ~QueryInterface +//----------------------------------------------------------- +HRESULT +SymReader::QueryInterface( + REFIID riid, + void **ppvObject + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(IsValidIID(riid)); + _ASSERTE(IsValidWritePtr(ppvObject, void*)); + + IfFalseGo(ppvObject, E_INVALIDARG); + if (riid == IID_ISymUnmanagedReader) + { + *ppvObject = (ISymUnmanagedReader*) this; + } + else + if (riid == IID_IUnknown) + { + *ppvObject = (IUnknown*)this; + } + else + { + *ppvObject = NULL; + hr = E_NOINTERFACE; + } + + if (*ppvObject) + { + AddRef(); + } + +ErrExit: + + return hr; +} + +static HRESULT ReadFromStream(IStream *pIStream, void *pv, ULONG cb) +{ + HRESULT hr = NOERROR; + ULONG ulBytesRead; + + IfFailGo(pIStream->Read(pv, cb, &ulBytesRead)); + if (ulBytesRead != cb) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// Initialize +// Pass in the required information to read in the debug info +// If a stream is passed in, it is used, otherwise a filename +// must be passed in +//----------------------------------------------------------- +HRESULT SymReader::Initialize( + IUnknown *importer, // Cash it to be consistent with CLR + const WCHAR* szFileName, // File name of the ildb + const WCHAR* szsearchPath, // Search Path + IStream *pIStream // IStream + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(szFileName || pIStream); + IfFalseGo(szFileName || pIStream, E_INVALIDARG ); + + _ASSERTE(!m_fInitialized); + IfFalseGo(!m_fInitialized, E_UNEXPECTED); + + // If it's passed in, we need to AddRef to be consistent the + // desktop version since ReleaseImporterFromISymUnmanagedReader (ceeload.cpp) + // assumes there's an addref + if (importer) + { + m_pImporter = importer; + m_pImporter->AddRef(); + } + + // See if we're reading from a file or stream + if (pIStream == NULL) + { + // We're initializing from a file + m_fInitializeFromStream = false; + IfFailGo(InitializeFromFile(szFileName, szsearchPath)); + } + else + { + // We're reading in from a stream + m_fInitializeFromStream = true; + IfFailGo(InitializeFromStream(pIStream)); + } + + // Note that up to this point, the data we've read in has not been validated. Since we don't trust + // our input, it's important that we don't proceed with using this data until validation has been + // successful. + IfFailGo(ValidateData()); + + +ErrExit: + // If we have not succeeded, then we need to clean up our data structures. This would allow a client to call + // Initialize again, but also ensures we can't possibly use partial or otherwise invalid (possibly + // malicious) data. + if (FAILED(hr)) + { + Cleanup(); + } + else + { + // Otherwise we are not properly initialized + m_fInitialized = true; + } + + return hr; +} + +//----------------------------------------------------------- +// Initialize the data structures by reading from the supplied stream +// Note that upon completion the data has not yet been validated for safety. +//----------------------------------------------------------- +HRESULT SymReader::InitializeFromStream( + IStream *pIStream // IStream + ) +{ + GUID GuidVersion; + BYTE *pSignature; + HRESULT hr = S_OK; + + // Reset the stream to the begining + LARGE_INTEGER li; + li.u.HighPart = 0; + li.u.LowPart = 0; + + // Make sure we're at the beginning of the stream + IfFailGo(pIStream->Seek(li, STREAM_SEEK_SET, NULL)); + + IfNullGo(pSignature = (BYTE *)_alloca(ILDB_SIGNATURE_SIZE)); + IfFailGo(ReadFromStream(pIStream, pSignature, ILDB_SIGNATURE_SIZE)); + + // Verify that we're looking at an ILDB File + if (memcmp(pSignature, ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)) + { + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + } + + IfFailGo(ReadFromStream(pIStream, &GuidVersion, sizeof(GUID))); + + SwapGuid(&GuidVersion); + + if (memcmp(&GuidVersion, &ILDB_VERSION_GUID, sizeof(GUID))) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + IfNullGo(m_pPDBInfo = NEW(PDBInfo)); + + memset(m_pPDBInfo, 0 , sizeof(PDBInfo)); + IfFailGo(ReadFromStream(pIStream, m_pPDBInfo, sizeof(PDBInfo))); + + // Swap the counts + m_pPDBInfo->ConvertEndianness(); + + if (m_pPDBInfo->m_CountOfConstants) + { + IfNullGo(m_DataPointers.m_pConstants = NEW(SymConstant[m_pPDBInfo->m_CountOfConstants])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pConstants, m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant))); + } + + if (m_pPDBInfo->m_CountOfMethods) + { + IfNullGo(m_DataPointers.m_pMethods = NEW(SymMethodInfo[m_pPDBInfo->m_CountOfMethods])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pMethods, m_pPDBInfo->m_CountOfMethods*sizeof(SymMethodInfo))); + } + + if (m_pPDBInfo->m_CountOfScopes) + { + IfNullGo(m_DataPointers.m_pScopes = NEW(SymLexicalScope[m_pPDBInfo->m_CountOfScopes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pScopes, m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope))); + } + + if (m_pPDBInfo->m_CountOfVars) + { + IfNullGo(m_DataPointers.m_pVars = NEW(SymVariable[m_pPDBInfo->m_CountOfVars])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pVars, m_pPDBInfo->m_CountOfVars*sizeof(SymVariable))); + } + + if (m_pPDBInfo->m_CountOfUsing) + { + IfNullGo(m_DataPointers.m_pUsings = NEW(SymUsingNamespace[m_pPDBInfo->m_CountOfUsing])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pUsings, m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace))); + } + + if (m_pPDBInfo->m_CountOfSequencePoints) + { + IfNullGo(m_DataPointers.m_pSequencePoints = NEW(SequencePoint[m_pPDBInfo->m_CountOfSequencePoints])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pSequencePoints, m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint))); + } + + if (m_pPDBInfo->m_CountOfDocuments) + { + IfNullGo(m_DataPointers.m_pDocuments = NEW(DocumentInfo[m_pPDBInfo->m_CountOfDocuments])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pDocuments, m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo))); + } + + if (m_pPDBInfo->m_CountOfBytes) + { + IfNullGo(m_DataPointers.m_pBytes = NEW(BYTE[m_pPDBInfo->m_CountOfBytes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pBytes, m_pPDBInfo->m_CountOfBytes*sizeof(BYTE))); + } + + + if (m_pPDBInfo->m_CountOfStringBytes) + { + IfNullGo(m_DataPointers.m_pStringsBytes = NEW(BYTE[m_pPDBInfo->m_CountOfStringBytes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pStringsBytes, m_pPDBInfo->m_CountOfStringBytes)); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// ValidateData +// Checks the contents of everything in m_DataPointers (i.e. all the structures read from the file) +// to make sure it is valid. Specifically, validates that all indexes are within bounds for the +// sizes allocated. +//----------------------------------------------------------- +HRESULT SymReader::ValidateData() +{ + HRESULT hr = S_OK; + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfConstants; i++) + { + SymConstant & c = m_DataPointers.m_pConstants[i]; + IfFalseGo(c.ParentScope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(c.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFailGo(ValidateBytes(c.Signature(), c.SignatureSize())); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfMethods; i++) + { + // Note that start/end values may equal the count (i.e. point one past the end) because + // the end is the extent, and start can equal end to indicate no entries. + SymMethodInfo & m = m_DataPointers.m_pMethods[i]; + IfFalseGo(m.StartScopes() <= m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndScopes() <= m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartScopes() <= m.EndScopes(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartVars() <= m_pPDBInfo->m_CountOfVars, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndVars() <= m_pPDBInfo->m_CountOfVars, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartVars() <= m.EndVars(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartUsing() <= m_pPDBInfo->m_CountOfUsing, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndUsing() <= m_pPDBInfo->m_CountOfUsing, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartUsing() <= m.EndUsing(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartConstant() <= m_pPDBInfo->m_CountOfConstants, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndConstant() <= m_pPDBInfo->m_CountOfConstants, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartConstant() <= m.EndConstant(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartDocuments() <= m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndDocuments() <= m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartDocuments() <= m.EndDocuments(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartSequencePoints() <= m_pPDBInfo->m_CountOfSequencePoints, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndSequencePoints() <= m_pPDBInfo->m_CountOfSequencePoints, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartSequencePoints() <= m.EndSequencePoints(), HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfScopes; i++) + { + SymLexicalScope & s = m_DataPointers.m_pScopes[i]; + IfFalseGo((s.ParentScope() == (UINT32)-1) || (s.ParentScope() < m_pPDBInfo->m_CountOfScopes), HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfVars; i++) + { + SymVariable & v = m_DataPointers.m_pVars[i]; + IfFalseGo(v.Scope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(v.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFailGo(ValidateBytes(v.Signature(), v.SignatureSize())); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfUsing; i++) + { + SymUsingNamespace & u = m_DataPointers.m_pUsings[i]; + IfFalseGo(u.ParentScope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(u.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfSequencePoints; i++) + { + SequencePoint & s = m_DataPointers.m_pSequencePoints[i]; + IfFalseGo(s.Document() < m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + DocumentInfo & d = m_DataPointers.m_pDocuments[i]; + IfFailGo(ValidateBytes(d.CheckSumEntry(), d.CheckSumSize())); + IfFailGo(ValidateBytes(d.SourceEntry(), d.SourceSize())); + IfFalseGo(d.UrlEntry() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + } + + // Nothing to validate for the m_pBytes array - each reference must validate above that it's + // length doesn't exceed the array + + // We expect all strings to be null terminated. To ensure no string operation overruns the buffer + // it sufficies to check that the buffer ends in a null character + if (m_pPDBInfo->m_CountOfStringBytes > 0) + { + IfFalseGo(m_DataPointers.m_pStringsBytes[m_pPDBInfo->m_CountOfStringBytes-1] == '\0', HrFromWin32(ERROR_BAD_FORMAT)); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// Validate a reference to the bytes array +//----------------------------------------------------------- +HRESULT SymReader::ValidateBytes(UINT32 bytesIndex, UINT32 bytesLen) +{ + S_UINT32 extent = S_UINT32(bytesIndex) + S_UINT32(bytesLen); + if (!extent.IsOverflow() && + (extent.Value() <= m_pPDBInfo->m_CountOfBytes)) + { + return S_OK; + } + + return HrFromWin32(ERROR_BAD_FORMAT); +} + +//----------------------------------------------------------- +// VerifyPEDebugInfo +// Verify that the debug info in the PE is the one we want +//----------------------------------------------------------- +HRESULT SymReader::VerifyPEDebugInfo(const WCHAR* szFileName) +{ + HRESULT hr = E_FAIL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapFile = INVALID_HANDLE_VALUE; + BYTE *pMod = NULL; + DWORD dwFileSize; + IMAGE_DEBUG_DIRECTORY *pDebugDir; + RSDSI *pDebugInfo; + DWORD dwUtf8Length; + DWORD dwUnicodeLength; + + // We need to change the .pdb extension to .ildb + // compatible with VS7 + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + + IMAGE_NT_HEADERS*pNT; + + hFile = WszCreateFile(szFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + // Get the last error if we can + return HrFromWin32(GetLastError()); + } + + dwFileSize = GetFileSize(hFile, NULL); + if (dwFileSize < ILDB_HEADER_SIZE) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + hMapFile = WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapFile == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + pMod = (BYTE *) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + if (pMod == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + pNT = Cor_RtlImageNtHeader(pMod, dwFileSize); + + // If there is no DebugEntry, then just error out + if (VAL32(pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) == 0) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + + // NOTE: This code is not secure against malformed PE files - any of the pointer additions below + // may be outside the range of memory mapped for the file. If we ever want to use this code + // on untrusted PE files, we should properly validate everything (probably by using PEDecoder). + + DWORD offset; + offset = Cor_RtlImageRvaToOffset(pNT, VAL32(pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress), dwFileSize); + if (offset == NULL) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + pDebugDir = (IMAGE_DEBUG_DIRECTORY *)(pMod + offset); + pDebugInfo = (RSDSI *)(pMod + VAL32(pDebugDir->PointerToRawData)); + + if (pDebugInfo->dwSig != VAL32(0x53445352)) // "SDSR" + { + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + } + + + // Try the returned Stored Name since it might be a fully qualified path + dwUtf8Length = VAL32(pDebugDir->SizeOfData) - sizeof(RSDSI); + dwUnicodeLength = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) pDebugInfo->szPDB, dwUtf8Length, fullpath, COUNTOF(fullpath) - 1); + + // Make sure it's NULL terminated + _ASSERTE(dwUnicodeLength < COUNTOF(fullpath)); + fullpath[dwUnicodeLength]='\0'; + + // Replace the extension with ildb + if (_wsplitpath_s( fullpath, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s(m_szStoredSymbolName, MAX_LONGPATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + + // It looks valid, make sure to set the return code + hr = S_OK; +ErrExit: + if (pMod) + UnmapViewOfFile(pMod); + if (hMapFile != INVALID_HANDLE_VALUE) + CloseHandle(hMapFile); + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + return hr; + +} + +//----------------------------------------------------------- +// InitializeFromFile +// Initialize the reader using the passed in file name +// Note that upon completion the data hasn't yet been validated for safety. +//----------------------------------------------------------- +HRESULT +SymReader::InitializeFromFile( + const WCHAR* szFileName, + const WCHAR* szsearchPath) +{ + HRESULT hr = S_OK; + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapFile = INVALID_HANDLE_VALUE; + HMODULE hMod = NULL; + BYTE *CurrentOffset; + DWORD dwFileSize; + S_UINT32 dwDataSize; + GUID VersionInfo; + + _ASSERTE(szFileName); + IfFalseGo(szFileName, E_INVALIDARG ); + + IfFailGo(VerifyPEDebugInfo(szFileName)); + // We need to open the exe and check to see if the DebugInfo matches + + if (_wsplitpath_s( szFileName, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s( fullpath, _MAX_PATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + IfFailGo(HrFromWin32(ERROR_INSUFFICIENT_BUFFER)); + + hFile = WszCreateFile(m_szPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + + // If the stored string is empty, don't do anything + if (m_szStoredSymbolName[0] == '\0') + { + return HrFromWin32(GetLastError()); + } + + if (_wsplitpath_s( m_szStoredSymbolName, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s( fullpath, _MAX_PATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + IfFailGo(HrFromWin32(ERROR_INSUFFICIENT_BUFFER)); + + hFile = WszCreateFile(m_szPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + return HrFromWin32(GetLastError()); + } + } + + dwFileSize = GetFileSize(hFile, NULL); + if (dwFileSize < ILDB_HEADER_SIZE) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + hMapFile = WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapFile == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + hMod = (HMODULE) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + if (hMod == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + // We've opened the file, now let's get the pertinent info + CurrentOffset = (BYTE *)hMod; + + // Verify that we're looking at an ILDB File + if (memcmp(CurrentOffset, ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)) + { + IfFailGo(E_FAIL); + } + CurrentOffset += ILDB_SIGNATURE_SIZE; + + memcpy( &VersionInfo, CurrentOffset, sizeof(GUID)); + SwapGuid( &VersionInfo ); + CurrentOffset += sizeof(GUID); + + if (memcmp(&VersionInfo, &ILDB_VERSION_GUID, sizeof(GUID))) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + IfNullGo(m_pPDBInfo = NEW(PDBInfo)); + + memcpy(m_pPDBInfo, CurrentOffset, sizeof(PDBInfo)); + + // Swap the counts + m_pPDBInfo->ConvertEndianness(); + + // Check to make sure we have enough data to be read in. + dwDataSize = S_UINT32(ILDB_HEADER_SIZE); + dwDataSize += m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant); + dwDataSize += m_pPDBInfo->m_CountOfMethods * sizeof(SymMethodInfo); + dwDataSize += m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope); + dwDataSize += m_pPDBInfo->m_CountOfVars*sizeof(SymVariable); + dwDataSize += m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace); + dwDataSize += m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint); + dwDataSize += m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo); + dwDataSize += m_pPDBInfo->m_CountOfBytes*sizeof(BYTE); + dwDataSize += m_pPDBInfo->m_CountOfStringBytes*sizeof(BYTE); + + if (dwDataSize.IsOverflow() || dwDataSize.Value() > dwFileSize) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + CurrentOffset += sizeof(PDBInfo); + + if (m_pPDBInfo->m_CountOfConstants) + { + m_DataPointers.m_pConstants = (SymConstant*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant)); + } + + if (m_pPDBInfo->m_CountOfMethods) + { + m_DataPointers.m_pMethods = (SymMethodInfo *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfMethods*sizeof(SymMethodInfo)); + } + + if (m_pPDBInfo->m_CountOfScopes) + { + m_DataPointers.m_pScopes = (SymLexicalScope *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope)); + } + + if (m_pPDBInfo->m_CountOfVars) + { + m_DataPointers.m_pVars = (SymVariable *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfVars*sizeof(SymVariable)); + } + + if (m_pPDBInfo->m_CountOfUsing) + { + m_DataPointers.m_pUsings = (SymUsingNamespace *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace)); + } + + if (m_pPDBInfo->m_CountOfSequencePoints) + { + m_DataPointers.m_pSequencePoints = (SequencePoint*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint)); + } + + if (m_pPDBInfo->m_CountOfDocuments) + { + m_DataPointers.m_pDocuments = (DocumentInfo*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo)); + } + + if (m_pPDBInfo->m_CountOfBytes) + { + m_DataPointers.m_pBytes = (BYTE*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfBytes*sizeof(BYTE)); + } + + if (m_pPDBInfo->m_CountOfStringBytes) + { + m_DataPointers.m_pStringsBytes = (BYTE*)CurrentOffset; + } + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetDocument +// Get the document for the passed in information +//----------------------------------------------------------- +HRESULT +SymReader::GetDocument( + __in LPWSTR wcsUrl, // URL of the document + GUID language, // Language for the file + GUID languageVendor, // Language vendor + GUID documentType, // Type of document + ISymUnmanagedDocument **ppRetVal // [out] Document + ) +{ + HRESULT hr = S_OK; + unsigned i; + SymDocument* pDoc = NULL; + WCHAR *wcsDocumentUrl = NULL; + WCHAR *wcsDocumentUrlAlloc = NULL; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppRetVal && wcsUrl); + IfFalseGo(ppRetVal, E_INVALIDARG); + IfFalseGo(wcsUrl, E_INVALIDARG); + + // Init Out Parameter + *ppRetVal = NULL; + + for (i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + int cchName; + + // Convert the UTF8 string to Wide + cchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_DataPointers.m_pStringsBytes[m_DataPointers.m_pDocuments[i].UrlEntry()]), + -1, + 0, + NULL); + IfNullGo( wcsDocumentUrlAlloc = NEW(WCHAR[cchName]) ); + + cchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_DataPointers.m_pStringsBytes[m_DataPointers.m_pDocuments[i].UrlEntry()]), + -1, + wcsDocumentUrlAlloc, + cchName); + wcsDocumentUrl = wcsDocumentUrlAlloc; + + // Compare the url + if (wcscmp(wcsUrl, wcsDocumentUrl) == 0) + { + IfFailGo(GetDocument(i, &pDoc)); + break; + } + DELETEARRAY(wcsDocumentUrlAlloc); + wcsDocumentUrlAlloc = NULL; + } + + if (pDoc) + { + IfFailGo( pDoc->QueryInterface( IID_ISymUnmanagedDocument, + (void**) ppRetVal ) ); + } + +ErrExit: + DELETEARRAY(wcsDocumentUrlAlloc); + + RELEASE( pDoc ); + + return hr; +} + +//----------------------------------------------------------- +// GetDocuments +// Get the documents for this reader +//----------------------------------------------------------- +HRESULT +SymReader::GetDocuments( + ULONG32 cDocs, + ULONG32 *pcDocs, + ISymUnmanagedDocument *pDocs[] + ) +{ + HRESULT hr = S_OK; + unsigned cDocCount = 0; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pDocs || pcDocs); + IfFalseGo(pDocs || pcDocs, E_INVALIDARG); + + cDocs = min(cDocs, m_pPDBInfo->m_CountOfDocuments); + + for (cDocCount = 0; cDocCount < cDocs; cDocCount++) + { + if (pDocs) + { + SymDocument *pDoc; + IfFailGo(GetDocument(cDocCount, &pDoc)); + pDocs[cDocCount] = pDoc; + } + } + if (pcDocs) + { + *pcDocs = (ULONG32)m_pPDBInfo->m_CountOfDocuments; + } + +ErrExit: + if (FAILED(hr)) + { + unsigned i; + for (i = 0; i < cDocCount; i++) + { + RELEASE(pDocs[cDocCount]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetUserEntryPoint +// Get the entry point for the pe +//----------------------------------------------------------- +HRESULT +SymReader::GetUserEntryPoint( + mdMethodDef *pRetVal + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // If it wasn't set then return E_FAIL + if (m_pPDBInfo->m_userEntryPoint == mdTokenNil) + { + hr = E_FAIL; + } + else + { + *pRetVal = m_pPDBInfo->m_userEntryPoint; + } +ErrExit: + return hr; +} + +// Compare the method token with the SymMethodInfo Entry and return the +// value expected by bsearch +int __cdecl CompareMethodToToken(const void *pMethodToken, const void *pMethodInfoEntry) +{ + mdMethodDef MethodDef = *(mdMethodDef *)pMethodToken; + SymMethodInfo *pMethodInfo = (SymMethodInfo *)pMethodInfoEntry; + + return MethodDef - pMethodInfo->MethodToken(); +} + +//----------------------------------------------------------- +// GetMethod +// Get the method for the methoddef +//----------------------------------------------------------- +HRESULT +SymReader::GetMethod( + mdMethodDef method, // MethodDef + ISymUnmanagedMethod **ppRetVal // [out] Method + ) +{ + HRESULT hr = S_OK; + UINT32 MethodEntry = 0; + SymMethodInfo *pMethodInfo; + SymMethod * pMethod = NULL; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + pMethodInfo = (SymMethodInfo *)bsearch(&method, m_DataPointers.m_pMethods, m_pPDBInfo->m_CountOfMethods, sizeof(SymMethodInfo), CompareMethodToToken); + IfFalseGo(pMethodInfo, E_FAIL); // no matching method found + + // Found a match + MethodEntry = UINT32 (pMethodInfo - m_DataPointers.m_pMethods); + _ASSERTE(m_DataPointers.m_pMethods[MethodEntry].MethodToken() == method); + IfNullGo( pMethod = NEW(SymMethod(this, &m_DataPointers, MethodEntry)) ); + *ppRetVal = pMethod; + pMethod->AddRef(); + hr = S_OK; + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetMethodByVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodByVersion( + mdMethodDef method, + int version, + ISymUnmanagedMethod **ppRetVal + ) +{ + // Don't support multiple version of the same Method so just + // call GetMethod + return GetMethod(method, ppRetVal); +} + + +//----------------------------------------------------------- +// GetMethodFromDocumentPosition +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodFromDocumentPosition( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ISymUnmanagedMethod **ppRetVal +) +{ + HRESULT hr = S_OK; + UINT32 DocumentEntry; + UINT32 Method; + UINT32 point; + SequencePoint *pSequencePointBefore; + SequencePoint *pSequencePointAfter; + bool found = false; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(document && ppRetVal); + IfFalseGo(document, E_INVALIDARG); + IfFalseGo(ppRetVal, E_INVALIDARG); + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Init out parameter + *ppRetVal = NULL; + + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + + // This function returns the first match if more than one methods cover the specified position. + for (Method = 0; Method < m_pPDBInfo->m_CountOfMethods; Method++) + { + pSequencePointBefore = NULL; + pSequencePointAfter = NULL; + + // Walk the sequence points + for (point = m_DataPointers.m_pMethods[Method].StartSequencePoints(); + point < m_DataPointers.m_pMethods[Method].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_DataPointers.m_pSequencePoints[point].Document() == DocumentEntry) + { + // If the point is position is within the sequence point then + // we're done. + if (m_DataPointers.m_pSequencePoints[point].IsWithin(line, column)) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), ppRetVal)); + found = true; + break; + } + + // If the sequence is before the point then just remember the point + if (m_DataPointers.m_pSequencePoints[point].IsUserLine() && + m_DataPointers.m_pSequencePoints[point].IsLessThan(line, column)) + { + pSequencePointBefore = &m_DataPointers.m_pSequencePoints[point]; + } + + // If the sequence is before the point then just remember the point + if (m_DataPointers.m_pSequencePoints[point].IsUserLine() && + m_DataPointers.m_pSequencePoints[point].IsGreaterThan(line, column)) + { + pSequencePointAfter = &m_DataPointers.m_pSequencePoints[point]; + } + } + } + + // If we found an exact match, we're done. + if (found) + { + break; + } + + // If we found sequence points within the method before and after + // the line/column then we may have found the method. Record the return value, but keep looking + // to see if we find an exact match. This may not actually be the right method. Iron Python, for instance, + // issues a "method" containing sequence points for all the method headers in a class. Sequence points + // in this "method" would then span the sequence points in the bodies of all but the last method. + if (pSequencePointBefore && pSequencePointAfter) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), ppRetVal)); + } + } + + // This function returns E_FAIL if no match is found. + // Note that this is different from the behaviour of GetMethodsFromDocumentPosition() (see below). + if (*ppRetVal == NULL) + { + hr = E_FAIL; + } + +ErrExit: + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Return all methods with sequence points covering the specified source location. This +// is actually not as straighforward as it sounds, since we need to mimic the behaviour of +// diasymreader and PDBs here. For PDBs, diasymreader actually does two passes over the +// sequence points. It tries to find an exact match in the first pass, and if that fails, +// it'll do a second pass looking for an approximate match. An approximate match is a sequence +// point which doesn't start on the specified line but covers it. If there's an exact match, +// then it ignores all the approximate matches. In both cases, diasymreader only checks the +// start line number of the sequence point and it ignores the column number. +// +// For backward compatibility, I'm leaving GetMethodFromDocumentPosition() unchanged. +// + +HRESULT +SymReader::GetMethodsFromDocumentPosition( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 cMethod, + ULONG32* pcMethod, //[Optional]: How many method actually returned + ISymUnmanagedMethod** ppRetVal + ) +{ + HRESULT hr = S_OK; + UINT32 DocumentEntry; + UINT32 Method; + UINT32 point; + + UINT CurMethod = 0; + bool found = false; + bool fExactMatch = true; + + ULONG32 maxPreLine = 0; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(document); + IfFalseGo(document, E_INVALIDARG); + + _ASSERTE((cMethod == 0) || (ppRetVal != NULL)); + IfFalseGo(cMethod == 0 || ppRetVal != NULL, E_INVALIDARG); + + // Initialize the out parameter if it has been provided. + if (pcMethod != NULL) + { + *pcMethod = 0; + } + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Enumerate the sequence points in two passes. + while (true) + { + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + + for (Method = 0; Method < m_pPDBInfo->m_CountOfMethods; Method++) + { + found = false; + + // Walk the sequence points + for (point = m_DataPointers.m_pMethods[Method].StartSequencePoints(); + point < m_DataPointers.m_pMethods[Method].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_DataPointers.m_pSequencePoints[point].Document() == DocumentEntry) + { + // PDBs (more specifically the DIA APIs) only check the start line number and not the end line number when + // trying to determine whether a sequence point covers the specified line number. We need to match this + // behaviour here. For backward compatibility reasons, GetMethodFromDocumentPosition() is still checking + // against the entire range of a sequence point, but we should revisit that in the next release. + ULONG32 curLine = m_DataPointers.m_pSequencePoints[point].StartLine(); + + if (fExactMatch) + { + if (curLine == line) + { + found = true; + } + else if ((maxPreLine < curLine) && (curLine < line)) + { + // This is not an exact match, but let's keep track of the sequence point closest to the specified + // line. We'll use this info if we have to do a second pass. + maxPreLine = curLine; + } + } + else + { + // We are in the second pass, looking for approximate matches. + if ((maxPreLine != 0) && (maxPreLine == curLine)) + { + // Make sure the sequence point covers the specified line. + if (m_DataPointers.m_pSequencePoints[point].IsWithinLineOnly(line)) + { + found = true; + } + } + } + + // If we have found a match (whether it's exact or approximate), then save this method unless the caller is + // only interested in the number of matching methods or the array provided by the caller isn't big enough. + if (found) + { + if (CurMethod < cMethod) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), &(ppRetVal[CurMethod]))); + } + CurMethod++; + break; + } + } + } + + if (found) + { + // If we have already filled out the entire array provided by the caller, then we are done. + if ((cMethod > 0) && (cMethod == CurMethod)) + { + break; + } + else + { + // Otherwise move on to the next method. + continue; + } + } + } + + // If we haven't found an exact match, then try it again looking for a sequence point covering the specified line. + if (fExactMatch && (CurMethod == 0)) + { + fExactMatch = false; + continue; + } + else + { + // If we have found an exact match, or if we have done two passes already, then bail. + break; + } + } + + // Note that unlike GetMethodFromDocumentPosition(), this function returns S_OK even if a match is not found. + if (SUCCEEDED(hr)) + { + if (pcMethod != NULL) + { + *pcMethod = CurMethod; + } + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetSymbolStoreFileName +//----------------------------------------------------------- +HRESULT +SymReader::GetSymbolStoreFileName( + ULONG32 cchName, // Length of szName + ULONG32 *pcchName, // [Optional] + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [Optional] + ) +{ + _ASSERTE(m_fInitialized); + if (!m_fInitialized) + return E_UNEXPECTED; + + if (pcchName) + { + *pcchName = (ULONG32)(wcslen(m_szPath)+1); + } + + if( szName ) + { + if (wcsncpy_s( szName, cchName, m_szPath, _TRUNCATE) == STRUNCATE) + return HrFromWin32(ERROR_INSUFFICIENT_BUFFER); + } + + return NOERROR; +} + +//----------------------------------------------------------- +// GetMethodVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodVersion( + ISymUnmanagedMethod * pMethod, + int* pVersion + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pMethod && pVersion); + IfFalseGo( pMethod && pVersion, E_INVALIDARG); + // This symbol store only supports one version of a method + *pVersion = 0; +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetDocumentVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetDocumentVersion( + ISymUnmanagedDocument* pDoc, + int* pVersion, + BOOL* pbCurrent // [Optional] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pVersion && pDoc); + IfFalseGo(pVersion, E_INVALIDARG); + IfFalseGo(pDoc, E_INVALIDARG); + + // This symbol store only supports one version of a document + *pVersion = 0; + if (pbCurrent) + { + *pbCurrent = TRUE; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetDocument +// Return the document for the given entry +//----------------------------------------------------------- +HRESULT SymReader::GetDocument( + UINT32 DocumentEntry, + SymDocument **ppDocument) +{ + HRESULT hr = NOERROR; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppDocument); + IfFalseGo(ppDocument, E_INVALIDARG); + + _ASSERTE(DocumentEntry < m_pPDBInfo->m_CountOfDocuments); + IfFalseGo(DocumentEntry < m_pPDBInfo->m_CountOfDocuments, E_INVALIDARG); + + if (m_pDocs == NULL) + { + IfNullGo(m_pDocs = NEW(SymDocument *[m_pPDBInfo->m_CountOfDocuments])); + memset(m_pDocs, 0, m_pPDBInfo->m_CountOfDocuments * sizeof(void *)); + } + + if (m_pDocs[DocumentEntry] == NULL) + { + m_pDocs[DocumentEntry] = NEW(SymDocument(this, &m_DataPointers, m_pPDBInfo->m_CountOfMethods, DocumentEntry)); + IfNullGo(m_pDocs[DocumentEntry]); + // AddRef the table version + m_pDocs[DocumentEntry]->AddRef(); + + } + + //Set and AddRef the Out Parameter + *ppDocument = m_pDocs[DocumentEntry]; + (*ppDocument)->AddRef(); + +ErrExit: + return hr; +} + +HRESULT +SymReader::UpdateSymbolStore( + const WCHAR *filename, + IStream *pIStream + ) +{ + // This symbol store doesn't support updating the symbol store. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +HRESULT +SymReader::ReplaceSymbolStore( + const WCHAR *filename, + IStream *pIStream + ) +{ + // This symbol store doesn't support updating the symbol store. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetVariables +//----------------------------------------------------------- +HRESULT +SymReader::GetVariables( + mdToken parent, + ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[] + ) +{ + // + // This symbol reader doesn't support non-local variables. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetGlobalVariables +//----------------------------------------------------------- +HRESULT +SymReader::GetGlobalVariables( + ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[] + ) +{ + // + // This symbol reader doesn't support non-local variables. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetSymAttribute +//----------------------------------------------------------- +HRESULT +SymReader::GetSymAttribute( + mdToken parent, + __in LPWSTR name, + ULONG32 cBuffer, + ULONG32 *pcBuffer, + __out_bcount_part_opt(cBuffer, *pcBuffer) BYTE buffer[] + ) +{ + // This symbol store doesn't support attributes + // VS may query for certain attributes, but will survive without them, + // so don't ASSERT here. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetNamespaces +//----------------------------------------------------------- +HRESULT +SymReader::GetNamespaces( + ULONG32 cNameSpaces, + ULONG32 *pcNameSpaces, + ISymUnmanagedNamespace *namespaces[] + ) +{ + // This symbol store doesn't support this + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymDocument class + * ------------------------------------------------------------------------- */ + +HRESULT +SymDocument::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedDocument) + *ppInterface = (ISymUnmanagedDocument*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedDocument*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + + +//----------------------------------------------------------- +// GetURL +//----------------------------------------------------------- +HRESULT +SymDocument::GetURL( + ULONG32 cchUrl, // The allocated size of the buffer + ULONG32 *pcchUrl, // [optional,out] The number of characters available for return + __out_ecount_part_opt(cchUrl, *pcchUrl) WCHAR szUrl[] // [optional,out] The string buffer. + ) +{ + if (pcchUrl) + { + // Convert the UTF8 string to Wide + *pcchUrl = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pDocuments[m_DocumentEntry].UrlEntry()]), + -1, + 0, + NULL); + } + + if( szUrl ) + { + // Convert the UTF8 string to Wide + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pDocuments[m_DocumentEntry].UrlEntry()]), + -1, + szUrl, + cchUrl); + } + return NOERROR; +} + +//----------------------------------------------------------- +// GetDocumentType +//----------------------------------------------------------- +HRESULT +SymDocument::GetDocumentType( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].DocumentType(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLanguage +//----------------------------------------------------------- +HRESULT +SymDocument::GetLanguage( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].Language(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLanguageVendor +//----------------------------------------------------------- +HRESULT +SymDocument::GetLanguageVendor( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].LanguageVendor(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetCheckSumAlgorithmId +//----------------------------------------------------------- +HRESULT +SymDocument::GetCheckSumAlgorithmId( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].AlgorithmId(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetCheckSum +//----------------------------------------------------------- +HRESULT +SymDocument::GetCheckSum( + ULONG32 cData, // The allocated size of the buffer. + ULONG32 *pcData, // [optional] The number of bytes available for return + BYTE data[]) // [optional] The buffer to receive the checksum. +{ + BYTE *pCheckSum = &m_pData->m_pBytes[m_pData->m_pDocuments[m_DocumentEntry].CheckSumEntry()]; + ULONG32 CheckSumSize = m_pData->m_pDocuments[m_DocumentEntry].CheckSumSize(); + if (pcData) + { + *pcData = CheckSumSize; + } + if(data) + { + memcpy(data, pCheckSum, min(CheckSumSize, cData)); + } + return NOERROR; +} + +//----------------------------------------------------------- +// FindClosestLine +// Search the sequence points looking a line that is closest +// line following this one that is a sequence point +//----------------------------------------------------------- +HRESULT +SymDocument::FindClosestLine( + ULONG32 line, + ULONG32 *pRetVal + ) +{ + HRESULT hr = NOERROR; + UINT32 Method; + UINT32 point; + ULONG32 closestLine = 0; // GCC can't tell this isn't used before initialization + bool found = false; + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + for (Method = 0; Method < m_CountOfMethods; Method++) + { + // Walk the sequence points + for (point = m_pData->m_pMethods[Method].StartSequencePoints(); + point < m_pData->m_pMethods[Method].EndSequencePoints(); + point++) + { + SequencePoint & sp = m_pData->m_pSequencePoints[point]; + // Check to see if this sequence point is in this doc, and is at or + // after the requested line + if ((sp.Document() == m_DocumentEntry) && sp.IsUserLine()) + { + if (sp.IsWithin(line, 0) || sp.IsGreaterThan(line, 0)) + { + // This sequence point is at or after the requested line. If we haven't + // already found a "closest", or this is even closer than the one we have, + // then mark this as the best line so far. + if (!found || m_pData->m_pSequencePoints[point].StartLine() < closestLine) + { + found = true; + closestLine = m_pData->m_pSequencePoints[point].StartLine(); + } + } + } + } + } + + if (found) + { + *pRetVal = closestLine; + } + else + { + // Didn't find any lines at or after the one requested. + hr = E_FAIL; + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymDocument HasEmbeddedSource +//----------------------------------------------------------- +HRESULT +SymDocument::HasEmbeddedSource( + BOOL *pRetVal + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymDocument GetSourceLength +//----------------------------------------------------------- +HRESULT +SymDocument::GetSourceLength( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymDocument GetSourceRange +//----------------------------------------------------------- +HRESULT +SymDocument::GetSourceRange( + ULONG32 startLine, + ULONG32 startColumn, + ULONG32 endLine, + ULONG32 endColumn, + ULONG32 cSourceBytes, + ULONG32 *pcSourceBytes, + BYTE source[] + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymMethod class + * ------------------------------------------------------------------------- */ +HRESULT +SymMethod::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedMethod) + *ppInterface = (ISymUnmanagedMethod*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedMethod*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetToken +//----------------------------------------------------------- +HRESULT +SymMethod::GetToken( + mdMethodDef *pRetVal +) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pMethods[m_MethodEntry].MethodToken(); +ErrExit: + return hr; +} + + +//----------------------------------------------------------- +// GetSequencePointCount +//----------------------------------------------------------- +HRESULT +SymMethod::GetSequencePointCount( + ULONG32* pRetVal + ) +{ + + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + *pRetVal = (ULONG32)(m_pData->m_pMethods[m_MethodEntry].EndSequencePoints() - + m_pData->m_pMethods[m_MethodEntry].StartSequencePoints()); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetSequencePoints +//----------------------------------------------------------- +HRESULT +SymMethod::GetSequencePoints( + ULONG32 cPoints, // The size of the allocated arrays. + ULONG32* pcPoints, // [optional] The number of sequence points available for return. + ULONG32 offsets[], // [optional] + ISymUnmanagedDocument *documents[], // [Optional] + ULONG32 lines[], // [Optional] + ULONG32 columns[], // [Optional] + ULONG32 endLines[], // [Optional] + ULONG32 endColumns[] // [Optional] + ) +{ + HRESULT hr = NOERROR; + UINT32 i = 0; + ULONG32 Points = 0; + + for (i = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + (i < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints()); + i++, Points++) + { + if (Points < cPoints) + { + if (documents) + { + SymDocument *pDoc; + IfFailGo(m_pReader->GetDocument(m_pData->m_pSequencePoints[i].Document(), &pDoc)); + documents[Points] = pDoc; + } + + if (offsets) + { + offsets[Points] = m_pData->m_pSequencePoints[i].Offset(); + } + + if (lines) + { + lines[Points] = m_pData->m_pSequencePoints[i].StartLine(); + } + if (columns) + { + columns[Points] = m_pData->m_pSequencePoints[i].StartColumn(); + } + if (endLines) + { + endLines[Points] = m_pData->m_pSequencePoints[i].EndLine(); + } + if (endColumns) + { + endColumns[Points] = m_pData->m_pSequencePoints[i].EndColumn(); + } + } + } + + if (pcPoints) + { + *pcPoints = Points; + } + +ErrExit: + if (FAILED(hr)) + { + if (documents) + { + unsigned j; + for (j = 0; j < i; j++) + { + RELEASE(documents[i]); + } + } + } + return hr; +} + +//----------------------------------------------------------- +// GetRootScope +//----------------------------------------------------------- +HRESULT +SymMethod::GetRootScope( + ISymUnmanagedScope **ppRetVal + ) +{ + HRESULT hr = S_OK; + SymScope *pScope = NULL; + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + // Init Out Param + *ppRetVal = NULL; + if (m_pData->m_pMethods[m_MethodEntry].EndScopes() - m_pData->m_pMethods[m_MethodEntry].StartScopes()) + { + IfNullGo(pScope = NEW(SymScope(this, m_pData, m_MethodEntry, m_pData->m_pMethods[m_MethodEntry].StartScopes()))); + pScope->AddRef(); + *ppRetVal = pScope; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetOffset +// Given a position in a document, gets the offset within the +// method that corresponds to the position. +//----------------------------------------------------------- +HRESULT +SymMethod::GetOffset( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 *pRetVal + ) +{ + HRESULT hr = S_OK; + bool fFound = false; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + UINT32 point; + UINT32 DocumentEntry; + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Walk the sequence points + for (point = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry) + { + // Check to see if it's within the sequence point + if (m_pData->m_pSequencePoints[point].IsWithin(line, column)) + { + *pRetVal = m_pData->m_pSequencePoints[point].Offset(); + fFound = true; + break; + } + } + } + if (!fFound) + { + hr = E_FAIL; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetRanges +//----------------------------------------------------------- +HRESULT +SymMethod::GetRanges( + ISymUnmanagedDocument *pDocument, // [in] Document we're working on + ULONG32 line, // [in] The document line corresponding to the ranges. + ULONG32 column, // [in] Ignored + ULONG32 cRanges, // [in] The size of the allocated ranges[] array. + ULONG32 *pcRanges, // [out] The number of ranges available for return + ULONG32 ranges[] // [out] The range array. + ) +{ + HRESULT hr = NOERROR; + DWORD iRange = 0; + UINT32 DocumentEntry; + UINT32 point; + bool fFound = false; + + // Validate some of the parameters + _ASSERTE(pDocument && (cRanges % 2) == 0); + IfFalseGo(pDocument, E_INVALIDARG); + IfFalseGo((cRanges % 2) == 0, E_INVALIDARG); + + // Init out parameter + if (pcRanges) + { + *pcRanges=0; + } + + DocumentEntry = ((SymDocument *)pDocument)->GetDocumentEntry(); + + // Walk the sequence points + for (point = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry) + { + // Check to see if the line is within this sequence + // Note, to be compatible with VS7, ignore the column information + if (line >= m_pData->m_pSequencePoints[point].StartLine() && + line <= m_pData->m_pSequencePoints[point].EndLine()) + { + fFound = true; + break; + } + } + } + + if (fFound) + { + for (;point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); point++) + { + + // Search through all the sequence points since line might have there + // IL spread accross multiple ranges (for loops for example) + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry && + line >= m_pData->m_pSequencePoints[point].StartLine() && + line <= m_pData->m_pSequencePoints[point].EndLine()) + { + if (iRange < cRanges) + { + ranges[iRange] = m_pData->m_pSequencePoints[point].Offset(); + } + iRange++; + if (iRange < cRanges) + { + if (point+1 < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints()) + { + ranges[iRange] = m_pData->m_pSequencePoints[point+1].Offset(); + } + else + { + // Then it must be till the end of the function which is the root scope's endoffset + ranges[iRange] = m_pData->m_pScopes[m_pData->m_pMethods[m_MethodEntry].StartScopes()].EndOffset()+1; + } + } + iRange++; + } + } + if (pcRanges) + { + // If cRanges passed in, return the number + // of elements actually filled in + if (cRanges) + { + *pcRanges = min(iRange, cRanges); + } + else + { + // Otherwise return the max number + *pcRanges = iRange; + } + } + } + else + { + return E_FAIL; + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetScopeFromOffset +//----------------------------------------------------------- +HRESULT +SymMethod::GetScopeFromOffset( + ULONG32 offset, + ISymUnmanagedScope **pRetVal + ) +{ + // + // This symbol reader doesn't support this functionality + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetParameters +//----------------------------------------------------------- +HRESULT +SymMethod::GetParameters( + ULONG32 cParams, + ULONG32 *pcParams, + ISymUnmanagedVariable *params[] + ) +{ + // + // This symbol reader doesn't support parameter access. Parameters + // can be found in the normal metadata. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetNamespace +//----------------------------------------------------------- +HRESULT +SymMethod::GetNamespace( + ISymUnmanagedNamespace **ppRetVal + ) +{ + // + // This symbol reader doesn't support namespaces + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetSourceStartEnd +//----------------------------------------------------------- +HRESULT +SymMethod::GetSourceStartEnd( + ISymUnmanagedDocument *docs[2], + ULONG32 lines[2], + ULONG32 columns[2], + BOOL *pRetVal + ) +{ + // + // This symbol reader doesn't support source start/end for methods. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymScope class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymScope::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedScope) + *ppInterface = (ISymUnmanagedScope*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedScope*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetMethod +//----------------------------------------------------------- +HRESULT +SymScope::GetMethod( + ISymUnmanagedMethod **ppRetVal + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + *ppRetVal = m_pSymMethod; + m_pSymMethod->AddRef(); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetParent +//----------------------------------------------------------- +HRESULT +SymScope::GetParent( + ISymUnmanagedScope **ppRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + if (m_pData->m_pScopes[m_ScopeEntry].ParentScope() != (UINT32)-1) + { + IfNullGo(*ppRetVal = static_cast<ISymUnmanagedScope *>(NEW(SymScope(m_pSymMethod, m_pData, m_MethodEntry, + m_pData->m_pScopes[m_ScopeEntry].ParentScope())))); + (*ppRetVal)->AddRef(); + } + else + { + *ppRetVal = NULL; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetChildren +//----------------------------------------------------------- +HRESULT +SymScope::GetChildren( + ULONG32 cChildren, // [optional] Number of entries in children + ULONG32 *pcChildren, // [optional, out] Number of Children available for retur + ISymUnmanagedScope *children[] // [optional] array to store children into + ) +{ + HRESULT hr = S_OK; + ULONG32 ChildrenCount = 0; + _ASSERTE(pcChildren || (children && cChildren)); + IfFalseGo((pcChildren || (children && cChildren)), E_INVALIDARG); + + if (m_pData->m_pScopes[m_ScopeEntry].HasChildren()) + { + UINT32 ScopeEntry; + for(ScopeEntry = m_pData->m_pMethods[m_MethodEntry].StartScopes(); + (ScopeEntry < m_pData->m_pMethods[m_MethodEntry].EndScopes()); + ScopeEntry++) + { + if (m_pData->m_pScopes[ScopeEntry].ParentScope() == m_ScopeEntry) + { + if (children && ChildrenCount < cChildren) + { + SymScope *pScope; + // Found a child + IfNullGo(pScope = NEW(SymScope(m_pSymMethod, m_pData, m_MethodEntry, ScopeEntry))); + children[ChildrenCount] = pScope; + pScope->AddRef(); + } + ChildrenCount++; + } + } + } + + if (pcChildren) + { + *pcChildren = ChildrenCount; + } + +ErrExit: + if (FAILED(hr) && ChildrenCount) + { + unsigned i; + for (i =0; i< ChildrenCount; i++) + { + RELEASE(children[i]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetStartOffset +//----------------------------------------------------------- +HRESULT +SymScope::GetStartOffset( + ULONG32* pRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pScopes[m_ScopeEntry].StartOffset(); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetEndOffset +//----------------------------------------------------------- +HRESULT +SymScope::GetEndOffset( + ULONG32* pRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pScopes[m_ScopeEntry].EndOffset(); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLocalCount +//----------------------------------------------------------- +HRESULT +SymScope::GetLocalCount( + ULONG32 *pRetVal + ) +{ + HRESULT hr = S_OK; + ULONG32 LocalCount = 0; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // Init out parameter + *pRetVal = 0; + if (m_pData->m_pScopes[m_ScopeEntry].HasVars()) + { + UINT32 var; + // Walk and get the locals for this Scope + for (var = m_pData->m_pMethods[m_MethodEntry].StartVars(); + var < m_pData->m_pMethods[m_MethodEntry].EndVars(); + var++) + { + if (m_pData->m_pVars[var].Scope() == m_ScopeEntry && + m_pData->m_pVars[var].IsParam() == false) + { + LocalCount++; + } + } + } + + *pRetVal = LocalCount; +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLocals +// Input: either pcLocals or +// cLocals and pLocals +//----------------------------------------------------------- +HRESULT +SymScope::GetLocals( + ULONG32 cLocals, // [optional] available entries in pLocals + ULONG32 *pcLocals, // [optional, out] Number of locals returned + ISymUnmanagedVariable *pLocals[] // [optional] array to store locals into + ) +{ + HRESULT hr = S_OK; + + ULONG32 LocalCount = 0; + _ASSERTE(pcLocals || pLocals); + IfFalseGo(pcLocals || pLocals, E_INVALIDARG); + + if (m_pData->m_pScopes[m_ScopeEntry].HasVars()) + { + UINT32 var; + // Walk and get the locals for this Scope + for (var = m_pData->m_pMethods[m_MethodEntry].StartVars(); + var < m_pData->m_pMethods[m_MethodEntry].EndVars(); + var++) + { + if (m_pData->m_pVars[var].Scope() == m_ScopeEntry && + m_pData->m_pVars[var].IsParam() == false) + { + if (pLocals && LocalCount < cLocals) + { + SymReaderVar *pVar; + IfNullGo( pVar = NEW(SymReaderVar(this, m_pData, var))); + pLocals[LocalCount] = pVar; + pVar->AddRef(); + } + LocalCount++; + } + } + } + if (pcLocals) + { + *pcLocals = LocalCount; + } +ErrExit: + if (FAILED(hr) && LocalCount != 0) + { + unsigned i; + for (i =0; i < LocalCount; i++) + { + RELEASE(pLocals[i]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetNamespaces +// Input: either pcNameSpaces or +// cNameSpaces and pNameSpaces +//----------------------------------------------------------- +HRESULT +SymScope::GetNamespaces( + ULONG32 cNameSpaces, // [optional] number of entries pNameSpaces + ULONG32 *pcNameSpaces, // [optional, out] Maximum number of Namespace + ISymUnmanagedNamespace *pNameSpaces[] // [optinal] array to store namespaces into + ) +{ + HRESULT hr = NOERROR; + unsigned i; + UINT32 NameSpace; + unsigned NameSpaceCount = 0; + + _ASSERTE(pcNameSpaces || (pNameSpaces && cNameSpaces)); + IfFalseGo(pcNameSpaces || (pNameSpaces && cNameSpaces), E_INVALIDARG); + + for (NameSpace = m_pData->m_pMethods[m_MethodEntry].StartUsing(); + NameSpace < m_pData->m_pMethods[m_MethodEntry].EndUsing(); + NameSpace++) + { + if (m_pData->m_pUsings[NameSpace].ParentScope() == m_ScopeEntry) + { + if (pNameSpaces && (NameSpaceCount < cNameSpaces) ) + { + IfNullGo(pNameSpaces[NameSpaceCount] = NEW(SymReaderNamespace(this, m_pData, NameSpace))); + pNameSpaces[NameSpaceCount]->AddRef(); + } + NameSpaceCount++; + } + } + if (pcNameSpaces) + { + *pcNameSpaces = NameSpaceCount; + } +ErrExit: + if (FAILED(hr) && pNameSpaces) + { + for (i = 0; (i < cNameSpaces) && (i < NameSpaceCount); i++) + { + RELEASE(pNameSpaces[i]); + } + } + return hr; +} + +/* ------------------------------------------------------------------------- * + * SymReaderVar class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymReaderVar::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedVariable) + *ppInterface = (ISymUnmanagedVariable*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedVariable*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetName +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetName( + ULONG32 cchName, // [optional] Length of szName buffer + ULONG32 *pcchName, // [optional, out] Total size needed to return the name + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [optional] Buffer to store the name into. + ) +{ + HRESULT hr = S_OK; + + // We must have at least one combination + _ASSERTE(pcchName || (szName && cchName)); + IfFalseGo( (pcchName || (szName && cchName)), E_INVALIDARG ); + + if (pcchName) + { + // Convert the UTF8 string to Wide + *pcchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pVars[m_VarEntry].Name()]), + -1, + 0, + NULL); + + } + if (szName) + { + // Convert the UTF8 string to Wide + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pVars[m_VarEntry].Name()]), + -1, + szName, + cchName); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetAttributes +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAttributes( + ULONG32 *pRetVal // [out] + ) +{ + if (pRetVal == NULL) + return E_INVALIDARG; + + *pRetVal = m_pData->m_pVars[m_VarEntry].Attributes(); + return S_OK; +} + +//----------------------------------------------------------- +// GetSignature +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetSignature( + ULONG32 cSig, // Size of allocated buffer passed in (sig) + ULONG32 *pcSig, // [optional, out] Total size needed to return the signature + BYTE sig[] // [Optional] Signature + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pcSig || sig); + IfFalseGo( pcSig || sig, E_INVALIDARG ); + if (pcSig) + { + *pcSig = m_pData->m_pVars[m_VarEntry].SignatureSize(); + } + if (sig) + { + cSig = min(m_pData->m_pVars[m_VarEntry].SignatureSize(), cSig); + memcpy(sig, &m_pData->m_pBytes[m_pData->m_pVars[m_VarEntry].Signature()],cSig); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetAddressKind +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressKind( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + *pRetVal = m_pData->m_pVars[m_VarEntry].AddrKind(); +ErrExit: + return S_OK; +} + +//----------------------------------------------------------- +// GetAddressField1 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField1( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr1(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetAddressField2 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField2( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr2(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetAddressField3 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField3( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr3(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetStartOffset +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetStartOffset( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support variable sub-offsets. + // + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetEndOffset +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetEndOffset( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support variable sub-offsets. + // + return E_NOTIMPL; +} + + +/* ------------------------------------------------------------------------- * + * SymReaderNamespace class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::QueryInterface( + REFIID riid, + void** ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedNamespace) + *ppInterface = (ISymUnmanagedNamespace*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedNamespace*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetName +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetName( + ULONG32 cchName, // [optional] Chars available in szName + ULONG32 *pcchName, // [optional] Total size needed to return the name + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [optional] Location to store the name into. + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pcchName || (szName && cchName)); + IfFalseGo( (pcchName || (szName && cchName)), E_INVALIDARG ); + + if (pcchName) + { + *pcchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pUsings[m_NamespaceEntry].Name()]), + -1, + 0, + NULL); + } + if (szName) + { + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pUsings[m_NamespaceEntry].Name()]), + -1, + szName, + cchName); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetNamespaces +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetNamespaces( + ULONG32 cNamespaces, + ULONG32 *pcNamespaces, + ISymUnmanagedNamespace* namespaces[] + ) +{ + // This symbol store doesn't support namespaces. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetVariables +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetVariables( + ULONG32 cVariables, + ULONG32 *pcVariables, + ISymUnmanagedVariable *pVars[]) +{ + // This symbol store doesn't support namespaces. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + + +/* ------------------------------------------------------------------------- * + * SequencePoint struct functions + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// IsWithin - Is the point given within this sequence point +//----------------------------------------------------------- +bool SequencePoint::IsWithin( + ULONG32 line, + ULONG32 column) +{ + // If the sequence point starts on the same line + // Check the start column (if present) + if (StartLine() == line) + { + if (0 < column && StartColumn() > column) + { + return false; + } + } + + // If the sequence point ends on the same line + // Check the end column + if (EndLine() == line) + { + if (EndColumn() < column) + { + return false; + } + } + + // Make sure the line is within this sequence point + if (!((StartLine() <= line) && (EndLine() >= line))) + { + return false; + } + + // Yep it's within this sequence point + return true; + +} + +//----------------------------------------------------------- +// IsWithinLineOnly - Is the given line within this sequence point +//----------------------------------------------------------- +bool SequencePoint::IsWithinLineOnly( + ULONG32 line) +{ + return ((StartLine() <= line) && (line <= EndLine())); +} + +//----------------------------------------------------------- +// IsGreaterThan - Is the sequence point greater than the position +//----------------------------------------------------------- +bool SequencePoint::IsGreaterThan( + ULONG32 line, + ULONG32 column) +{ + return (StartLine() > line) || + (StartLine() == line && StartColumn() > column); +} + +//----------------------------------------------------------- +// IsLessThan - Is the sequence point less than the position +//----------------------------------------------------------- +bool SequencePoint::IsLessThan +( + ULONG32 line, + ULONG32 column +) +{ + return (StartLine() < line) || + (StartLine() == line && StartColumn() < column); +} + +//----------------------------------------------------------- +// IsUserLine - Is the sequence part of user code +//----------------------------------------------------------- +bool SequencePoint::IsUserLine() +{ + return StartLine() != CODE_WITH_NO_SOURCE; +} diff --git a/src/debug/ildbsymlib/symread.h b/src/debug/ildbsymlib/symread.h new file mode 100644 index 0000000000..6264e42bdc --- /dev/null +++ b/src/debug/ildbsymlib/symread.h @@ -0,0 +1,554 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: SymRead.h +// + +// =========================================================================== + +#ifndef SYMREAD_H_ +#define SYMREAD_H_ + +class SymScope; +class SymReaderVar; +class SymDocument; + +// ------------------------------------------------------------------------- +// SymReader class +// ------------------------------------------------------------------------- + +class SymReader : public ISymUnmanagedReader +{ +// ctor/dtor +public: + SymReader() + { + m_refCount = 0; + m_pPDBInfo = NULL; + m_pDocs = NULL; + m_pImporter = NULL; + m_fInitialized = false; + m_fInitializeFromStream = false; + memset(&m_DataPointers, 0, sizeof(PDBDataPointers)); + m_szPath[0] = '\0'; + } + virtual ~SymReader(); + static HRESULT NewSymReader( REFCLSID clsid, void** ppObj ); + +public: + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +// ISymUnmanagedReader +public: + STDMETHOD(GetDocument)(__in LPWSTR url, + GUID language, + GUID languageVendor, + GUID documentType, + ISymUnmanagedDocument **pRetVal); + STDMETHOD(GetDocuments)(ULONG32 cDocs, + ULONG32 *pcDocs, + ISymUnmanagedDocument *pDocs[]); + STDMETHOD(GetUserEntryPoint)(mdMethodDef *pRetVal); + STDMETHOD(GetMethod)(mdMethodDef method, + ISymUnmanagedMethod **pRetVal); + STDMETHOD(GetMethodByVersion)(mdMethodDef method, + int version, + ISymUnmanagedMethod **pRetVal); + STDMETHOD(GetVariables)(mdToken parent, + ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[]); + STDMETHOD(GetGlobalVariables)(ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[]); + STDMETHOD(GetMethodFromDocumentPosition)(ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ISymUnmanagedMethod **pRetVal); + STDMETHOD(GetSymAttribute)(mdToken parent, + __in LPWSTR name, + ULONG32 cBuffer, + ULONG32 *pcBuffer, + __out_bcount_part_opt(cBuffer, *pcBuffer) BYTE buffer[]); + STDMETHOD(GetNamespaces)(ULONG32 cNameSpaces, + ULONG32 *pcNameSpaces, + ISymUnmanagedNamespace *namespaces[]); + STDMETHOD(Initialize)(IUnknown *importer, + const WCHAR* szFileName, + const WCHAR* szsearchPath, + IStream *pIStream); + STDMETHOD(UpdateSymbolStore)(const WCHAR *filename, + IStream *pIStream); + + STDMETHOD(ReplaceSymbolStore)(const WCHAR *filename, + IStream *pIStream); + + STDMETHOD(GetSymbolStoreFileName)(ULONG32 cchName, + ULONG32 *pcchName, + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); + + STDMETHOD(GetMethodsFromDocumentPosition)(ISymUnmanagedDocument* document, + ULONG32 line, + ULONG32 column, + ULONG32 cMethod, + ULONG32* pcMethod, + ISymUnmanagedMethod* pRetVal[]); + + STDMETHOD(GetDocumentVersion)(ISymUnmanagedDocument *pDoc, int* version, BOOL* pbCurrent); + + STDMETHOD(GetMethodVersion)(ISymUnmanagedMethod* pMethod, int* version); + + //----------------------------------------------------------- + // Methods not exposed via a COM interface. + //----------------------------------------------------------- +public: + HRESULT GetDocument(UINT32 DocumentEntry, SymDocument **ppDocument); +private: + void Cleanup(); + + HRESULT InitializeFromFile(const WCHAR* szFileName, + const WCHAR* szsearchPath); + + HRESULT InitializeFromStream(IStream * pIStream); + + HRESULT VerifyPEDebugInfo(const WCHAR* szFileName); + + HRESULT ValidateData(); + + HRESULT ValidateBytes(UINT32 bytesIndex, UINT32 bytesLength); + +private: + // Data Members + UINT32 m_refCount; + + // Symbol File Name + WCHAR m_szPath[ _MAX_PATH ]; + WCHAR m_szStoredSymbolName[ _MAX_PATH ]; + + PDBInfo *m_pPDBInfo; + SymDocument **m_pDocs; + IUnknown *m_pImporter; + PDBDataPointers m_DataPointers; + + // Are we initialized yet? + bool m_fInitialized; + + // Did we initialize from stream + bool m_fInitializeFromStream; + }; + +/* ------------------------------------------------------------------------- * + * SymDocument class + * ------------------------------------------------------------------------- */ + +class SymDocument : public ISymUnmanagedDocument +{ +// ctor/dtor +public: + SymDocument(SymReader *pReader, + PDBDataPointers *pData, + UINT32 CountOfMethods, + UINT32 DocumentEntry) + { + m_refCount = 0; + m_pData = pData; + m_DocumentEntry = DocumentEntry; + m_CountOfMethods = CountOfMethods; + m_pReader = pReader; + pReader->AddRef(); + + } + virtual ~SymDocument() + { + RELEASE(m_pReader); + } + +// IUnknown +public: + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +// ISymUnmanagedDocument +public: + STDMETHOD(GetURL)(ULONG32 cchUrl, + ULONG32 *pcchUrl, + __out_ecount_part_opt(cchUrl, *pcchUrl) WCHAR szUrl[]); + STDMETHOD(GetDocumentType)(GUID *pRetVal); + STDMETHOD(GetLanguage)(GUID *pRetVal); + STDMETHOD(GetLanguageVendor)(GUID *pRetVal); + STDMETHOD(GetCheckSumAlgorithmId)(GUID *pRetVal); + STDMETHOD(GetCheckSum)(ULONG32 cData, + ULONG32 *pcData, + BYTE data[]); + STDMETHOD(FindClosestLine)(ULONG32 line, ULONG32 *pRetVal); + STDMETHOD(HasEmbeddedSource)(BOOL *pRetVal); + STDMETHOD(GetSourceLength)(ULONG32 *pRetVal); + STDMETHOD(GetSourceRange)(ULONG32 startLine, + ULONG32 startColumn, + ULONG32 endLine, + ULONG32 endColumn, + ULONG32 cSourceBytes, + ULONG32 *pcSourceBytes, + BYTE source[]); + + //----------------------------------------------------------- + // Methods not exposed via a COM interface. + //----------------------------------------------------------- + UINT32 GetDocumentEntry() + { + return m_DocumentEntry; + } + +// Data members +private: + UINT32 m_refCount; + + SymReader *m_pReader; + + // Data Pointer + PDBDataPointers *m_pData; + + // Entry into the document array + UINT32 m_DocumentEntry; + + // Total number of methods in the ildb + UINT32 m_CountOfMethods; + +}; + +/* ------------------------------------------------------------------------- * + * SymMethod class + * ------------------------------------------------------------------------- */ + +class SymMethod : public ISymUnmanagedMethod +{ +// ctor/dtor +public: + SymMethod(SymReader *pSymReader, PDBDataPointers *pData, UINT32 MethodEntry) + { + m_pData = pData; + m_MethodEntry = MethodEntry; + m_refCount = 0; + m_pReader = pSymReader; + pSymReader->AddRef(); + } + + virtual ~SymMethod() + { + RELEASE(m_pReader); + }; + +public: + + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +// ISymUnmanagedMethod +public: + STDMETHOD(GetToken)(mdMethodDef *pRetVal); + STDMETHOD(GetSequencePointCount)(ULONG32 *pRetVal); + + STDMETHOD(GetRootScope)(ISymUnmanagedScope **pRetVal); + STDMETHOD(GetScopeFromOffset)(ULONG32 offset, + ISymUnmanagedScope **pRetVal); + STDMETHOD(GetOffset)(ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 *pRetVal); + STDMETHOD(GetRanges)(ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 cRanges, + ULONG32 *pcRanges, + ULONG32 ranges[]); + STDMETHOD(GetParameters)(ULONG32 cParams, + ULONG32 *pcParams, + ISymUnmanagedVariable *params[]); + STDMETHOD(GetNamespace)(ISymUnmanagedNamespace **pRetVal); + STDMETHOD(GetSourceStartEnd)(ISymUnmanagedDocument *docs[2], + ULONG32 lines[2], + ULONG32 columns[2], + BOOL *pRetVal); + STDMETHOD(GetSequencePoints)(ULONG32 cpoints, + ULONG32* pcpoints, + ULONG32 offsets[], + ISymUnmanagedDocument *documents[], + ULONG32 lines[], + ULONG32 columns[], + ULONG32 endlines[], + ULONG32 endcolumns[]); + +// Data members +private: + // AddRef/Release support + UINT32 m_refCount; + + // Data Pointer + PDBDataPointers *m_pData; + + // SymReader + SymReader *m_pReader; + + // Entry into the SymMethodInfo array + UINT32 m_MethodEntry; + +}; + +/* ------------------------------------------------------------------------- * + * SymScope class + * ------------------------------------------------------------------------- */ + +class SymScope : public ISymUnmanagedScope +{ +// ctor/dtor +public: + SymScope( + ISymUnmanagedMethod *pSymMethod, + PDBDataPointers *pData, + UINT32 MethodEntry, + UINT32 ScopeEntry) + { + m_pSymMethod = pSymMethod; + m_pSymMethod->AddRef(); + m_pData = pData; + m_MethodEntry = MethodEntry; + m_ScopeEntry = ScopeEntry; + m_refCount = 0; + } + virtual ~SymScope() + { + RELEASE(m_pSymMethod); + } + +public: + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +// ISymUnmanagedScope +public: + STDMETHOD(GetMethod)(ISymUnmanagedMethod **pRetVal); + STDMETHOD(GetParent)(ISymUnmanagedScope **pRetVal); + STDMETHOD(GetChildren)(ULONG32 cChildren, + ULONG32 *pcChildren, + ISymUnmanagedScope *children[]); + STDMETHOD(GetStartOffset)(ULONG32 *pRetVal); + STDMETHOD(GetEndOffset)(ULONG32 *pRetVal); + STDMETHOD(GetLocalCount)(ULONG32 *pRetVal); + STDMETHOD(GetLocals)(ULONG32 cLocals, + ULONG32 *pcLocals, + ISymUnmanagedVariable *locals[]); + STDMETHOD(GetNamespaces)(ULONG32 cNameSpaces, + ULONG32 *pcNameSpaces, + ISymUnmanagedNamespace *namespaces[]); + +// Data members +private: + + UINT32 m_refCount; // Add/Ref Release + + ISymUnmanagedMethod *m_pSymMethod; + + // Data Pointer + PDBDataPointers *m_pData; + // Entry into the SymMethodInfo array + UINT32 m_MethodEntry; + // Entry into the scope array + UINT32 m_ScopeEntry; +}; + +/* ------------------------------------------------------------------------- * + * SymReaderVar class + * ------------------------------------------------------------------------- */ + +class SymReaderVar : public ISymUnmanagedVariable +{ +// ctor/dtor +public: + SymReaderVar(SymScope *pScope, PDBDataPointers *pData, UINT32 VarEntry) + { + m_pData = pData; + m_VarEntry = VarEntry; + m_refCount = 0; + m_pScope = pScope; + pScope->AddRef(); + } + virtual ~SymReaderVar() + { + RELEASE(m_pScope); + } + +public: + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +// ISymUnmanagedReaderVar +public: + STDMETHOD(GetName)(ULONG32 cchName, + ULONG32 *pcchName, + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); + STDMETHOD(GetAttributes)(ULONG32 *pRetVal); + STDMETHOD(GetSignature)(ULONG32 cSig, + ULONG32 *pcSig, + BYTE sig[]); + STDMETHOD(GetAddressKind)(ULONG32 *pRetVal); + STDMETHOD(GetAddressField1)(ULONG32 *pRetVal); + STDMETHOD(GetAddressField2)(ULONG32 *pRetVal); + STDMETHOD(GetAddressField3)(ULONG32 *pRetVal); + STDMETHOD(GetStartOffset)(ULONG32 *pRetVal); + STDMETHOD(GetEndOffset)(ULONG32 *pRetVal); + + +// Data members +private: + UINT32 m_refCount; // Add/Ref Release + + // Data Pointer + PDBDataPointers *m_pData; + + // Scope of the variable + SymScope *m_pScope; + + // Entry into the SymMethodInfo array + UINT32 m_VarEntry; +}; + +class SymReaderNamespace : public ISymUnmanagedNamespace +{ + +public: + SymReaderNamespace(SymScope *pScope, PDBDataPointers *pData, UINT32 NamespaceEntry) + { + m_pData = pData; + m_NamespaceEntry = NamespaceEntry; + m_refCount = 0; + m_pScope = pScope; + pScope->AddRef(); + } + virtual ~SymReaderNamespace() + { + } + +public: + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject); + +public: + //----------------------------------------------------------- + // ISymUnmanagedNamespace support + //----------------------------------------------------------- + STDMETHOD(GetName)(ULONG32 cchName, + ULONG32 *pcchName, + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); + STDMETHOD(GetNamespaces)(ULONG32 cNamespaces, + ULONG32 *pcNamespaces, + ISymUnmanagedNamespace* namespaces[]); + STDMETHOD(GetVariables)(ULONG32 cchName, + ULONG32 *pcchName, + ISymUnmanagedVariable *pVars[]); + +private: + UINT32 m_refCount; // Add/Ref Release + + // Owning scope + SymScope *m_pScope; + + // Data Pointer + PDBDataPointers *m_pData; + // Entry into the NameSpace array + UINT32 m_NamespaceEntry; + +}; + +#endif diff --git a/src/debug/ildbsymlib/symwrite.cpp b/src/debug/ildbsymlib/symwrite.cpp new file mode 100644 index 0000000000..c09fd37498 --- /dev/null +++ b/src/debug/ildbsymlib/symwrite.cpp @@ -0,0 +1,1553 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: symwrite.cpp +// + +// +// Note: The various SymWriter_* and SymDocumentWriter_* are entry points +// called via PInvoke from the managed symbol wrapper used by managed languages +// to emit debug information (such as jscript) +// =========================================================================== + +#include "pch.h" +#include "symwrite.h" + + +// ------------------------------------------------------------------------- +// SymWriter class +// ------------------------------------------------------------------------- + +// This is a COM object which is called both directly from the runtime, and from managed code +// via PInvoke (CoreSymWrapper) and IJW (ISymWrapper). This is an unusual pattern, and it's not +// clear exactly how best to address it. Eg., should we be using BEGIN_EXTERNAL_ENTRYPOINT +// macros? Conceptually this is just a drop-in replacement for diasymreader.dll, and could +// live in a different DLL instead of being statically linked into the runtime. But since it +// relies on utilcode (and actually gets the runtime utilcode, not the nohost utilcode like +// other external tools), it does have some properties of runtime code. +// + +//----------------------------------------------------------- +// NewSymWriter +// Static function used to create a new instance of SymWriter +//----------------------------------------------------------- +HRESULT SymWriter::NewSymWriter(const GUID& id, void **object) +{ + if (id != IID_ISymUnmanagedWriter) + return (E_UNEXPECTED); + + SymWriter *writer = NEW(SymWriter()); + + if (writer == NULL) + return (E_OUTOFMEMORY); + + *object = (ISymUnmanagedWriter*)writer; + writer->AddRef(); + + return (S_OK); +} + +//----------------------------------------------------------- +// SymWriter Constuctor +//----------------------------------------------------------- +SymWriter::SymWriter() : + m_refCount(0), + m_openMethodToken(mdMethodDefNil), + m_LargestMethodToken(mdMethodDefNil), + m_pmethod(NULL), + m_currentScope(k_noScope), + m_hFile(NULL), + m_pIStream(NULL), + m_pStringPool(NULL), + m_closed( false ), + m_sortLines (false), + m_sortMethodEntries(false) +{ + memset(m_szPath, 0, sizeof(m_szPath)); + memset(&ModuleLevelInfo, 0, sizeof(PDBInfo)); +} + +//----------------------------------------------------------- +// SymWriter QI +//----------------------------------------------------------- +COM_METHOD SymWriter::QueryInterface(REFIID riid, void **ppInterface) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedWriter ) + *ppInterface = (ISymUnmanagedWriter*)this; + /* ROTORTODO: Pretend that we do not implement ISymUnmanagedWriter2 to prevent C# compiler from using it. + This is against COM rules since ISymUnmanagedWriter3 inherits from ISymUnmanagedWriter2. + else if (riid == IID_ISymUnmanagedWriter2 ) + *ppInterface = (ISymUnmanagedWriter2*)this; + */ + else if (riid == IID_ISymUnmanagedWriter3 ) + *ppInterface = (ISymUnmanagedWriter3*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedWriter*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter Destructor +//----------------------------------------------------------- +SymWriter::~SymWriter() +{ + // Note that this must be thread-safe - it may be invoked on the finalizer thread + // But since this dtor can only be invoked when all references have been released, + // no other threads can be manipulating the writer. + // Ideally we'd probably just add locking to all methods, but this is low-priority + // because diasymreader.dll isn't thread-safe and so we need to ensure the CLR's use + // of these interfaces are properly syncrhonized. + if ( !m_closed ) + Close(); + RELEASE(m_pIStream); + DELETE(m_pStringPool); +} + +//----------------------------------------------------------- +// SymWriter Initialize the SymWriter +//----------------------------------------------------------- +COM_METHOD SymWriter::Initialize +( + IUnknown *emitter, // Emitter (IMetaData Emit/Import) - unused by ILDB + const WCHAR *szFilename, // FileName of the exe we're creating + IStream *pIStream, // Stream to store into + BOOL fFullBuild // Is this a full build or an incremental build +) +{ + HRESULT hr = S_OK; + + // Incremental compile not implemented in Rotor + _ASSERTE(fFullBuild); + + if (emitter == NULL) + return E_INVALIDARG; + + if (pIStream != NULL) + { + m_pIStream = pIStream; + pIStream->AddRef(); + } + else + { + if (szFilename == NULL) + { + IfFailRet(E_INVALIDARG); + } + } + + m_pStringPool = NEW(StgStringPool()); + IfFailRet(m_pStringPool->InitNew()); + + if (szFilename != NULL) + { + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + _wsplitpath_s( szFilename, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 ); + _wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("ildb") ); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + return HrFromWin32(ERROR_INSUFFICIENT_BUFFER); + } + + // Note that we don't need the emitter - ILDB is agnostic to the module metadata. + + return hr; +} + +//----------------------------------------------------------- +// SymWriter Initialize2 the SymWriter +// Delegate to Initialize then use the szFullPathName param +//----------------------------------------------------------- +COM_METHOD SymWriter::Initialize2 +( + IUnknown *emitter, // Emitter (IMetaData Emit/Import) + const WCHAR *szTempPath, // Location of the file + IStream *pIStream, // Stream to store into + BOOL fFullBuild, // Full build or not + const WCHAR *szFullPathName // Final destination of the ildb +) +{ + HRESULT hr = S_OK; + IfFailGo( Initialize( emitter, szTempPath, pIStream, fFullBuild ) ); + // We don't need the final location of the ildb + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter GetorCreateDocument +// creates a new symbol document writer for a specified source +// Arguments: +// input: wcsUrl - The source file name +// output: ppRetVal - The new document writer +// Return Value: hr - S_OK if success, OOM otherwise +//----------------------------------------------------------- +HRESULT SymWriter::GetOrCreateDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) +{ + ULONG UrlEntry; + DWORD strLength = WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, 0, 0, 0, 0); + LPSTR multiByteURL = (LPSTR) new char [strLength+1]; + HRESULT hr = S_OK; + + if (multiByteURL == NULL) + { + return E_OUTOFMEMORY; + } + + WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, multiByteURL, strLength+1, 0, 0); + + if (m_pStringPool->FindString(multiByteURL, &UrlEntry) == S_FALSE) // no file of that name has been seen before + { + hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); + } + else // we already have a writer for this file + { + UINT32 docInfo = 0; + + CRITSEC_COOKIE cs = ClrCreateCriticalSection(CrstLeafLock, CRST_DEFAULT); + + ClrEnterCriticalSection(cs); + + while ((docInfo < m_MethodInfo.m_documents.count()) && (m_MethodInfo.m_documents[docInfo].UrlEntry() != UrlEntry)) + { + docInfo++; + } + + if (docInfo == m_MethodInfo.m_documents.count()) // something went wrong and we didn't find the writer + { + hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); + } + else + { + *ppRetVal = m_MethodInfo.m_documents[docInfo].DocumentWriter(); + (*ppRetVal)->AddRef(); + } + ClrLeaveCriticalSection(cs); + } + + delete [] multiByteURL; + return hr; + +} // SymWriter::GetOrCreateDocument + +//----------------------------------------------------------- +// SymWriter CreateDocument +// creates a new symbol document writer for a specified source +// Arguments: +// input: wcsUrl - The source file name +// output: ppRetVal - The new document writer +// Return Value: hr - S_OK if success, OOM otherwise +//----------------------------------------------------------- +HRESULT SymWriter::CreateDocument(const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) + +{ + DocumentInfo* pDocument = NULL; + SymDocumentWriter *sdw = NULL; + UINT32 DocumentEntry; + ULONG UrlEntry; + HRESULT hr = NOERROR; + + DocumentEntry = m_MethodInfo.m_documents.count(); + IfNullGo(pDocument = m_MethodInfo.m_documents.next()); + memset(pDocument, 0, sizeof(DocumentInfo)); + + // Create the new document writer. + sdw = NEW(SymDocumentWriter(DocumentEntry, this)); + IfNullGo(sdw); + + pDocument->SetLanguage(*pLanguage); + pDocument->SetLanguageVendor(*pLanguageVendor); + pDocument->SetDocumentType(*pDocumentType); + pDocument->SetDocumentWriter(sdw); + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + hr = m_pStringPool->AddStringW(wcsUrl, (UINT32 *)&UrlEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + + pDocument->SetUrlEntry(UrlEntry); + + // Pass out the new ISymUnmanagedDocumentWriter. + sdw->AddRef(); + *ppRetVal = (ISymUnmanagedDocumentWriter*)sdw; + sdw = NULL; + +ErrExit: + DELETE(sdw); + return hr; +} + +//----------------------------------------------------------- +// SymWriter DefineDocument +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) +{ + HRESULT hr = NOERROR; + + IfFalseGo(wcsUrl, E_INVALIDARG); + IfFalseGo(pLanguage, E_INVALIDARG); + IfFalseGo(pLanguageVendor, E_INVALIDARG); + IfFalseGo(pDocumentType, E_INVALIDARG); + IfFalseGo(ppRetVal, E_INVALIDARG); + + // Init out parameter + *ppRetVal = NULL; + + hr = GetOrCreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); +ErrExit: + return hr; +} + + +//----------------------------------------------------------- +// SymWriter SetDocumentSrc +//----------------------------------------------------------- +HRESULT SymWriter::SetDocumentSrc( + UINT32 DocumentEntry, + DWORD SourceSize, + BYTE* pSource +) +{ + DocumentInfo* pDocument = NULL; + HRESULT hr = S_OK; + + IfFalseGo( SourceSize == 0 || pSource, E_INVALIDARG); + IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG); + + pDocument = &m_MethodInfo.m_documents[DocumentEntry]; + + if (pSource) + { + UINT32 i; + IfFalseGo( m_MethodInfo.m_bytes.grab(SourceSize, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], pSource, SourceSize); + pDocument->SetSourceEntry(i); + pDocument->SetSourceSize(SourceSize); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetDocumentCheckSum +//----------------------------------------------------------- +HRESULT SymWriter::SetDocumentCheckSum( + UINT32 DocumentEntry, + GUID AlgorithmId, + DWORD CheckSumSize, + BYTE* pCheckSum +) +{ + DocumentInfo* pDocument = NULL; + HRESULT hr = S_OK; + + IfFalseGo( CheckSumSize == 0 || pCheckSum, E_INVALIDARG); + IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG); + + pDocument = &m_MethodInfo.m_documents[DocumentEntry]; + + if (pCheckSum) + { + UINT32 i; + IfFalseGo( m_MethodInfo.m_bytes.grab(CheckSumSize, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], pCheckSum, CheckSumSize); + pDocument->SetCheckSumEntry(i); + pDocument->SetCheckSymSize(CheckSumSize); + } + + pDocument->SetAlgorithmId(AlgorithmId); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetUserEntryPoint +//----------------------------------------------------------- +COM_METHOD SymWriter::SetUserEntryPoint(mdMethodDef entryMethod) +{ + HRESULT hr = S_OK; + + // Make sure that an entry point hasn't already been set. + if (ModuleLevelInfo.m_userEntryPoint == 0) + ModuleLevelInfo.m_userEntryPoint = entryMethod; + + return hr; +} + +//----------------------------------------------------------- +// SymWriter OpenMethod +// Get ready to get information about a new method +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenMethod(mdMethodDef method) +{ + HRESULT hr = S_OK; + + // We can only have one open method at a time. + if (m_openMethodToken != mdMethodDefNil) + return E_INVALIDARG; + + m_LargestMethodToken = max(method, m_LargestMethodToken); + + if (m_LargestMethodToken != method) + { + m_sortMethodEntries = true; + // Check to see if we're trying to open a method we've already done + unsigned i; + for (i = 0; i < m_MethodInfo.m_methods.count(); i++) + { + if (m_MethodInfo.m_methods[i].MethodToken() == method) + { + return E_INVALIDARG; + } + } + } + + // Remember the token for this method. + m_openMethodToken = method; + + IfNullGo( m_pmethod = m_MethodInfo.m_methods.next() ); + m_pmethod->SetMethodToken(m_openMethodToken); + m_pmethod->SetStartScopes(m_MethodInfo.m_scopes.count()); + m_pmethod->SetStartVars(m_MethodInfo.m_vars.count()); + m_pmethod->SetStartUsing(m_MethodInfo.m_usings.count()); + m_pmethod->SetStartConstant(m_MethodInfo.m_constants.count()); + m_pmethod->SetStartDocuments(m_MethodInfo.m_documents.count()); + m_pmethod->SetStartSequencePoints(m_MethodInfo.m_auxSequencePoints.count()); + + // By default assume the lines are inserted in the correct order + m_sortLines = false; + + // Initialize the maximum scope end offset for this method + m_maxScopeEnd = 1; + + // Open the implicit root scope for the method + _ASSERTE(m_currentScope == k_noScope); + + IfFailRet(OpenScope(0, NULL)); + + _ASSERTE(m_currentScope != k_noScope); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::OpenMethod2( + mdMethodDef method, + ULONG32 isect, + ULONG32 offset) +{ + // This symbol writer doesn't support section offsets + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// compareAuxLines +// Used to sort SequencePoint +//----------------------------------------------------------- +int __cdecl SequencePoint::compareAuxLines(const void *elem1, const void *elem2 ) +{ + SequencePoint* p1 = (SequencePoint*)elem1; + SequencePoint* p2 = (SequencePoint*)elem2; + return p1->Offset() - p2->Offset(); +} + +//----------------------------------------------------------- +// SymWriter CloseMethod +// We're done with this function, write it out. +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseMethod() +{ + HRESULT hr = S_OK; + UINT32 CountOfSequencePoints; + + // Must have an open method. + if (m_openMethodToken == mdMethodDefNil) + return E_UNEXPECTED; + + // All scopes up to the root must have been closed (and the root must not have been closed). + _ASSERTE(m_currentScope != k_noScope); + if (m_MethodInfo.m_scopes[m_currentScope].ParentScope() != k_noScope) + return E_FAIL; + + // Close the implicit root scope using the largest end offset we've seen in this method, or 1 if none. + IfFailRet(CloseScopeInternal(m_maxScopeEnd)); + + m_pmethod->SetEndScopes(m_MethodInfo.m_scopes.count()); + m_pmethod->SetEndVars(m_MethodInfo.m_vars.count()); + m_pmethod->SetEndUsing(m_MethodInfo.m_usings.count()); + m_pmethod->SetEndConstant(m_MethodInfo.m_constants.count()); + m_pmethod->SetEndDocuments(m_MethodInfo.m_documents.count()); + m_pmethod->SetEndSequencePoints(m_MethodInfo.m_auxSequencePoints.count()); + + CountOfSequencePoints = m_pmethod->EndSequencePoints() - m_pmethod->StartSequencePoints(); + // Write any sequence points. + if (CountOfSequencePoints > 0 ) { + // sort the sequence points + if ( m_sortLines ) + { + qsort(&m_MethodInfo.m_auxSequencePoints[m_pmethod->StartSequencePoints()], + CountOfSequencePoints, + sizeof( SequencePoint ), + SequencePoint::compareAuxLines ); + } + } + + // All done with this method. + m_openMethodToken = mdMethodDefNil; + + return hr; +} + +//----------------------------------------------------------- +// SymWriter DefineSequencePoints +// Define the sequence points for this function +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineSequencePoints( + ISymUnmanagedDocumentWriter *document, // + ULONG32 spCount, // Count of sequence points + ULONG32 offsets[], // Offsets + ULONG32 lines[], // Beginning Lines + ULONG32 columns[], // [optional] Columns + ULONG32 endLines[], // [optional] End Lines + ULONG32 endColumns[] // [optional] End Columns +) +{ + HRESULT hr = S_OK; + DWORD docnum; + + // We must have a document, offsets, and lines. + IfFalseGo(document && offsets && lines, E_INVALIDARG); + // Must have some sequence points + IfFalseGo(spCount != 0, E_INVALIDARG); + // Must have an open method. + IfFalseGo(m_openMethodToken != mdMethodDefNil, E_INVALIDARG); + + // Remember that we've loaded the sequence points and + // which document they were for. + docnum = (DWORD)((SymDocumentWriter *)document)->GetDocumentEntry();; + + // if sets of lines have been inserted out-of-order, remember to sort when emitting + if ( m_MethodInfo.m_auxSequencePoints.count() > 0 && m_MethodInfo.m_auxSequencePoints[ m_MethodInfo.m_auxSequencePoints.count()-1 ].Offset() > offsets[0] ) + m_sortLines = true; + + // Copy the incomming arrays into the internal format. + + for ( UINT32 i = 0; i < spCount; i++) + { + SequencePoint * paux; + IfNullGo(paux = m_MethodInfo.m_auxSequencePoints.next()); + paux->SetOffset(offsets[i]); + paux->SetStartLine(lines[i]); + paux->SetStartColumn(columns ? columns[i] : 0); + // If no endLines specified, assume same as start + paux->SetEndLine(endLines ? endLines[i] : lines[i]); + paux->SetEndColumn(endColumns ? endColumns[i]: 0); + paux->SetDocument(docnum); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter OpenScope +// Open a new scope for this function +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenScope(ULONG32 startOffset, ULONG32 *scopeID) +{ + HRESULT hr = S_OK; + + // Make sure the startOffset is within the current scope. + if ((m_currentScope != k_noScope) && + (unsigned int)startOffset < m_MethodInfo.m_scopes[m_currentScope].StartOffset()) + return E_INVALIDARG; + + // Fill in the new scope. + UINT32 newScope = m_MethodInfo.m_scopes.count(); + + // Make sure that adding 1 below won't overflow (although "next" should fail much + // sooner if we were anywhere near close enough). + if (newScope >= UINT_MAX) + return E_UNEXPECTED; + + SymLexicalScope *sc; + IfNullGo( sc = m_MethodInfo.m_scopes.next()); + sc->SetParentScope(m_currentScope); // parent is the current scope. + sc->SetStartOffset(startOffset); + sc->SetHasChildren(FALSE); + sc->SetHasVars(FALSE); + sc->SetEndOffset(0); + + // The current scope has a child now. + if (m_currentScope != k_noScope) + m_MethodInfo.m_scopes[m_currentScope].SetHasChildren(TRUE); + + // The new scope is now the current scope. + m_currentScope = newScope; + _ASSERTE(m_currentScope != k_noScope); + + // Pass out the "scope id", which is a _1_ based id for the scope. + if (scopeID) + *scopeID = m_currentScope + 1; + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter CloseScope +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseScope( + ULONG32 endOffset // Closing offset of scope +) +{ + // This API can only be used to close explicit user scopes. + // The implicit root scope is only closed internally by CloseMethod. + if ((m_currentScope == k_noScope) || (m_MethodInfo.m_scopes[m_currentScope].ParentScope() == k_noScope)) + return E_FAIL; + + HRESULT hr = CloseScopeInternal(endOffset); + + _ASSERTE(m_currentScope != k_noScope); + + return hr; +} + +//----------------------------------------------------------- +// CloseScopeInternal +// Implementation for ISymUnmanagedWriter::CloseScope but can be called even to +// close the implicit root scope. +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseScopeInternal( + ULONG32 endOffset // Closing offset of scope +) +{ + _ASSERTE(m_currentScope != k_noScope); + + // Capture the end offset + m_MethodInfo.m_scopes[m_currentScope].SetEndOffset(endOffset); + + // The current scope is now the parent scope. + m_currentScope = m_MethodInfo.m_scopes[m_currentScope].ParentScope(); + + // Update the maximum scope end offset for this method + if (endOffset > m_maxScopeEnd) + m_maxScopeEnd = endOffset; + + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter SetScopeRange +// Set the Start/End Offset for this scope +//----------------------------------------------------------- +COM_METHOD SymWriter::SetScopeRange( + ULONG32 scopeID, // ID for the scope + ULONG32 startOffset, // Start Offset + ULONG32 endOffset // End Offset +) +{ + if (scopeID <= 0) + return E_INVALIDARG; + + if (scopeID > m_MethodInfo.m_scopes.count() ) + return E_INVALIDARG; + + // Remember the new start and end offsets. Also remember that the + // scopeID is _1_ based!!! + SymLexicalScope *sc = &(m_MethodInfo.m_scopes[scopeID - 1]); + sc->SetStartOffset(startOffset); + sc->SetEndOffset(endOffset); + + // Update the maximum scope end offset for this method + if (endOffset > m_maxScopeEnd) + m_maxScopeEnd = endOffset; + + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineLocalVariable +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineLocalVariable( + const WCHAR *name, // Name of the variable + ULONG32 attributes, // Attributes for the var + ULONG32 cSig, // Signature for the variable + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3, + ULONG32 startOffset, ULONG32 endOffset) +{ + HRESULT hr = S_OK; + ULONG NameEntry; + + // We must have a current scope. + if (m_currentScope == k_noScope) + return E_FAIL; + + // We must have a name and a signature. + if (!name || !signature) + return E_INVALIDARG; + + if (cSig == 0) + return E_INVALIDARG; + + // Make a new local variable and copy the data. + SymVariable *var; + IfNullGo( var = m_MethodInfo.m_vars.next()); + var->SetIsParam(FALSE); + var->SetAttributes(attributes); + var->SetAddrKind(addrKind); + var->SetIsHidden(attributes & VAR_IS_COMP_GEN); + var->SetAddr1(addr1); + var->SetAddr2(addr2); + var->SetAddr3(addr3); + + + // Length of the sig? + ULONG32 sigLen; + sigLen = cSig; + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + var->SetName(NameEntry); + + // Copy the signature + // Note that we give this back exactly as-is, but callers typically remove any calling + // convention prefix. + UINT32 i; + IfFalseGo(m_MethodInfo.m_bytes.grab(sigLen, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], signature, sigLen); + var->SetSignature(i); + var->SetSignatureSize(sigLen); + + // This var is in the current scope + var->SetScope(m_currentScope); + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + + var->SetStartOffset(startOffset); + var->SetEndOffset(endOffset); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::DefineLocalVariable2( + const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3, + ULONG32 startOffset, ULONG32 endOffset) +{ + // This symbol writer doesn't support definiting signatures via tokens + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter DefineParameter +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineParameter( + const WCHAR *name, // Param name + ULONG32 attributes, // Attribute for the parameter + ULONG32 sequence, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + HRESULT hr = S_OK; + ULONG NameEntry; + + // We must have a method. + if (m_openMethodToken == mdMethodDefNil) + return E_INVALIDARG; + + // We must have a name. + if (!name) + return E_INVALIDARG; + + SymVariable *var; + IfNullGo( var = m_MethodInfo.m_vars.next()); + var->SetIsParam(TRUE); + var->SetAttributes(attributes); + var->SetAddrKind(addrKind); + var->SetIsHidden(attributes & VAR_IS_COMP_GEN); + var->SetAddr1(addr1); + var->SetAddr2(addr2); + var->SetAddr3(addr3); + var->SetSequence(sequence); + + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + var->SetName(NameEntry); + + // This var is in the current scope + if (m_currentScope != k_noScope) + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + + var->SetStartOffset(0); + var->SetEndOffset(0); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// verifyConstTypes +// Verify that the type is a type we support +//----------------------------------------------------------- +static bool verifyConstTypes( DWORD vt ) +{ + switch ( vt ) { + case VT_UI8: + case VT_I8: + case VT_I4: + case VT_UI1: // value < LF_NUMERIC + case VT_I2: + case VT_R4: + case VT_R8: + case VT_BOOL: // value < LF_NUMERIC + case VT_DATE: + case VT_BSTR: + case VT_I1: + case VT_UI2: + case VT_UI4: + case VT_INT: + case VT_UINT: + case VT_DECIMAL: + return true; + } + return false; +} + +//----------------------------------------------------------- +// SymWriter DefineConstant +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineConstant( + const WCHAR __RPC_FAR *name, + VARIANT value, + ULONG32 cSig, + unsigned char __RPC_FAR signature[]) +{ + HRESULT hr = S_OK; + ULONG ValueBstr = 0; + ULONG Name; + + // currently we only support local constants + + // We must have a method. + if (m_openMethodToken == mdMethodDefNil) + return E_INVALIDARG; + + // We must have a name and signature. + IfFalseGo(name, E_INVALIDARG); + IfFalseGo(signature, E_INVALIDARG); + IfFalseGo(cSig > 0, E_INVALIDARG); + + // + // Support byref decimal values + // + if ( (V_VT(&value)) == ( VT_BYREF | VT_DECIMAL ) ) { + if ( V_DECIMALREF(&value) == NULL ) + return E_INVALIDARG; + V_DECIMAL(&value) = *V_DECIMALREF(&value); + V_VT(&value) = VT_DECIMAL; + } + + // we only support non-ref constants + if ( ( V_VT(&value) & VT_BYREF ) != 0 ) + return E_INVALIDARG; + + if ( !verifyConstTypes( V_VT(&value) ) ) + return E_INVALIDARG; + + // If it's a BSTR, we need to persist the Bstr as an entry into + // the stringpool + if (V_VT(&value) == VT_BSTR) + { + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the bstrValue. + hr = m_pStringPool->AddStringW(V_BSTR(&value), (UINT32 *)&ValueBstr); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + V_BSTR(&value) = NULL; + } + + SymConstant *con; + IfNullGo( con = m_MethodInfo.m_constants.next()); + con->SetValue(value, ValueBstr); + + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&Name); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + con->SetName(Name); + + // Copy the signature + UINT32 i; + IfFalseGo(m_MethodInfo.m_bytes.grab(cSig, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], signature, cSig); + con->SetSignature(i); + con->SetSignatureSize(cSig); + + // This const is in the current scope + con->SetParentScope(m_currentScope); + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::DefineConstant2( + const WCHAR *name, + VARIANT value, + mdSignature sigToken) +{ + // This symbol writer doesn't support definiting signatures via tokens + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter Abort +//----------------------------------------------------------- +COM_METHOD SymWriter::Abort(void) +{ + m_closed = true; + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineField +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineField( + mdTypeDef parent, + const WCHAR *name, + ULONG32 attributes, + ULONG32 csig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol store doesn't support extra random variable + // definitions. + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineGlobalVariable +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineGlobalVariable( + const WCHAR *name, + ULONG32 attributes, + ULONG32 csig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol writer doesn't support global variables + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +COM_METHOD SymWriter::DefineGlobalVariable2( + const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol writer doesn't support global variables + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// compareMethods +// Used to sort method entries +//----------------------------------------------------------- +int __cdecl SymMethodInfo::compareMethods(const void *elem1, const void *elem2 ) +{ + SymMethodInfo* p1 = (SymMethodInfo*)elem1; + SymMethodInfo* p2 = (SymMethodInfo*)elem2; + return p1->MethodToken() - p2->MethodToken(); +} + +//----------------------------------------------------------- +// SymWriter Close +//----------------------------------------------------------- +COM_METHOD SymWriter::Close() +{ + HRESULT hr = Commit(); + m_closed = true; + for (UINT32 docInfo = 0; docInfo < m_MethodInfo.m_documents.count(); docInfo++) + { + m_MethodInfo.m_documents[docInfo].SetDocumentWriter(NULL); + } + return hr; +} + +//----------------------------------------------------------- +// SymWriter Commit +//----------------------------------------------------------- +COM_METHOD SymWriter::Commit(void) +{ + // Sort the entries if need be + if (m_sortMethodEntries) + { + // First remap any tokens we need to + if (m_MethodMap.count()) + { + unsigned i; + for (i = 0; i< m_MethodMap.count(); i++) + { + m_MethodInfo.m_methods[m_MethodMap[i].MethodEntry].SetMethodToken(m_MethodMap[i].m_MethodToken); + } + } + + // Now sort the array + qsort(&m_MethodInfo.m_methods[0], + m_MethodInfo.m_methods.count(), + sizeof( SymMethodInfo ), + SymMethodInfo::compareMethods ); + m_sortMethodEntries = false; + } + return WritePDB(); +} + +//----------------------------------------------------------- +// SymWriter SetSymAttribute +//----------------------------------------------------------- +COM_METHOD SymWriter::SetSymAttribute( + mdToken parent, + const WCHAR *name, + ULONG32 cData, + BYTE data[]) +{ + // Setting attributes on the symbol isn't supported + + // ROTORTODO: #156785 in PS + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter OpenNamespace +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenNamespace(const WCHAR *name) +{ + // This symbol store doesn't support namespaces. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter OpenNamespace +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseNamespace() +{ + // This symbol store doesn't support namespaces. + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter UsingNamespace +// Add a Namespace to the list of namespace for this method +//----------------------------------------------------------- +COM_METHOD SymWriter::UsingNamespace(const WCHAR *fullName) +{ + HRESULT hr = S_OK; + ULONG Name; + + // We must have a current scope. + if (m_currentScope == k_noScope) + return E_FAIL; + + // We must have a name. + if (!fullName) + return E_INVALIDARG; + + + SymUsingNamespace *use; + IfNullGo( use = m_MethodInfo.m_usings.next()); + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(fullName, (UINT32 *)&Name); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + use->SetName(Name); + + use->SetParentScope(m_currentScope); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetMethodSourceRange +//----------------------------------------------------------- +COM_METHOD SymWriter::SetMethodSourceRange( + ISymUnmanagedDocumentWriter *startDoc, + ULONG32 startLine, + ULONG32 startColumn, + ISymUnmanagedDocumentWriter *endDoc, + ULONG32 endLine, + ULONG32 endColumn) +{ + // This symbol store doesn't support source ranges. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// UnicodeToUTF8 +// Translate the Unicode string to a UTF8 string +// Return the length in UTF8 of the Unicode string +// Including NULL terminator +//----------------------------------------------------------- +inline int WINAPI UnicodeToUTF8( + LPCWSTR pUni, // Unicode string + __out_bcount_opt(cbUTF) PSTR pUTF8, // [optional, out] Buffer for UTF8 string + int cbUTF // length of UTF8 buffer +) +{ + // Pass in the length including the NULL terminator + int cchSrc = (int)wcslen(pUni)+1; + return WideCharToMultiByte(CP_UTF8, 0, pUni, cchSrc, pUTF8, cbUTF, NULL, NULL); +} + +//----------------------------------------------------------- +// SymWriter GetDebugCVInfo +// Get the size and potentially the debug info +//----------------------------------------------------------- +COM_METHOD SymWriter::GetDebugCVInfo( + DWORD cbBuf, // [optional] Size of buf + DWORD *pcbBuf, // [out] Size needed for the DebugInfo + BYTE buf[]) // [optional, out] Buffer for DebugInfo +{ + + if ( m_szPath == NULL || *m_szPath == 0 ) + return E_UNEXPECTED; + + // We need to change the .ildb extension to .pdb to be + // compatible with VS7 + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + if (_wsplitpath_s( m_szPath, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + return E_FAIL; + if (_wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("pdb") )) + return E_FAIL; + + // Get UTF-8 string size, including the Null Terminator + int Utf8Length = UnicodeToUTF8( fullpath, NULL, 0 ); + if (Utf8Length < 0 ) + return HRESULT_FROM_GetLastError(); + + DWORD dwSize = sizeof(RSDSI) + DWORD(Utf8Length); + + // If the caller is just checking for the size + if ( cbBuf == 0 && pcbBuf != NULL ) + { + *pcbBuf = dwSize; + return S_OK; + } + + if (cbBuf < dwSize) + { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + if ( buf == NULL ) + { + return E_INVALIDARG; + } + + RSDSI* pRsdsi = (RSDSI*)buf; + pRsdsi->dwSig = VAL32(0x53445352); // "SDSR"; + pRsdsi->guidSig = ILDB_VERSION_GUID; + SwapGuid(&(pRsdsi->guidSig)); + // Age of 0 represent VC6.0 format so make sure it's 1 + pRsdsi->age = VAL32(1); + UnicodeToUTF8( fullpath, pRsdsi->szPDB, Utf8Length ); + if ( pcbBuf ) + *pcbBuf = dwSize; + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter GetDebugInfo +// Get the size and potentially the debug info +//----------------------------------------------------------- +COM_METHOD SymWriter::GetDebugInfo( + IMAGE_DEBUG_DIRECTORY *pIDD, // [out] IDD to fill in + DWORD cData, // [optional] size of data + DWORD *pcData, // [optional, out] return needed size for DebugInfo + BYTE data[]) // [optional] Buffer to store into +{ + HRESULT hr = S_OK; + if ( cData == 0 && pcData != NULL ) + { + // just checking for the size + return GetDebugCVInfo( 0, pcData, NULL ); + } + + if ( pIDD == NULL ) + return E_INVALIDARG; + + DWORD cTheData = 0; + IfFailGo( GetDebugCVInfo( cData, &cTheData, data ) ); + + memset( pIDD, 0, sizeof( *pIDD ) ); + pIDD->Type = VAL32(IMAGE_DEBUG_TYPE_CODEVIEW); + pIDD->SizeOfData = VAL32(cTheData); + + if ( pcData ) { + *pcData = cTheData; + } + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::RemapToken(mdToken oldToken, mdToken newToken) +{ + HRESULT hr = NOERROR; + if (oldToken != newToken) + { + // We only care about methods + if ((TypeFromToken(oldToken) == mdtMethodDef) || + (TypeFromToken(newToken) == mdtMethodDef)) + { + // Make sure they are both methods + _ASSERTE(TypeFromToken(newToken) == mdtMethodDef); + _ASSERTE(TypeFromToken(oldToken) == mdtMethodDef); + + // Make sure we sort before saving + m_sortMethodEntries = true; + + // Check to see if we're trying to map a token we know about + unsigned i; + for (i = 0; i < m_MethodInfo.m_methods.count(); i++) + { + if (m_MethodInfo.m_methods[i].MethodToken() == oldToken) + { + // Remember the map, we need to actually do the actual + // mapping later because we might already have a function + // with a token 'newToken' + SymMap *pMethodMap; + IfNullGo( pMethodMap = m_MethodMap.next() ); + pMethodMap->m_MethodToken = newToken; + pMethodMap->MethodEntry = i; + break; + } + } + } + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter Write +// Write the information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::Write(void *pData, DWORD SizeOfData) +{ + HRESULT hr = NOERROR; + DWORD NumberOfBytesWritten = 0; + if (m_pIStream) + { + IfFailGo(m_pIStream->Write(pData, + SizeOfData, + &NumberOfBytesWritten)); + } + else + { + // Write out a signature to recognize that we're an ildb + if (!WriteFile(m_hFile, pData, SizeOfData, &NumberOfBytesWritten, NULL)) + return HrFromWin32(GetLastError()); + } + _ASSERTE(NumberOfBytesWritten == SizeOfData); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter WriteStringPool +// Write the information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::WriteStringPool() +{ + IStream *pIStream = NULL; + BYTE *pStreamMem = NULL; + + HRESULT hr = NOERROR; + if (m_pIStream) + { + IfFailGo(m_pStringPool->PersistToStream(m_pIStream)); + } + else + { + LARGE_INTEGER disp = { {0, 0} }; + DWORD NumberOfBytes; + DWORD SizeOfData; + STATSTG statStg; + + IfFailGo(CreateStreamOnHGlobal(NULL, + TRUE, + &pIStream)); + + IfFailGo(m_pStringPool->PersistToStream(pIStream)); + + IfFailGo(pIStream->Stat(&statStg, STATFLAG_NONAME)); + SizeOfData = statStg.cbSize.u.LowPart; + + IfFailGo(pIStream->Seek(disp, STREAM_SEEK_SET, NULL)); + + pStreamMem = NEW(BYTE[SizeOfData]); + IfFailGo(pIStream->Read(pStreamMem, SizeOfData, &NumberOfBytes)); + + if (!WriteFile(m_hFile, pStreamMem, SizeOfData, &NumberOfBytes, NULL)) + return HrFromWin32(GetLastError()); + + _ASSERTE(NumberOfBytes == SizeOfData); + + } +ErrExit: + RELEASE(pIStream); + DELETEARRAY(pStreamMem); + return hr; +} + +//----------------------------------------------------------- +// SymWriter WritePDB +// Write the PDB information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::WritePDB() +{ + + HRESULT hr = NOERROR; + GUID ildb_guid = ILDB_VERSION_GUID; + + // Make sure the ModuleLevelInfo is set + ModuleLevelInfo.m_CountOfVars = VAL32(m_MethodInfo.m_vars.count()); + ModuleLevelInfo.m_CountOfBytes = VAL32(m_MethodInfo.m_bytes.count()); + ModuleLevelInfo.m_CountOfUsing = VAL32(m_MethodInfo.m_usings.count()); + ModuleLevelInfo.m_CountOfScopes = VAL32(m_MethodInfo.m_scopes.count()); + ModuleLevelInfo.m_CountOfMethods = VAL32(m_MethodInfo.m_methods.count()); + if (m_pStringPool) + { + DWORD dwSaveSize; + IfFailGo(m_pStringPool->GetSaveSize((UINT32 *)&dwSaveSize)); + ModuleLevelInfo.m_CountOfStringBytes = VAL32(dwSaveSize); + } + else + { + ModuleLevelInfo.m_CountOfStringBytes = 0; + } + ModuleLevelInfo.m_CountOfConstants = VAL32(m_MethodInfo.m_constants.count()); + ModuleLevelInfo.m_CountOfDocuments = VAL32(m_MethodInfo.m_documents.count()); + ModuleLevelInfo.m_CountOfSequencePoints = VAL32(m_MethodInfo.m_auxSequencePoints.count()); + + // Open the file + if (m_pIStream == NULL) + { + // We need to open the output file. + m_hFile = WszCreateFile(m_szPath, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (m_hFile == INVALID_HANDLE_VALUE) + { + IfFailGo(HrFromWin32(GetLastError())); + } + } + else + { + // We're writing to a stream. Make sure we're at the beginning + // (eg. if this is being called more than once). + // Note that technically we should probably call SetSize to truncate the + // stream to ensure we don't leave reminants of the previous contents + // at the end of the new stream. But with our current CGrowableStream + // implementation, this would have a big performance impact (causing us to + // do linear growth and lots of reallocations at every write). We only + // ever add data to a symbol writer (don't remove anything), and so subsequent + // streams should always get larger. Regardless, ILDB supports trailing garbage + // without a problem (we used to always have the remainder of a page at the end + // of the stream), and so this is not an issue of correctness. + LARGE_INTEGER pos0; + pos0.QuadPart = 0; + IfFailGo(m_pIStream->Seek(pos0, STREAM_SEEK_SET, NULL)); + } + +#if _DEBUG + // We need to make sure the Variant entry in the constants is 8 byte + // aligned so make sure everything up to the there is aligned correctly + if ((ILDB_SIGNATURE_SIZE % 8) || + (sizeof(PDBInfo) % 8) || + (sizeof(GUID) % 8)) + { + _ASSERTE(!"We need to safe the data in an aligned format"); + } +#endif + + // Write out a signature to recognize that we're an ildb + IfFailGo(Write((void *)ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)); + // Write out a guid representing the version + SwapGuid(&ildb_guid); + IfFailGo(Write((void *)&ildb_guid, sizeof(GUID))); + + // Now we need to write the Project level + IfFailGo(Write(&ModuleLevelInfo, sizeof(PDBInfo))); + + // Now we have to write out each array as appropriate + IfFailGo(Write(m_MethodInfo.m_constants.m_array, sizeof(SymConstant) * m_MethodInfo.m_constants.count())); + + // These members are all 4 byte aligned + IfFailGo(Write(m_MethodInfo.m_methods.m_array, sizeof(SymMethodInfo) * m_MethodInfo.m_methods.count())); + IfFailGo(Write(m_MethodInfo.m_scopes.m_array, sizeof(SymLexicalScope) * m_MethodInfo.m_scopes.count())); + IfFailGo(Write(m_MethodInfo.m_vars.m_array, sizeof(SymVariable) * m_MethodInfo.m_vars.count())); + IfFailGo(Write(m_MethodInfo.m_usings.m_array, sizeof(SymUsingNamespace) * m_MethodInfo.m_usings.count())); + IfFailGo(Write(m_MethodInfo.m_auxSequencePoints.m_array, sizeof(SequencePoint) * m_MethodInfo.m_auxSequencePoints.count())); + IfFailGo(Write(m_MethodInfo.m_documents.m_array, sizeof(DocumentInfo) * m_MethodInfo.m_documents.count())); + IfFailGo(Write(m_MethodInfo.m_bytes.m_array, sizeof(BYTE) * m_MethodInfo.m_bytes.count())); + IfFailGo(WriteStringPool()); + +ErrExit: + if (m_hFile) + CloseHandle(m_hFile); + return hr; +} + +/* ------------------------------------------------------------------------- * + * SymDocumentWriter class + * ------------------------------------------------------------------------- */ +SymDocumentWriter::SymDocumentWriter( + UINT32 DocumentEntry, + SymWriter *pEmitter +) : + m_refCount ( 0 ), + m_DocumentEntry ( DocumentEntry ), + m_pEmitter( pEmitter ) +{ + _ASSERTE(pEmitter); + m_pEmitter->AddRef(); +} + +SymDocumentWriter::~SymDocumentWriter() +{ + // Note that this must be thread-safe - it may be invoked on the finalizer thread + RELEASE(m_pEmitter); +} + +COM_METHOD SymDocumentWriter::QueryInterface(REFIID riid, void **ppInterface) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedDocumentWriter) + *ppInterface = (ISymUnmanagedDocumentWriter*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedDocumentWriter*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// SymDocumentWriter SetSource +//----------------------------------------------------------- +COM_METHOD SymDocumentWriter::SetSource(ULONG32 sourceSize, + BYTE source[]) +{ + return m_pEmitter->SetDocumentSrc(m_DocumentEntry, sourceSize, source); +} + +//----------------------------------------------------------- +// SymDocumentWriter SetCheckSum +//----------------------------------------------------------- +COM_METHOD SymDocumentWriter::SetCheckSum(GUID algorithmId, + ULONG32 checkSumSize, + BYTE checkSum[]) +{ + return m_pEmitter->SetDocumentCheckSum(m_DocumentEntry, algorithmId, checkSumSize, checkSum); +} + + +//----------------------------------------------------------- +// DocumentInfo SetDocumentWriter +//----------------------------------------------------------- +// Set the pointer to the SymDocumentWriter instance corresponding to this instance of DocumentInfo +// An argument of NULL will call Release +// Arguments +// input: pDoc - pointer to the associated SymDocumentWriter or NULL + +void DocumentInfo::SetDocumentWriter(SymDocumentWriter * pDoc) +{ + if (m_pDocumentWriter != NULL) + { + m_pDocumentWriter->Release(); + } + m_pDocumentWriter = pDoc; + if (m_pDocumentWriter != NULL) + { + pDoc->AddRef(); + } +} diff --git a/src/debug/ildbsymlib/symwrite.h b/src/debug/ildbsymlib/symwrite.h new file mode 100644 index 0000000000..055b8ec21f --- /dev/null +++ b/src/debug/ildbsymlib/symwrite.h @@ -0,0 +1,1226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: SymWrite.h +// + +// =========================================================================== + +#ifndef SYMWRITE_H_ +#define SYMWRITE_H_ +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#include <windows.h> +#include <stdlib.h> +#include <stdio.h> + +#include "cor.h" +#include "umisc.h" +#include "stgpool.h" +#include "safemath.h" + +#include <corsym.h> +#include "pdbdata.h" + +class SymDocumentWriter; + +#if BIGENDIAN +/*** +*PUBLIC void VariantSwap +*Purpose: +* Swap the Variant members +* +*Entry: +* SrcInBigEndian = whether pvarg is in BIGENDIAN or not +* pvargDest = Destination variant +* pvarg = pointer to a VARIANT to swap +* +*Exit: +* Filled in pvarDest +* +***********************************************************************/ +inline HRESULT VariantSwap(bool SrcInBigEndian, VARIANT FAR *pvargDest, VARIANT FAR* pvarg) +{ + if (pvargDest == NULL || pvarg == NULL) + return E_INVALIDARG; + VARTYPE vt = VT_EMPTY; + + if (SrcInBigEndian) + { + vt = V_VT(pvarg); + } + *(UINT32*)pvargDest = VAL32(*(UINT32*)pvarg); + if (!SrcInBigEndian) + { + vt = V_VT(pvargDest); + } + + switch (vt) + { + case VT_EMPTY: + case VT_NULL: + // No Value to swap + break; + + // 1 byte + case VT_I1: + case VT_UI1: + V_I1(pvargDest) = V_I1(pvarg); + break; + + // 2 bytes + case VT_I2: + case VT_UI2: + case VT_INT: + case VT_UINT: + case VT_BOOL: + V_I2(pvargDest) = VAL16(V_I2(pvarg)); + break; + + // 4 bytes + case VT_I4: + case VT_UI4: + case VT_R4: + V_I4(pvargDest) = VAL32(V_I4(pvarg)); + break; + + // 8 bytes + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_DATE: + V_I8(pvargDest) = VAL64(V_I8(pvarg)); + break; + + case VT_DECIMAL: + DECIMAL_HI32(V_DECIMAL(pvargDest)) = VAL32(DECIMAL_HI32(V_DECIMAL(pvarg))); + DECIMAL_LO32(V_DECIMAL(pvargDest)) = VAL32(DECIMAL_LO32(V_DECIMAL(pvarg))); + DECIMAL_MID32(V_DECIMAL(pvargDest)) = VAL32(DECIMAL_MID32(V_DECIMAL(pvarg))); + break; + + // These aren't currently supported + case VT_CY: //6 + case VT_BSTR: //8 + case VT_DISPATCH: //9 + case VT_ERROR: //10 + case VT_VARIANT: //12 + case VT_UNKNOWN: //13 + case VT_VOID: //24 + case VT_HRESULT: //25 + case VT_PTR: //26 + case VT_SAFEARRAY: //27 + case VT_CARRAY: //28 + case VT_USERDEFINED://29 + case VT_LPSTR: //30 + case VT_LPWSTR: //31 + case VT_FILETIME: //64 + case VT_BLOB: //65 + case VT_STREAM: //66 + case VT_STORAGE: //67 + case VT_STREAMED_OBJECT: //68 + case VT_STORED_OBJECT: //69 + case VT_BLOB_OBJECT: //70 + case VT_CF: //71 + case VT_CLSID: //72 + default: + _ASSERTE(!"NYI"); + break; + } + return NOERROR; +} +#endif // BIGENDIAN + +// Default space sizes for the various arrays. Make it too small in a +// checked build so we exercise the growing code. +#ifdef _DEBUG +#define DEF_LOCAL_SPACE 2 +#define DEF_MISC_SPACE 64 +#else +#define DEF_LOCAL_SPACE 64 +#define DEF_MISC_SPACE 1024 +#endif + +/* ------------------------------------------------------------------------- * + * SymVariable struct + * ------------------------------------------------------------------------- */ +struct SymVariable +{ +private: + UINT32 m_Scope; // index of parent scope + UINT32 m_Name; // index into misc byte array + ULONG32 m_Attributes; // Attributes + UINT32 m_Signature; // index into misc byte array + ULONG32 m_SignatureSize; // Signature size + ULONG32 m_AddrKind; // Address Kind + ULONG32 m_Addr1; // Additional info + ULONG32 m_Addr2; + ULONG32 m_Addr3; + ULONG32 m_StartOffset; // StartOffset + ULONG32 m_EndOffset; // EndOffset + ULONG32 m_Sequence; + BOOL m_IsParam; // parameter? + BOOL m_IsHidden; // Is not visible to the user + +public: + UINT32 Scope() + { + return VAL32(m_Scope); + } + void SetScope(UINT32 Scope) + { + m_Scope = VAL32(Scope); + } + + UINT32 Name() + { + return VAL32(m_Name); + } + void SetName(UINT32 Name) + { + m_Name = VAL32(Name); + } + + ULONG32 Attributes() + { + return VAL32(m_Attributes); + } + void SetAttributes(ULONG32 Attributes) + { + m_Attributes = VAL32(Attributes); + } + + UINT32 Signature() + { + return VAL32(m_Signature); + } + void SetSignature(UINT32 Signature) + { + m_Signature = VAL32(Signature); + } + ULONG32 SignatureSize() + { + return VAL32(m_SignatureSize); + } + void SetSignatureSize(ULONG32 SignatureSize) + { + m_SignatureSize = VAL32(SignatureSize); + } + + ULONG32 AddrKind() + { + return VAL32(m_AddrKind); + } + void SetAddrKind(ULONG32 AddrKind) + { + m_AddrKind = VAL32(AddrKind); + } + ULONG32 Addr1() + { + return VAL32(m_Addr1); + } + void SetAddr1(ULONG32 Addr1) + { + m_Addr1 = VAL32(Addr1); + } + + ULONG32 Addr2() + { + return VAL32(m_Addr2); + } + void SetAddr2(ULONG32 Addr2) + { + m_Addr2 = VAL32(Addr2); + } + + ULONG32 Addr3() + { + return VAL32(m_Addr3); + } + void SetAddr3(ULONG32 Addr3) + { + m_Addr3 = VAL32(Addr3); + } + + ULONG32 StartOffset() + { + return VAL32(m_StartOffset); + } + void SetStartOffset(ULONG32 StartOffset) + { + m_StartOffset = VAL32(StartOffset); + } + ULONG32 EndOffset() + { + return VAL32(m_EndOffset); + } + void SetEndOffset(ULONG EndOffset) + { + m_EndOffset = VAL32(EndOffset); + } + ULONG32 Sequence() + { + return VAL32(m_Sequence); + } + void SetSequence(ULONG32 Sequence) + { + m_Sequence = VAL32(Sequence); + } + + BOOL IsParam() + { + return VAL32(m_IsParam); + } + void SetIsParam(BOOL IsParam) + { + m_IsParam = IsParam; + } + BOOL IsHidden() + { + return VAL32(m_IsHidden); + } + void SetIsHidden(BOOL IsHidden) + { + m_IsHidden = IsHidden; + } +}; + +/* ------------------------------------------------------------------------- * + * SymLexicalScope struct + * ------------------------------------------------------------------------- */ +struct SymLexicalScope +{ +private: + + UINT32 m_ParentScope; // parent index (-1 for no parent) + ULONG32 m_StartOffset; // start offset + ULONG32 m_EndOffset; // end offset + BOOL m_HasChildren; // scope has children + BOOL m_HasVars; // scope has vars? +public: + UINT32 ParentScope() + { + return VAL32(m_ParentScope); + } + void SetParentScope(UINT32 ParentScope) + { + m_ParentScope = VAL32(ParentScope); + } + + ULONG32 StartOffset() + { + return VAL32(m_StartOffset); + } + void SetStartOffset(ULONG32 StartOffset) + { + m_StartOffset = VAL32(StartOffset); + } + ULONG32 EndOffset() + { + return VAL32(m_EndOffset); + } + void SetEndOffset(ULONG32 EndOffset) + { + m_EndOffset = VAL32(EndOffset); + } + BOOL HasChildren() + { + return m_HasChildren; + } + void SetHasChildren(BOOL HasChildren) + { + m_HasChildren = HasChildren; + } + BOOL HasVars() + { + return m_HasVars; + } + void SetHasVars(BOOL HasVars) + { + m_HasVars = HasVars; + } + +}; + +/* ------------------------------------------------------------------------- * + * SymUsingNamespace struct + * ------------------------------------------------------------------------- */ +struct SymUsingNamespace +{ +private: + + UINT32 m_ParentScope; // index of parent scope + UINT32 m_Name; // Index of name +public: + UINT32 ParentScope() + { + return VAL32(m_ParentScope); + } + void SetParentScope(UINT32 ParentScope) + { + m_ParentScope = VAL32(ParentScope); + } + UINT32 Name() + { + return VAL32(m_Name); + } + void SetName(UINT32 Name) + { + m_Name = VAL32(Name); + } +}; + +/* ------------------------------------------------------------------------- * + * SymConstant struct + * ------------------------------------------------------------------------- */ +struct SymConstant +{ +private: + + VARIANT m_Value; // Constant Value + UINT32 m_ParentScope; // Parent scope + UINT32 m_Name; // Name index + UINT32 m_Signature; // Signature index + ULONG32 m_SignatureSize;// Signature size + UINT32 m_ValueBstr; // If the variant is a bstr, store the string + +public: + UINT32 ParentScope() + { + return VAL32(m_ParentScope); + } + void SetParentScope(UINT32 ParentScope) + { + m_ParentScope = VAL32(ParentScope); + } + UINT32 Name() + { + return VAL32(m_Name); + } + void SetName(UINT32 Name) + { + m_Name = VAL32(Name); + } + UINT32 Signature() + { + return VAL32(m_Signature); + } + void SetSignature(UINT32 Signature) + { + m_Signature = VAL32(Signature); + } + ULONG32 SignatureSize() + { + return VAL32(m_SignatureSize); + } + void SetSignatureSize(ULONG32 SignatureSize) + { + m_SignatureSize = VAL32(SignatureSize); + } + VARIANT Value(UINT32 *pValueBstr) + { + *pValueBstr = VAL32(m_ValueBstr); +#if BIGENDIAN + VARIANT VariantValue; + VariantInit(&VariantValue); + // VT_BSTR's are dealt with ValueBStr + if (m_ValueBstr) + { + V_VT(&VariantValue) = VT_BSTR; + } + else + { + VariantSwap(false, &VariantValue, &m_Value); + } + return VariantValue; +#else + return m_Value; +#endif + } + void SetValue(VARIANT VariantValue, UINT32 ValueBstr) + { + m_Value = VariantValue; + m_ValueBstr = VAL32(ValueBstr); +#if BIGENDIAN + // VT_BSTR's are dealt with ValueBStr + if (m_ValueBstr) + { + V_VT(&m_Value) = VAL16(VT_BSTR); + } + else + { + VariantSwap(true, &m_Value, &VariantValue); + } +#endif + } +}; + +/* ------------------------------------------------------------------------- * + * SymMethodInfo struct + * ------------------------------------------------------------------------- */ +struct SymMethodInfo +{ +private: + + mdMethodDef m_MethodToken; // Method token + + // Start/End Entries into the respective tables + // End values are extents - one past the last index (and so may actually be an index off + // the end of the array). Start may equal end if the method has none of the item. + UINT32 m_StartScopes; + UINT32 m_EndScopes; + UINT32 m_StartVars; + UINT32 m_EndVars; + UINT32 m_StartUsing; + UINT32 m_EndUsing; + UINT32 m_StartConstant; + UINT32 m_EndConstant; + UINT32 m_StartDocuments; + UINT32 m_EndDocuments; + UINT32 m_StartSequencePoints; + UINT32 m_EndSequencePoints; + +public: + static int __cdecl compareMethods(const void *elem1, const void *elem2 ); + + mdMethodDef MethodToken() + { + return VAL32(m_MethodToken); + } + void SetMethodToken(mdMethodDef MethodToken) + { + m_MethodToken = VAL32(MethodToken); + } + UINT32 StartScopes() + { + return VAL32(m_StartScopes); + } + void SetStartScopes(UINT32 StartScopes) + { + m_StartScopes = VAL32(StartScopes); + } + UINT32 EndScopes() + { + return VAL32(m_EndScopes); + } + void SetEndScopes(UINT32 EndScopes) + { + m_EndScopes = VAL32(EndScopes); + } + UINT32 StartVars() + { + return VAL32(m_StartVars); + } + void SetStartVars(UINT32 StartVars) + { + m_StartVars = VAL32(StartVars); + } + UINT32 EndVars() + { + return VAL32(m_EndVars); + } + void SetEndVars(UINT32 EndVars) + { + m_EndVars = VAL32(EndVars); + } + UINT32 StartUsing() + { + return VAL32(m_StartUsing); + } + void SetStartUsing(UINT32 StartUsing) + { + m_StartUsing = VAL32(StartUsing); + } + UINT32 EndUsing() + { + return VAL32(m_EndUsing); + } + void SetEndUsing(UINT32 EndUsing) + { + m_EndUsing = VAL32(EndUsing); + } + UINT32 StartConstant() + { + return VAL32(m_StartConstant); + } + void SetStartConstant(UINT32 StartConstant) + { + m_StartConstant = VAL32(StartConstant); + } + UINT32 EndConstant() + { + return VAL32(m_EndConstant); + } + void SetEndConstant(UINT32 EndConstant) + { + m_EndConstant = VAL32(EndConstant); + } + UINT32 StartDocuments() + { + return VAL32(m_StartDocuments); + } + void SetStartDocuments(UINT32 StartDocuments) + { + m_StartDocuments = VAL32(StartDocuments); + } + UINT32 EndDocuments() + { + return VAL32(m_EndDocuments); + } + void SetEndDocuments(UINT32 EndDocuments) + { + m_EndDocuments = VAL32(EndDocuments); + } + UINT32 StartSequencePoints() + { + return VAL32(m_StartSequencePoints); + } + void SetStartSequencePoints(UINT32 StartSequencePoints) + { + m_StartSequencePoints = VAL32(StartSequencePoints); + } + UINT32 EndSequencePoints() + { + return VAL32(m_EndSequencePoints); + } + void SetEndSequencePoints(UINT32 EndSequencePoints) + { + m_EndSequencePoints = VAL32(EndSequencePoints); + } +}; + +/* ------------------------------------------------------------------------- * + * SymMap struct + * ------------------------------------------------------------------------- */ +struct SymMap +{ + mdMethodDef m_MethodToken; // New Method token + UINT32 MethodEntry; // Method Entry +}; + +/* ------------------------------------------------------------------------- * + * SequencePoint struct + * ------------------------------------------------------------------------- */ +struct SequencePoint { + +private: + + DWORD m_Offset; + DWORD m_StartLine; + DWORD m_StartColumn; + DWORD m_EndLine; + DWORD m_EndColumn; + DWORD m_Document; + +public: + bool IsWithin(ULONG32 line, ULONG32 column); + bool IsWithinLineOnly(ULONG32 line); + bool IsGreaterThan(ULONG32 line, ULONG32 column); + bool IsLessThan(ULONG32 line, ULONG32 column); + bool IsUserLine(); + static int __cdecl compareAuxLines(const void *elem1, const void *elem2 ); + + DWORD Offset() + { + return VAL32(m_Offset); + } + void SetOffset(DWORD Offset) + { + m_Offset = VAL32(Offset); + } + DWORD StartLine() + { + return VAL32(m_StartLine); + } + void SetStartLine(DWORD StartLine) + { + m_StartLine = VAL32(StartLine); + } + + DWORD StartColumn() + { + return VAL32(m_StartColumn); + } + void SetStartColumn(DWORD StartColumn) + { + m_StartColumn = VAL32(StartColumn); + } + + DWORD EndLine() + { + return VAL32(m_EndLine); + } + void SetEndLine(DWORD EndLine) + { + m_EndLine = VAL32(EndLine); + } + DWORD EndColumn() + { + return VAL32(m_EndColumn); + } + void SetEndColumn(DWORD EndColumn) + { + m_EndColumn = VAL32(EndColumn); + } + DWORD Document() + { + return VAL32(m_Document); + } + void SetDocument(DWORD Document) + { + m_Document = VAL32(Document); + } +}; + + +/* ------------------------------------------------------------------------- * + * DocumentInfo struct + * ------------------------------------------------------------------------- */ +typedef struct DocumentInfo { + +private: + + GUID m_Language; + GUID m_LanguageVendor; + GUID m_DocumentType; + GUID m_AlgorithmId; + DWORD m_CheckSumSize; + UINT32 m_CheckSumEntry; + UINT32 m_SourceSize; + UINT32 m_SourceEntry; + UINT32 m_UrlEntry; + SymDocumentWriter * m_pDocumentWriter; + +public: + + GUID Language() + { + GUID TmpGuid = m_Language; + SwapGuid(&TmpGuid); + return TmpGuid; + } + void SetLanguage(GUID Language) + { + SwapGuid(&Language); + m_Language = Language; + } + GUID LanguageVendor() + { + GUID TmpGuid = m_LanguageVendor; + SwapGuid(&TmpGuid); + return TmpGuid; + } + void SetLanguageVendor(GUID LanguageVendor) + { + SwapGuid(&LanguageVendor); + m_LanguageVendor = LanguageVendor; + } + GUID DocumentType() + { + GUID TmpGuid = m_DocumentType; + SwapGuid(&TmpGuid); + return TmpGuid; + } + void SetDocumentType(GUID DocumentType) + { + SwapGuid(&DocumentType); + m_DocumentType = DocumentType; + } + + // Set the pointer to the SymDocumentWriter instance corresponding to this instance of DocumentInfo + // An argument of NULL will call Release + void SetDocumentWriter(SymDocumentWriter * pDoc); + + // get the associated SymDocumentWriter + SymDocumentWriter * DocumentWriter() + { + return m_pDocumentWriter; + } + + GUID AlgorithmId() + { + GUID TmpGuid = m_AlgorithmId; + SwapGuid(&TmpGuid); + return TmpGuid; + } + void SetAlgorithmId(GUID AlgorithmId) + { + SwapGuid(&AlgorithmId); + m_AlgorithmId = AlgorithmId; + } + + DWORD CheckSumSize() + { + return VAL32(m_CheckSumSize); + } + void SetCheckSymSize(DWORD CheckSumSize) + { + m_CheckSumSize = VAL32(CheckSumSize); + } + UINT32 CheckSumEntry() + { + return VAL32(m_CheckSumEntry); + } + void SetCheckSumEntry(UINT32 CheckSumEntry) + { + m_CheckSumEntry = VAL32(CheckSumEntry); + } + UINT32 SourceSize() + { + return VAL32(m_SourceSize); + } + void SetSourceSize(UINT32 SourceSize) + { + m_SourceSize = VAL32(SourceSize); + } + UINT32 SourceEntry() + { + return VAL32(m_SourceEntry); + } + void SetSourceEntry(UINT32 SourceEntry) + { + m_SourceEntry = VAL32(SourceEntry); + } + UINT32 UrlEntry() + { + return VAL32(m_UrlEntry); + } + void SetUrlEntry(UINT32 UrlEntry) + { + m_UrlEntry = VAL32(UrlEntry); + } + +} DocumentInfo; + +template <class T> +class ArrayStorage +{ +public: + + ArrayStorage( int initialSize = 0 ) + : m_spaceSize(0), m_instanceCount( 0 ), m_array( NULL ) + { + grow( initialSize ); + } + ~ArrayStorage() + { + + if ( m_array ) + DELETEARRAY(m_array); + m_array = NULL; + m_spaceSize = 0; + m_instanceCount = 0; + } + T* next() + { + if( !grow ( m_instanceCount ) ) + return NULL; + _ASSERTE( m_instanceCount < m_spaceSize ); + return &m_array[ m_instanceCount++ ]; + } + bool grab(UINT32 n, UINT32 * pIndex) + { + S_UINT32 newSize = S_UINT32(m_instanceCount) + S_UINT32(n); + if (newSize.IsOverflow()) + return false; + if (!grow(newSize.Value())) + return false; + _ASSERTE( m_instanceCount < m_spaceSize ); + *pIndex = m_instanceCount; + m_instanceCount += n; + return true; + } + + T& operator[]( UINT32 i ) { + _ASSERTE( i < m_instanceCount ); + if (i >= m_instanceCount) + { + // Help mitigate the impact of buffer overflow + // Fail fast with a null-reference AV + return *(static_cast<T*>(0)) ; + } + return m_array[ i ]; + } + void reset() { + m_instanceCount = 0; + } + UINT32 size() { + return m_spaceSize; + } + UINT32 count() { + return m_instanceCount; + } + + UINT32 m_spaceSize; // Total size of array in elements + UINT32 m_instanceCount; // total T's in the file + T *m_array; // array of T's +private: + bool grow( UINT32 n ) + { + if (n >= m_spaceSize) + { + // Make a new, bigger array. + UINT32 newSpaceSize; + + if (n == 0) + newSpaceSize = DEF_LOCAL_SPACE; + else + newSpaceSize = max( m_spaceSize * 2, n); + + // Make sure we're not asking for more than 4GB of bytes to ensure no integer-overflow attacks are possible + S_UINT32 newBytes = S_UINT32(newSpaceSize) * S_UINT32(sizeof(T)); + if (newBytes.IsOverflow()) + return false; + + T *newTs; + newTs = NEW(T[newSpaceSize]); + if ( newTs == NULL ) + return false; + + // Copy over the old Ts. + memcpy(newTs, m_array, + sizeof(T) * m_spaceSize); + + // Delete the old Ts. + DELETEARRAY(m_array); + + // Hang onto the new array. + m_array = newTs; + m_spaceSize = newSpaceSize; + } + return true; + } + +}; + +typedef struct MethodInfo { + + ArrayStorage<SymMethodInfo> m_methods; // Methods information + ArrayStorage<SymLexicalScope> m_scopes; // Scope information for the method + ArrayStorage<SymVariable> m_vars; // Variables + ArrayStorage<SymUsingNamespace> m_usings; // using/imports + ArrayStorage<SymConstant> m_constants; // Constants + ArrayStorage<DocumentInfo> m_documents; // Document Source Format + ArrayStorage<SequencePoint> m_auxSequencePoints; // Sequence Points + // Array of various bytes (variable signature, etc) + ArrayStorage<BYTE> m_bytes; + + +public: + + MethodInfo() : + m_bytes( DEF_MISC_SPACE ) + { + } +} MethodInfo; + +/* ------------------------------------------------------------------------- * + * SymWriter class + * ------------------------------------------------------------------------- */ + +class SymWriter : public ISymUnmanagedWriter3 +{ +public: + SymWriter(); + virtual ~SymWriter(); + + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + // Note that this must be thread-safe - it may be invoked on the finalizer thread + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + COM_METHOD QueryInterface(REFIID riid, void **ppInterface); + + //----------------------------------------------------------- + // ISymUnmanagedWriter + //----------------------------------------------------------- + COM_METHOD DefineDocument(const WCHAR *url, + const GUID *language, + const GUID *languageVendor, + const GUID *documentType, + ISymUnmanagedDocumentWriter **pRetVal); + COM_METHOD SetUserEntryPoint(mdMethodDef entryMethod); + COM_METHOD OpenMethod(mdMethodDef method); + COM_METHOD CloseMethod(); + COM_METHOD DefineSequencePoints(ISymUnmanagedDocumentWriter *document, + ULONG32 spCount, + ULONG32 offsets[], + ULONG32 lines[], + ULONG32 columns[], + ULONG32 endLines[], + ULONG32 encColumns[]); + COM_METHOD OpenScope(ULONG32 startOffset, ULONG32 *scopeID); + COM_METHOD CloseScope(ULONG32 endOffset); + COM_METHOD SetScopeRange(ULONG32 scopeID, ULONG32 startOffset, ULONG32 endOffset); + COM_METHOD DefineLocalVariable(const WCHAR *name, + ULONG32 attributes, + ULONG32 cSig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3, + ULONG32 startOffset, ULONG32 endOffset); + COM_METHOD DefineParameter(const WCHAR *name, + ULONG32 attributes, + ULONG32 sequence, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3); + COM_METHOD DefineField(mdTypeDef parent, + const WCHAR *name, + ULONG32 attributes, + ULONG32 cSig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3); + COM_METHOD DefineGlobalVariable(const WCHAR *name, + ULONG32 attributes, + ULONG32 cSig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3); + COM_METHOD Close(); + COM_METHOD SetSymAttribute(mdToken parent, + const WCHAR *name, + ULONG32 cData, + BYTE data[]); + COM_METHOD OpenNamespace(const WCHAR *name); + COM_METHOD CloseNamespace(); + COM_METHOD UsingNamespace(const WCHAR *fullName); + COM_METHOD SetMethodSourceRange(ISymUnmanagedDocumentWriter *startDoc, + ULONG32 startLine, + ULONG32 startColumn, + ISymUnmanagedDocumentWriter *endDoc, + ULONG32 endLine, + ULONG32 endColumn); + COM_METHOD GetDebugCVInfo(DWORD cData, + DWORD *pcData, + BYTE data[]); + + COM_METHOD Initialize(IUnknown *emitter, + const WCHAR *filename, + IStream *pIStream, + BOOL fFullBuild); + + COM_METHOD Initialize2(IUnknown *emitter, + const WCHAR *pdbTempPath, // location to write pdb file + IStream *pIStream, + BOOL fFullBuild, + const WCHAR *pdbFinalPath); // location exe should contain for pdb file + + COM_METHOD GetDebugInfo(IMAGE_DEBUG_DIRECTORY *pIDD, + DWORD cData, + DWORD *pcData, + BYTE data[]); + + COM_METHOD RemapToken(mdToken oldToken, + mdToken newToken); + + COM_METHOD DefineConstant(const WCHAR __RPC_FAR *name, + VARIANT value, + ULONG32 cSig, + unsigned char __RPC_FAR signature[ ]); + + COM_METHOD Abort(void); + + //----------------------------------------------------------- + // ISymUnmanagedWriter2 + //----------------------------------------------------------- + COM_METHOD DefineLocalVariable2(const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, + ULONG32 addr2, + ULONG32 addr3, + ULONG32 startOffset, + ULONG32 endOffset); + + COM_METHOD DefineGlobalVariable2(const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, + ULONG32 addr2, + ULONG32 addr3); + + COM_METHOD DefineConstant2(const WCHAR *name, + VARIANT value, + mdSignature sigToken); + + //----------------------------------------------------------- + // ISymUnmanagedWriter3 + //----------------------------------------------------------- + + COM_METHOD OpenMethod2(mdMethodDef method, + ULONG32 isect, + ULONG32 offset); + + COM_METHOD Commit(); + + //----------------------------------------------------------- + // Methods not exposed via a COM interface. + //----------------------------------------------------------- + + static HRESULT NewSymWriter(REFIID clsid, void** ppObj); + HRESULT SetDocumentCheckSum( + UINT32 DocumentEntry, + GUID AlgorithmId, + DWORD CheckSumSize, + BYTE* pCheckSum); + HRESULT SetDocumentSrc(UINT32 DocumentEntry, + DWORD SourceSize, + BYTE* pSource); + + COM_METHOD Write(void *pData, DWORD SizeOfData); + COM_METHOD WriteStringPool(); + COM_METHOD WritePDB(); + + COM_METHOD Initialize(const WCHAR *szFilename, IStream *pIStream); + + void SetFullPathName(const WCHAR *szFullPathName) + { + + } + +private: + // Helper API for CloserScope + COM_METHOD CloseScopeInternal(ULONG32 endOffset); + HRESULT GetOrCreateDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter + ); + HRESULT CreateDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter + ); + + + //----------------------------------------------------------- + // Data members + //----------------------------------------------------------- +private: + UINT32 m_refCount; // AddRef/Release + + mdMethodDef m_openMethodToken; + mdMethodDef m_LargestMethodToken; + SymMethodInfo * m_pmethod; + + // index of currently open scope + UINT32 m_currentScope; + + // special scope "index" meaning there is no such scope + static const UINT32 k_noScope = (UINT32)-1; + + // maximum scope end offset seen so far in this method + ULONG32 m_maxScopeEnd; + + MethodInfo m_MethodInfo; + ArrayStorage<SymMap> m_MethodMap; // Methods information + + // Symbol File Name + WCHAR m_szPath[ _MAX_PATH ]; + // File Handle + HANDLE m_hFile; + // Stream we're storing into if asked to. + IStream* m_pIStream; + + // StringPool we use to store the string into + StgStringPool *m_pStringPool; + + // Project level symbol information + PDBInfo ModuleLevelInfo; + + bool m_closed; // Have we closed the file yet? + bool m_sortLines; // sort the line for current method + bool m_sortMethodEntries; // Sort the method entries + + +}; + +/* ------------------------------------------------------------------------- * + * SymDocumentWriter class + * ------------------------------------------------------------------------- */ + +class SymDocumentWriter : public ISymUnmanagedDocumentWriter +{ +public: + SymDocumentWriter(UINT32 DocumentEntry, + SymWriter *pEmitter); + + virtual ~SymDocumentWriter(); + + //----------------------------------------------------------- + // IUnknown support + //----------------------------------------------------------- + ULONG STDMETHODCALLTYPE AddRef() + { + return (InterlockedIncrement((LONG *) &m_refCount)); + } + + ULONG STDMETHODCALLTYPE Release() + { + // Note that this must be thread-safe - it may be invoked on the finalizer thread + LONG refCount = InterlockedDecrement((LONG *) &m_refCount); + if (refCount == 0) + DELETE(this); + + return (refCount); + } + COM_METHOD QueryInterface(REFIID riid, void **ppInterface); + + //----------------------------------------------------------- + // ISymUnmanagedDocumentWriter + //----------------------------------------------------------- + COM_METHOD SetSource(ULONG32 sourceSize, BYTE source[]); + COM_METHOD SetCheckSum(GUID algorithmId, + ULONG32 checkSumSize, BYTE checkSum[]); + + //----------------------------------------------------------- + // Methods not exposed via a COM interface. + //----------------------------------------------------------- + // + // Commit the doc to the pdb + // + UINT32 GetDocumentEntry() + { + return m_DocumentEntry; + } + + //----------------------------------------------------------- + // Data members + //----------------------------------------------------------- +private: + UINT32 m_refCount; // AddRef/Release + UINT32 m_DocumentEntry; // Entry into the documents array + SymWriter *m_pEmitter; // Associated SymWriter +}; + +// Debug Info +struct RSDSI // RSDS debug info +{ + DWORD dwSig; // RSDS + GUID guidSig; + DWORD age; + char szPDB[0]; // followed by a zero-terminated UTF8 file name +}; + +#endif /* SYMWRITE_H_ */ diff --git a/src/debug/ildbsymlib/umisc.h b/src/debug/ildbsymlib/umisc.h new file mode 100644 index 0000000000..fda40474ff --- /dev/null +++ b/src/debug/ildbsymlib/umisc.h @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: umisc.h +// + +// =========================================================================== + + +// Abstract: +// +// A collection of utility macros. +// + +#ifndef UMISC_H +#define UMISC_H + +#define COM_METHOD HRESULT STDMETHODCALLTYPE + +inline HRESULT HrFromWin32(DWORD dwWin32Error) +{ + return HRESULT_FROM_WIN32(dwWin32Error); +} + +// Some helper #def's to safely Release, close & delete Objects under +// failure conditions + +#define RELEASE(x) \ + do \ + { \ + if (x) \ + { \ + IUnknown *punk = x; \ + x = NULL; \ + punk->Release(); \ + } \ + } while (0) + + +#include "debugmacros.h" +// +// Good for verifying params withing range. +// +#define IfFalseGo(expr, HR) IfFailGo((expr) ? S_OK : (HR)) + +// ---------------------------------------------------------------------------- +// Validation macros +// Note that the Win32 APIs like IsBadReadPtr are banned +// +#define IsValidReadPtr(ptr, type) ((ptr)!=NULL) + +#define IsValidWritePtr(ptr, type) ((ptr)!=NULL) + +#define IsValidReadBufferPtr(ptr, type, len) ((ptr)!=NULL) + +#define IsValidWriteBufferPtr(ptr, type, len) ((ptr)!=NULL) + +#define IsValidInterfacePtr(ptr, type) ((ptr)!=NULL) + +#define IsValidCodePtr(ptr) ((ptr)!=NULL) + +#define IsValidStringPtr(ptr) ((ptr)!=NULL) + +#define IsValidIID(iid) TRUE + +#define IsValidCLSID(clsid) TRUE + +#endif |