/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   John Bandhauer <jband@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/* Win32 x86/x64 code for stack walking, symbol resolution, and function hooking */

#ifndef __nsDebugHelpWin32_h__
#define __nsDebugHelpWin32_h__

#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
  #ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
  #endif
  #include <windows.h>
  #include <imagehlp.h>
  #include <crtdbg.h>
#else
  #error "nsDebugHelpWin32.h should only be included in Win32 x86/x64 builds"
#endif

// XXX temporary hack...
//#include "hacky_defines.h"


/***************************************************************************/
// useful macros...

#define DHW_DECLARE_FUN_TYPE(retval_, conv_, typename_, args_) \
    typedef retval_ ( conv_ * typename_ ) args_ ;

#ifdef DHW_IMPLEMENT_GLOBALS
#define DHW_DECLARE_FUN_GLOBAL(typename_, name_) typename_ dhw##name_
#else
#define DHW_DECLARE_FUN_GLOBAL(typename_, name_) extern typename_ dhw##name_
#endif

#define DHW_DECLARE_FUN_PROTO(retval_, conv_, name_, args_) \
    retval_ conv_ name_ args_

#define DHW_DECLARE_FUN_STATIC_PROTO(retval_, name_, args_) \
    static retval_ conv_ name_ args_

#define DHW_DECLARE_FUN_TYPE_AND_PROTO(name_, retval_, conv_, typename_, args_) \
    DHW_DECLARE_FUN_TYPE(retval_, conv_, typename_, args_); \
    DHW_DECLARE_FUN_PROTO(retval_, conv_, name_, args_)

#define DHW_DECLARE_FUN_TYPE_AND_STATIC_PROTO(name_, retval_, conv_, typename_, args_) \
    DHW_DECLARE_FUN_TYPE(retval_, conv_, typename_, args_); \
    DHW_DECLARE_FUN_STATIC_PROTO(retval_, conv_, name_, args_)

#define DHW_DECLARE_FUN_TYPE_AND_GLOBAL(typename_, name_, retval_, conv_, args_) \
    DHW_DECLARE_FUN_TYPE(retval_, conv_, typename_, args_); \
    DHW_DECLARE_FUN_GLOBAL(typename_, name_)


/**********************************************************/
// These are used to get 'original' function addresses from DHWImportHooker.

#define DHW_DECLARE_ORIGINAL(type_, name_, hooker_) \
    type_ name_ = (type_) hooker_ . GetOriginalFunction()

#define DHW_DECLARE_ORIGINAL_PTR(type_, name_, hooker_) \
    type_ name_ = (type_) hooker_ -> GetOriginalFunction()

#define DHW_ORIGINAL(type_, hooker_) \
    ((type_) hooker_ . GetOriginalFunction())

#define DHW_ORIGINAL_PTR(type_, hooker_) \
    ((type_) hooker_ -> GetOriginalFunction())

/***************************************************************************/
// Global declarations of entry points into ImgHelp functions
#if 0
DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMINITIALIZEPROC, SymInitialize, \
                                BOOL, __stdcall, (HANDLE, LPSTR, BOOL));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMSETOPTIONS, SymSetOptions, \
                                DWORD, __stdcall, (DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMGETOPTIONS, SymGetOptions, \
                                DWORD, __stdcall, ());

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMGETMODULEINFO, SymGetModuleInfo, \
                                BOOL, __stdcall, (HANDLE, DWORD, PIMAGEHLP_MODULE));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMGETSYMFROMADDRPROC, SymGetSymFromAddr, \
                                BOOL, __stdcall, (HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL));

#endif

#ifndef _WIN64
DHW_DECLARE_FUN_TYPE_AND_GLOBAL(ENUMERATELOADEDMODULES, EnumerateLoadedModules, \
                                BOOL, __stdcall, (HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID));
#else
DHW_DECLARE_FUN_TYPE_AND_GLOBAL(ENUMERATELOADEDMODULES64, EnumerateLoadedModules64, \
                                BOOL, __stdcall, (HANDLE, PENUMLOADED_MODULES_CALLBACK64, PVOID));
#endif

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(IMAGEDIRECTORYENTRYTODATA, ImageDirectoryEntryToData, \
                                PVOID, __stdcall, (PVOID, BOOL, USHORT, PULONG));

// We aren't using any of the below yet...

