/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the w64 mingw-runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */

//#include <windows.h>
#include <stdarg.h>
#include <windef.h>
#include <winbase.h>
#include <string.h>

#if defined (_WIN64) && defined (__ia64__)
#error FIXME: Unsupported __ImageBase implementation.
#else
#ifdef __GNUC__
/* Hack, for bug in ld.  Will be removed soon.  */
#define __ImageBase __MINGW_LSYMBOL(_image_base__)
#endif
/* This symbol is defined by the linker.  */
extern IMAGE_DOS_HEADER __ImageBase;
#endif

WINBOOL _ValidateImageBase (PBYTE);

WINBOOL
_ValidateImageBase (PBYTE pImageBase)
{
  PIMAGE_DOS_HEADER pDOSHeader;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_OPTIONAL_HEADER pOptHeader;

  pDOSHeader = (PIMAGE_DOS_HEADER) pImageBase;
  if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
    return FALSE;
  pNTHeader = (PIMAGE_NT_HEADERS) ((PBYTE) pDOSHeader + pDOSHeader->e_lfanew);
  if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
    return FALSE;
  pOptHeader = (PIMAGE_OPTIONAL_HEADER) &pNTHeader->OptionalHeader;
  if (pOptHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
    return FALSE;
  return TRUE;
}

PIMAGE_SECTION_HEADER _FindPESection (PBYTE, DWORD_PTR);

PIMAGE_SECTION_HEADER
_FindPESection (PBYTE pImageBase, DWORD_PTR rva)
{
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_SECTION_HEADER pSection;
  unsigned int iSection;

  pNTHeader = (PIMAGE_NT_HEADERS) (pImageBase + ((PIMAGE_DOS_HEADER) pImageBase)->e_lfanew);

  for (iSection = 0, pSection = IMAGE_FIRST_SECTION (pNTHeader);
    iSection < pNTHeader->FileHeader.NumberOfSections;
    ++iSection,++pSection)
    {
      if (rva >= pSection->VirtualAddress
	  && rva < pSection->VirtualAddress + pSection->Misc.VirtualSize)
	return pSection;
    }
  return NULL;
}

PIMAGE_SECTION_HEADER _FindPESectionByName (const char *);

PIMAGE_SECTION_HEADER
_FindPESectionByName (const char *pName)
{
  PBYTE pImageBase;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_SECTION_HEADER pSection;
  unsigned int iSection;

  /* Long names aren't supported.  */
  if (strlen (pName) > IMAGE_SIZEOF_SHORT_NAME)
    return NULL;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return NULL;

  pNTHeader = (PIMAGE_NT_HEADERS) (pImageBase + ((PIMAGE_DOS_HEADER) pImageBase)->e_lfanew);

  for (iSection = 0, pSection = IMAGE_FIRST_SECTION (pNTHeader);
    iSection < pNTHeader->FileHeader.NumberOfSections;
    ++iSection,++pSection)
    {
      if (!strncmp ((char *) &pSection->Name[0], pName, IMAGE_SIZEOF_SHORT_NAME))
	return pSection;
    }
  return NULL;
}

int __mingw_GetSectionCount (void);
PIMAGE_SECTION_HEADER __mingw_GetSectionForAddress (LPVOID p);

PIMAGE_SECTION_HEADER
__mingw_GetSectionForAddress (LPVOID p)
{
  PBYTE pImageBase;
  DWORD_PTR rva;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return NULL;

  rva = (DWORD_PTR) (((PBYTE) p) - pImageBase);
  return _FindPESection (pImageBase, rva);
}

int
__mingw_GetSectionCount (void)
{
  PBYTE pImageBase;
  PIMAGE_NT_HEADERS pNTHeader;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return 0;

  pNTHeader = (PIMAGE_NT_HEADERS) (pImageBase + ((PIMAGE_DOS_HEADER) pImageBase)->e_lfanew);

  return (int) pNTHeader->FileHeader.NumberOfSections;
}


PIMAGE_SECTION_HEADER _FindPESectionExec (size_t);

PIMAGE_SECTION_HEADER
_FindPESectionExec (size_t eNo)
{
  PBYTE pImageBase;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_SECTION_HEADER pSection;
  unsigned int iSection;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return NULL;

  pNTHeader = (PIMAGE_NT_HEADERS) (pImageBase + ((PIMAGE_DOS_HEADER) pImageBase)->e_lfanew);

  for (iSection = 0, pSection = IMAGE_FIRST_SECTION (pNTHeader);
    iSection < pNTHeader->FileHeader.NumberOfSections;
    ++iSection,++pSection)
    {
      if ((pSection->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0)
      {
	if (!eNo)
	  return pSection;
	--eNo;
      }
    }
  return NULL;
}

PBYTE _GetPEImageBase (void);

PBYTE
_GetPEImageBase (void)
{
  PBYTE pImageBase;
  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return NULL;
  return pImageBase;
}

WINBOOL _IsNonwritableInCurrentImage (PBYTE);

WINBOOL
_IsNonwritableInCurrentImage (PBYTE pTarget)
{
  PBYTE pImageBase;
  DWORD_PTR rvaTarget;
  PIMAGE_SECTION_HEADER pSection;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return FALSE;
  rvaTarget = pTarget - pImageBase;
  pSection = _FindPESection (pImageBase, rvaTarget);
  if (pSection == NULL)
    return FALSE;
  return (pSection->Characteristics & IMAGE_SCN_MEM_WRITE) == 0;
}

const char *
__mingw_enum_import_library_names (int);

const char *
__mingw_enum_import_library_names (int i)
{
  PBYTE pImageBase;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_IMPORT_DESCRIPTOR importDesc;
  PIMAGE_SECTION_HEADER pSection;
  DWORD importsStartRVA;

  pImageBase = (PBYTE) &__ImageBase;
  if (! _ValidateImageBase (pImageBase))
    return NULL;

  pNTHeader = (PIMAGE_NT_HEADERS) (pImageBase + ((PIMAGE_DOS_HEADER) pImageBase)->e_lfanew);

  importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  if (!importsStartRVA)
    return NULL;

  pSection = _FindPESection (pImageBase, importsStartRVA);
  if (!pSection)
      return NULL;

  importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (pImageBase + importsStartRVA);
  if (!importDesc)
    return NULL;

  for (;;)
    {
      if (importDesc->TimeDateStamp == 0 && importDesc->Name == 0)
        break;

      if (i <= 0)
       return (char *) (pImageBase + importDesc->Name);
      --i;
      importDesc++;
    }

  return NULL;
}

HMODULE __mingw_get_msvcrt_handle(void);
HMODULE __mingw_get_msvcrt_handle(void)
{
    static HANDLE msvcrt_handle;

    if(!msvcrt_handle) {
        const char *lib_name;
        int i = 0;

        while ((lib_name = __mingw_enum_import_library_names (i++))) {
            if((lib_name[0] == 'm' || lib_name[0] == 'M')
               && (lib_name[1] == 's' || lib_name[1] == 'S')
               && (lib_name[2] == 'v' || lib_name[2] == 'V')
               && (lib_name[3] == 'c' || lib_name[3] == 'C')
               && (lib_name[4] == 'r' || lib_name[4] == 'R')
               && (lib_name[5] == 't' || lib_name[5] == 'T' || ('0' <= lib_name[5] && lib_name[5] <= '9')))
                break;
        }

       if(lib_name)
            msvcrt_handle = GetModuleHandleA(lib_name);
        if(!msvcrt_handle)
            msvcrt_handle = LoadLibraryW(L"msvcrt.dll");
    }

    return msvcrt_handle;
}