/*
DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMCLEANUPPROC, SymCleanup, \
                                BOOL, __stdcall, (HANDLE));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(STACKWALKPROC, StackWalk, \
                                BOOL, 
                                __stdcall, 
                                (DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, \
                                       PREAD_PROCESS_MEMORY_ROUTINE, \
                                       PFUNCTION_TABLE_ACCESS_ROUTINE, \
                                       PGET_MODULE_BASE_ROUTINE, \
                                       PTRANSLATE_ADDRESS_ROUTINE));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMFUNCTIONTABLEACCESSPROC, SymFunctionTableAccess, \
                                LPVOID, __stdcall, (HANDLE, DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMGETMODULEBASEPROC, SymGetModuleBase, \
                                DWORD, __stdcall, (HANDLE, DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMLOADMODULE, SymLoadModule, \
                                DWORD, __stdcall, (HANDLE, HANDLE, PSTR, PSTR, DWORD, DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(UNDECORATESYMBOLNAME, _UnDecorateSymbolName, \
                                DWORD, __stdcall, (LPCSTR, LPSTR, DWORD, DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMUNDNAME, SymUnDName, \
                                BOOL, __stdcall, (PIMAGEHLP_SYMBOL, LPSTR, DWORD));

DHW_DECLARE_FUN_TYPE_AND_GLOBAL(SYMGETLINEFROMADDR, SymGetLineFromAddr, \
                                BOOL, __stdcall, (HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE));

*/

/***************************************************************************/

extern PRBool
dhwEnsureImageHlpInitialized();

/***************************************************************************/

DHW_DECLARE_FUN_TYPE(FARPROC, __stdcall, GETPROCADDRESS, (HMODULE, PCSTR));

class DHWImportHooker
{
public: 

    DHWImportHooker(const char* aModuleName,
                    const char* aFunctionName,
                    PROC aHook,
                    PRBool aExcludeOurModule = PR_FALSE);
                    
    ~DHWImportHooker();

    PROC GetOriginalFunction()  {return mOriginal;}

    PRBool PatchAllModules();
    PRBool PatchOneModule(HMODULE aModule, const char* name);
    static PRBool ModuleLoaded(HMODULE aModule, DWORD flags);


    // I think that these should be made not static members, but allocated
    // things created in an explicit static 'init' method and cleaned up in
    // an explicit static 'finish' method. This would allow the application
    // to have proper lifetime control over all the hooks.

    static DHWImportHooker &getLoadLibraryWHooker();
    static DHWImportHooker &getLoadLibraryExWHooker();
    static DHWImportHooker &getLoadLibraryAHooker();
    static DHWImportHooker &getLoadLibraryExAHooker();
    static DHWImportHooker &getGetProcAddressHooker();

    static HMODULE WINAPI LoadLibraryA(PCSTR path);

private:
    DHWImportHooker* mNext;
    const char*      mModuleName;
    const char*      mFunctionName;
    PROC             mOriginal;
    PROC             mHook;
    HMODULE          mIgnoreModule;
    PRBool           mHooking;

private:
    static PRLock* gLock;
    static DHWImportHooker* gHooks;
    static GETPROCADDRESS gRealGetProcAddress;
    
    static HMODULE WINAPI LoadLibraryW(PCWSTR path);
    static HMODULE WINAPI LoadLibraryExW(PCWSTR path, HANDLE file, DWORD flags);
    static HMODULE WINAPI LoadLibraryExA(PCSTR path, HANDLE file, DWORD flags);

    static FARPROC WINAPI GetProcAddress(HMODULE aModule, PCSTR aFunctionName);
};

/***************************************************************************/
// This supports the _CrtSetAllocHook based hooking.
// This system sucks because you don't get to see the allocated pointer. I
// don't think it appropriate for nsTraceMalloc, but is useful as a means to make
// malloc fail for testing purposes.
#if 0 //comment out this stuff. not necessary

class DHWAllocationSizeDebugHook
{
public:
    virtual PRBool AllocHook(size_t size) = 0;
    virtual PRBool ReallocHook(size_t size, size_t sizeOld) = 0;
    virtual PRBool FreeHook(size_t size) = 0;
};

extern PRBool dhwSetAllocationSizeDebugHook(DHWAllocationSizeDebugHook* hook);
extern PRBool dhwClearAllocationSizeDebugHook();

/***************************************************************************/
#endif //0

#endif /* __nsDebugHelpWin32_h__ */
